《Unity Shader入门精要》自学笔记(五)第八章 透明效果

官方API:ShaderLab: Blending

这一章的内容巨多啊,信息量比五六七章加起来还多

不过整清楚渲染顺序、深度测试、深度写入、透明度测试、透明度混合,以及它们对渲染的影响,这一章理起来也就顺了

杂七杂八但是很重要的知识点

在这里插入图片描述

深度缓冲区Z-Buffer
存储屏幕上的每个像素当前的深度值,值越大,离屏幕越远

深度测试
将当前片元的深度值与深度缓冲区中的深度值比较,若比深度缓冲区中的值大,则说明这个片元会被之前渲染过的片元挡住,没有必要再继续渲染了,所以会被丢弃(当然也可以手工设置远被舍弃,还是近被舍弃,还是一些其他的判断标准)

深度写入ZWrite
当一个片元通过了深度测试,并且pass开启了深度写入,这个片元的深度值就会覆盖深度缓冲区中的深度值;否则不会对深度缓冲区做修改

颜色缓冲区和颜色写入

通过了各种测试的片元会将自己的颜色存入颜色缓冲区(前提是开启了颜色写入)。当有新的片元通过了重重测试,会根据渲染模式来决定,是直接用新片元的颜色覆盖颜色缓冲区,还是将新老颜色根据透明度做一个混合

透明度测试和透明度混合

透明度测试
没通过透明度测试,片元也会被直接丢弃。但和深度测试不一样的是,它没有缓冲区,只是将片元的透明度和一个设定的值相比较,是否满足一个设定的条件(< > = ≥ ≤ 等),不满足直接丢弃
在这里插入图片描述
也就是“要么看得见要么看不见”的简单粗暴效果,还会因为浮点数精度问题有明显的锯齿

一般会使用Clip()函数:给定参数的任意一个分量是负数,都会舍弃该片元

透明度混合
简单来讲,它要做的就是上面提到的“颜色缓冲区新老颜色按照透明度进行混合”,这个才是比较像真正的透明效果

在这里插入图片描述
 

渲染顺序、关闭深度写入后的一系列填坑操作

渲染顺序
就是渲染不同类型物体的顺序:背景、不透明物体、透明物体等
unity中准备了5个队列,为SubShader提供了Queue标签:
在这里插入图片描述

开启深度写入的情况下,完全不用关心渲染顺序,因为每个片元的覆盖与否都会经过比较得出结果
但是为了获得透明效果,需要在渲染透明物体时关闭深度写入,这个时候就体现了渲染顺序的重要:
如果透明物体A和不透明物体B一前一后,先渲染了透明物体A,B来的时候A并没有把深度值写入,所以B按照流程理所应当的把自己的颜色整个填进了颜色缓冲区,覆盖了A的颜色,并且没有执行透明度混合
如果先渲染了不透明的B,再渲染透明的A的话,A发现自己没被遮挡后,会执行透明度混合,达到透明的效果

谁让不透明物体的shader就是不会管这么多呢,关闭深度写入和透明度混合只写在透明物体自己的shader里呀,那只能你殿后了鸭

那…

为啥要关闭深度写入
首先,深度写入被关闭的是透明物体
因为我们是可以看到透明物体背后的其他物体的,也就是说我们不能直接丢弃深度值大于透明物体的物体
一旦开启了深度写入,深度检测就会丢弃透明物体后面的物体
因此只好关闭深度写入了,虽然带来了一系列的麻烦

开启深度写入的透明效果

一些情况下就算安排好了渲染顺序,还是会有意外发生

因为渲染顺序是针对每一个模型的,而每一个模型可以是各种形状,就导致了模型之间的前后顺序并不绝对
在这里插入图片描述

而对于一些本身就比较复杂的模型,自己一个人也可以玩出这种错乱效果

在这里插入图片描述

这时可以将物体切分成多个网格,但是还是无法完全避免穿帮,因为它依然不是针对每一个像素进行深度测试

或者,干脆在不影响透明度混合的前提下把可爱的深度写入安回来:

