《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=SrcAlpha∗Srgb+(1−SrcAlpha)∗DrgbOa=SrcAlpha∗Sa+(1−SrcAlpha)∗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=(1−DstColor)∗Srgb+DrgbOa=(1−DstColor)∗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=DstColor∗SrgbOa=DstColor∗Sa
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=DstColor∗Srgb+SrcColor∗DrgbOa=DstColor∗Sa+SrcColor∗Da
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=(1−DstColor)∗Srgb+DrgbOa=(1−DstColor)∗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的图层类型哈哈哈