BVH structure not working in shaders but work in cpp code

49 views Asked by At

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.

1

There are 1 answers

0
TooGood On BEST ANSWER

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.