遮挡剔除技术分析

转自:http://www.gameres.com/486336.html

umbra具体怎么搞的不得而知,在用不了umbra的情况下,推荐几个固定管线时代就出现的确实能在一周内实现的纯软件方法:

  传统标准PVS

  标准 PVS其实就是两步:

  1. 先求解简易模型:减面,枚举模型上每个顶点,找到一个点使得删除该顶点,模型变形最小,不停的寻找并删除影响最小的点直到模型变形超过一定阀值。最终求解出简易场景模型,为第二步计算做准备。

  2. 划分成小的三维格子,再格子里面均匀或随机选取 N个采样点做为摄像机位置,每个采样点 360度全方向做一定数量的射线出去,和场景中的模型判断交点,求解出该采样点的PVS,然后合并格子里N个采样点的结果为该格子的PVS。有离线计算好的,也有实时计算摄像机周围空间未计算格子的,等摄像机移动到那里时已经计算好了,无外乎精度不同。实际绘制时将所在格子的PVS提取出来再做一次视锥剔除就行。

  误差:当然有,有些游戏从室外某些堆叠的山石间隔处本来是可以看到后面的东西的,但是换个角度有时候就看不到了,再往前走两步又看得到了,这些效果在游戏里面很常见,其实就是PVS求解不精确的问题。

  优化:更小的格子划分,格子内更密集的采样点,同采样点内更密集的射线模拟,多计算一下,可以解决大部分问题。相邻格子的PVS集合如果差异不大可以归并(比如基于八叉树的格子归并),降低存储空间。

  BSP:如果场景完全使用bsp进行构建的话,pvs的计算可以基于bsp的叶子节点进行。

  精确性:QA测试或者美术检查的时候在场景中主要位置多跑跑,发现这种问题就在编辑器里手动更改一下pvs结果。其实主要位置跑完,每跑一个位置四处看看,也就差不多了。

  这是标准的 PVS做法。

  光线投射子划分

  将场景内相交的多边形进行切割,保证场景内的三角形没有相交(如bsp),将屏幕分成8x8的像素网格,做8x8条从视点开始的射线出去即可,这样做能保证比较高效。然后对光线进行碰撞检测看光线落到哪个多边形上。如果相邻射线没有落在同一个三角形上,那么以这两条射线的中点再做出一条新的射线出去,直到相邻射线落在相同的三角形上或者同一个像素上。这样相交多边形中围绕每束光线的多边形基本都能被绘制了。性能很好但存在一定误差,两束光线中间极小的多边形可能被错过。

  光栅化线段遮挡法

  不同于光栅化z,光栅化z计算量大些,这只是将待渲染多边形光栅化为有左右两个端点的水平扫描线,然后与同一行的扫描线进行比较和切割,只保留最靠近摄像机的线段,如果待绘制线段在该像素行比较下来全部被其它更靠近摄像机的线段覆盖了,则该行不可见,三角形所有行不可见则该包围体不可见。光栅化z适合gpu,这个方法更适合cpu,特别适合分辨率很高而多边形数量不算太高的时候,优化方法是x轴均匀分为8个区间,这样可以迅速的定位。

  八叉树简易PVS

  说道简易版本的 PVS,其实就是八叉树了,这是很多3D引擎的偷懒版本实现:八叉树组织空间,下面接 BSP, Portal,地形四叉树lod,先判断顶层包围盒在不在视锥,不再就直接退出了,再的话,递归八个子节点的包围盒再不再视锥,不再就直接剔除,再的话再在该节点递归下去:


  八叉树建立时先将场景放入个唯一的盒子里,然后切成八块,再递归切下去,直到单个节点内多边形数量/模型数量少于阀值。交界处的三角形/模型可以切到两个叶节点里或同时属于两节点。 


  先全部放一个cube里面,然后切割成八分,递归下去:


  继续将节点内多边形数高于阀值的叶子进行切割递归 继续将节点内多边形数高于阀值的叶子进行切割递归


  比如: 


  最终类似:

  切割好以后,按照视锥范围,从顶部开始剔除,不再视锥范围的直接跳过,根本用不着再往下递归它的子节点了。再视锥范围内才往下递归,直到碰到没有子节点的纯粹包含实际多边形集合的叶子节点,然后渲染出来,这是简易PVS方法。 切割好以后,按照视锥范围,从顶部开始剔除,不再视锥范围的直接跳过,根本用不着再往下递归它的子节点了。再视锥范围内才往下递归,直到碰到没有子节点的纯粹包含实际多边形集合的叶子节点,然后渲染出来,这是简易PVS方法。

  不少3D引擎还停留在这种简陋的做法上,因为实际场景复杂度没上到一定量的话,用它也可以一战,只是有些过于简陋罢了。

  上面几个都是一周内能实现的切实可行的计算方法,建议的话先上简陋版本,你的瓶颈如果真的出在这里,octree解决不了,就上其他方法吧。