现在的shader是这样的

SubShader
{
    
    
	Tags{
    
    "Queue" = "Transparent", "IgnoreProjector" = "True", "RenderType" = "Transparent"}
	Pass
	{
    
    
		Tags{
    
    "LightMode" = "ForwardBase"}
		ZWrite Off
		Blend SrcAlpha OneMinusSrcAlpha
		
		// ...
	}
}

嗯?ZWrite off和Blend命令写在了Pass里,那再写一个ZWrite On的命令在另一个Pass里不就好了嘛!先做个深度测试,再上色!(代价是需要多渲染一遍,因为增加了一个pass)

SubShader
{
    
    
	Tags{
    
    "Queue" = "Transparent", "IgnoreProjector" = "True", "RenderType" = "Transparent"}
	Pass
	{
    
    
		ZWrite On
		ColorMask 0
	}
	Pass
	{
    
    
		Tags{
    
    "LightMode" = "ForwardBase"}
		ZWrite Off
		Blend SrcAlpha OneMinusSrcAlpha
		
		// ...
	}
}

效果:
不会错乱了,但是细节也少了:

在这里插入图片描述
自己被自己遮挡的部分完全看不见了,只能看见下面的不透明平面

在这里插入图片描述
被另一个透明物体遮挡的部分也看不见了,只能看见下面的不透明平面

这些都是因为被遮挡的片元被深度测试卡掉了

双面渲染的透明效果 —— Cull命令

上面的透明效果都是只能看到正面,物体内部和背面的情况完全看不到,是因为引擎自动剔除了物体背面(相对于摄像机的方向)的渲染图元

想要双面效果,就要渲染出背面的图元,unity中要用到Cull指令:

Cull Back | Front | Off

分别代表剔除背面(默认情况)、前面、关闭剔除

对于双面的透明度测试,只要在Pass内关闭剔除就可以了

但是对于双面的透明度混合,就要考虑到渲染顺序,谁让万恶之源是ZWrite Off呢…

由于Pass是按顺序执行的,所以可以将背面和正面的渲染分开到先后两个Pass中,使用Cull Front和Cull Back分别渲染

在这里插入图片描述

不过这样物体之间的顺序还是乱的,因为没有了深度写入

混合类型:混合因子、混合命令

应该会有很多高级玩法待开发呢

在这里插入图片描述
混合因子就是混合命令中的SrcFactor和DstFactor,分别代表源颜色(该片元的颜色)的混合因子和目标颜色(颜色缓冲区中的颜色)的混合因子
表格第三行的命令写成式子就是:
O r g b O_{rgb} Orgb = S r c F a c t o r SrcFactor SrcFactor * S r g b S_{rgb} Srgb + D s t F a c t o r DstFactor DstFactor * D r g b D_{rgb} Drgb
O a O_a Oa = S r c F a c t o r A SrcFactorA SrcFactorA * S a S_a Sa + D s t F a c t o r A DstFactorA DstFactorA * D a D_a Da
表格第二行的命令中SrcFactorA就是SrcFactor,DstFactorA就是DstFactor

混合因子SrcFactor、DstFactor可以是什么呢
在这里插入图片描述
emm其实就是按照目标值&源值、rgb值&alpha值、原值&1-原值、0&1啦…

混合操作BlendOp Operation都有啥?
BlendOp Add就是默认的混合操作,需要改变的话要在指定混合因子前添加BlendOp命令

在这里插入图片描述
可以看出Min和Max只与源颜色和目标颜色的rgba值相关,所以在用他们的时候混合因子可以直接指定为One或者Zero

未列出的DirectX混合操作见官方API:ShaderLab: Blending

混合类型有哪些呢?
书中和工程文件中提供了一些常见的类型
在这里插入图片描述
再分开看看
在这里插入图片描述

Blend SrcAlpha OneMinusSrcAlpha

