从零实现3D图像引擎:(14)背面消隐的三大陷阱

    技术2022-05-20  54

    1. 为什么要背面消隐

    通过之前的DEMO,能够知道如果在渲染过程中多边形越多,那么要不处理的内容就越多,就越消费计算机的处理能力。对于物体来说,一般我们只看到它面对我们的面,可能不是正对着,但是肯定有很多面是完全背对我们的,我们就应该在渲染时跳过他们。这将减少大约一半的三角形渲染量。原理其实很简单,就是向量与平面的关系,这个原理我在参数化直线与平面的那篇文章中特意推导了整个来龙去脉。

    我喜欢在2D俯视图中来分析问题,如下图:

     

     

     

    2. 注意事项一:背面测试的是“视线与面朝向”的关系,而与相机朝向不直接相关

    在刚接触背面消隐时,我以为是拿相机的朝向向量和面的法向量来求夹角,虽然也可以消除一些面,但发现是不对的,因为存在下面的这种情况:

     

    上面红色的面和绿色的面的法向量是相同的,但是一个面是正面,一个面是背面。因为相机朝向是固定的,所以对于这种情况,相机朝向与面法向量求夹角而得出的结论是不正确的。

     

    在图中可以发现,如果我们任取面上一点,与相机的位置做连线,那么这种情况就可以被区分出来。因为视线向量与法向量的夹角不同,一个是锐角,一个是钝角。(乍看都是锐角,把他们的起始点都移到原点就能看出区别了)

     

    而且可以注意到,无论取面上的哪一点来做连线都是正确的。

     

     

     

    3. 注意事项二:求面法向量的结果与“顶点的存储顺序”息息相关,各面的顶点存储顺序必须一致

    在做背面消隐时有一段时间怎么调都调不对,我觉得肯定没问题的东西,怎么会不对呢?

    于是我在纸上推算了半天,发现一个致命的错误——我每个三角形的顶点存储顺序是任意的,造成法向量的不一致性。

    看下图:

    如果求一个面的法向量,最简单的方法是得到两个向量,然后把它们做叉乘。比如上面的三角形OAC来说,可以用点A减去点O,得到OA向量。点C减点O得到OC向量,然后用OA×OC,得到法线向量,这里隐含了一个重要的数学性质,那就是叉乘不满足交换律。

    即:OA×OC != OC×OA

     

    因为当我们拿到三个顶点的时候,是不知道这三个顶点怎么排序的,那么如果某个面1是逆时针来存储的,如:O-A-C,又有某个面是顺时针来存储的,如O-C-A,那么在求叉乘时,结果会截然不同。

     

    所以对于各个面的顶点存储时,一定要约定好顺序,比如都进行下面的步骤:

    1) 从物体的外面来看这个面,我们正对这个面

    2) 逆时针来存储这些点(顺时、逆时无所谓,但所有面应统一)

     

    示例1:

    对于面OAC,我们首先在物体的外面来看这个面,而不是在物体内部来看。然后逆时针来存储这些点,则存储顺序为:O-A-C

     

    示例2:

    对于面ABC,我们首先在物体的外面来看这个面。然后逆时针来存储这些点,则存储顺序为:A-B-C,而不是A-C-B

     

    正是因为这个原理,所以如果把3ds max的物体导出之后,就可以发现3ds存储的物体面中的顶点存储顺序都是满足上面一致性约定的。

     

     

     

    4. 注意事项三:顶点存储顺序、向量叉乘顺序、视线向量的微妙关系

    如果要写出正确的背面测试函数,就必须要把这三因素的关系搞明白,因为有一个不正确,你的结果就是相反的。(如果有两个搞错,反而会是正确的,有趣吧,但是可能计算的法向量是错误的,而影响以后的光照计算,所以我们还是要把这个搞清楚)。

     

    这里给出一个正确的示例,可以有所体会:

    1) 顶点存储顺序约定:从物体外面来看,逆时针存储

    2) 在准备做叉乘时,可以拿到已经排好序的3个顶点(这里假设为p0,p1,p2),统一约定使用u = p1 - p0,v = p2 - p0,也就是说从u转到v,也是一个逆时针旋转。因为p0->p1->p2是逆时针。

    3) 求u×v,得到面法向量n,因为点的存储的统一性和u->v的转向相同,所以这时的面法向量是正确的,是朝向物体外面的。

    4) 视线向量view用相机坐标减去面上任意一点,这时这个向量是从三角面指向相机的。

    5) 求n与view的点积,如果点积大于0,则为锐角,是正面,如果点积小于0,则为钝角,是背面。这个性质的推导在向量函数库的那篇文章中有介绍。

     

     

     

    5. 总结

    至此我们的背面消隐函数终于是正确的了。在《3D大师》这本书中,虽然他代码是正确的,可是翻译出来的解释真的有时让人摸不着头脑,以后有时间还是看原版吧~这次如果我不是重新推导了一遍,光看那本书所说的文字真的是不会搞明白这里面的玄机。而且这次对写BLOG的认识又加深了,在写这篇文章的同时我又发现了新的认识,又发现了代码中隐藏在其中的逻辑错误,真爽。

     

     

     

    6. 代码下载

    这次的代码修改了上次的几个错误。

    1) 所有的顶点使用统一逆序的存储

    2) 使用视线而不是相机朝向来判断

     

    项目完整源代码下载:>>点击进入下载页<<

     

    效果和上次差不多,就不截图了。


    最新回复(0)