透明度测试 & 透明度混合

透明度测试

 1、透明测试是用于处理哪种透明需求

在游戏开发中对象的某些部位完全透明而其他部位完全不透明,这种透明需求往往不需要半透明效果,相对比较极端,只有看得见和看不见之分比如树叶、草、栅栏等等。(即一张图除了主要物体有颜色,其他区域都为透明)

2、透明测试的基本原理

基本原理:通过一个阈值来决定哪些像素应该被保留,哪些应该被丢弃

具体实现:片元携带的颜色信息中的透明度(A值),不满足条件时(通常是小于某个阈值),该片元就会被舍弃,被舍弃的片元不会在进行任何处理,不会对颜色缓冲区产生任何影响;满足条件时(通常是大于等于某个阈值),该片元会按照不透明物体的处理方式来处理

阈值判断使用的方法:利用CG中的内置函数:clip(参数)
该函数有重载,参数类型可以是 float4 float3 float2 float 等等,如果传入的参数任何一个分量是负数就会舍弃当前片元它的内部实现会用到一个 discard 指令,代表剔除该片元 不再参与渲染

void clip(float4 x) {
    if (any(x < 0))
        discard;
}

3、透明测试实现

  • 复制颜色纹理结合光照模型的Shader
  • 在属性中加一个阈值_Cutoff,取值范围为0~1,用来设定用来判断的阈值。并在CG中添加属性的映射成员
  • 将渲染队列设置为AlphaTest,并配合IgnoreProjector和RenderType一起设置
  • 在片元着色器中获取了颜色贴图颜色后,就进行阈值判断

注意:透明度测试不需要关闭深度写入

选用下面这张各个区域透明度不同的纹理图片

Shader "ShaderProj/3/TransparentTest"
{
    Properties
    {
        _MainTex("MainTex", 2D) = ""{}
        _MainColor("MainColor", Color) = (1,1,1,1)
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        _SpecularNum("SpecularNum", Range(0,20)) = 15
        _CutOff("CutOff", Range(0,1)) = 0       // 透明度测试阈值
    }
    SubShader
    {
        Tags{
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="TransparentCutout"
            }

        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _MainColor;
            fixed4 _SpecularColor;
            float _SpecularNum;
            fixed _CutOff;

            struct v2f
            {
                float4 pos:SV_POSITION;
                float2 uv:TEXCOORD0;
                float3 wNormal:NORMAL;
                float3 wPos:TEXCOORD01;
            };

            fixed3 getLambertColor(float3 normal, float3 lightDir, fixed3 albedo)
            {
                fixed3 color = _LightColor0 * albedo * max(0, dot(lightDir, normal));

                return color;
            }

            fixed3 getSpecularColor(float3 worldPos, float3 normal, float3 lightDir)
            {
                //float3 viewDir = normalize(_WorldSpaceCameraPos - worldPos);
                float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                float3 halfAngle = normalize(viewDir + lightDir);
                fixed3 color = _LightColor0 * _SpecularColor * pow(max(0, dot(normal, halfAngle)), _SpecularNum);

                return color;
            }
        
            v2f vert (appdata_base v)
            {
                v2f data;

                data.pos = UnityObjectToClipPos(v.vertex);
                data.wNormal = UnityObjectToWorldNormal(v.normal);
                data.wPos = mul(unity_ObjectToWorld, v.vertex);
                data.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                //data.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                return data;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // 透明度测试
                fixed4 texColor = tex2D(_MainTex, i.uv);
                clip(texColor.a - _CutOff);

                fixed3 albedo = texColor.rgb * _MainColor.rgb;                
                float3 lightDir = normalize(_WorldSpaceLightPos0);
                fixed3 lambertColor = getLambertColor(i.wNormal, lightDir, albedo);
                fixed3 specularColor = getSpecularColor(i.wPos, i.wNormal, lightDir);
                fixed3 color = lambertColor + specularColor + UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                return fixed4(color, 1);
            }
            ENDCG
        }
    }
}

透明度混合

1、透明度混合是用来处理哪种需求

透明度测试,并不能用于实现半透明效果,它只存在看得见和完全看不见两种状态,一般用来处理镂空效果,透明度混合主要就是用来实现半透明效果的

2、透明度混合的基本原理

基本原理:关闭深度写入,开启混合,让片元颜色和颜色缓冲区中颜色进行混合计算

3、透明度混合实现

  • 我们复制 Lesson50 中颜色纹理结合光照模型的Shader(因为我们的测试资源没有法线贴图等数据)
  • 在属性中加一个阈值_AlphaScale,取值范围为0~1,用来设定对象整体透明度。并在CG中添加属性的映射成员
  • 将渲染队列设置为Transparent,并配合IgnoreProjector和RenderType一起设置
  • 关闭深度写入Zwrite off,设置混合因子Blend SrcAlpha OneMinusSrcAlpha
  • 在片元着色器中获取了颜色贴图颜色后,修改最后返回颜色的A值为纹理.a * _AlphaScale
