3

NOTE: THIS QUESTION HAS BEEN DRASTICALLY EDITED FROM ITS ORIGINAL FORM

I am attempting to create a logarithmic raytracer by implementing an oct tree data structure combined with voxelization to achieve fast ray tracing.

Currently I am having issues with the ray collision detection.

The expected output should be the voxelized stanford dragon with its normal map.

Currrently the issue is that some regions are transparent:

The full dragon:

Full Dragon

Transparent regions:

enter image description here

From these images it should be clear that the geometry is correct, but the collision checks are wrong.

There are 2 fragment shaders involved in this process:

The voxelizer fragment shader:

#version 430

in vec3 f_pos;
in vec3 f_norm;
in vec2 f_uv;

out vec4 f_color;
struct Voxel
{
    vec4 position;
    vec4 normal;
    vec4 color;
};

struct Node
{
    int children[8];
};

layout(std430, binding = 0) buffer voxel_buffer
{
    Voxel voxels[];
};
layout(std430, binding = 1) buffer buffer_index
{
    uint index;
};
layout(std430, binding = 2) buffer tree_buffer
{
    Node tree[];
};
layout(std430, binding = 3) buffer tree_index
{
    uint t_index;
};

out vec4 fragment_color;

uniform int voxel_resolution;
uniform int cube_dim;

int getVIndex(vec3 position, int level)
{
    float size = cube_dim / pow(2,level);

    int bit2 = int(position.x > size);
    int bit1 = int(position.y > size);
    int bit0 = int(position.z > size);

    return 4*bit2 + 2*bit1 + bit0;
}

void main()
{

    uint m_index = atomicAdd(index, 1);

    voxels[m_index].position = vec4(f_pos*cube_dim,1);
    voxels[m_index].normal = vec4(f_norm,1);
    voxels[m_index].color = vec4(f_norm,1);

    int max_level = int(log2(voxel_resolution));
    int node = 0;
    vec3 corner = vec3(-cube_dim);
    int child;

    for(int level=0; level<max_level-1; level++)
    {
        float size = cube_dim / pow(2,level);
        vec3 corners[] =
            {corner,                    corner+vec3(0,0,size),
            corner+vec3(0,size,0),      corner+vec3(0,size,size),
            corner+vec3(size,0,0),      corner+vec3(size,0,size),
            corner+vec3(size,size,0),   corner+vec3(size,size,size)};

        vec3 offsetPos = (vec3(voxels[m_index].position));
       child = getVIndex(offsetPos-corner, level);

        int mrun = 500;
        while ((tree[node].children[child] <= 0) && (mrun > 0)){
            mrun--;
            if( (atomicCompSwap( tree[node].children[child] , 0 , -1) == 0 ))
            {
                tree[node].children[child] = int(atomicAdd(t_index, 1));
            }
        }

        if(mrun < 1)
            discard;

        if(level==max_level-2)
            break;

        node = tree[node].children[child];

        corner = corners[child];
    }

    tree[node].children[child] = int(m_index);

}

I understand the logic may not be clear so let me explain:

We start with a 3D psoition voxels[m_index].position = vec4(f_pos*cube_dim,1); And we know there is a cube with dimensions (-cube_dim,-cube_dim,-cube_dim) to (cube_dim,cube_dim,cube_dim) So a cube whose diagonals intersect at the origin with side length of 2*cube_dim. That has been divided into multiple little cubes with side length 2*cube_dim/voxel_resolution. Basically this is just a cube subdivided n times to make a cartesian grid.

Using this coordinate we start at the big cube, subdividing it into 8 equal sized subsapaces and detecting which of these subspaces contians the coordinate.

We do this until we find the smallest box containing the position.

The raytracer

 #version 430

in vec2 f_coord;

out vec4 fragment_color;

struct Voxel
{
    vec4 position;
    vec4 normal;
    vec4 color;
};

struct Node
{
    int children[8];
};

