【Unity Shader】(8)透明效果 透明度测试 和 透明度混合 控制 深度缓冲 与 颜色缓冲 并调整 渲染顺序

Unity Shader实现全透明半透明效果



实现透明物体的两个方法:

对于不透明物体,我们不必考虑它们的渲染顺序也可以得到正确的渲染结果。因为Unity中强大的深度缓冲(Depth Buffer 或称作 Z-Buffer)可以帮助我们完成物体的前后效果。

对于半透明物体则需要使用透明度混合,且需要正确的渲染顺序


  • 透明度测试(Alpha Test)

使用的是一个极端霸道的机制——只要一个片元的透明度不满足设定的阈值,那么直接丢弃该片元。被丢弃的片元不会再进行任何处理(包括颜色缓冲);但是如果满足设定的阈值,那么就会继续按照平常的不透明物体的渲染方式来处理它(即进行深度测试、深度写入(ZWirte)等)。也就是说透明度测试不需要关闭深度写入
——最终的结果 要么完全透明(看不见);要么完全不透明(就是不透明)。

  • 透明度混合(Alpha Blending)

该方法是可以得到真正的半透明效果!它会将当前的片元的透明度作为混合银子,与存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入!这就要我们格外小心物体的渲染顺序(Render Queue)。但此时是没有关闭深度测试的。也就是说——对于透明度混合来说,深度缓冲是只读的
因此,当使用透明度混合来渲染一个片元的时候,还是会比较他的深度值与深度缓冲中的深度值,如果距离摄像机更远,那么就不会再进行混合操作;如果距离摄像机更远,那么另一个不透明物体就会被渲染到该片元前面将其遮挡,最终的效果也是正常的。




深度缓冲的工作原理:

对于每一个开启了深度测试的片元,在渲染之前会将自身的深度值深度缓冲中的值进行比较,最终会得到两个结果:

  • 距离摄像机更远的片元——不被渲染
  • 距离摄像机更近的片元——会被渲染

这是因为,在深度测试之后,如果片元的深度值更近,那么会覆盖深度缓冲中原有的深度值,且直接丢弃原有片元,最终只渲染深度缓冲中的片元。

最终我们的物体有了如下两种情况:

  • 半透明物体A 远于 不透明物体B

最终Depth_B会被覆盖深度缓冲中的值,这样可以的得到正确的半透明效果。

  • 半透明物体A 近于 不透明物体B

那么对于不同的渲染顺序,会有两种情况:

①如果先对A进行深度测试,再到B——由于A是半透明物体关闭了深度写入,那么A不会对深度缓冲写入数据。到物体B后,由于此时深度缓冲中并没有数据,那么物体B的深度值DepthB会写入到深度缓冲之中。最后的效果回事B再A的前面,最后得到的结果是恰恰相反的,是错误的。

②如果先对B进行深度测试,再到A——DepthB会首先填入深度缓冲之中,而之后A由于关闭了深度写入,因此不会刷新覆盖深度缓冲,最终的渲染效果与上面情况类似(都是错误的)。


综上,我们应该在不透明物体渲染完之后,再渲染半透明物体。

【Unity Shader】(8)透明效果 透明度测试 和 透明度混合 控制 深度缓冲 与 颜色缓冲 并调整 渲染顺序_第1张图片



渲染顺序的重要性

对于两个不透明物体,可以参照上述深度缓冲的工作原理。但是由于我们的半透明物体是关闭深度写入的!
【Unity Shader】(8)透明效果 透明度测试 和 透明度混合 控制 深度缓冲 与 颜色缓冲 并调整 渲染顺序_第2张图片

如果:半透明物体A 近于 半透明物体B
并且:根据不同的渲染顺序
那么:可以得到如下两个渲染结果:

  • 先渲染B,再渲染A——物体B的颜色值ColorB会首先正常写入颜色缓冲中(半透明物体),然后ColorA会和颜色缓冲中的ColorB进行颜色混合。最终,可以得到正确的半透明效果。
  • 先渲染A,再渲染B——ColorA先写入颜色缓冲,再到ColorB与ColorA进行混合。结果会是相反的,也就是看上去是物体B在物体A前面,显然是错的!


正确的渲染“姿势”

!① 先渲染所有不透明物体,并开启不透明物体的深度测试和深度写入
!② 将半透明物体按照从后往前的顺序渲染,并且开启深度测试 关闭深度写入



Unity Shader的渲染顺序

为了解决上述的②,Unity为此提供了渲染队列(Render Queue)来解决这个问题

【Unity Shader】(8)透明效果 透明度测试 和 透明度混合 控制 深度缓冲 与 颜色缓冲 并调整 渲染顺序_第3张图片



在Shader代码中的实现方法:

使用透明度测试实现全透明效果的代码:

SubShader{
	Tags{"Queue"="AlphaTest"}
	Pass{
		...
	}
}

使用透明度测试实现半透明效果的代码:

SubShader{
	Tags{"Queue"="Transparent"}
	//ZWirte Off
	Pass{
		ZWirter Off
		//关闭深度写入
		//如果写在SubShader之下,那么会对SubShader之下的所有Pass起作用
		//如果写在Pass之下,仅对该Pass起作用
		
		...
	}
}





代码实现

你可能感兴趣的:(Shader,TA,untiy,图形学,着色器,Unity,Shader,TA)