分享

《Unity Shader入门精要》总结 #第八章 透明效果

 印度阿三17 2018-09-28

Unity中实现透明效果通过透明度测试和透明度混合

深度缓冲用于解决可见性问题,决定哪个物体那些部分被渲染在前面,哪些物体被遮挡。使用深度缓冲可以不用关心不透明物体的渲染顺序,但若想实现透明效果,此时关闭了深度写入ZWrite。

透明度测试:霸道机制。片元透明度不满足条件,通常小于某个阈值,则其对应的片元将舍去。透明度测试不需要关闭深度写入,它和其他不透明物体最大的不同是它会根据透明度舍弃片元。结果极端,要么完全透明看不到,要么完全不透明。

透明度混合:得到真正的半透明效果。使用当前片元透明度作为混合因子与已经存储在颜色缓冲中的颜色进行混合得到新的颜色。但是透明度混合需要关闭深度写入,必须小心物体的渲染顺序。深度缓冲只读。

1、渲染顺序

半透明物体背后可以被看到,但开启的话会被直接剔除,但关闭的话会破坏深度缓冲的工作机制。

EG:

A-半透明物体开启颜色写入,关闭深度写入  B-不透明物体,开启写入颜色缓冲和深度缓冲

-若先渲染B再渲染A,B首先会写入颜色缓冲和深度缓冲。随后渲染A,透明物体仍会进行深度测试,发现A比B更近,因此使用A的透明度来与颜色缓冲中B的颜色进行混合

-若先渲染A,深度缓冲中无任何数据,A直接写入颜色缓冲,但对半透明物体关闭深度写入,A不修改深度缓冲。B深度测试时将会认为是第一人而覆盖A的颜色

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

对于均为半透明物体A、B,A在前B在后

-先B后A,B写入颜色缓冲,A将和颜色缓冲中的B颜色进行混合,得到正确的半透明结果

-先A后B,A县写入颜色缓冲,随后B和颜色缓冲中的A混合,结果相反

渲染引擎对于物体的排序方法

(1)先渲染所有不透明物体,并开启他们的深度测试和深度写入

(2)把半透明物体按它们距离摄像机的远近进行排序,然后按照从后往前的顺序渲染,并开启深度测试,但关闭深度写入

2、Unity Shader的渲染顺序

渲染队列,SubShader的Queue标签

名称 队列索引号 描述
Background 1000 渲染队列会在其他队列之前被渲染,绘制需要绘制在背景上的物体
Geometry 2000 默认的渲染队列,大多数物体可使用这个队列。不透明物体使用这个序列
AlphaTest 2450 需要透明度测试的物体。在不透明物体渲染后再渲染这个队列会更高效
Transparent 3000 队列中的物体会在所有Geometry和AlphaTest物体渲染后再从后往前的顺序渲染。任何使用了透明度混合(例如关闭了深度写入的Shader)的物体都应使用该队列
Overlay 4000 用于实现一些叠加效果。最后渲染的物体。

通过透明度测试实现透明效果:

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

通过透明度混合实现透明效果:

SubShader{
    Tags{"Queue" = "Transparent"}
    Pass{
        ZWrite Off
    }
}

可写在Pass中,也可写在SubShader中,意味着所有Pass都会关闭深度写入。

3、透明度测试

透明度测试:只要有一个片元的透明度不满足条件,那么其对应的片元将会被舍弃。被舍弃的片元将不再进行任何处理,也不会对颜色缓冲产生任何影响,否则按照普通的不透明物体的处理方式来处理它。

使用clip函数进行透明度测试

如果给定参数的任何一个分量是负数,则舍弃当前像素的输出颜色

