第8章 透明效果

实现透明效果的方法:1.透明度测试alpha test, 2.透明度混合alpha blending.

8.3透明度测试

void clip(float4 x); …… void clip(float x);

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

Shader "my Alpha test" {
Properties {
  _Color ("Main Tint", Color) = (1,1,1,1)
  _MainTex("Main Tex", 2D) = "white" {}
  _Cutoff ("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
    #incude "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 = mul(UNITY_MATRIX_MVP, v.vertex);
    	o.worldNormal = UnityObjectToWorldNormal(v.normal);
    	o.worldPos = mul(_Object2World, v.vertex).xyz;
    	o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    	return o;
    }
    
    fixed4 frag(v2f i): SV_Target {
        fixed3 worldNormal = normalize(i.worldNormal);
        fiexd3 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);
    }
    
    ENDCG
  }
}
}

8.4透明度混合

Blend命令

  • Blend Off 关闭混合
  • Blend SrcFactor DstFactor 开启混合,并设置混合因子。源颜色乘以SrcFactor,而目标颜色(已经存在颜色缓存的颜色)会乘以DstFactor。然后把两者相加后再存入颜色缓冲中
  • Blend SrcFactor DstFactor, SrcFactorA DstFactorA 和上面几乎一样
  • BlendOp BlendOperation 使用BlendOperation 对它们进行其它操作
...
Properties {
  _Color ("Main Tint", Color) = (1,1,1,1)
  _MainTex ("Maint Tex", 2D) = "white" {}
  _AlphaScale ("Alpha Scale", Range(0,1)) = 1
}
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
SubShader {
  Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
  Pass {
    Tags {"LightMode"="ForwardBase"}
    
    ZWrite Off
    Blend ScrAlpha OneMinusSrcAlpha
    
    fixed4 frag(v2f i) :SV_Target {
      fixed3 worldNormal = normalize(i.worldNormal);
      fixed3 worldLightDir = normalize(UnitWorldSpaceLightDir(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);
    }
  }
}
Fallback "Transparent/VertexLit"
...

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

使用两个pass来渲染模型:第一个pass开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓冲中;第二个pass进行正常的透明混合。

Shader "Alpha Blending ZWrite" {
  Properties {
    _Color ("Main 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 //颜色通道写掩码(write mask), 参数可以是RGB | A | 0 | RGBA的各种组合
    }
    Pass {
      //和8.4节同样的代码
    }
  }
  FallBack "Diffuse"
}

8.6 shaderLab的混合命令

  • Blend SrcFactor DstFactor
  • Blend SrcFactor DstFactor SrcFactorA DstFactorA

混合公式(颜色和透明度):

Orgb = SrcFactor * Srgb + DstFactor * Drgb

Oa = SrcFactorA * Sa + DstFactorA * Da

ShaderLab中的混合因子

  • One
  • Zero
  • SrcColor
  • SrcAlpha
  • DstColor
  • DstAlpha
  • OneMinusSrcColor
  • OneMinusSrcAlpha
  • OneMinusDstColor
  • OneMinusDstAlpha

混合操作(源颜色和目标颜色的混合操作):

  • Add
  • Sub
  • RevSub
  • Min
  • Max

8.7 双面渲染的透明效果

可以使用Cull指令来控制需要剔除哪个面的渲染图元。

Cull Back | Front | Off
  • Back 背对着摄像机的渲染图元就不会被渲染,这是默认剔除状态。
  • Front 面向摄像机的渲染图元不会被渲染。
  • Off 关闭剔除功能。

透明度测试的双面渲染

CullOff

透明度混合的双面渲染

使用两个pass。一个pass只渲染背面,另一个只渲染正面。

...
Pass {
   ...
   Cull Front
   ...
}
Pass {
   ...
   Cull Back
   ...
}
...