【图形学】26 透明效果基础

来源:《UNITY SHADER入门精要》

1、透明度测试

  透明度测试(Alpha Test),这出来的效果不是真的透明,而更像是会使得物体的一部分消失。通过设置一个 Alpha 通道的阈值,不满足条件的片元就直接舍弃,这样,一个物体可能就不会完全的显示。

2、透明度混合

  透明度混合(Alpha Blending),这种方法可以得到真正的半透明效果,它会使用当前已有的片元颜色 和 透明度值 进行混合,得到新的颜色。
  开启了透明度混合就一定要关闭深度写入,所以必须注意渲染顺序,否则会让透明的物体不会因为在前面而遮挡致使不透明的物体不画。同时,对于透明混合来说,深度缓冲只是只读的。

  为了使得半透明的效果正常,我们会对物体的渲染顺序进行排序,然后再渲染:
(1)先渲染所有不透明的物体,此时开启他们的深度测试和深度写入。
(2)对于半透明的物体,按照离摄像机的远近进行排序,然后从后往前渲染这些半透明的物体,此时开启它们的深度测试,但是关闭深度写入。

3、UnityShader的渲染队列

  为了解决渲染顺序问题,Unity 提供了**渲染队列(Render Queue)**这一解决方案。我们可以使用 SubShader Tags 设置为 AlphaTest 或者 Transparent 来决定我们的模型处于哪个渲染队列。序号越小,越早被渲染。例子:

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TOqe8yzR-1663118270470)(assets/image-20220624172932425.png)]

4、半透明测试的代码解读

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

  为了减少负担,我们这里不再进行光照的计算,仅仅是采样贴图纹理,然后设置 Alpha Test 的阈值。

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

  这里的 Tags 设置的比较多,第一个,Queue 设置为 AlphaTest,指明了这个 SubShader 使用了透明度测试。
  第二个,我们设置 IgnoreProjector 为 True。意味着这个 Shader 不会受到 投影器(Projectors)的影响,也就是说,不会被其他物体打上阴影。
  第三个,RenderType可以把这个 SubShader 设置提前定义的组中,这里是 TransparentCutout 组中。
  最后,我们在 Pass 中定义了 LightMode 的方式为 ForwardBase。这样,我们定义了这个 Pass 在光照流水管线中的角色。只有定义了正确的 LightMode,我们才能正确的到一些 Unity 的内置光照变量,例如 _LightColor0

  通常我们进行 透明度测试 的 Shader 都应该在 SubShader 中设置这三个标签。

			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _Cutoff;
			
			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);
				
				// Alpha test
				clip (texColor.a - _Cutoff);
				// Equal to 
//				if ((texColor.a - _Cutoff) < 0.0) {
//					discard;
//				}
				
				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, 1.0);
			}

  与之前不一样的地方是:这里没有计算高光,这里用 clip() 裁剪之后的 texColor 来计算反射率。

			ENDCG
		}
	} 
	FallBack "Transparent/Cutout/VertexLit"
}

猜你喜欢

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