我一直在学习渲染,在学习光线追踪之后,我想探索加速结构并编写自己的 BVH 实现。我正在通过在一个方向内发送阴影光线并检查它是否击中物体(三角形)来测试它。但是,我无法让射线与任何三角形相交(它确实成功地与 AABB 框相交)。我将代码从计算着色器复制到 cpp 代码中,它似乎成功与三角形相交。
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;
}
}
}
}
这就是追迹射线函数的样子。我不确定我是否以正确的方式遍历它,但这就是我阅读如何做到这一点的方式。我通过递归在CPU上构建了结构,三角形也在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);
我首先认为问题出在着色器存储缓冲区内的数据对齐,但是修复此问题后似乎没有任何变化。然后我认为我计算世界空间坐标的计算不正确
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;
}
不过这似乎也不错 。我还仔细检查了 AABB 交集和 Intersect Triangle 函数的实现,但找不到任何东西。我还尝试使用 Nsight 或 Renderdoc 等工具进行调试(当它不崩溃时),除了存储对齐之外,它们似乎没有为我的问题提供任何有用的信息。
问题是迭代基元,似乎如果 glsl 知道会有一个无效索引,它就会完全跳过该部分,我添加了一个变量来跟踪叶节点中基元的数量(我应该这样做)以前)这似乎解决了问题。