Shader "Unity Shaders Book/Chapter8/AlphaTest" {
	Properties {
		_MyColor ("Color", Color) = (1,1,1,1)
		_MyMainTex ("Main Tex", 2D) = "white" {}
		_MyCutoff("Alpha Cutoff", Range(0, 1)) = 0.5
	}
	SubShader {
		Tags { 	"Queue" = "AlphaTest"
				"IgnoreProjector" = "True"//不受到投影器影响
				"RenderType" = "TransparentCutout" //着色器替换功能
		}
		Pass{
			Tags{"LightMode" = "ForwardBase"}
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "Lighting.cginc"

				fixed4 _MyColor;
				sampler2D _MyMainTex;
				float4 _MyMainTex_ST;
				float _MyCutoff;

				struct a2v{
					float4 vertex : POSITION;
					float3 normal : NORMAL;
					float4 texcoord : TEXCOORD0;
				};

				struct v2f{
					float4 pos : SV_POSITION;
					float3 worldNormal : TEXCOORD0;
					float3 worldPos : TEXCOORD1;
					float2 uv : TEXCOORD2;
				};

				v2f vert(a2v v)
				{
					v2f o;
					o.pos = UnityObjectToClipPos(v.vertex);
					o.worldNormal = UnityObjectToWorldNormal(v.normal);
					o.worldPos = UnityObjectToWorldDir(v.vertex).xyz;
					o.uv = TRANSFORM_TEX(v.texcoord, _MyMainTex);
					//o.uv = o.uv.xy * _MyMainTex_ST.xy   _MyMainTex_ST.zw;

					return o;
				}

				fixed4 frag(v2f i) : SV_Target{
					fixed3 worldNormal = normalize(i.worldNormal);
					fixed3 worldLight = UnityWorldSpaceLightDir(i.worldPos);

					float4 texColor = tex2D(_MyMainTex, i.uv);
					clip(texColor.a - _MyCutoff);
					/*
					if((texColor.a - _MyCutoff) < 0.0){
						discard;
					}*/
					fixed3 albedo = texColor.rbg * _MyColor.rgb;
					fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
					fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLight));

					return fixed4(albedo   ambient   diffuse, 1.0);
				}

				ENDCG
			}
		}
	FallBack "Transparent/Sutout/VertexLit"
}

透明度测试所实现的透明度是完全透明的

4、透明度混合

关闭深度写入,使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合得到新的颜色。

需要使用Blend命令。

语义 描述
Blend Off 关闭混合
Blend SrcFactor DstFactor 开启混合,并设置混合因子,原颜色(该片元产生的颜色)乘以SrcFactor,目标颜色(已经存在与颜色缓存的颜色)会乘以DstFactor,两者相加后存入颜色缓冲
Blend SrcFactor DstFactor, SrcFactorA DstFactorA 和上面几乎一样,只是使用不同的因子来混和透明通道
BlendOp BlendOperation 并非把原颜色和目标颜色简单相加后混合,而是使用BlendOperation对它们进行其他操作

未在Pass中开启Blend模式,未设置混合因子。将源颜色的混合因子SrcFactor设置为SrcAlpha,而目标颜色的混合因子DstFactor设置为OneMinusSrcAlpha,则新颜色:

DstColor_{new}=SrcAlpha*SrcColor (1-SrcAlpha)*DstColor_{old}

