我正在尝试实现一个光线跟踪器,我正在使用“基于Physicall的渲染”一书中的kd-tree构造方法。
本书使用一种名为SAH的方法来选择分割边界框的位置。 它从场景中物体边界框的边缘选择分割位置。边缘沿着轴从低到高排序。
它找到了最好的分割:
//<Compute cost of all splits for axis to find best>
int nBelow = 0, nAbove = nObjects;
for( int i = 0; i < 2 * nObjects ; ++i){
if( edges[axis][i].type == END ) -- nAbove;
float edget = edges[axis][i].t;
if( edget > nodeBounds.pMin[axis] &&
edget < nodeBounds.pMax[axis] ){
//<Compute cost for split at ith edges>
}
if( edges[axis][i].type == START ) ++ nBelow;
}
我随机地在场景中放置了100个球体:
for( int i = 1 ; i < 100 ; ++ i ){
float x,y,z,r;
x = 800 * float(rand())/float(RAND_MAX) - 200 ;
y = 800 * float(rand())/float(RAND_MAX) - 200 ;
z = 400 * float(rand())/float(RAND_MAX) - 100 ;
r = 50 * float(rand())/float(RAND_MAX) + 25;
initSphere(..., Point3D(x,y,z) , r ,...);
}
显然,球体可以相互重叠。
并且没有好的分割位置可以让两个子盒子覆盖所有物体。穿过分割位置的对象不在任何子框中。 我添加了一个新的条件,只有当分割位置以下的对象数量加上上面的数字等于边界框中所有对象的数量时,才会记录该位置。
//update best split if this is lowest cost so far
if( cost < bestCost && (nBelow + nAbove == nObjects ) ){
bestCost = cost;
bestAxis = axis;
bestOffset = i;
}
(nBelow + nAbove == nObjects)总是“假”。如果
如果我们在这里创建一个叶子节点,那么kd-tree是无意义的,它只是退化为顺序遍历,因为整个树只包含一个叶子节点。
所以,有什么解决方案吗?谢谢!
这里有一些定义:
struct Point3D{
float x,y,z;
}
struct BBox{
float pMax[3],pMin[3];
}
struct BoundEdge{
float t;
int type; // START or END
}
BoundEdge *edges[3];
ps.i希望我的可怜英语清楚地解释了这个问题......
答案 0 :(得分:2)
KD树需要在两个子树中存储穿过分割平面的所有对象。您假设对象仅存储在子树中的一个中是不正确的。正确的断言是:
(nBelow + nShared + nAbove == nObjects)
这是为什么BVH通常优于KD-Tree的原因之一(即BVH仅在一个子树中存储对象,因为子树边界框可以重叠)。
具有许多交叉对象的分割平面将具有更高的SAH成本(因为交叉对象被计数2次),因此KD树分割代码仍将尝试最小化共享对象的数量(但通常会有一些重复)。