Shader "ShaderProj/3/TransparentBlend"
{
    Properties
    {
        _MainTex("MainTex", 2D) = ""{}
        _MainColor("MainColor", Color) = (1,1,1,1)
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        _SpecularNum("SpecularNum", Range(0,20)) = 15
        _AlphaScale("AlphaScale", Range(0,1)) = 0     // 控制对象整体透明度的值
    }
    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 "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _MainColor;
            fixed4 _SpecularColor;
            float _SpecularNum;
            fixed _AlphaScale;

            struct v2f
            {
                float4 pos:SV_POSITION;
                float2 uv:TEXCOORD0;
                float3 wNormal:NORMAL;
                float3 wPos:TEXCOORD01;
            };

            fixed3 getLambertColor(float3 normal, float3 lightDir, fixed3 albedo)
            {
                fixed3 color = _LightColor0 * albedo * max(0, dot(lightDir, normal));

                return color;
            }

            fixed3 getSpecularColor(float3 worldPos, float3 normal, float3 lightDir)
            {
                //float3 viewDir = normalize(_WorldSpaceCameraPos - worldPos);
                float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                float3 halfAngle = normalize(viewDir + lightDir);
                fixed3 color = _LightColor0 * _SpecularColor * pow(max(0, dot(normal, halfAngle)), _SpecularNum);

                return color;
            }
        
            v2f vert (appdata_base v)
            {
                v2f data;

                data.pos = UnityObjectToClipPos(v.vertex);
                data.wNormal = UnityObjectToWorldNormal(v.normal);
                data.wPos = mul(unity_ObjectToWorld, v.vertex);
                data.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                //data.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                return data;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 texColor = tex2D(_MainTex, i.uv);

                fixed3 albedo = texColor.rgb * _MainColor.rgb;                
                float3 lightDir = normalize(_WorldSpaceLightPos0);
                fixed3 lambertColor = getLambertColor(i.wNormal, lightDir, albedo);
                fixed3 specularColor = getSpecularColor(i.wPos, i.wNormal, lightDir);
                fixed3 color = lambertColor + specularColor + UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                // 修改 Alpha 返回值决定颜色透明度
                return fixed4(color, texColor.a * _AlphaScale);
            }
            ENDCG
        }
    }
}

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

1、开启深度写入的半透明效果是用来处理哪种需求

对于一些复杂的模型,关闭深度写入会产生错误的渲染效果,比如

虽然可以通过拆分模型的方式解决部分问题,但是对于一些结构复杂的模型,拆分模型的方式会增加工作量,因此可以采用开启深度写入的半透明Shader来优化效果

2、开启深度写入的半透明效果的基本原理

基本原理:使用两个Pass渲染通道来处理渲染逻辑

  •     第一个Pass:开启深度写入,不输出颜色目的是让该模型各片元的深度值能写入深度缓冲
  •     第二个Pass:进行正常的透明度混合

这样做的话,当执行第一个Pass时,会执行深度测试,并进行深度写入,如果此时该片元没有通过深度测试会直接丢弃,不会再执行第二个Pass,对于同一个模型中处于屏幕同一位置的片元们,会进行该位置的深度测试再决定渲染哪个片元

如何做到不输出颜色?
使用 ColorMask 颜色遮罩 渲染状态(命令),它主要用于控制颜色分量是否写入到颜色缓冲区中

  • ColorMask RGBA 表示写入颜色的RGBA通道
  • ColorMask 0    表示不写入颜色
  • ColorMask RB 表示只写入红色和蓝色通道

注意:

  • 开启深度写入的半透明效果,模型内部之间不会有任何半透明效果(因为模型内部深度较大的片元会被丢弃掉)
  • 由于有两个Pass渲染通道,因此它会带来一定的性能开销