Shader "Unity Shaders Book/Chapter8/AlphaBlend" {
	Properties {
		_Color ("Color Tint", Color) = (1,1,1,1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_AlphaScale("Alpha Scale", Range(0,1)) = 1
	}
	SubShader {
		Tags { 	"Queue"="Transparent"
				"IgnoreProjector" = "True"
				"RenderType" = "Transparent" }
		Pass{
			Tags{"LightMode" = "ForwardBase"}
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "Lighting.cginc"

			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			float _AlphaScale;

			struct a2v{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};

			struct v2f{
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TESSFACTOR2;
			};

			v2f vert(a2v v){
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldPos = UnityObjectToWorldDir(v.vertex).xyz;
				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target{
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldPos = normalize(i.worldPos);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

				fixed4 texColor = tex2D(_MainTex, i.uv);
				fixed3 albedo = texColor.rgb * _Color.rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
				
				return fixed4(ambient diffuse, texColor.a * _AlphaScale);
			}

			ENDCG

		}
		
	}
	FallBack "Transparent/VertexLit"
}

5、开启深度写入的半透明效果

-使用两个Pass,第一个Pass开启深度写入但不输出颜色,仅将该模型的深度值写入深度缓冲中。第二个Pass进行正常的透明度混合,又遇上一个Pass已经得到了逐像素的正确的深度信息,即可按照像素级别的深度排序结果进行透明渲染。性能降低

添加

Pass{
			ZWrite On
			ColorMask 0
		}

ColorMask用于设置颜色通道的写掩码:ColorMask RGB|A|0|其他任何RGBA组合

当ColorMask设置为0时,即意味着Pass不写入任何颜色通道,不输出任何颜色

6、ShaderLab的混合命令

着色器产生一个颜色时可以选择与颜色缓存中的颜色进行混合,与两个操作数相关:原颜色S、目标颜色D输出颜色O,均包含RGBA四个通道。

6.1 混合等式和参数

已知SD,欲得O,即混合等式。

语义 描述
Blend SrcFactor DstFactor 开启混合,并设置混合因子,原颜色(该片元产生的颜色)乘以SrcFactor,目标颜色(已经存在与颜色缓存的颜色)会乘以DstFactor,两者相加后存入颜色缓冲
Blend SrcFactor DstFactor, SrcFactorA DstFactorA 和上面几乎一样,只是使用不同的因子来混和透明通道

第一个提供了两个因子,即SrcFactorA = SrcFactor, DstFactorA = DstFactor,使用这些因子的混合公式:

O_{rgb}=SrcFactor*S_{rgb} DstFactor*D_{rgb}

O_{a}=SrcFactorA*S_{a} DstFactorA*D_{a}

ShaderLab支持的混合因子:

参数 描述
One 因子为1
Zero 因子为0
SrcColor 因子为源颜色值,当用于混合RGB的混合等式时,使用SrcColor的RGB分量作为混合因子;当用于混合A通道的混合等式时,使用SrcColor的A分量作为混合因子
SrcAlpha 因子为原颜色的透明度值(A通道)
DstColor 因子为目标颜色值,当用于混合RGB的混合等式时,使用DstColor的RGB分量作为混合因子;当用于混合A通道的混合等式时,使用SrcColor的A分量作为混合因子
DstAlpha 因子为目标颜色的透明度值(A通道)
OneMinusSrcColor 因子为(1-源颜色)。当用于混合RGB的混合等式时,使用结果的RGB分量作为混合因子;使用混合A的混合模式时,使用结果的A分量作为混合因子
OneMinusSrcAlpha 因为为(1-源颜色的透明度值)
OneMinusDstColor 因子为(1-目标颜色)。当用于混合RGB的混合模式时,使用结果的RGB分量作为混合因子;当用于混合A模式时,使用结果的A分量作为混合因子
OneMinusDstAlpha 因子为(1-目标颜色的透明度值)

使用上面指令进行设置时,RGB通道的混合因子和A通道的混合因子都是一样的,有时我们希望可以使用不同的参数混合A通道,此时可以利用Blend SrcFactor DstFactor, SrcFactorA DstFactorA。例如想在混合后输出颜色的透明度值就是源颜色的透明度,可使用

Blend SrcAlpha OneMinusSrcAlpha, One Zero

6.2 混合操作

可以使用ShaderLab的BlendOp, BlendOperation,即混合操作命令

操作

描述

Add

混合后的源颜色与目的颜色相加,默认的混合操作:

O_{rgb}=SrcFactor*S_{rgb} DstFactor*D_{rgb}

O_{a}=SrcFactorA*S_{a} DstFactorA*D_{a}

Sub

混合后的源颜色减去混合后的目的颜色,使用的混合等式:

O_{rgb}=SrcFactor*S_{rgb}-DstFactor*D_{rgb}

O_{a}=SrcFactorA*S_{a}-DstFactorA*D_{a}

RevSub

混合后的目的颜色减去混合后的源颜色,使用等式:

O_{rgb}=DstFactor*D_{rgb}-SrcFactor*S_{rgb}

O_{a}=DstFactorA*D_{a}-SrcFactorA*S_{a}​​​​​​​

Min

使用源颜色和目的颜色中较小的值,逐分量比较:

O_{rgba}=(min(S_{r},D_{r}),min(S_{g},D_{g}),min(S_{b},D_{b}),min(S_{a},D_{a}))

Max

使用源颜色和目的颜色中较大的值,逐分量比较:

O_{rgba}=(max(S_{r},D_{r}),max(S_{g},D_{g}),max(S_{b},D_{b}),max(S_{a},D_{a}))​​​​​​​

其他逻辑操作 仅DirectX11.1支持

6.3 常见的混合类型

//正常,透明度混合
Blend SrcAlpha OneMinusSrcAlpha

//柔和相加
Blend OneMinusDstColor One

//正片叠底,相乘
Blend DstColor Zero

//两倍相乘
Blend DstColor SrcColor

//变暗
BlendOp Min
Blend One One

//变亮
BlendOp Max
Blend One One

//滤色
Blend OneMinusDstColor One
//等同于
Blend One OneMinusSrcColor

//线性减淡
Blend One One

7、双面渲染的透明效果

一个物体透明,我们不仅可以透过它看到其他物体的样子,还可以看到它的内部结构,因为默认情况下渲染引擎提出了物体背面的渲染图元。若想得到双面渲染的效果,可以使用Cull指令来控制需要提出哪个面的渲染图元

Cull指令的语法如下:Cull Back | Front | Off

Back:背对摄像机的渲染图元将不会被渲染;Front:朝向摄像机的渲染图元不会被渲染;Off:关闭剔除功能,所有的渲染图元都会被渲染,但此时渲染数目成倍增加

7.1 透明度测试的双面渲染

只在AlphaTest代码的Pass块中增加了

Cull Off

7.2 透明度混合的双面渲染

第一个Pass只渲染背面,第二个Pass只渲染正面,可以保证背面总是在正面被渲染之前渲染

Pass{
    Tags{"LightMode" = "ForwardBase"}
    Cull Front
    ....
}
Pass{
    Tags{"LightMode" = "ForwardBase"}
    Cull Back
    ....
}

 

来源:http://www./content-4-27941.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多