【图形学】27 透明度混合

来源:《UNITY SHADER入门精要》

1、Unity实现透明度混合

  正如之前所说,为了实现透明度混合,我们必须在绘制半透明物体的时候关闭 深度值写入。Unity已经给我们提供了混合的命令:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7SgNCtif-1663118310572)(assets/image-20220624214702272.png)]

  我们书中使用的是表中第二种语义,我们会把 SrcFactor 设为 SrcAlpha,而目标颜色的混合因子 DstFactor 设为 OneMinusSrcAlpha。那么,混合之后的颜色是:
D s t C o l o r n e w = S r c A l p h a × S r c C o l o r + ( 1 − S r c A l p h a ) × D s t C o l o r o l d DstColor_{new}=SrcAlpha \times SrcColor + (1 - SrcAlpha) \times DstColor_{old} DstColornew=SrcAlpha×SrcColor+(1SrcAlpha)×DstColorold

2、代码学习

Shader "Unity Shaders Book/Chapter 8/Alpha Blend" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_AlphaScale ("Alpha Scale", Range(0, 1)) = 1
	}

  我们新增了一个 _AlphaScale来控制透明度的变化。

	SubShader {
		Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
		
		Pass {
			Tags { "LightMode"="ForwardBase" }

  SubShader 的 Tags 中,设置了 "Queue"="Transparent" 使用了Transparent 的渲染队列,RenderType 标签可以让 Unity 把这个 Shader 归入到提前定义的组里面(这里就是 Transparent 组)中,用来指明该 Shader 是一个使用了透明度混合的 Shader。别忘了我们还 设置了 "IgnoreProjector"="True",来禁止被投影。
  当然 Pass 中光照还是设置为 ForwardBase。

			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha

  Pass中采用 Blend 命令

			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _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 : TEXCOORD2;
			};
			
			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
				
				return o;
			}

  emmmm维持不变,没啥好说的。

			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				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);
			}

  唯一变动的部分就是第 13 行,返回的时候,最后一项,用 texColor.a * AlphaScale 构造了最后一项透明度。其他的,因为我们开启了 Blend,Unity会帮我们自动完成。

			ENDCG
		}
	} 
	FallBack "Transparent/VertexLit"
}

3、透明度混合可能存在的问题

  如果一个模型本身就有复杂的结构和网格,就会因为各种排序的问题而且深度值关闭,导致成像有问题:

  所以,我们打算采用开启深度值的方法写入半透明效果。我们使用两个 Pass 来渲染模型:第一个 Pass 开启深度写入,但不输出颜色,仅仅是将在最表面的物体写入深度缓冲中。第二个 Pass 就直接依据上一个 Pass 深度值排序的结果来进行渲染。

4、开启深度写入的透明度混合

Shader "Unity Shaders Book/Chapter 8/Alpha Blending With ZWrite" {
	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"}
		
		// Extra pass that renders to depth buffer only
		Pass {
			ZWrite On
			ColorMask 0
		}

  后面的代码都和前面是一样的,就这里因为多定义了一个Pass,所以不一样了一些,这里 ZWrite On 开启了深度值测试,而后用 ColorMask 0 又关闭了颜色输出。

  这里的 ColorMask 的语义是:

ColorMask RGB \ A \ 0 \ 其它任何 R、G、B、A 的任意组合

		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;
			fixed _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 : TEXCOORD2;
			};
			
			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				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"
}

猜你喜欢

转载自blog.csdn.net/qq_40891541/article/details/126845880