Unity Shader 之 透明效果

透明效果

透明效果一般有两种实现方法:

  • 第一种,使用透明度测试(Alpha Test)
  • 第二种,使用透明度混合(Alpha Blending)

透明度测试透明度混合机制

透明度测试(Alpha Test):只要一个片元的透明度不满足条件(小于某阀值),那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则,就按照普通的不透明物体处理,即进行深度测试、深度写入。透明度测试不需要关闭深度写入。

透明度混合(Alpha Blending):使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色值。但是,这需要关闭深度写入。虽然关闭了深度写入,但是没有关闭了深度缓冲,此时深度缓冲还是可读的。

不同的渲染顺序带来的结果:(A半透明,B不透明)

渲染顺序

第一种:先渲染B,再渲染A.

结果:能得到正确的渲染效果。

第二种:先渲染A,再渲染B.

结果:不能得到正确的渲染效果,因为在渲染A时已经关闭了深度写入,所以A不会修改深度缓冲,等到渲染B时,B直接覆盖了A的颜色。

渲染常用顺序:

  1. 先渲染所有不透明物体,开启深度测试和深度写入。
  2. 半透明物体按离摄像机距离远近排序,从后往前渲染,开启深度测试,关闭深度写入。

部分相互重叠的物体不适用,解决方法是分割网格。

unity shader的渲染顺序

Unity为了解决渲染顺序问题提供了渲染队列(render queue)解决方案。

unity渲染队列

实现透明度测试时需要添加代码:

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

实现透明度混合需要添加代码:

SubShader{
    Tags {"Queue" = "Transform"}
    Pass{
        ZWrite Off
        ...
    }
}

透明度测试

关键函数

clip函数是CG中的一个函数:

void clip(float4 x);
void clip(float3 x);
void clip(float2 x);
void clip(float1 x);
void clip(float x);

全部代码

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
    }
    SubShader {
        Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
        
        Pass {
            Tags { "LightMode"="ForwardBase" }
            
            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 = 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);
                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);
            }
            
            ENDCG
        }
    } 
    FallBack "Transparent/Cutout/VertexLit"
}

效果

透明度测试

透明度混合

透明度混合的本质就是用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新颜色。

混合公式如下:

透明度混合公式

unity提供的混合命令:

shaderlab中的Blend命令

透明度混合完整代码

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
    }
    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;
            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 = 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);
                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"
}

效果

透明度混合

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

前面提到由于关闭深度写入而造成的错误排序的情况。可以使用两个Pass的办法来解决

第一个Pass: 开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓冲中

第二个Pass: 进行正常的透明度混合,由于上个Pass已经的到了像素的正确深度信息,所以可以得到正确的渲染结果。

这种办法的缺点是对性能有一定影响

ShaderLab的混合命令

ShaderLab中的混合因子

参数描述
OneThe value of one - use this to let either the source or the destination color come through fully.
ZeroThe value zero - use this to remove either the source or the destination values.
SrcColorThe value of this stage is multiplied by the source color value.
SrcAlphaThe value of this stage is multiplied by the source alpha value.
DstColorThe value of this stage is multiplied by frame buffer source color value.
DstAlphaThe value of this stage is multiplied by frame buffer source alpha value.
OneMinusSrcColorThe value of this stage is multiplied by (1 - source color).
OneMinusSrcAlphaThe value of this stage is multiplied by (1 - source alpha).
OneMinusDstColorThe value of this stage is multiplied by (1 - destination color).
OneMinusDstAlphaThe value of this stage is multiplied by (1 - destination alpha).

ShaderLab中的混合操作

操作描述
AddAdd source and destination together.
SubSubtract destination from source.
RevSubSubtract source from destination.
MinUse the smaller of source and destination.
MaxUse the larger of source and destination.
LogicalClearLogical operation: Clear (0) DX11.1 only.
LogicalSetLogical operation: Set (1) DX11.1 only.
LogicalCopyLogical operation: Copy (s) DX11.1 only.
LogicalCopyInvertedLogical operation: Copy inverted (!s) DX11.1 only.
LogicalNoopLogical operation: Noop (d) DX11.1 only.
LogicalInvertLogical operation: Invert (!d) DX11.1 only.
LogicalAndLogical operation: And (s & d) DX11.1 only.
LogicalNandLogical operation: Nand !(s & d) DX11.1 only.
LogicalOrLogical operation: Or (s | d) DX11.1 only.
LogicalNorLogical operation: Nor !(s | d) DX11.1 only.
LogicalXorLogical operation: Xor (s ^ d) DX11.1 only.
LogicalEquivLogical operation: Equivalence !(s ^ d) DX11.1 only.
LogicalAndReverseLogical operation: Reverse And (s & !d) DX11.1 only.
LogicalAndInvertedLogical operation: Inverted And (!s & d) DX11.1 only.
LogicalOrReverseLogical operation: Reverse Or (s | !d) DX11.1 only.
LogicalOrInvertedLogical operation: Inverted Or (!s | d) DX11.1 only.

双面渲染的透明效果

上面的透明度测试和透明度混合都无法将物体的背面显示出来,这是因为渲染引擎剔除了物体的背面,所以需要在渲染透明物体使用Cull指令控制需要剔除的渲染图元。

  • Cull Back | Front | Off

分别是:剔除背面的图元 | 剔除前面的图元 | 关闭功能

完整代码

Shader "Unity Shaders Book/Chapter 8/Alpha Blend With Both Side" {
    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" }
            
            // First pass renders only back faces 
            Cull Front
            
            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 = 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);
                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
        }
        
        Pass {
            Tags { "LightMode"="ForwardBase" }
            
            // Second pass renders only front faces 
            Cull Back
            
            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 = 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);
                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"
}

效果

双面渲染的透明效果

转载于:https://www.cnblogs.com/yoyo-sincerely/p/7899088.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值