今天来谈谈3D开发中的两个重要知识点Culling(剔除)和Clipping(切分)。
左图表示一个完整的3D渲染流程,可以看到Culling和Clipping处理发生在投射计算和渲染计算之前。
Culling过程是用于剔除不需要参与渲染的物体和三角面,从而达到节约资源的目的。一般来说Culling分为以下4种:
- Frustum culling:视锥剔除,PV3D里最重要的剔除方式,下面详述。
- Back-face culling:当面背对相机时候剔除,由PV3D自动执行。
- Contribution culling:当物体在画面上过小情况下的剔除,PV3D里面没有这种机制,但是可以用远平面(camera.far)来剔除。
- Occlusion culling:包藏剔除,适用于一个物体完全被另一个物体遮挡的情况,PV3D不包含这种机制。
Frustum culling在PV3D中分两种:分别是基于camera(相机)和基于viewport(视口)的。
使用以下代码开启基于相机的剔除功能(DebugCamera是默认开启的):
camera.useCulling = true;
通过查看do3d.culled属性可以知道物体是否被剔除。要注意这种剔除机制是针对整个物体(do3d)计算的,只有当整个物体完全不在视锥内,才会被剔除。
另一种基于视口的剔除在PV3D中是默认开启的,也可以用以下代码来制定:
viewport.autoCulling = true;
这种剔除机制是基于面(triangle)的,任何超过视口平面的矩形渲染区的三角面都不会被渲染。
注意:如果物体的某些面超过了近平面(camera.near),那么即使视口剔除是关闭的,超过的面也会被剔除。
我们可以通过查看PV3D状态视图中给出的信息,来了解一下剔除的实际作用,在左图中的COb、CTr分别代表了被剔除的物体和三角面。
了解了剔除机制,那么Clipping又是什么呢?试想一下,如果物体的某些面超出了视锥(比如镜头进入一个球体内部,必定有部分面是处于近平面的后方而被剔除的)。如果被剔除的三角面有部分顶点是在视口内部的,就会出现破面的问题(如下图左边黑色的区域)。
Clipping是用切分面的方法(通过计算三角面与视锥平面之间的交点来生成新的三角面)来解决由于culling产生的破面问题。在PV3D中Clipping是通过FrustumClipping类来实现的:
ender.clipping = new FrustumClipping(FrustumClipping.ALL);
该类需要的唯一参数表示视锥的某些面。视锥的每个面都可以参与计算Clipping,但是这么做资源耗费很大,一般情况下只要计算近平面Clipping就足够了:
ender.clipping = new FrustumClipping(FrustumClipping.NEAR);
通过下面两张图可以对比一下两种计算方式的差别:
ALL:
NEAR:
两种计算方式都解决了破面问题,可以看到仅使用近平面Clipping的方式效率更高一些。
虽然我们通过Culling减轻了渲染计算量,但是也带来了破面的副作用。然后又通过Clipping计算来解决这个问题,只要设置合理,还是能节约大量的 CPU资源。记住上面提到过的在PV3D中超过近平面的三角面是确定一定以及肯定会被剔除的,所以在某天你肯定会遇到破面的问题,在你打开 Clipping功能前,考虑以下三个建议:
- 优先考虑仅近平面Clipping,大部分情况下足够了。
- 如果可以肯定某物体不会超出视锥范围,可以设置其useClipping属性为 false来排除计算。
- 配合设置camera.useCulling = true。记住在一个渲染流程中,Culling在Clipping之前检测。如果整个物体都被剔除,那么也就没必要检测基于面的Clipping了。只有当物体与视锥相交时才有必要计算Clipping。
参考资料: