2

我已经成功实施了 PBRT 中描述的 BVH。这个虽然有一个稍微大的问题 - 遍历查看与射线相交的所有节点,这是错误的(就性能而言)。

所以我最终优化了光线遍历,目前我使用 Aila & Laine 实现的版本,他们的“Understanding theefficient of ray traveral on GPU”。首先,这里是代码:

INLINE bool BVH::Traverse(TriangleWoop* prims, Ray* ray, IntersectResult* result)
{
unsigned int todo[32];
unsigned int todoOffset = 0;
unsigned int nodeNum = 0;

bool hit = false;
IntersectResult tmp = IntersectResult();
*(int*)&tmp.data.w = -1;
float tmin = 2e30f;

float4 origin = ray->origin;
float4 direction = ray->direction;
float4 invdir = rcp(direction);

float tmpx = 0.0f, tmpy = 0.0f;

while(true)
{
    while(this->nodes[nodeNum].prim_count == 0)
    {
        tmpx += 0.01f;
        tmpy += 0.001f;

        float4 c0v1 = (this->nodes[nodeNum + 1].bounds.minPt - origin) * invdir;
        float4 c0v2 = (this->nodes[nodeNum + 1].bounds.maxPt - origin) * invdir;
        float4 c1v1 = (this->nodes[this->nodes[nodeNum].above_child].bounds.minPt - origin) * invdir;
        float4 c1v2 = (this->nodes[this->nodes[nodeNum].above_child].bounds.maxPt - origin) * invdir;
        float4 c0n = f4min(c0v1, c0v2);
        float4 c0f = f4max(c0v1, c0v2);
        float4 c1n = f4min(c1v1, c1v2);
        float4 c1f = f4max(c1v1, c1v2);

        float n0 = max(c0n.x, max(c0n.y, c0n.z));
        float f0 = min(c0f.x, min(c0f.y, c0f.z));
        float n1 = max(c1n.x, max(c1n.y, c1n.z));
        float f1 = min(c1f.x, min(c1f.y, c1f.z));

        bool child0 = (f0 > 0.0f) && (n0 < f0);
        bool child1 = (f1 > 0.0f) && (n1 < f1);

        child0 &= (n0 < tmin);
        child1 &= (n1 < tmin);

        unsigned int nodeAddr = this->nodes[nodeNum].above_child;
        nodeNum = nodeNum + 1;

        if(child0 != child1)
        {
            if(child1)
            {
                nodeNum = nodeAddr;
            }
        }
        else
        {
            if(!child0)
            {
                if(todoOffset == 0)
                {
                    goto result;
                }

                nodeNum = todo[--todoOffset];
            }
            else
            {
                if(n1 < n0)
                {
                    swap(nodeNum, nodeAddr);
                }

                todo[todoOffset++] = nodeAddr;
            }
        }
    }

    if(this->nodes[nodeNum].prim_count > 0)
    {
        for(unsigned int i = this->nodes[nodeNum].prim_offset; i < this->nodes[nodeNum].prim_offset + this->nodes[nodeNum].prim_count; i++)
        {
            const TriangleWoop* tri = &prims[this->indexes[i]];

            if(IntersectRayTriangleWoop(ray, tri, &tmp))
            {
                if(tmp.data.z > 0.0f && tmp.data.z < result->data.z)
                {
                    tmin = tmp.data.z;
                    result->data.z = tmp.data.z;
                    result->data.x = tmp.data.x;
                    result->data.y = tmp.data.y;
                    *(int*)&result->data.w = this->indexes[i];
                    hit = true;
                }
            }
        }
    }

    if(todoOffset == 0)
    {
        goto result;
    }

    nodeNum = todo[--todoOffset];
}

result:
result->data.x = tmpx;
result->data.y = tmpy;

return hit;
}

从技术上讲,它只是一个标准的 while-while 堆栈 ray-bvh 遍历。现在到主要问题,看下一张图片(从外面查看 sponza),颜色可以看到 BVH 中的节点已被访问过多少(全红色 = 100,全黄色 = 1100): Sponza BVH

下一张图片显示了类似的情况: Sponza BVH

正如你所看到的,这是一个问题——它只需要遍历比预期更多的节点。有人能看出我的代码有问题吗?欢迎任何建议,因为我已经坚持了几天并且想不出一些解决方案。

4

0 回答 0