(论文)[1991] Shaft Culling for Efficient Ray-Cast Radiosity
Shaft Culling for Efficient Ray-Cast Radiosity
思路
- 在 emitter 和 receiver 之间,我们可能会做多次光线投射的可见性测试,因此我们进行预处理
- 首先我们生成一个尽可能小的包围盒,将 emitter 和 receiver 包含进去
- 然后我们生成一个 list,list 里面包含可能和这个包围盒相交的 bv(可能对 emitter 和 receiver 之间的可见性又影响的 bv)
- 之后我们对每一条光线,只需要判断它和 list 中的光线是否有交即可
说明
- object:一个可渲染的图元
- item:一个 object 或者多个 item
- 算法准备工作:根据给定的 object 建立 bvh(bounding volume hierarchy)
- reference items:the emitter and the receiver
- candidate list:可能挡住 emitter 和 receiver 之间可见性的一组 item
- 每次需要判断两个采样点之间的可见性时,我们遍历整个 candidate list,直至找到交点或者遍历结束
- 怎么生成 candidate list?如下是两种比较 trivial 的方法
- 可以使用整个场景所有 object 构成 candidate list
- 只使用 bvh 的根节点,遍历整棵树
- 如果我们能够在空间上定义一个 volume,它能够包含整个空间中在
reference items 之间的所有点,这样便能够形成一个比较好的 candidate list
- 这样只需要判断 object 是否在 volume 之内即可
- 时间花费:建立 candidate list 花费的时间和使用它节省的时间
- 可以估计允许我们使用多少时间生成 candidate list
- 是否生成 candidate list
- emitter 和 receiver 相隔很远,不生成
- 预计算潜在的在 reference items 之间的光线,如果大于预设值则生成使用
candidate list,否则不生成使用
- 简单的测试
candidate list 算法
- candidate list 算法有 3 个步骤
- forming the testing volume
- creating the candidate list by using this volume
- accessing the candidate list for visibility determination between samples
(1) Forming the Testing Structure
- 主要步骤如下
- Obtain the bounding boxes for the reference items.
- Compute the extent bounding box containing both reference items.
- Create the plane set between the two reference items' boxes.
第一步
- 我们使用 bvh,因此可以很容易得到 reference item 的包围盒,AABB 包围盒(axis aligned)
- \(lo.x,lo.y,lo.z,hi.x,hi.y,hi.z\)
第二步
- 直接对两个 reference item 的包围盒取边界值(大的取大,小的取小)
- 同时找出 culled edges(扩展包包围盒中不属于 reference item 的边)
第三步
找到一组平面,将两个 reference item 的包围盒连接在一起
形成 minimal bounding volume
我们削去包围盒中和 culled edges 相邻的一块空间
- 我们将 volume 称为 shaft
同时需要记录哪一个生成扩展包围盒的时候,哪一个包围盒的顶点成为了边界值(最大最小)
- 称为 list
- 用于确定 culled edges
两个 reference item 都需要有一个 list,每个 list 最多有 6 个 entry
每一个 entry 记录类型(minimum / maximum)和方向(X / Y / Z)
- 例如 MIN_Z
如果 reference item 的最小/大值相同,则两个列表都不会出现这个 entry
运行过程
- 构造 list
- emitter list:MAX_Y
- receiver list:MIN_X,MIN_Y,MAX_X
- 第三步构造 planes,两个 list
中的元素两两组合(去除方向匹配的边),剩下的边就是 culled edge
- MAX_Y——MIN_X
- MAX_Y——MIN_Y(方向匹配上了,去除)
- MAX_Y——MAX_X
- MAX_Y——MIN_X
- 切去边 :\((lo.x,hi.y,lo.z)-(lo.x,hi.y,hi.z)\) 关联的边(culled edge)
- 生成平面的法线(就和我们平时法线的定义一致),得归一化
- 未参与 \(Z.direction=0\)
- \(X.direction=Receiver.hi.y-Emitter.hi.y\)
- \(Y.direction=-Receiver.lo.x+Emitter.lo.x\)
- 平面与原点的距离(原点指向平面,法线)
- \(P.distance=-(X.direction\ast lo.x+Y.direction\ast hi.y)\)
- \(lo.x,hi,y\) 使用任意 reference item 的都行,是等价的(画图即可看出来)
- 形成的 plane 的集合保存在一个 list 中,称为 plane set
- 可能 plane set 为空
- 当一个 reference item 整个都在另一个 reference item 的里面时,plane set 为空
- 其他例子
(2) Creating the Candidate List
使用第一步建立的 shaft,和场景中的物体求交,生成 candidate list,具体生成方法如下
首先使用 bvh 的根节点和 shaft 求交,可能出现如下的 3 种情况
- The item is entirely outside of the shaft.(在 shaft 外面)
- The item is entirely inside the shaft. (在 shaft 里面)
- Else, the item overlaps the shaft. (和 shaft 部分相交)
如果在 shaft 外面,直接忽略
如果是在 shaft 里面或部分相交,则有多种策略
- always open
- 始终递归进行,直到叶子结点(不是 bounding volume)
- candidate list 由所有不是 bv 的 object 组成
- 非 always open 策略
- 如果是在 shaft 里面,则将其加入到 candidate list 中
- 如果光线 miss 则会加速(不需要和内部的 item 进行测试)
- 如果是和 shaft 部分相交,则有多种策略
- 如果是在 shaft 里面,则将其加入到 candidate list 中
- always open
和 shaft 部分相交的多种非 always open 策略
- 叶子结点的处理
- 如果是一个 object 而不是 bv,则加入 candidate list
- keep closed
- 直接将 bv 加入到 candidate list 中
- overlap open
- 和 always open 一样,递归展开至叶子结点
- 当 reference item 之间的 object 比较少的时候可以使用
- 但是可能这始终打开 bv 是一个比较耗时的操作(增加光线的测试)
- ratio open
- 设定一个 ratio 判定是否值得打开 bv
- 例如 0.4
- 如果 bv 中有超过 40% 的物体在 shaft 中或者与 shaft 相交,则不打开 bv,直接将其放入 candidate list
- 例如 0.4
- 值得注意的是,即使是满足上面 ratio 的条件,bv 也有可能被展开,bv 被保留的条件是内部的 item 比 bv 测试难得多得多
- 设定一个 ratio 判定是否值得打开 bv
- 多种策略
- 叶子结点的处理
(3) Shaft Cull Testing
- cull test:shaft 和 bv 求交
- 被测试的 bv 被称为 test volume
- 只考虑 sphere 包围盒和 axis aligned 包围盒
- 判断流程如下
- 判断 test volume 和 extent box 的位置关系:inside、outside、overlapping
- 如果是 outside ,测试结束;否则判断 test volume 是否和 reference item 有重合部分(overlap)
- 如果有重合部分,测试结束;否则将 test volume 和 plane set 进行位置关系的测试
- 如果 test volume 是 outside ,测试结束;否则需要进一步测试(策略相关),判断 test volume 是否完全在 shaft 里面,或者是否展开 bv 等
Sphere bv 测试
- 第一步:outside 测试
- Arvo's algorithm for box/sphere
comparison [Arvo, 90].
- 每个轴分开处理
- 如果在某个轴上,圆心在 box 内部,则求出圆心到这个轴对应的近一点的面的距离
- 对上面的距离求平方和,和半径的平方作比较
- 如果比平方和大,就是 outside
- Arvo's algorithm for box/sphere
comparison [Arvo, 90].
- 第二步:overlap 测试
- 和第一步相似,将 test volume 和 reference item 进行测试
- 如果相交,则一定和 shaft 相交
- 第三步:plane set,outside 测试
- T=dot_product(Plane.normal, Sphere.center) + Plane.distance
- 画图发现就是圆心到平面的距离
- 如果 T>Sphere.radius,说明相离(outside)
- 如果是内部的话,计算出来会的 T 是负数
- 如果法线对于所有 plane set 中的 plane,都是 outside 的话,说明 bv 在 shaft 的外面
- 仍然有可能出现 bv 在 shaft 外面的情况(false positive)
- 这不会犯错,但是会增加计算量(误将其放入 candidate list)
- T=dot_product(Plane.normal, Sphere.center) + Plane.distance
- 第四步:inside 测试
- 如果对所有的 plane set 中的 plane,都满足 T > -Sphere.radius,则在 shaft 内部
- 否则就归类于 overlap
AABB 测试
- boxes 的测试不需要对 plane set 中的法向进行归一化
- sphere 需要和半径比较因此需要归一化,但是这里比较的都是和 0 比,因此 scale 无关紧要
- 定义 the distance of a point from the plane 如下
- T = dot_product( Plane.normal, Point) + Plane.distance
- 平面定义了半空间(half space)
- T>0:outside
- T=0:on
- T<0:inside
- 认为距离有正负大小区分
- 给定一个平面,总有一个顶点到平面距离最大(farthest),有一个点到平面距离最小(nearest)
- 这个顶点是平面法线的函数
- farthest:对于法线,如果某一个轴值为正,则使用 box 的 hi 值;0 使用 0;负使用 box 的 lo 值
- nearest:正:lo;0:0;负:hi
- 例如,法线:[-3, 8, 4]
- farthest:[lo.x,hi.y,hi.z]
- nearest:[hi.x,lo.y,lo.z]
- 这个顶点是平面法线的函数
- 因此测试过程和 sphere 一摸一样
- extent box
- reference item boxes
- each plane in the plane set
- 使用 nearest_corner
- If dot_product( Plane.normal, Box.near_corner) +
Plane.distance > 0
- box is outside the plane (and the shaft)
- 如果通过了上述测试,说明 test volume 和 shaft 的位置只有 inside/overlap
- overlap 测试(一个满足则是 overlap)
- If dot_product( Plane.normal, Box.far_corner ) + Plane.distance >
0
- box overlaps shaft
- If dot_product( Plane.normal, Box.far_corner ) + Plane.distance >
0
- 以上的方法虽然快,但是可能会将 outside 的物体判定为 overlap
- plane set 的测试,我们用的是整个平面,可能会误判的问题,但是这个只会增加计算量而不会导致错误
(4) Candidate List Access
- 对于 receiver 和 emitter 上的两个采样点,我们形成一条光线,使用 candidate list 进行测试
- 使用栈实现,对于 bv,如果相交,则将其子结点压栈
- 最终要么相交(和一个不透明的物体相交),要么栈弹空结束
算法改进
- 我们知道两个 reference item 之间的光线一定是起源于某个
box,终止与另外一个 box
- 因此如果 test volume 完全将某个 reference item 包含,则光线必定相交
- 因此可以在第一步的时候,做一下这个测试
- 基于辐射度算法会出现如下情况:一个 emitter,对多个
receiver,如下预处理很有效
- 对 bv 进行预求交,判断它是否完全包含 emitter 的 reference
box,形成一个 candidate list
- 如果完全包含,直接展开为子节点
- 这样的预计算可以减少重复计算
- 之后和上面一样,当然之后 reference item 的完全包含测试只需要测试 receiver 即可
- 同时在预处理的时候,可以在 candidate list 中保存 emitter 的 reference item 的位置关系,如果 emitter 是 overlap 的话,则后面算法中的 outside 测试就不需要了
- 相当这一部分的改进就是,将后面可能的重复计算提前预计算了
- 对 bv 进行预求交,判断它是否完全包含 emitter 的 reference
box,形成一个 candidate list
- 从 emitter 的 bv 开始向上展开形成 candidate list
- 测试其父结点的所有子结点,然后重复向上(不需要测试父结点,一定相交)
- emitter 的出射方向有限的时候,可以将不可见的面直接剔除