来源:
中国IT实验室收集整理 作者:
CC 时间:2011-3-23 10:08:17
|
|
隐藏面剔除算法的出现是基于以下的现实,在游戏场景中你不必每帧都将所有的物体渲染到屏幕上,它的作用主要有两个:一个是减少GPU的屏幕填充率,另一个是减少CPU和GPU的传输带宽。根据当前的硬件现状GPU的填充率已经不存在任何问题,而现在唯一的限制位于AGP上,它那可怜的一点带宽已经成为图形渲染的瓶颈,因此当前HSR算法大部分都放在CPU端来做以尽量减少每帧需要传输的数据量,这里你不需要对CPU的负担感到担忧,因为现在的CPU完全可以适应这样的要求。当你将场景采用适当的场景分割算法如BSP、OC tree算法组织起来后,就需要在每帧渲染时使用适当的HSR算法对其中无用的信息进行剔除,现在的HSR算法基本上可以分为四种:backface culling、frustum culling、portal culling、occlusion culling。它们的作用分别如下:backface culling用于剔除mesh背面的triangle,frustum culling用于剔除屏幕之外的物体,portal culling用于剔除不能通过portal看到的物体,而occlusion culling用于剔除场景中被自己前面的物体所遮挡的物体。其中前两种算是最常用的算法了,对于backface culling来说它已经在GPU的硬件上实现,不过为了减少传输带宽,在适当的地方你也可以将其使用到CPU部分,主要是指地形这样的大规模MESH上,如何使用这需要看你是如何组织地形数据的,当你使用triangle strip渲染地形时,尽量按块来对地形进行culling,而当你使用triangle list来渲染地形时,你就可以尝试使用逐triangle来对其进行culling,关于实际使用应该使用哪种方法,需要你自己试验一下。frustum culling由于太普遍这里不再解释,而portal culling如果详细介绍的话那就是另外一篇文章了,这里只是简单的介绍一下,通常的portal culling实际分为两种,一个是用于bsp的PVS算法,这种方法的优点是通过预计算获得可见性信息,速度非常快,而且场景中的portal可以通过程序获得,不需要手工进行指定,但是缺点也非常明显,基本上它只能用于室内场景,而且由于PVS是一个可能的可见集合因此不能对不可见场景作到完全的剔除。另一个可以被称为实时的portal culling,这种方法的优点很明显,如果场景安排合理的话它可以作到完全的剔除,而且它可以用于任意的场景中,indoor、city、terrain它都可以使用,缺点也非常明显,portal必须手工进行指定,而且由于portal culling每帧都需要计算因此每帧出现在屏幕上的portal必须进行限制,还有最重要一点通常由于场景的限制尤其是在outdoor场景中它也不能完全作到完全剔除,下面我举一个简单的例子来看看它为什么不能作到完全的剔除: 上面可以看作是一个房子,B为房间的门,也就是一个portal,A是场景中的一个物体如果我们做portal culling计算时,如果一个npc位于D点那么可见的,但是当位于C点时,由于前面存在物体A,应当是不可见的,但是portal culling并没有将其剔除因此npc还是会被传送到GPU进行渲染。 在上图中物体N为occluder,A、B、C为occludee,我们假定它们投影到屏幕上都为一个矩形,我们这里称N的上下两条edge a、b为扫描线,从上图中可以看出如果物体A位于N的后面那么A一定被N所遮挡,这可以通过比较N和A所在surface的z值来进行比较,这也就是本算法中Z缓冲器的来历,现在我们假设物体A、B、C都位于N的后面,可以看出A被完全遮挡,B是部分遮挡,C没有被遮挡,如何判断呢,先看A的情况,N和A存在四条扫描线按照y从小到大顺序分别是b,d,c,a,在扫描线b和d构成的区间中由于不存在A的edge因此跳过,检查扫描线d和c构成的区间,我们称位于两条扫描线之间的edge为活化边,它们保存在活化边列表active edge list中,每一条活化边保存了两个surface指针,分别是左surface和右surface,例如对于N的左edge而言,它的左surface不存在因此指向屏幕的background,也就是z值为最大的surface,而右surface为N所在的surface,其他类似。现在我们检查扫描线d和c之间的活化边列表,首先获得A的x值最小的火花边为A的左edge,接着获得N的活化边列表中x大于A的左edge的活化边,为N的右edge,比较两条edge的左surface,A的左edge的左surface的z大于N的右edge的左surface的z,因此A的左edge被N所遮挡,接着同样的方法检查A的右edge还是被遮挡,这样我们就可以判断A完全被N所遮挡。使用同样的方法检查B,在扫描线f和b构成的区间中,由于N不存在活化边列表可以判断B没有被N完全遮挡。再检查C,在扫描线h和g之间N的活化边列表中所有的edge的x都小于C的左edge,可以判断C没有被N遮挡。对于其它复杂的情况例如存在多个occluder的情况你自己可以验证,这种算法在任何情况下都可以对物体是否被完全遮挡进行精确的判断。 |