layout(std430, binding = 0) buffer voxel_buffer
{
    Voxel voxels[];
};
layout(std430, binding = 1) buffer buffer_index
{
    uint index;
};
layout(std430, binding = 2) buffer tree_buffer
{
    Node tree[];
};
layout(std430, binding = 3) buffer tree_index
{
    uint t_index;
};

uniform vec3 camera_pos;
uniform float aspect_ratio;
uniform float cube_dim;
uniform int voxel_resolution;

float planeIntersection(vec3 origin, vec3 ray, vec3 pNormal, vec3 pPoint)
{
    pNormal = normalize(pNormal);
    return (dot(pPoint,pNormal)-dot(pNormal,origin))/dot(ray,pNormal);
}

#define EPSILON 0.001
bool inBoxBounds(vec3 corner, float size, vec3 position)
{
    bool inside = true;
    position-=corner;

    for(int i=0; i<3; i++)
    {
        inside = inside && (position[i] > -EPSILON);
        inside = inside && (position[i] < size+EPSILON);
    }

    return inside;
}


float boxIntersection(vec3 origin, vec3 dir, vec3 corner0, float size)
{
    dir = normalize(dir);
    vec3 corner1 = corner0 + vec3(size,size,size);

    vec3 normals[6] =
    { vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1), vec3(1,0,0), vec3(0,1,0), vec3(0,0,1) };

    float coeffs[6];

    for(uint i=0; i<3; i++)
        coeffs[i] = planeIntersection(origin, dir, normals[i], corner0);
    for(uint i=3; i<6; i++)
        coeffs[i] = planeIntersection(origin, dir, normals[i], corner1);

    float t = 1.f/0.f;

    for(uint i=0; i<6; i++){
        coeffs[i] = coeffs[i] < 0 ? 1.f/0.f : coeffs[i];
        t = inBoxBounds(corner0,size,origin+dir*coeffs[i]) ? min(coeffs[i],t) : t;
    }

    return t;
}

void sort(float elements[8], int indices[8], vec3 vectors[8])
{
    for(uint i=0; i<8; i++)
    {
        for(uint j=i; j<8; j++)
        {
            if(elements[j] < elements[i])
            {
                float swap = elements[i];
                elements[i] = elements[j];
                elements[j] = swap;

                int iSwap = indices[i];
                indices[i] = indices[j];
                indices[j] = iSwap;

                vec3 vSwap = vectors[i];
                vectors[i] = vectors[j];
                vectors[j] = vSwap;
            }
        }
    }
}

int getVIndex(vec3 position, int level)
{
    float size = cube_dim / pow(2,level);

    int bit2 = int(position.x > size);
    int bit1 = int(position.y > size);
    int bit0 = int(position.z > size);

    return 4*bit2 + 2*bit1 + bit0;
}

#define MAX_TREE_HEIGHT 11
int nodes[8*MAX_TREE_HEIGHT];
int levels[8*MAX_TREE_HEIGHT];
vec3 positions[8*MAX_TREE_HEIGHT];
int sp=0;