Shader "ShaderProj/3/Transparent_ZWrite"
{
    Properties
    {
        _MainTex("MainTex", 2D) = ""{}
        _MainColor("MainColor", Color) = (1,1,1,1)
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        _SpecularNum("SpecularNum", Range(0,20)) = 15
        _AlphaScale("AlphaScale", Range(0,1)) = 0     // 控制对象整体透明度的值
    }
    SubShader
    {
        Tags{
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            }

        // 主要进行深度写入(深度测试),但是不输出颜色
        Pass
        {
            ZWrite On
            ColorMask 0
        }

        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            // 处理透明效果需要关闭深度写入,设置混合因子
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _MainColor;
            fixed4 _SpecularColor;
            float _SpecularNum;
            fixed _AlphaScale;

            struct v2f
            {
                float4 pos:SV_POSITION;
                float2 uv:TEXCOORD0;
                float3 wNormal:NORMAL;
                float3 wPos:TEXCOORD01;
            };

            fixed3 getLambertColor(float3 normal, float3 lightDir, fixed3 albedo)
            {
                fixed3 color = _LightColor0 * albedo * max(0, dot(lightDir, normal));

                return color;
            }

            fixed3 getSpecularColor(float3 worldPos, float3 normal, float3 lightDir)
            {
                //float3 viewDir = normalize(_WorldSpaceCameraPos - worldPos);
                float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                float3 halfAngle = normalize(viewDir + lightDir);
                fixed3 color = _LightColor0 * _SpecularColor * pow(max(0, dot(normal, halfAngle)), _SpecularNum);

                return color;
            }
        
            v2f vert (appdata_base v)
            {
                v2f data;

                data.pos = UnityObjectToClipPos(v.vertex);
                data.wNormal = UnityObjectToWorldNormal(v.normal);
                data.wPos = mul(unity_ObjectToWorld, v.vertex);
                data.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                //data.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                return data;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 texColor = tex2D(_MainTex, i.uv);

                fixed3 albedo = texColor.rgb * _MainColor.rgb;                
                float3 lightDir = normalize(_WorldSpaceLightPos0);
                fixed3 lambertColor = getLambertColor(i.wNormal, lightDir, albedo);
                fixed3 specularColor = getSpecularColor(i.wPos, i.wNormal, lightDir);
                fixed3 color = lambertColor + specularColor + UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                // 修改 Alpha 返回值决定颜色透明度
                return fixed4(color, texColor.a * _AlphaScale);
            }
            ENDCG
        }
    }
}

双面渲染的透明效果

1、面渲染的透明效果用来处理哪种需求

对于现实世界的半透明物体,不仅可以透过它看到其他物体的样子,也可以看到这个物体自己的内部结构,但是之前实现的 透明度测试 和 透明度混合 相关Shader都无法看到模型的内部结构,如图:

而双面渲染的透明效果Shader就是来解决该问题的,不仅可以透过半透明物体看到其他物体的样子还可以看到自己的内部结构

2、双面渲染的透明效果的基本原理

基本原理:默认情况下,Unity会自动剔除物体的背面,而只渲染物体的正面,双面渲染的基本原理就是利用我们之前学习过的 Cull 剔除指令来进行指定操作

  • Cull Back     背面剔除
  • Cull Front 正面剔除
  • Cull Off      不剔除
  • 不设置的话,默认为背面剔除

对于透明度测试Shader:由于它无需混合,因此我们直接 关闭剔除即可

Shader "ShaderProj/3/TranparentTest_Both"
{
    Properties
    {
        _MainTex("MainTex", 2D) = ""{}
        _MainColor("MainColor", Color) = (1,1,1,1)
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        _SpecularNum("SpecularNum", Range(0,20)) = 15
        _CutOff("CutOff", Range(0,1)) = 0       // 透明度测试阈值
    }
    SubShader
    {
        Tags{
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="TransparentCutout"
            }

        Pass
        {
            Tags { "LightMode"="ForwardBase" }

            // 关闭剔除,正面背面都被渲染
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _MainColor;
            fixed4 _SpecularColor;
            float _SpecularNum;
            fixed _CutOff;

            struct v2f
            {
                float4 pos:SV_POSITION;
                float2 uv:TEXCOORD0;
                float3 wNormal:NORMAL;
                float3 wPos:TEXCOORD01;
            };

            fixed3 getLambertColor(float3 normal, float3 lightDir, fixed3 albedo)
            {
                fixed3 color = _LightColor0 * albedo * max(0, dot(lightDir, normal));

                return color;
            }

            fixed3 getSpecularColor(float3 worldPos, float3 normal, float3 lightDir)
            {
                //float3 viewDir = normalize(_WorldSpaceCameraPos - worldPos);
                float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                float3 halfAngle = normalize(viewDir + lightDir);
                fixed3 color = _LightColor0 * _SpecularColor * pow(max(0, dot(normal, halfAngle)), _SpecularNum);

                return color;
            }
        
            v2f vert (appdata_base v)
            {
                v2f data;

                data.pos = UnityObjectToClipPos(v.vertex);
                data.wNormal = UnityObjectToWorldNormal(v.normal);
                data.wPos = mul(unity_ObjectToWorld, v.vertex);
                data.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                //data.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                return data;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // 透明度测试
                fixed4 texColor = tex2D(_MainTex, i.uv);
                clip(texColor.a - _CutOff);

                fixed3 albedo = texColor.rgb * _MainColor.rgb;                
                float3 lightDir = normalize(_WorldSpaceLightPos0);
                fixed3 lambertColor = getLambertColor(i.wNormal, lightDir, albedo);
                fixed3 specularColor = getSpecularColor(i.wPos, i.wNormal, lightDir);
                fixed3 color = lambertColor + specularColor + UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                return fixed4(color, 1);
            }
            ENDCG
        }
    }
}

 

