我正在使用vulkan计算着色器开发路径跟踪器。我实现了一个代表bounding volume hierachy的树。 BVH的想法是使需要进行射线相交测试的对象数量最少。
#1天真实施
我的第一个实现非常快,它将树遍历到BVH树的单个叶。但是,射线可能与多个叶子相交。然后,这段代码会导致某些三角形无法渲染(尽管应该渲染)。
int box_index = -1;
for (int i = 0; i < boxes_count; i++) {
// the first box has no parent, boxes[0].parent is set to -1
if (boxes[i].parent == box_index) {
if (intersect_box(boxes[i], ray)) {
box_index = i;
}
}
}
if (box_index > -1) {
uint a = boxes[box_index].ids_offset;
uint b = a + boxes[box_index].ids_count;
for (uint j = a; j < b; j++) {
uint triangle_id = triangle_references[j];
// triangle intersection code ...
}
}
#2多叶实施
我的第二个实现考虑到可能会相交多个叶子的事实。但是,此实现比实现#1慢 36x (好吧,我错过了#1中的一些交集测试,但仍然...)
bool[boxes.length()] hits;
hits[0] = intersect_box(boxes[0], ray);
for (int i = 1; i < boxes_count; i++) {
if (hits[boxes[i].parent]) {
hits[i] = intersect_box(boxes[i], ray);
} else {
hits[i] = false;
}
}
for (int i = 0; i < boxes_count; i++) {
if (!hits[i]) {
continue;
}
// only leaves have ids_offset and ids_count defined (not set to -1)
if (boxes[i].ids_offset < 0) {
continue;
}
uint a = boxes[i].ids_offset;
uint b = a + boxes[i].ids_count;
for (uint j = a; j < b; j++) {
uint triangle_id = triangle_references[j];
// triangle intersection code ...
}
}
这种性能差异使我发疯。似乎只有if(dynamically_modified_array[some_index])
这样的单个语句会对性能产生巨大影响。我怀疑SPIR-V或GPU编译器不再能够执行其优化功能吗?所以这是我的问题:
这确实是一个优化问题吗?
如果是,我可以将实现#2进行更好的优化吗? 我可以以某种方式给出优化提示吗?
在着色器中是否有实现BVH树查询的标准方法?
答案 0 :(得分:3)
经过一番挖掘,我找到了解决方案。重要的是要理解,BVH树不排除人们需要评估所有叶子的可能性。
下面的实施#3,使用点击和未命中链接。需要对框进行排序,以便在最坏的情况下以正确的顺序查询所有框(因此,一个循环就足够了)。但是,链接用于跳过不需要评估的节点。当当前节点为叶节点时,将执行实际的三角形相交。
图片取自here。相关的论文和源代码也来自Toshiya Hachisuka教授的page。 this paper referenced in the slides中也描述了相同的概念。
具有点击链接和缺失链接的#3 BVH树
我必须扩展通过链接推送到着色器的数据。另外,还需要一些离线摆弄才能正确存储树。最初,我尝试使用while循环(直到box_index_next
为-1为止),这再次导致了疯狂的速度下降。无论如何,以下操作相当快:
int box_index_next = 0;
for (int box_index = 0; box_index < boxes_count; box_index++) {
if (box_index != box_index_next) {
continue;
}
bool hit = intersect_box(boxes[box_index], ray);
bool leaf = boxes[box_index].ids_count > 0;
if (hit) {
box_index_next = boxes[box_index].links.x; // hit link
} else {
box_index_next = boxes[box_index].links.y; // miss link
}
if (hit && leaf) {
uint a = boxes[box_index].ids_offset;
uint b = a + boxes[box_index].ids_count;
for (uint j = a; j < b; j++) {
uint triangle_id = triangle_references[j];
// triangle intersection code ...
}
}
}
此代码比快速但有缺陷的实现#1慢大约3倍。这是可以预料的,现在速度取决于实际的树,而不取决于gpu优化。例如,考虑一个退化的情况,即三角形沿轴对齐:同一方向的射线可能与所有三角形相交,然后需要评估所有树叶。
教授Toshiya Hachisuka针对in his sildes(第36页及以后)的情况提出了进一步的优化方法:一种存储BVH树的多个版本,并沿x,-x,y,-y,z和-z在空间上进行排序。为了遍历,需要根据射线选择正确的版本。然后,只要从叶子上切下一个三角形,就可以停止遍历,因为所有要访问的其余节点都将在空间上位于该节点之后(从射线的角度来看)。