电子说
裁剪是个很大的概念,裁剪包括了视锥体裁剪(应用程序阶段)、视口裁剪(图元装配——几何阶段的最后一步)、背面剔除(光栅化阶段)、遮挡剔除(光栅化阶段)。
1.视锥体裁剪
视锥裁剪算法是在应用程序阶段执行的。虚拟摄像机制定了场景对观察者可见的部分,即我们将依据哪部分3D场景来创建2D图像。
2. 视口裁剪
几何阶段处理结束后,送到光栅化阶段的是一堆三角形面片,所以中几何阶段中需要对顶点进行图元装配。所谓的图元装配,即根据顶点原始的连接关系,还原出模型的网格结构。网格由顶点和索引组成,在之前的流水线中是对顶点的处理,而在这阶段是根据索引将顶点连接中一起,组成线、面单元。然后对超出视口外的三角形进行裁剪(视口裁剪)。
3.背面剔除(光栅化阶段)
渲染的对象都是三角面,有相机,就是有正面和反面。正面可以被看见,反面看不见,就需要渲染,就叫背面剔除。
4.遮挡裁剪
在cry引擎中的遮挡算法,使用的是软件光栅化,大致流程就是,在编辑器中放置一些正交的长方体作为遮挡体,在渲染时,每帧都在CPU上面光栅化这些遮挡体(当然是在分辨率比较小的渲染目标上进行),然后对远处物体进行查询。
现代GPU中运用了Early-Z的技术,在Vertex阶段和Fragment阶段之间(光栅化之后,fragment之前)进行一次深度测试,如果深度测试失败,就不必进行fragment阶段的计算了,因此在性能上会有很大的提升。但是最终的ZTest仍然需要进行,以保证最终的遮挡关系结果正确。
二、遮挡剔除的方法
1. 传统的PVS光线投射子划分
将场景内相交的多边形进行切割,保证场景内的三角形没有相交(如bsp),将屏幕分成8x8的像素网格,做8x8条从视点开始的射线出去即可,这样做能保证比较高效。然后对光线进行碰撞检测看光线落到哪个多边形上。如果相邻射线没有落在同一个三角形上,那么以这两条射线的中点再做出一条新的射线出去,直到相邻射线落在相同的三角形上或者同一个像素上。这样相交多边形中围绕每束光线的多边形基本都能被绘制了。性能很好但存在一定误差,两束光线中间极小的多边形可能被错过。
2. 光栅化线段遮挡法
不同于光栅化z,光栅化z计算量大些,这只是将待渲染多边形光栅化为有左右两个端点的水平扫描线,然后与同一行的扫描线进行比较和切割,只保留最靠近摄像机的线段,如果待绘制线段在该像素行比较下来全部被其它更靠近摄像机的线段覆盖了,则该行不可见,三角形所有行不可见则该包围体不可见。光栅化z适合gpu,这个方法更适合cpu,特别适合分辨率很高而多边形数量不算太高的时候,优化方法是x轴均匀分为8个区间,这样可以迅速的定位。
3. 八叉树简易PVS
说道简易版本的 PVS,其实就是八叉树了,这是很多3D引擎的偷懒版本实现:八叉树组织空间,下面接 BSP, Portal,地形四叉树lod,先判断顶层包围盒在不在视锥,不再就直接退出了,再的话,递归八个子节点的包围盒再不再视锥,不再就直接剔除,再的话再在该节点递归下去
Untiiy5中的遮挡剔除使用的是Umbra的解决方案,Umbra也在虚幻3和虚幻4中作为插件来使用。
三、讨论的两个问答题
1.在Unity中遮挡剔除在渲染过程中,处于哪个阶段?
遮挡剔除一般在光栅化阶段进行。
关于遮挡剔除,开始的时候觉得应该在应用阶段进行,只需要一个八叉树的搜素,但是这个需要相机来判断,其实相机作为为虚拟相机,只需要给对象做标记即可。
在官方文档中说,遮挡剔除是需要在场景中建立一个虚拟相机,来判断是否可见。准备好后,unity确保只有可见的对象送去渲染 。
unity中的遮挡裁剪以Cell作为基础单元,每个cell对应一个二叉树。unity中使用了两颗树,一棵对应静态对象,一棵对应动态对象。
需要注意的是,对象若太大,对遮挡剔除来说,意义不大,太小构建时间太长,顶点数也多,造成过多的drawCall,总之,注意平衡!没有放之四海而皆准的方法。
2.裁剪,相机(视锥体)的裁剪和背面剔除,这个是分别在不同阶段。
视锥裁剪一般在应用程序阶段进行, 这个没有问题。而背面裁剪剔除一般在光栅化阶段进行。
背面剔除在光栅化阶段进行,在Vertex Shader 之后,在Fragment Shader片元着色器之前。
全部0条评论
快来发表一下你的评论吧 !