data-structuresgraphics3draytracing

BVH structure not working in shaders but work in cpp code


I have been learning about rendering and after learning about raytracing I wanted to explore acceleration structures and write my own BVH implementation. I am testing it out by dispatching shadow rays within a direction and checking to see if it hits an object or not (triangle). However, I can't get the ray to intersect with any triangle (it does successfully intersect AABB boxes). I copied the code over from the compute shader into cpp code and it seems to successfully intersect with triangles.

void TraceRay(inout RayPayload Payload)
{
     float tMin = FLOAT_MAX;
    
    int Stack[MAX_STACK_SIZE];
    int StackPtr = 0;
       
    Stack[StackPtr++] = 0;

    while(StackPtr > 0)
    {
        int CurrentNode = Stack[--StackPtr];

        if (BVHStructure.nodes[CurrentNode].Primitive[0] != -1) // is a leaf
        {
            for (int i = 0; i < 20; i++)
            {
                if (BVHStructure.nodes[CurrentNode].Primitive[i] == -1)
                    break;
              
                if (IntersectTriangle(Payload, BVHStructure.nodes[CurrentNode].Primitive[i]))
                {
                    Payload.InLight = false;
                    return; 
                }
            }
        }
        else
        {
            int Child1 = BVHStructure.nodes[CurrentNode].LeftNode;
            int Child2 = BVHStructure.nodes[CurrentNode].RightNode;

            if (Child1 != -1)
            {
                float Distance1 = IntersectAABB(Payload, BVHStructure.nodes[Child1].BoundingBox);
                if (Distance1 != FLOAT_MAX)
                    Stack[StackPtr++] = Child1;
                   
            }

            if (Child2 != -1)
            {
                float Distance2 = IntersectAABB(Payload, BVHStructure.nodes[Child2].BoundingBox);
                if (Distance2 != FLOAT_MAX)
                    Stack[StackPtr++] = Child2;

            }
        }
    }
}

this is what the trace ray functions looks like. I am not 100% sure if i am traversing it the correct way but this is how I read how to do it. I built the structure on the CPU through recursion and the triangles are also sorted on the CPU.

void BVHBuilder::BuildBVH(int NodeIndex, int min, int max)
{
    if (max <= min)
        return;

    if (max - min <= 20)
    {
        size_t k = 0;
        BVHNode leafNode{};

        for (size_t i = std::max(min, 0); i < max; i++)
        {
            leafNode.BoundingBox.MinValue = glm::min(ComputeMinimum(m_TriangleData[i]), leafNode.BoundingBox.MinValue);
            leafNode.BoundingBox.MaxValue = glm::max(ComputeMaximum(m_TriangleData[i]), leafNode.BoundingBox.MaxValue);
            leafNode.Primitive[k] = i;
            k++;
        }

        m_Nodes.push_back(leafNode);
        m_Nodes[NodeIndex].RightNode = m_Nodes.size() - 1;
        
        return;
    }

    int median = (min + max) / 2;

    BVHNode LeftChild{};
    LeftChild.BoundingBox.MinValue = ComputeMinimum(m_TriangleData[min]);
    LeftChild.BoundingBox.MaxValue = ComputeMaximum(m_TriangleData[median]);

    m_Nodes.push_back(LeftChild);
    m_Nodes[NodeIndex].LeftNode = m_Nodes.size() - 1;
    

    BVHNode RightChild{};
    RightChild.BoundingBox.MinValue = ComputeMinimum(m_TriangleData[median]);
    RightChild.BoundingBox.MaxValue = ComputeMaximum(m_TriangleData[max]);

    m_Nodes.push_back(RightChild);
    m_Nodes[NodeIndex].RightNode = m_Nodes.size() - 1;

    BuildBVH(m_Nodes[NodeIndex].LeftNode, min, median);
    BuildBVH(m_Nodes[NodeIndex].RightNode, median, max);

I first thought that the issue was with data alignment within the shader storage buffer, however after fixing this it seems that nothing has changed. I then thought that the calculation I was doing to calculate world space coordinates was incorrect

RayPayload DispatchShadowRay(in ivec2 Coordinate, in vec3 LightDirection)
{ 
    RayPayload Payload;
    Payload.Valid = true;
    Payload.InLight = true;

    
    vec2 NormalizedCoord = vec2(Coordinate) / vec2(WINDOW_WIDTH, WINDOW_HEIGHT);
    float DepthValue = texture(DepthBuffer, NormalizedCoord).r;

    if(DepthValue >= 1.0 - EPSILON)
    {
        Payload.Valid = false;
        return Payload;
    }

    DepthValue = DepthValue * 2.0 - 1.0;
    vec4 ViewSpace = InverseProjection * vec4(NormalizedCoord * 2.0 - 1.0, 
                                                       DepthValue, 1.0);

    ViewSpace /= ViewSpace.w;
    vec4 WorldSpace = InverseView * ViewSpace;
    Payload.Origin = WorldSpace.xyz;

    Payload.Direction = normalize(-LightDirection);
    Payload.Origin += Payload.Direction * 0.001;


    TraceRay(Payload);

    return Payload;
}

however this seems to be fine as well . I also double checked the implementations of AABB intersection and Intersect Triangle function and couldn't find anything. I have also tried debugging with tools like Nsight or Renderdoc(when it doesn't crash) and they didn't seem to provide any useful information to my problem apart from alignment of storage.


Solution

  • The problem was iterating through the primitives, it seems that if glsl knows there will be an invalid index it just skips that section entirely, I added a variable to keep track of the number of primitives in a leaf node (which I should have done previously) and that seemed to fix the problem.