void push(int node, int level, vec3 corner)
{
    nodes[sp] = node;
    levels[sp] = level;
    positions[sp] = corner;
    sp++;
}
void main()
{
    vec3 r = vec3(f_coord.x, f_coord.y, 1.f/tan(radians(40)));
    r.y/=aspect_ratio;
    vec3 dir = r;
    r += vec3(0,0,-1.f/tan(radians(40))) + camera_pos;

    fragment_color = vec4(0);
    //int level = 0;
    int max_level = int(log2(voxel_resolution));
    push(0,0,vec3(-cube_dim));
    float tc = 1.f;
    int level=0;
    int node=0;
    do
    {
        sp--;
        node = nodes[sp];
        level = levels[sp];
        vec3 corner = positions[sp];

        float size = cube_dim / pow(2,level);
        vec3 corners[] =
            {corner,                        corner+vec3(0,0,size),
            corner+vec3(0, size,0),         corner+vec3(0,size,size),
            corner+vec3(size,0,0),          corner+vec3(size,0,size),
            corner+vec3(size,size,0),       corner+vec3(size,size,size)};

        float t = boxIntersection(r, dir, corner, size*2);
        if(!isinf(t))
            tc *= 0.9f;

        float coeffs[8];
        for(int child=0; child<8; child++)
        {
            if(tree[node].children[child]>0)
                coeffs[child] = boxIntersection(r, dir, corners[child], size);
            else
                coeffs[child] = 1.f/0.f;
        }
        int indices[8] = {0,1,2,3,4,5,6,7};
        sort(coeffs, indices, corners);

        for(uint i=7; i>=0; i--)
        {
            if(!isinf(coeffs[i]))
            {
                push(tree[node].children[indices[i]],
                    level+1, corners[i]);
            }
        }
    }while(level < (max_level-1) && sp>0);

    if(level==max_level-1)
    {
        fragment_color = abs(voxels[node].normal);
    }

    else
    {
        fragment_color=vec4(tc);
    }

}
}

In here, we start at the biggest cube, testing intersections with each set of 8 children (the 8 cubes resulting from subdividing a cube). Each time we successfully detect a collision, we move down the tree, until we reach the lowest level which describes the actual geometry and we color the scene based on that.

Debugging and Problem

The important part is that there are 2 buffers, one to store the tree except the leafs, and one to store the leafs.

So in both the voxelization and the ray tracing, the last layer needs to be treated differently.

The issues I have noticed about the transparency are as follows:

  • It happens only on planes aligned with the cartesian grid

  • It seems it happens when the ray moves in a negative direction (down or to the left). (At least that's my imperssion but it's not 100%
    certain)

I am not sure what I am doing wrong.

EDIT:

The original issue seems to have been fixed, however the raytracer is still bugged. I have edited the question to refelct the current state of the problem.

Makogan
  • 8,208
  • 7
  • 44
  • 112
  • 3
    Please if you downvote tell me what the problem is so that I can edit my post accordingly – Makogan Jun 08 '18 at 19:59
  • At level 0 your child cubes are half the size of `cube_dim`, so should the calculation be: `float size = cube_dim / pow(2,level+1);`? – samgak Jun 09 '18 at 07:08
  • There was a change and variable names are a bit misleading currently. The cube has dimensions (-cube_dim, cube dim) (negative to possitive), meaning that a cube with side length of cube_dim is actually half the size of the initial cube (I apologize for the confusing variable name) – Makogan Jun 09 '18 at 07:39
  • @samgak, also please, refer to the latest changes in teh question, as the problem is now quite different – Makogan Jun 09 '18 at 08:59
  • `coeffs[child] = 1.f/0.f` is `inf`. Thus, the `sort` function may fail due to it doesn't handle `inf`s. – Ripi2 Jun 09 '18 at 12:23

1 Answers1

0

The error comes from the sorting function as someone in the comments mentioned although not for the same reasons.

What has happened is that, I thought the sort function would modify the arrays passed to it, but it seems to be copying the data, so it does not return anything.

In other words:

void sort(float elements[8], int indices[8], vec3 vectors[8])
{
    for(uint i=0; i<8; i++)
    {
        for(uint j=i; j<8; j++)
        {
            if((elements[j] < elements[i]))
            {
                float swap = elements[i];
                elements[i] = elements[j];
                elements[j] = swap;

                int iSwap = indices[i];
                indices[i] = indices[j];
                indices[j] = iSwap;

                vec3 vSwap = vectors[i];
                vectors[i] = vectors[j];
                vectors[j] = vSwap;
            }
        }
    }
}

Does not return the correct values inside of elements, indices and vectors, so calling this function does nothing but waste computation cycles.

Makogan
  • 8,208
  • 7
  • 44
  • 112
  • 1
    Then add `inout` before each parameter type: `void sort(inout float elements[8], inout int indices[8], inout vec3 vectors[8])` – Ripi2 Jun 09 '18 at 17:39