先从最开始就一直萦绕在心里的困惑说起,在Unity Documentation中降到Draw call batching( https://docs.unity3d.com/Manual/DrawCallBatching.html )时,对Dynamic batching的限制有这样一段描述:
GameObjects are not batched if they contain mirroring on the transform (for example GameObject A with +1 scale and GameObject B with –1 scale cannot be batched together).
这里其实我一直不解,为什么mirroring了会打断Dynamic batching,因为Dynamic batching和Static batching本质上是一样的,都是把各自模型的顶点转换到世界空间,再合并成一个Mesh,跟Mirroring有什么关系(mirroring的信息都在worldTransformMatrix里面,顶点转换到世界空间后,这个信息就在顶点自身上了)。不过也一直没有去深究其中的原因。
直到自己做模型静态合并的时候,美术在场景中将一个模型Transform的ScaleY设置成了负数,我将其顶点转到世界空间后合并成新的模型,发现模型正反面跟Unity中的不一样,从图形学角度来看,我这个正反面才是正确的。
满怀期待地用Frame Debugger抓了一下,Cull都是Back啊,这就有点颠覆我的认知了。如果没有改CullMode的话,难不成Unity还把顶点索引顺序变掉了,那样的话我就要鄙视他了。总感觉Frame Debugger抓的不靠谱,有些信息没有完全显示出来(后话,看完后面才知道它显示的根本就不是最终GPU真正渲染管线的状态)。于是打成exe包,用NSight抓DX的渲染设置:
果然,Unity偷偷地将CullMode换掉了,这就能说明之前的现象了。经过测试,当有一个轴缩放为负时,CullMode会设置为相反,当有两个轴缩放为负时,CullMode不会改变,当三个轴都为负时,又会设置为相反。可见,Unity处理的原则是,只要缩放导致坐标系从左手(Unity世界坐标系)变到了右手,就会将CullMode设置为相反,无论你缩放怎么变,只要坐标系左右手没变,就不用做改变。这也能解释最上面说到的Mirroring为什么会导致Dynamic batching打断了,虽然材质都是一样的,但是渲染状态发生了变化,就无法合批了。然后也告诉了我们,Frame Debugger中显示的不是GPU的渲染状态,而是Shader里设置的渲染状态。