问题描述:
在3D场景中使用Spine模型(2D), 因为Spine需要像Billboard类似的处理,永远是垂直与相机的, 所以一般情况下2D模型能正常显示且前后的阻挡关系也是没有问题的,如下图所示,一切都很美好.(这里用一个四边形,图中的福字表示2D模型)
可是如果后面蓝色的格子比较高,或者2D角色本身也比较高,那么就会出现模型穿插的问题,如下图所示:
如果吧2D模型竖立起来,前后关系肯定是没有问题的,所以也能避免模型穿插的问题,可是就没有Billboard效果,圆形的福字已经被压扁了,如下图:
所以现在的解决办法是,2D模型还是斜着放(不让这里的福压扁),而做深度检测的时候用竖立起来的那个模型所在的深度值(避免模型穿插问题,使前后关系看起来是正确的)
怎么才能既保证2D模型的正常显示(Billboard效果),又能有预期的前后遮挡关系(不穿模),鱼和熊掌是可以兼得的,解决思路来源:https://www.bilibili.com/video/BV1rr4y1c7wz
解决方案:
使用斜着的2D模型经过MVP变化后的结果进行渲染,但是在顶点变换的最后,需要用上面竖立时所处的深度值修改顶点的深度值,就能欺骗过VertShader与FragmentShader之间的深度检测环节,就可以达到预期想要的遮挡关系,防止前后穿模。
下面是将2D模型竖立起来,沿着2D模型的脚下旋转到垂直:
需要注意的是我们是从侧面来看,则是沿着ZY平面进行旋转的,所以需要先找到旋转的中心点(脚下),如这个四边形的旋转中心就是(z=0,y=-0.5),先将模型平移到旋转中心,然后进行ZY平面的旋转(角度和实际相机角度相关联),旋转结束后再做反向的平移操作即可, 而一般的spine模型制作应该都是将脚下的点设为锚点,所以也就可以省去旋转前后的两次平移操作。
完整的VertShader代码:
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
//在MVP变换之后再进行旋转操作,并修改顶点的Z值(深度)
//弧度
fixed radian = _angle / 180 * 3.14159;
fixed cosTheta = cos(radian);
fixed sinTheta = sin(radian);
//旋转中心点(测试用的四边形, 正常的spine做的模型脚下旋转的点就是(0,0), 可以省去下面这一步已经旋转完成后的 +center操作)
half2 center = half2(0, -0.5);
v.vertex.zy -= center;
half z = v.vertex.z * cosTheta - v.vertex.y * sinTheta;
half y = v.vertex.z * sinTheta + v.vertex.y * cosTheta;
v.vertex = half4(v.vertex.x, y, z, v.vertex.w);
v.vertex.zy += center;
float4 verticalClipPos = UnityObjectToClipPos(v.vertex);
o.vertex.z = verticalClipPos.z / verticalClipPos.w * o.vertex.w ;
return o;
}
最终效果(Game窗口)达到预期: