在最近的Shader学习中,学习到了深度写入ZWrite和深度测试ZTest,其中关于两个物体的深度测试全部设置为Always,在渲染上会出现一些让我困惑的点,在这里进行分享。
总所周知,unity对于不透明的物体渲染是按照里摄像机深度来判断的,优先渲染距离摄像机近的物体。而深度测试是用于对渲染物体进行测试,判断他是否需要渲染到屏幕上,如果说将ZTest设置为always意味着物体始终通过测试,在屏幕上一直显示该物体。
如上图所示,我们将一个cube放置到蓝色平面上并将设置蓝色平面的深度写入和深度测试ZWrite on,ZTest always,此时我们将摄像机从上往下看,我们发现尽管cube在蓝色平面上,但屏幕上显示的却是蓝色平面,没有显示cube,这是因为蓝色平面的ZTest设置为always所以覆盖了cube的显示。
于是此时本人想到了一个问题,如果说将两个物体都设置为ZTest always,其渲染结果是什么样子的呢,下面介绍我的测试。
在此场景中,我们放置了两个平面,其中蓝色平面在红色平面的上面,同时创建两个材质,用上相同的shader,并且全部开启ZTest always
此时摄像机的显示为红色平面
这符合我的认知,首先unity对近处的蓝色平面进行渲染,发现他开启了深度写入把自己的颜色写入到缓存中,同时设置深度测试always,将蓝色平面绘制到屏幕上。然后对红色平面开启深度写入,并设置深度测试为always,此时红色平面将蓝色平面覆盖,所以最终在屏幕上显示的是红色。我们查看帧调试面板验证一下。
发现结果和我预料的一样。本来这次测试就到此结束了,但是我无意间调节了摄像机的高度,发现在只改变摄像机位置的情况下出现了渲染在红色前面的情况。
我开始疑惑了,为什么只调节了摄像机的高度却发生了不同的渲染效果,而且在我的认知里面第二种结果应该是一个错误的渲染结果,于是带着疑问我打开了帧调试面板查看。
这一看我更加疑惑了,untiy居然先渲染的是红色平面,再渲染蓝色平面,也就是先渲染的远处的物体,再渲染的近处物体,这与unity对不透明物体先渲染近处物体再渲染远处物体完全相反。
我开始怀疑是不是我对渲染机制出现了理解错误,于是我开始排查问题原因,在一次测试中我交换了两个平面的材质。
交换后
然后我意外的发现问题解决了,渲染顺序正确了,现在无论我怎么移动摄像机都是远处的物体先渲染。我们打开帧调试器查看。
这此的渲染结果是正确的,因为更换了材质后蓝色在远处,红色在近处。
此时我更加疑惑了,明明是两个相同的材质,用的是相同的shader,只是颜色不一样,为什么会出现交换材质渲染结果不同的情况。
于是我猜想是不是和材质的创建时间有关,于是我对ZWriteA材质赋值了4份
经过测试发现居然真的和创建时间有关系,如果说先创建的材质放在了远处的平面,后创建的材质放在了近处的平面就会出现渲染错误。如果先创建的材质放在了近处的平面,后创建的材质放在了远处的平面渲染就正确了。但是我猜测untiy应该是不会识别材质创建的时间的,里面肯定有更深层的原因,于是我打开了材质面板的debug模式。我发现这五个材质的Instance ID是以此增高的
好的,现在真相出来了,当Instance ID大的材质放在了近平面,Instance ID小的材质放在远平面时会出现渲染错误,后面对于帧调试结果我就不放出来了。本次测试的结论就是这个,但是我无法理解为什么渲染结果会和材质的Instance ID有关,以及为什么深度测试开启always后,如果选择了错误的材质顺序,会影响到unity对不透明物体的渲染顺序,在我的理解中就是应该近处物体先渲染,远处物体后渲染,深度测试只是对缓冲区的深度进行筛选,应该不会改变渲染的顺序。不管怎么样,本文提醒一下大家在使用ZTest always后注意材质顺序。同时希望有没有大佬可以帮我解答一下这个困惑,感谢。