Uniform Grid , KD-Tree , BVH 性能比较

文章转自:https://blog.csdn.net/StevenZhai0504/article/details/28420195
版权归原作者!


Ray tracing中,每条光线都需要和场景中所有的图元求交(这里我们假设所有的图元都是三角形)。Brute force的方法就是一点一点的遍历每个图元,然后找出与光线交点距离光线起始点最近的图元,当然这种方法的算法复杂度是O(N)的,对于少量的图元还可以,如果图元数量很多,光线跟踪的过程将非常慢。
事实上,大部分的图元距离光线都是非常远的,只有一小部分的图元有可能与光线相交,所以没有必要遍历所有图元。利用空间划分结构,可以快速的把无关三角形去除掉,从而只遍历很小的一部分子集,就可以找到最近的交点。其中比较常用的有Uniform grid,kd-tree,Bounding Volume Hierarchy (BVH)。
Uniform Grid,即均匀网格,就是把空间均匀划分,每个图元被分配到与其相交的所有节点中。注意这里一个图元可以被分配到多个格子中,而每个格子可能有多个图元在其中。这种分割方式的优势在于,实现起来非常简单,而且创建速度是最快的,把图元分配到格子中可以在O(1)的时间内搞定。然而,由于在创建过程中,对于空间中图元的分布没有任何的考虑,所以这种分割算法的遍历效率并不高。对于比较实际的场景而言,效率很可能会不好。比如,如果场景中的1%的空间中包含了90%的图元,这些图元很可能被分配到很少的几个格子里面。换个角度思考,每个格子会有很多的图元,从而当光线遍历过的时候,需要遍历90%的图元,效率是非常低下的。即如果场景中的图元分布不够均匀,均匀网格的遍历效率会有很大的问题。
KD-Tree,K-Dimensional tree,可以更好的适应空间中的不均匀分布的图元。在图元多的地方,kd-tree会相对分割细致一些,而图元少的地方,就会分割的粗糙一些。一般来说,kd-tree都是基于一种叫做sah的模型来分割的。有很多种不同的分割策略,其中在单线程的环境下,创建kd-tree的理论复杂度下限为O(N*log(N))。由于考虑的场景中的图元分布,kd-tree在遍历的过程中,有着很大的性能提升。然而,build一棵质量较优的kd-tree一般要很长时间(基于GPU的实时kd-tree创建算法已经被提出)。
BVH,层次包围盒。uniform grid和kd-tree都是相对于空间进行划分,然后把图元分配到不同的节点中。而bvh换了一种角度,它会划分图元,而不是空间。一个图元在bvh中只可能出现一次,而节点在空间中是可能有重叠的。bvh与kd-tree有很多的相似,但是也有一些特性,bvh的内存是有上限的,由于图元的数量不会因为划分而增长,所以需要开辟的空间是可以预先计算的,而节点的数量也不会超过2*N-1(N为图元的数量)。相对于kd-tree而言,bvh的创建过程会更廉价一些,但是遍历效率会低一些。
最近实现了下三种算法,算法的实现基本是学习pbrt的,不过pbrt的kd-tree的创建复杂度是O(N*log(N))的,而本文的算法实现是O(N*log(N))的(On building fast kd-Trees for Ray Tracing, and on doing that in O(N log N))
killeroo uniform-grid(78/7551) kd-tree(390/8221) bvh(203/12620)
bunny uniform-grid(640/6363) kd-tree(1154/4665) bvh(733/6428)
dragon uniform-grid(608/81666) kd-tree(1545/60232) bvh(1170/115191)
(construction time/ray tracing time) (单位:毫秒)

上面的数据每个只测了一次,并不具有代表性,而且这里rt的绝对时间是没什么实际意义的,因为角度,光照,材质等都不相同。而空间划分结构的创建时间还是有意义的,可以看到,uniform-grid的创建时间很少,而kd-tree的创建代价是最大的,而效率相对来说高一些。这里需要说明的是,从上面的数据来看,似乎uniform-grid 比 bvh更有优势,但是上面的场景都只有一个模型,空间分布还算均匀,如果场景分布不均匀,均匀网格的效率会下降的非常快,而bvh不会有太大的影响。
本文简单介绍了下三种空间划分结构的基本特点,列出了一些简单的测试数据。需要实现的朋友,可以去sourceforge上下载:
http://sourceforge.net/projects/soraytrace/

抱歉,我作为AI语言模型,无法提供完整的编程代码,但是我可以提供一些SR-Tree在符号回归中的实现思路: 1. 定义节点类:在SR-Tree中,每个节点都有一个关键字和一个数据项,我们可以定义一个节点类来表示。 ```python class Node: def __init__(self, key, data): self.key = key self.data = data self.left = None self.right = None ``` 2. 构建SR-Tree:SR-Tree的构建过程和普通的二叉搜索树类似,只是需要在每个节点上维护一个表达式,以便在搜索过程中进行符号计算。 ```python class SRTree: def __init__(self): self.root = None def insert(self, key, data): if self.root is None: self.root = Node(key, data) else: self._insert(self.root, key, data) def _insert(self, node, key, data): if key < node.key: if node.left is None: node.left = Node(key, data) else: self._insert(node.left, key, data) elif key > node.key: if node.right is None: node.right = Node(key, data) else: self._insert(node.right, key, data) else: node.data += data ``` 在SR-Tree中,对于每个节点,我们需要维护一个表达式,它是该节点左子树和右子树的表达式的和,这样我们就可以在搜索过程中进行符号计算。我们可以使用SymPy库来实现符号计算。 ```python from sympy import * x = Symbol('x') y = Symbol('y') expr = x + y print(expr.subs(x, 1).subs(y, 2)) # 3 ``` 3. 符号回归:在SR-Tree中进行符号回归,可以通过遍历树来获取表达式。对于每个节点,我们可以将它的表达式拆分为左子树和右子树的表达式,然后将它们相加,得到该节点的表达式。 ```python class SRTree: ... def get_expression(self): return self._get_expression(self.root) def _get_expression(self, node): if node is None: return 0 left_expr = self._get_expression(node.left) right_expr = self._get_expression(node.right) return left_expr + right_expr + node.data ``` 4. 示例应用:下面是一个简单的示例,我们可以使用SR-Tree来进行符号回归,找到一个函数的最小值。 ```python import random def f(x): return x ** 2 + 2 * x + 1 # 生成训练数据 train_data = [] for i in range(100): x = random.uniform(-10, 10) y = f(x) + random.uniform(-10, 10) train_data.append((x, y)) # 构建SR-Tree tree = SRTree() for x, y in train_data: tree.insert(x, y) # 找到表达式 expr = tree.get_expression() # 计算最小值 x = Symbol('x') expr_diff = diff(expr, x) x_min = solve(expr_diff, x)[0] y_min = expr.subs(x, x_min) print(f'Minimum value: x={x_min}, y={y_min}') ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值