前言

在时隔两个月后,这个游戏引擎技术系列再次更新了,我都没想到整整摸了两个月的鱼,最近生活也比较稳定了,以后这一系列会尽量多更一些。

今天要说的就是透明贴图的实现技术,从技术原理来说,大致有透切(AC——AlphaCutout)、透混(AB——AlphaBlend)以及透叠(AD——Additive)。

以下会详细说一下不同技术的特点和实现方式。

 

透切AC

所谓透切就是使用透明贴图对物体进行裁切,一般用于有复杂轮廓和明确边缘的物体,例如镂空金属、裙摆边缘、卡通风格的头发、树叶、风格化的特效等等。优点是没有前后排序问题,不会出现前后遮挡关系混乱;缺点则是边缘较实,移动端性能较差。

在Unity中实现透切AC的shader代码如下:

// shader的路径和名称
Shader "Shader Forge/AlphaCutoff" {
    // 暴露属性到面板
    Properties {
        _ACmap("RGB: Color  A: Alpha", 2d) = "white"{}
        _Cutoff("Cutoff Threshold", range(0.0, 1.0)) = 0.5
    }
    SubShader {
        Tags {
            "RenderType"="TransparentCutout"     //改为cutoff对应的
            "ForceNoShadowCasting"="True"        //关闭阴影投射
            "IgnoreProjector"="True"             //不响应投射器
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            
            Cull Off         //双面显示
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            
            // 声明变量
            uniform sampler2D _ACmap; uniform float4 _ACmap_ST; //支持UV TillingOffset
            uniform half _Cutoff;
            
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 顶点shader
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.pos = UnityObjectToClipPos( v.vertex );
                o.uv = TRANSFORM_TEX(v.uv, _ACmap);
                return o;
            }
            
            // 像素shader
            float4 frag(VertexOutput i) : COLOR {
                half4 var_ACmap = tex2D(_ACmap, i.uv);
                clip(var_ACmap.a - _Cutoff);
                
                return var_ACmap;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

效果如下:

可以看出裁切的范围是通过Cutoff Threshold阈值控制的,边缘过渡不够自然。

 

透混AB

透混则是通过混合模式进行叠加,一般用于有复杂轮廓而无明确边缘的物体,例如头发、特效底层等。优点是边缘效果较好,缺点则是会有前后排序问题。

所谓透明排序问题,其实是因为默认情况下为节省性能消耗,在物体有前后遮挡关系时,先通过Z-buffer深度缓存计算物体每个顶点与摄像机的距离,渲染时只渲染靠摄像机最近的物体部分,被遮挡的物体部分或其他物体会被剔除掉,那么当最前方物体是透明的时候,后方物体也不会进行渲染。

在Unity中实现透混AB的shader代码如下:

// shader的路径和名称
Shader "Shader Forge/AlphaBlend" {
    // 暴露属性到面板
    Properties {
        _ABmap("RGB: Color  A: Alpha", 2d) = "gray"{}
        _Opacity("Opacity", range(0.0, 1.0)) = 1.0
    }
    SubShader {
        Tags {
            "Queue"="Transparent"                //调整渲染顺序
            "RenderType"="Transparent"           //改为对应的
            "ForceNoShadowCasting"="True"        //关闭阴影投射
            "IgnoreProjector"="True"             //不响应投射器
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            Blend One OneMinusSrcAlpha          //修改混合模式,如果不预乘也不shader乘此处应为SrcAlpha OneMinusSrcAlpha
            
            Cull Off         //双面显示
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            
            // 声明变量
            uniform sampler2D _ABmap; uniform float4 _ABmap_ST; //支持UV TillingOffset
            uniform half _Opacity;
            
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 顶点shader
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.pos = UnityObjectToClipPos( v.vertex );
                o.uv = TRANSFORM_TEX(v.uv, _ABmap);
                return o;
            }
            
            // 像素shader
            float4 frag(VertexOutput i) : COLOR {
                half4 var_ABmap = tex2D(_ABmap, i.uv);   
                half finalOpacity = var_ABmap.a * _Opacity;  
                return half4(var_ABmap.rgb * _Opacity,finalOpacity);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

效果如下:

可以看出裁切边缘过渡是通过Alpha控制的,效果比较自然,并可以通过Opacity参数整体控制透明度,但缺点则是会产生前后排序问题,在旋转时会有很明显的穿帮,需要再使用其他技术进行修复。

 

透叠AD

透叠与透混类似,同样是通过混合模式叠加,常用于发光体、辉光物体。一般特效会先使用AB打底,再叠加上AD辉光,但要注意AD也有排序问题,而且多层AD叠加会叠爆性能(OverDraw),因此辉光特效更适合使用后处理代替。

在Unity中实现透叠AD的shader代码如下:

// shader的路径和名称
Shader "Shader Forge/Additive" {
    // 暴露属性到面板
    Properties {
        _ADmap("RGB: Color  A:Alpha", 2d) = "gray"{}
        _Opacity("Opacity", range(0.0, 1.0)) = 1.0
    }
    SubShader {
        Tags {
            "Queue"="Transparent"                //调整渲染顺序
            "RenderType"="Transparent"           //改为对应的
            "ForceNoShadowCasting"="True"        //关闭阴影投射
            "IgnoreProjector"="True"             //不响应投射器
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            Blend One One
            
            Cull Off         //双面显示
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            
            // 声明变量
            uniform sampler2D _ADmap; uniform float4 _ADmap_ST; //支持UV TillingOffset
            uniform half _Opacity;
            
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 顶点shader
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.pos = UnityObjectToClipPos( v.vertex );
                o.uv = TRANSFORM_TEX(v.uv, _ABmap);
                return o;
            }
            
            // 像素shader
            float4 frag(VertexOutput i) : COLOR {
                half4 var_ADmap = tex2D(_ADmap, i.uv);   
                half finalOpacity = var_ADmap.a * _Opacity;  
                return half4(var_ADmap.rgb * _Opacity,finalOpacity);//如果非预乘Alpha的话为return half4(var_ADmap.rgb * finalOpacity,finalOpacity);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

效果如下:

可以看出参数、效果和AB类似,也产生了和AB同样的前后排序问题。

 

自定义混合模式

从上面的代码可以看出AB和AD都是通过不同混合模式的叠加实现的,因此可以通过将叠加功能作为可选项实现一个可自由选择的透明贴图模式。

在Unity中实现shader代码如下:

// shader的路径和名称
Shader "Shader Forge/CustomBlend" {
    // 暴露属性到面板
    Properties {
        _ADmap("RGB: Color", 2d) = "gray"{}
        _Opacity("Opacity", range(0.0, 1.0)) = 1.0
        [Enum(UnityEngine.Rendering.BlendMode)] _BlendSrc ("Blend Source", int) = 0
        [Enum(UnityEngine.Rendering.BlendMode)] _BlendDst ("Blend Destination", int) = 0
        [Enum(UnityEngine.Rendering.BlendOp)] _BlendOp ("Blend Operation", int) = 0
    }
    SubShader {
        Tags {
            "Queue"="Transparent"                //调整渲染顺序
            "RenderType"="Transparent"           //改为对应的
            "ForceNoShadowCasting"="True"        //关闭阴影投射
            "IgnoreProjector"="True"             //不响应投射器
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            BlendOp [_BlendOp]
            Blend [_BlendSrc] [_BlendDst]          //修改混合模式
            
            Cull Off         //双面显示
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            
            // 声明变量
            uniform sampler2D _ADmap; uniform float4 _ADmap_ST; //支持UV TillingOffset
            uniform half _Opacity;
            
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 顶点shader
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.pos = UnityObjectToClipPos( v.vertex );
                o.uv = TRANSFORM_TEX(v.uv, _ADmap);
                return o;
            }
            
            // 像素shader
            float4 frag(VertexOutput i) : COLOR {
                half4 var_ADmap = tex2D(_ADmap, i.uv);     
                half finalOpacity = var_ADmap.a * _Opacity;  
                return half4(var_ADmap.rgb * _Opacity,finalOpacity); //如果非预乘Alpha的话则为return half4(var_ADmap.rgb * finalOpacity,finalOpacity);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

效果如下:

也可以将选定的几种组合方式进行包装,做成预设供用户选择,是的,PhotoShop的图层混合模式就是这样做的。

 

本文同样参考自B站庄懂的技术美术入门课,感谢。


学习的本质,
不在于记住哪些知识,
而在于它触发了你的思考。

——迈克尔·桑德尔