O r g b = S r c A l p h a ∗ S r g b + ( 1 − S r c A l p h a ) ∗ D r g b O a = S r c A l p h a ∗ S a + ( 1 − S r c A l p h a ) ∗ D a \begin{aligned} O_{rgb} = SrcAlpha * S_{rgb} + (1 - SrcAlpha )* D_{rgb}\\ O_a = SrcAlpha * S_a + (1 - SrcAlpha ) * D_a \end{aligned} Orgb=SrcAlphaSrgb+(1SrcAlpha)DrgbOa=SrcAlphaSa+(1SrcAlpha)Da

在这里插入图片描述

Blend OneMinusDstColor One

O r g b = ( 1 − D s t C o l o r ) ∗ S r g b + D r g b O a = ( 1 − D s t C o l o r ) ∗ S a + D a \begin{aligned} O_{rgb} = ( 1 - DstColor)* S_{rgb} + D_{rgb}\\ O_a = ( 1 - DstColor)* S_a + D_a \end{aligned} Orgb=(1DstColor)Srgb+DrgbOa=(1DstColor)Sa+Da

在这里插入图片描述

Blend DstColor Zero

O r g b = D s t C o l o r ∗ S r g b O a = D s t C o l o r ∗ S a \begin{aligned} O_{rgb} = DstColor * S_{rgb}\\ O_a = DstColor * S_a \end{aligned} Orgb=DstColorSrgbOa=DstColorSa

在这里插入图片描述

Blend DstColor SrcColor

O r g b = D s t C o l o r ∗ S r g b + S r c C o l o r ∗ D r g b O a = D s t C o l o r ∗ S a + S r c C o l o r ∗ D a \begin{aligned} O_{rgb} = DstColor * S_{rgb} + SrcColor* D_{rgb}\\ O_a = DstColor * S_a + SrcColor* D_a \end{aligned} Orgb=DstColorSrgb+SrcColorDrgbOa=DstColorSa+SrcColorDa

在这里插入图片描述

BlendOp Min
Blend One One

O = ( m i n ( S r , D r ) , m i n ( S g , D g ) , m i n ( S b , D b ) , m i n ( S a , D a ) ) \begin{aligned} O = (min(S_r, D_r), min(S_g, D_g), min(S_b, D_b), min(S_a, D_a)) \end{aligned} O=(min(Sr,Dr),min(Sg,Dg),min(Sb,Db),min(Sa,Da))

在这里插入图片描述

BlendOp Max
Blend One One

O = ( m a x ( S r , D r ) , m a x ( S g , D g ) , m a x ( S b , D b ) , m a x ( S a , D a ) ) \begin{aligned} O = (max(S_r, D_r), max(S_g, D_g), max(S_b, D_b), max(S_a, D_a)) \end{aligned} O=(max(Sr,Dr),max(Sg,Dg),max(Sb,Db),max(Sa,Da))

在这里插入图片描述

Blend OneMinusDstColor One
(Blend One OneMinusSrcColor)

O r g b = ( 1 − D s t C o l o r ) ∗ S r g b + D r g b O a = ( 1 − D s t C o l o r ) ∗ S a + D a \begin{aligned} O_{rgb} = (1 - DstColor)* S_{rgb} + D_{rgb}\\ O_a = (1 - DstColor)* S_a + D_a \end{aligned} Orgb=(1DstColor)Srgb+DrgbOa=(1DstColor)Sa+Da

在这里插入图片描述

Blend One One

O r g b = S r g b + D r g b O a = S a + D a \begin{aligned} O_{rgb} = S_{rgb} + D_{rgb}\\ O_a = S_a + D_a \end{aligned} Orgb=Srgb+DrgbOa=Sa+Da

总结
感觉自己的笔记记得越来越乱了…果然知识多了就要勤记着点…
透明度混合这里有很多可以做实验的地方,各种混合操作的效果还不是特别有印象,抽空要多玩玩这里,回来补一下笔记

这一章终于开始更多的接触到tags啦,以及多个pass相互协助的使用,也让shader变得有趣起来了,后面的学习一定也会更有意思的吼吼吼!
也算是终于好好的理解一把ps的图层类型哈哈哈

猜你喜欢

转载自blog.csdn.net/weixin_44045614/article/details/109705085