赞
踩
首先解释一下什么是odd negative scaling,假设一个物体transform的scale为 ( a , b , c ) (a, b, c) (a,b,c),如果 a ∗ b ∗ c < 0 a*b*c<0 a∗b∗c<0那么该物体就具有odd negative scaling,即奇数个负缩放轴。例如 ( − 1 , 1 , 1 ) (-1,1,1) (−1,1,1)和 ( − 1 , − 1 , − 1 ) (-1,-1,-1) (−1,−1,−1)都是odd negative scaling, ( − 1 , − 1 , 1 ) (-1,-1,1) (−1,−1,1)则不是。
根据Unity文档的说明,具有odd negative scaling的物体是不可以被动态合批的。更准确地说,多个具有odd negative scaling的物体是可以被合批的,但是具有odd negative scaling的物体和不具有odd negative scaling的物体之间是无法合批的。
为什么一个简单的缩放就能够导致合批失败呢?首先无法动态合批的原因通常都是发生了render states的改变。因为动态合批是需要把多个物体合并到1个Draw Call中去的,所以被动态合批的物体必须具有相同的render states。
也就是说,渲染具有odd negative scaling的物体和渲染不具有odd negative scaling的物体,必然有render states的不同,这个不同的点在OpenGL中叫做glFrontFace,在DirectX中叫做D3DRS_CULLMODE,它的两种选择CW和CCW,分别表示顺时针和逆时针,决定了如何通过三角面顶点的环绕顺序(winding order)得到三角面的法线。
假设在Unity中,如上图左侧,有一个 s c a l e ( 1 , 1 , 1 ) scale(1,1,1) scale(1,1,1)的三角形,其顶点环绕顺序为 0 → 1 → 2 0→1→2 0→1→2。左手四指顺着顶点环绕方向,大拇指则指向面法线的方向,垂直屏幕向外。
接下来我们将这个三角形缩放为 s c a l e ( − 1 , 1 , 1 ) scale(-1,1,1) scale(−1,1,1),如上图右侧。此时如果用同样的方式去计算法线,右侧三角形的法线应该是垂直屏幕向内,朝向我们的变成了三角形的背面。
但实际上只要打开Unity试一试就知道,如果我们把一个三角形这样子缩放,原本朝向我们的正面仍然是正面,并没有发生改变。这是因为Unity在渲染左侧三角形时,采用了CW的方式计算法线,而在渲染具有odd negative scaling的右侧三角形时,采用了CCW的方式判断面法线,其结果是和CW相反的。换句话说,对于具有odd negative scaling的物体,Unity通过CCW的模式翻转了面法线。
那么Unity为什么要这样做呢?我的理解是因为这种方式让scale操作更加符合操作者的直觉。不仅Unity这样做,在建模软件中也有类似的做法。如上图的三角形,我们横向/竖向地拉伸scale至负值,面法线永远保持垂直屏幕向外;如果我们在垂直屏幕的方向上给它一个负的scale,它的法线就会翻转。这些结果非常符合直觉,而且方便使用。
要验证这一点也很容易,在场景中摆放一个具有odd negative scaling的物体和一个不具有odd negative scaling的物体,用RenderDoc抓帧,在Rasterizer栏下的FrontCCW项就是我们所说的状态。可以看到在绘制这两个物体时该状态是不同的。
总结一下,odd negative scaling会导致glFrontFace/D3DRS_CULLMODE状态的改变,因此不能与不具有odd negative scaling的模型一同动态合批。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。