对于透明度混合Shader:由于它需要进行混合,需要使用两个Pass,一个用于渲染背面,一个用于渲染正面,两个Pass中除了剔除命令不同 其他代码和之前一致

Shader "ShaderProj/3/TransparentBlend_Both"
{
    Properties
    {
        _MainTex("MainTex", 2D) = ""{}
        _MainColor("MainColor", Color) = (1,1,1,1)
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        _SpecularNum("SpecularNum", Range(0,20)) = 15
        _AlphaScale("AlphaScale", Range(0,1)) = 0     
    }
    SubShader
    {
        Tags{
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            }

        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Front

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _MainColor;
            fixed4 _SpecularColor;
            float _SpecularNum;
            fixed _AlphaScale;

            struct v2f
            {
                float4 pos:SV_POSITION;
                float2 uv:TEXCOORD0;
                float3 wNormal:NORMAL;
                float3 wPos:TEXCOORD01;
            };

            fixed3 getLambertColor(float3 normal, float3 lightDir, fixed3 albedo)
            {
                fixed3 color = _LightColor0 * albedo * max(0, dot(lightDir, normal));

                return color;
            }

            fixed3 getSpecularColor(float3 worldPos, float3 normal, float3 lightDir)
            {
                //float3 viewDir = normalize(_WorldSpaceCameraPos - worldPos);
                float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                float3 halfAngle = normalize(viewDir + lightDir);
                fixed3 color = _LightColor0 * _SpecularColor * pow(max(0, dot(normal, halfAngle)), _SpecularNum);

                return color;
            }
        
            v2f vert (appdata_base v)
            {
                v2f data;

                data.pos = UnityObjectToClipPos(v.vertex);
                data.wNormal = UnityObjectToWorldNormal(v.normal);
                data.wPos = mul(unity_ObjectToWorld, v.vertex);
                data.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                //data.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                return data;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 texColor = tex2D(_MainTex, i.uv);

                fixed3 albedo = texColor.rgb * _MainColor.rgb;                
                float3 lightDir = normalize(_WorldSpaceLightPos0);
                fixed3 lambertColor = getLambertColor(i.wNormal, lightDir, albedo);
                fixed3 specularColor = getSpecularColor(i.wPos, i.wNormal, lightDir);
                fixed3 color = lambertColor + specularColor + UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                return fixed4(color, texColor.a * _AlphaScale);
            }
            ENDCG
        }

        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Back

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _MainColor;
            fixed4 _SpecularColor;
            float _SpecularNum;
            fixed _AlphaScale;

            struct v2f
            {
                float4 pos:SV_POSITION;
                float2 uv:TEXCOORD0;
                float3 wNormal:NORMAL;
                float3 wPos:TEXCOORD01;
            };

            fixed3 getLambertColor(float3 normal, float3 lightDir, fixed3 albedo)
            {
                fixed3 color = _LightColor0 * albedo * max(0, dot(lightDir, normal));

                return color;
            }

            fixed3 getSpecularColor(float3 worldPos, float3 normal, float3 lightDir)
            {
                //float3 viewDir = normalize(_WorldSpaceCameraPos - worldPos);
                float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                float3 halfAngle = normalize(viewDir + lightDir);
                fixed3 color = _LightColor0 * _SpecularColor * pow(max(0, dot(normal, halfAngle)), _SpecularNum);

                return color;
            }
        
            v2f vert (appdata_base v)
            {
                v2f data;

                data.pos = UnityObjectToClipPos(v.vertex);
                data.wNormal = UnityObjectToWorldNormal(v.normal);
                data.wPos = mul(unity_ObjectToWorld, v.vertex);
                data.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                //data.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                return data;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 texColor = tex2D(_MainTex, i.uv);

                fixed3 albedo = texColor.rgb * _MainColor.rgb;                
                float3 lightDir = normalize(_WorldSpaceLightPos0);
                fixed3 lambertColor = getLambertColor(i.wNormal, lightDir, albedo);
                fixed3 specularColor = getSpecularColor(i.wPos, i.wNormal, lightDir);
                fixed3 color = lambertColor + specularColor + UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                return fixed4(color, texColor.a * _AlphaScale);
            }
            ENDCG
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值