1. 透明度测试
透明度测试的原理是将片元的 Alpha 值与某个指定的阈值进行比较,当 Alpha 超过阈值时,该片元正常显示,若 Alpha 值低于指定的阈值,则将该片元丢弃。准确来讲,透明度测试并不是为了渲染一种半透的效果,而是为了通过片元的 Alpha 值进行筛选,确定哪些像素需要渲染哪些像素不需要渲染,跟我们之前提到过的深度测试的效果是一样的,只不过筛选条件不同。
实际上,在透明度测试中,与阈值进行比较的并不一定必须是Alpha值,我们可以用采样得到的任意通道的值——甚至是某个计算得到的值,只要是我们通过某种方式可以掌控的值——来跟阈值进行比较。其重点是通过设定的阈值对像素进行筛选,以确定像素是否需要被丢弃。
因此,透明度测试的结果要么完全不显示,要么正常显示。
在【Unity Shader入门精要 第2章】渲染流水线 中我们曾经提到过,存在透明度测试时无法开启Early-Z,正是因为如果开启了 Early-Z,那么在进入片元着色器之前就已经完成了深度测试,此时像素的深度值就会被写入到深度缓冲当中。接着进入片元着色器,发现当前片元的 Alpha 值没有达到阈值,于是该片元被舍弃不会进行渲染,但已经写入深度缓冲中的深度值又不会被撤回,结果就导致深度缓存出错。
2. 示例
- 创建 Chapter_8_AlphaTest_Mat 作为测试材质
- 创建 Chapter_8_AlphaTest_Shader 作为测试shader,并赋给 Chapter_8_AlphaTest_Mat 材质
- 场景中创建一个用于测试的立方体,将 Chapter_8_AlphaTest_Mat 材质赋给立方体
- 场景中添加一盏平行光,并调整平行光角度
- 为了便于观察效果,在立方体下方创建一个Plane
使用的测试纹理如下:
在Shader中增加 _AlphaTest 属性,用于控制进行透明度测试的阈值
在SubShader中设置标签
Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}
在片元着色器中,使用了clip 方法进行裁剪,该方法的参数可以传入float
void clip(float _checkValue);
此时若 _checkValue 小于0,则会舍弃当前片元的颜色输出,否则正常输出。
也可以传入float2、float3等矢量
void clip(float3 _checkValue);
此时,若 _checkValue 中的任意一个分量小于0,均会舍弃当前片元的颜色输出,否则正常输出。
在 Inpector 面板中改变 AlphaTest属性的值,即可看到效果。
最终Shader如下:
Shader "MyShader/Chapter_8/Chapter_8_AlphaTest_Shader"
{Properties{_MainTex("MainTex", 2D) = "white"{}_AlphaTest("AlphaTest", range(0, 1)) = 0}SubShader{Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCurout"}Pass{Tags {"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float2 uv : TEXCOORD0;};struct v2f{float4 vertex : SV_POSITION;float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;};sampler2D _MainTex;float4 _MainTex_ST;fixed _AlphaTest;v2f vert(a2v i){v2f o;o.vertex = UnityObjectToClipPos(i.vertex);o.worldNormal = mul(i.normal, (float3x3)unity_WorldToObject);o.uv = TRANSFORM_TEX(i.uv, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;float3 _worldNormal = normalize(i.worldNormal);float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);fixed4 _sampledColor = tex2D(_MainTex, i.uv);clip(_sampledColor.w - _AlphaTest);fixed3 _diffuse = _LightColor0.rgb * _sampledColor.xyz * (0.5 * dot(_worldLight, _worldNormal) + 0.5);return fixed4(_ambient + _diffuse, 1);}ENDCG}}}
效果如下: