Kd算法从创建根BSP节点开始,通过对基元数组(三角形,球体......)进行分区,以创建两个用于创建其两个的新数组(左和右基元)子树。
通过将给定的基元数组分成两个数组来计算左和右基元。通过取每个三角形的间隔(投影到给定轴(x,y或z))的中间点的中值来计算分区平面位置。
例如,具有x坐标的三角形:1,2,3具有中点1 =(3-1)/ 2(沿x轴) 具有x坐标的三角形:2,3,8具有中点3 =(8-2)/ 2(沿着x轴) 具有x坐标的三角形:4,3,8具有中点2.5 =(8-3)/ 2(沿着x轴) 包含这三个三角形的原始数组由平行于xz平面x = 2.5(中值)的平面划分。生成的左基元数组包含三个三角形,生成的右基元数组包含三个三角形。
具有左和右基元的两个结果数组用于构造Kd节点的左右子树(基元仅存储在叶节点处)。
对于左子树:
If (the left primitives are empty) then the left subtree points to NULL
else if (the number of left primitives is smaller than the minimal number || the depth == 1) then the left subtree is a leaf node
else the left subtree is another tree.
create the left subtree with the left primitives along the axis (++axis % 3) with --depth as depth and the same minimal number.
类似于正确的子树。
实现的算法有效,但它非常慢,因为树的分区不是很好。当对5500个三角形的兔子进行光线跟踪时,有很多叶子节点有1个三角形,最后一个叶子节点仍然包含762个三角形。
是否有更好的分区算法(因为我只是对转换为曲面的单个点的Kd树的实现),这会平衡树?
更新:我搜索了一个算法或启发式算法,可以根据切割点将一个区间数组[t0,t1]分成两个区间数组。左侧数组包含切割点左侧的间隔和包含切割点的间隔。类似于正确的阵列。两个阵列必须具有大致相同的间隔数,并且重复间隔的数量必须尽可能少。该算法可能不具有复杂度O(n ^ 2)。
答案 0 :(得分:9)
我建议你使用表面区域启发式(SAH)来寻找最佳分割。
光线与子树相交的概率 - 与该子树的边界框的表面积成正比。
但是,如果叶子树包含许多三角形 - 这意味着我们必须遍历所有三角形。
因此,SAH的主要思想是最小化:子树的表面区域和它们内部的多边形数量。
让我们看一下小2D的例子:
此外,使用SAH - 有助于确定构建kd-tree期间的终止条件:
1)在构建kd-tree的每一步中,在分割子树之前 - 你必须计算当前子树的SAH
SAH_initial = number_of_polygons * area_of_subtree
2)Aftre你必须找到当前子树的最佳分割的SAH
SAH_optimal = min(S_left * N_left + S_right * N_right)
3)Aftre你必须比较:
define some split_cost
...
if( SAH_optimal + split_cost < SAH_initial ) {
it would be optimal to split that subtree
} else {
you don't have to split current subtree
}
这是另一个answer on Stackoverflow,其中包含有关SAH的文章的参考文献。
答案 1 :(得分:1)
中点的计算看起来不正确。对于x坐标为2,3,8的三角形,中点应为2 +(8-2)/ 2 = 5.具有x坐标1,2,3的三角形应具有中点1 +(3-1)/ 2 = 2.这可以解释不平衡的叶子。