43.双面渲染的透明效果

43.透明-效果实现-双面渲染的透明效果


43.1 知识点

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

  • 对于现实世界的半透明物体,我们不仅可以透过它看到其他物体的样子,也可以看到这个物体自己的内部结构。

  • 但是我们之前实现的 透明度测试 和 透明度混合 相关Shader,都无法看到模型的内部结构。

  • 拷贝透明度测试的shader,赋值给cube,可以看到,cube其实应该能看到后面的,模型内部还有内容。使用透明度混合的shader也有这个问题。

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

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

基本原理

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

  • Cull Back: 背面剔除
  • Cull Front: 正面剔除
  • Cull Off: 不剔除(渲染正面和背面)

透明度测试Shader

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

透明度混合Shader

对于透明度混合Shader,由于它需要进行混合,需要使用两个Pass,一个用于渲染背面,一个用于渲染正面。
Pass是可以有多个的,比如先画物体的轮廓,接着再画物体的主体。
两个Pass中除了剔除命令不同 其他代码和之前一致。

实现双面渲染的透明效果 Shader

透明度测试

关键步骤
  1. 复制 Lesson40_透明_效果实现_透明度测试 透明度测试相关Shader代码
  2. 在 Pass 中关闭剔除(Cull Off
复制 Lesson40_透明_效果实现_透明度测试 透明度测试相关Shader代码
Shader "Unlit/Lesson43_Transparent_Test_Both"
{
    Properties
    {
        //主要就是将单张纹理Shader和布林方光照模型逐片元Shader进行一个结合
        _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
    {
        //设置渲染队列 决定对象在何时渲染
        //"Queue"="AlphaTest":设置渲染队列,决定了该对象的渲染顺序。AlphaTest指的是该对象会在不透明对象之后、透明对象之前进行渲染。
        //"IgnoreProjector"="True":表示忽略投影机。这意味着该对象不会受到投影机(例如阴影投影器)的影响。可以用于不需要接收阴影的对象,以提高渲染性能。
        //"RenderType"="TransparentCutout":设置渲染类型为TransparentCutout,表示该材质使用透明裁剪技术,类似于Alpha测试,用于部分透明的物体。
        Tags
        {
            "Queue"="AlphaTest" "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; //xy代表缩放 zw代表平移
            
            //漫反射颜色、高光反射颜色、光泽度
            fixed4 _MainColor;
            fixed4 _SpecularColor;
            float _SpecularNum;
            
            //透明度测试用的阈值
            fixed _Cutoff;

            struct v2f
            {
                //裁剪空间下的顶点坐标
                float4 pos:SV_POSITION;
                //UV坐标
                float2 uv:TEXCOORD0;
                //世界空间下的法线
                float3 wNormal:NORMAL;
                //世界空间下的顶点坐标
                float3 wPos:TEXCOORD1;
            };

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                
                //把模型空间下的顶点转换到裁剪空间下
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                
                //uv坐标计算
                //appdata_base.texcoord.xy //代表uv坐标
                //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
                //先缩放 后平移 这个是一个固定的算法 规则如此
                //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
                //因为缩放默认值是1和1 ,平移默认值是0和0
                v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                
                //世界空间下的法线
                v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
                
                //世界空间下的顶点坐标
                v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex);
                
                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //颜色纹理的颜色信息
                fixed4 texColor = tex2D(_MainTex, v2f.uv);

                //判断贴图的 颜色信息中的 透明通道 有没有小于阈值 
                //如果小于了 就直接丢弃

                //使用clip函数写法
                clip(texColor.a - _Cutoff);
                
                //手动if写法 和上面等价
                //if(texColor.a - _Cutoff < 0)
                //if(texColor.a < _Cutoff)
                //    discard;

                //纹理颜色需要和漫反射材质颜色叠加(乘法) 共同决定最终的颜色
                // fixed3 albedo = tex2D(_MainTex, v2f.uv).rgb * _MainColor.rgb;//之前的代码 现在把纹理颜色tex2D(_MainTex, v2f.uv).rgb取出来用texColor接
                fixed3 albedo = texColor.rgb * _MainColor.rgb;
                
                //光的方向(指向光源方向)
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
                //新知识点:兰伯特光照模型计算时,漫反射材质颜色使用 1 中的叠加颜色计算
                fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(v2f.wNormal, lightDir));

                // 视角方向
                //float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
                float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.wPos));
                
                //半角向量 = 视角方向 + 光的方向
                float3 halfA = normalize(viewDir + lightDir);
                
                //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
                fixed3 specularColor = _LightColor0.rgb * _SpecularColor * pow(
                    max(0, dot(v2f.wNormal, halfA)), _SpecularNum);

                //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
                //新知识点:最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 最终的颜色 进行乘法叠加
                // * albedo为了避免最终的渲染效果偏灰 否则会偏灰
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

                return fixed4(color.rgb, 1);
            }
            ENDCG
        }
    }
}
在Pass中关闭剔除 Cull Off
//关闭剔除 让正面和背面都去进行渲染
Cull Off
创建材质,并赋值,可以看到内部结构

透明度混合

关键步骤
  1. 复制 Lesson41_透明_效果实现_透明度混合 透明度混合相关Shader代码
  2. 复制之前的Pass,变成两个一模一样的Pass
  3. 第一个 Pass 中剔除正面(Cull Front),第二个 Pass 中剔除背面(Cull Back)。 相当于一个片元先渲染背面再渲染正面
复制 Lesson41_透明_效果实现_透明度混合 透明度混合相关Shader代码
Shader "Unlit/Lesson43_Transparent_Blend_Both"
{
    Properties
    {
        //主要就是将单张纹理Shader和布林方光照模型逐片元Shader进行一个结合
        _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)) = 1
    }
    SubShader
    {
        //设置透明度混合的渲染队列 为 透明的Transparent
        //"Queue"="Transparent":设置渲染队列,决定了该对象的渲染顺序。会在不透明对象渲染完之后进行渲染。透明对象按照从后往前的顺序进行渲染,以保证正确的透明效果叠加。
        //"IgnoreProjector"="True":表示忽略投影机。这意味着该对象不会受到投影机(例如阴影投影器)的影响。可以用于不需要接收阴影的对象,以提高渲染性能。
        //"RenderType"="Transparent":Transparent,表示该材质的渲染类型为透明,通常用于完全透明或具有一定透明度的物体,以实现不同程度的透明效果,比如玻璃、水等。
        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; //xy代表缩放 zw代表平移
            
            //漫反射颜色、高光反射颜色、光泽度
            fixed4 _MainColor;
            fixed4 _SpecularColor;
            float _SpecularNum;
            
            //用于控制对象整体透明度的值
            fixed _AlphaScale;

            struct v2f
            {
                //裁剪空间下的顶点坐标
                float4 pos:SV_POSITION;
                //UV坐标
                float2 uv:TEXCOORD0;
                //世界空间下的法线
                float3 wNormal:NORMAL;
                //世界空间下的顶点坐标
                float3 wPos:TEXCOORD1;
            };

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                
                //把模型空间下的顶点转换到裁剪空间下
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                
                //uv坐标计算
                //appdata_base.texcoord.xy //代表uv坐标
                //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
                //先缩放 后平移 这个是一个固定的算法 规则如此
                //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
                //因为缩放默认值是1和1 ,平移默认值是0和0
                v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                
                //世界空间下的法线
                v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
                
                //世界空间下的顶点坐标
                v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex);
                
                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //取出纹理当中的颜色RGBA
                fixed4 texColor = tex2D(_MainTex, v2f.uv);
                
                //新知识点:纹理颜色需要和漫反射材质颜色叠加(乘法) 共同决定最终的颜色
                fixed3 albedo = texColor.rgb * _MainColor.rgb;
                
                //光的方向(指向光源方向)
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
                //新知识点:兰伯特光照模型计算时,漫反射材质颜色使用 1 中的叠加颜色计算
                fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(v2f.wNormal, lightDir));

                // 视角方向
                //float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
                float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.wPos));
                
                //半角向量 = 视角方向 + 光的方向
                float3 halfA = normalize(viewDir + lightDir);
                
                //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
                fixed3 specularColor = _LightColor0.rgb * _SpecularColor * pow(
                    max(0, dot(v2f.wNormal, halfA)), _SpecularNum);

                //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
                //新知识点:最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 最终的颜色 进行乘法叠加
                // * albedo为了避免最终的渲染效果偏灰 否则会偏灰
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

                //修改我们的 Alpha返回值 决定颜色的透明度
                return fixed4(color.rgb, texColor.a * _AlphaScale);
            }
            ENDCG
        }
    }
}
复制之前的Pass,变成两个一模一样的Pass
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; //xy代表缩放 zw代表平移
    
    //漫反射颜色、高光反射颜色、光泽度
    fixed4 _MainColor;
    fixed4 _SpecularColor;
    float _SpecularNum;
    
    //用于控制对象整体透明度的值
    fixed _AlphaScale;

    struct v2f
    {
        //裁剪空间下的顶点坐标
        float4 pos:SV_POSITION;
        //UV坐标
        float2 uv:TEXCOORD0;
        //世界空间下的法线
        float3 wNormal:NORMAL;
        //世界空间下的顶点坐标
        float3 wPos:TEXCOORD1;
    };

    v2f vert(appdata_base appdata_base)
    {
        v2f v2f;
        
        //把模型空间下的顶点转换到裁剪空间下
        v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
        
        //uv坐标计算
        //appdata_base.texcoord.xy //代表uv坐标
        //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
        //先缩放 后平移 这个是一个固定的算法 规则如此
        //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
        //因为缩放默认值是1和1 ,平移默认值是0和0
        v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
        
        //世界空间下的法线
        v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
        
        //世界空间下的顶点坐标
        v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex);
        
        return v2f;
    }

    fixed4 frag(v2f v2f) : SV_Target
    {
        //取出纹理当中的颜色RGBA
        fixed4 texColor = tex2D(_MainTex, v2f.uv);
        
        //新知识点:纹理颜色需要和漫反射材质颜色叠加(乘法) 共同决定最终的颜色
        fixed3 albedo = texColor.rgb * _MainColor.rgb;
        
        //光的方向(指向光源方向)
        float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
        
        //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
        //新知识点:兰伯特光照模型计算时,漫反射材质颜色使用 1 中的叠加颜色计算
        fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(v2f.wNormal, lightDir));

        // 视角方向
        //float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
        float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.wPos));
        
        //半角向量 = 视角方向 + 光的方向
        float3 halfA = normalize(viewDir + lightDir);
        
        //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
        fixed3 specularColor = _LightColor0.rgb * _SpecularColor * pow(
            max(0, dot(v2f.wNormal, halfA)), _SpecularNum);

        //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
        //新知识点:最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 最终的颜色 进行乘法叠加
        // * albedo为了避免最终的渲染效果偏灰 否则会偏灰
        fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

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

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; //xy代表缩放 zw代表平移
    
    //漫反射颜色、高光反射颜色、光泽度
    fixed4 _MainColor;
    fixed4 _SpecularColor;
    float _SpecularNum;
    
    //用于控制对象整体透明度的值
    fixed _AlphaScale;

    struct v2f
    {
        //裁剪空间下的顶点坐标
        float4 pos:SV_POSITION;
        //UV坐标
        float2 uv:TEXCOORD0;
        //世界空间下的法线
        float3 wNormal:NORMAL;
        //世界空间下的顶点坐标
        float3 wPos:TEXCOORD1;
    };

    v2f vert(appdata_base appdata_base)
    {
        v2f v2f;
        
        //把模型空间下的顶点转换到裁剪空间下
        v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
        
        //uv坐标计算
        //appdata_base.texcoord.xy //代表uv坐标
        //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
        //先缩放 后平移 这个是一个固定的算法 规则如此
        //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
        //因为缩放默认值是1和1 ,平移默认值是0和0
        v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
        
        //世界空间下的法线
        v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
        
        //世界空间下的顶点坐标
        v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex);
        
        return v2f;
    }

    fixed4 frag(v2f v2f) : SV_Target
    {
        //取出纹理当中的颜色RGBA
        fixed4 texColor = tex2D(_MainTex, v2f.uv);
        
        //新知识点:纹理颜色需要和漫反射材质颜色叠加(乘法) 共同决定最终的颜色
        fixed3 albedo = texColor.rgb * _MainColor.rgb;
        
        //光的方向(指向光源方向)
        float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
        
        //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
        //新知识点:兰伯特光照模型计算时,漫反射材质颜色使用 1 中的叠加颜色计算
        fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(v2f.wNormal, lightDir));

        // 视角方向
        //float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
        float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.wPos));
        
        //半角向量 = 视角方向 + 光的方向
        float3 halfA = normalize(viewDir + lightDir);
        
        //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
        fixed3 specularColor = _LightColor0.rgb * _SpecularColor * pow(
            max(0, dot(v2f.wNormal, halfA)), _SpecularNum);

        //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
        //新知识点:最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 最终的颜色 进行乘法叠加
        // * albedo为了避免最终的渲染效果偏灰 否则会偏灰
        fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

        //修改我们的 Alpha返回值 决定颜色的透明度
        return fixed4(color.rgb, texColor.a * _AlphaScale);
    }
    ENDCG
}
在第一个Pass中剔除正面 Cull Front,在第二个Pass中剔除背面Cull Back,相当于一个片元先渲染背面再渲染正面
Pass
{
    Tags
    {
        "LightMode" = "ForwardBase"
    }

    //剔除正面 先去渲染背面
    Cull Front
    
    //...

    ENDCG
}

Pass
{
    Tags
    {
        "LightMode" = "ForwardBase"
    }
    
    //剔除背面 只渲染正面
    Cull Back

    //...

    ENDCG
}
创建材质,并赋值,可以看到内部结构


43.2 知识点代码

Lesson43_透明_效果实现_双面渲染的透明效果

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson43_透明_效果实现_双面渲染的透明效果 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 双面渲染的透明效果用来处理哪种需求的?

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

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

        #endregion

        #region 知识点二 双面渲染的透明效果的基本原理

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

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

        //  对于透明度混合Shader
        //  由于它需要进行混合
        //  需要使用两个Pass,一个用于渲染背面,一个用于渲染正面
        //  Pass是可以有多个的,比如先画物体的轮廓,接着再画物体的主体
        //  两个Pass中除了剔除命令不同 其他代码和之前一致

        #endregion

        #region 知识点三 实现 双面渲染的透明效果Shader

        //透明度测试
        //  1.复制 Lesson40_透明_效果实现_透明度测试 透明度测试相关Shader代码
        //  2.在Pass中关闭剔除 Cull Off

        //透明度混合
        //  1.复制 Lesson41_透明_效果实现_透明度混合 透明度混合相关Shader代码
        //  2.复制之前的Pass,变成两个一模一样的Pass
        //  3.在第一个Pass中剔除正面 Cull Front,在第二个Pass中剔除背面Cull Back
        //    相当于一个片元先渲染背面再渲染正面

        #endregion
    }
}

Lesson43_Transparent_Test_Both.shader

Shader "Unlit/Lesson43_Transparent_Test_Both"
{
    Properties
    {
        //主要就是将单张纹理Shader和布林方光照模型逐片元Shader进行一个结合
        _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
    {
        //设置渲染队列 决定对象在何时渲染
        //"Queue"="AlphaTest":设置渲染队列,决定了该对象的渲染顺序。AlphaTest指的是该对象会在不透明对象之后、透明对象之前进行渲染。
        //"IgnoreProjector"="True":表示忽略投影机。这意味着该对象不会受到投影机(例如阴影投影器)的影响。可以用于不需要接收阴影的对象,以提高渲染性能。
        //"RenderType"="TransparentCutout":设置渲染类型为TransparentCutout,表示该材质使用透明裁剪技术,类似于Alpha测试,用于部分透明的物体。
        Tags
        {
            "Queue"="AlphaTest" "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; //xy代表缩放 zw代表平移
            
            //漫反射颜色、高光反射颜色、光泽度
            fixed4 _MainColor;
            fixed4 _SpecularColor;
            float _SpecularNum;
            
            //透明度测试用的阈值
            fixed _Cutoff;

            struct v2f
            {
                //裁剪空间下的顶点坐标
                float4 pos:SV_POSITION;
                //UV坐标
                float2 uv:TEXCOORD0;
                //世界空间下的法线
                float3 wNormal:NORMAL;
                //世界空间下的顶点坐标
                float3 wPos:TEXCOORD1;
            };

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                
                //把模型空间下的顶点转换到裁剪空间下
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                
                //uv坐标计算
                //appdata_base.texcoord.xy //代表uv坐标
                //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
                //先缩放 后平移 这个是一个固定的算法 规则如此
                //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
                //因为缩放默认值是1和1 ,平移默认值是0和0
                v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                
                //世界空间下的法线
                v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
                
                //世界空间下的顶点坐标
                v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex);
                
                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //颜色纹理的颜色信息
                fixed4 texColor = tex2D(_MainTex, v2f.uv);

                //判断贴图的 颜色信息中的 透明通道 有没有小于阈值 
                //如果小于了 就直接丢弃

                //使用clip函数写法
                clip(texColor.a - _Cutoff);
                
                //手动if写法 和上面等价
                //if(texColor.a - _Cutoff < 0)
                //if(texColor.a < _Cutoff)
                //    discard;

                //纹理颜色需要和漫反射材质颜色叠加(乘法) 共同决定最终的颜色
                // fixed3 albedo = tex2D(_MainTex, v2f.uv).rgb * _MainColor.rgb;//之前的代码 现在把纹理颜色tex2D(_MainTex, v2f.uv).rgb取出来用texColor接
                fixed3 albedo = texColor.rgb * _MainColor.rgb;
                
                //光的方向(指向光源方向)
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
                //新知识点:兰伯特光照模型计算时,漫反射材质颜色使用 1 中的叠加颜色计算
                fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(v2f.wNormal, lightDir));

                // 视角方向
                //float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
                float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.wPos));
                
                //半角向量 = 视角方向 + 光的方向
                float3 halfA = normalize(viewDir + lightDir);
                
                //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
                fixed3 specularColor = _LightColor0.rgb * _SpecularColor * pow(
                    max(0, dot(v2f.wNormal, halfA)), _SpecularNum);

                //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
                //新知识点:最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 最终的颜色 进行乘法叠加
                // * albedo为了避免最终的渲染效果偏灰 否则会偏灰
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

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

Lesson43_Transparent_Blend_Both.shader

Shader “Unlit/Lesson43_Transparent_Blend_Both”
{
Properties
{
//主要就是将单张纹理Shader和布林方光照模型逐片元Shader进行一个结合
_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)) = 1
}
SubShader
{
//设置透明度混合的渲染队列 为 透明的Transparent
//“Queue”=”Transparent”:设置渲染队列,决定了该对象的渲染顺序。会在不透明对象渲染完之后进行渲染。透明对象按照从后往前的顺序进行渲染,以保证正确的透明效果叠加。
//“IgnoreProjector”=”True”:表示忽略投影机。这意味着该对象不会受到投影机(例如阴影投影器)的影响。可以用于不需要接收阴影的对象,以提高渲染性能。
//“RenderType”=”Transparent”:Transparent,表示该材质的渲染类型为透明,通常用于完全透明或具有一定透明度的物体,以实现不同程度的透明效果,比如玻璃、水等。
Tags
{
“Queue”=”Transparent” “IgnoreProjector”=”True” “RenderType”=”Transparent”
}

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

        //剔除正面 先去渲染背面
        Cull Front
        
        //对于处理半透明效果 需要
        //关闭深度写入
        ZWrite Off
        //将混合因子 设置为半透明效果的 搭配
        Blend SrcAlpha OneMinusSrcAlpha

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

        //纹理贴图对应的映射成员
        
        //映射对应纹理属性的图片颜色相关数据
        sampler2D _MainTex;
        
        //映射对应纹理属性的 缩放 平(偏)移数据
        float4 _MainTex_ST; //xy代表缩放 zw代表平移
        
        //漫反射颜色、高光反射颜色、光泽度
        fixed4 _MainColor;
        fixed4 _SpecularColor;
        float _SpecularNum;
        
        //用于控制对象整体透明度的值
        fixed _AlphaScale;

        struct v2f
        {
            //裁剪空间下的顶点坐标
            float4 pos:SV_POSITION;
            //UV坐标
            float2 uv:TEXCOORD0;
            //世界空间下的法线
            float3 wNormal:NORMAL;
            //世界空间下的顶点坐标
            float3 wPos:TEXCOORD1;
        };

        v2f vert(appdata_base appdata_base)
        {
            v2f v2f;
            
            //把模型空间下的顶点转换到裁剪空间下
            v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
            
            //uv坐标计算
            //appdata_base.texcoord.xy //代表uv坐标
            //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
            //先缩放 后平移 这个是一个固定的算法 规则如此
            //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
            //因为缩放默认值是1和1 ,平移默认值是0和0
            v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
            
            //世界空间下的法线
            v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
            
            //世界空间下的顶点坐标
            v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex);
            
            return v2f;
        }

        fixed4 frag(v2f v2f) : SV_Target
        {
            //取出纹理当中的颜色RGBA
            fixed4 texColor = tex2D(_MainTex, v2f.uv);
            
            //新知识点:纹理颜色需要和漫反射材质颜色叠加(乘法) 共同决定最终的颜色
            fixed3 albedo = texColor.rgb * _MainColor.rgb;
            
            //光的方向(指向光源方向)
            float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
            
            //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
            //新知识点:兰伯特光照模型计算时,漫反射材质颜色使用 1 中的叠加颜色计算
            fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(v2f.wNormal, lightDir));

            // 视角方向
            //float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
            float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.wPos));
            
            //半角向量 = 视角方向 + 光的方向
            float3 halfA = normalize(viewDir + lightDir);
            
            //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
            fixed3 specularColor = _LightColor0.rgb * _SpecularColor * pow(
                max(0, dot(v2f.wNormal, halfA)), _SpecularNum);

            //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
            //新知识点:最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 最终的颜色 进行乘法叠加
            // * albedo为了避免最终的渲染效果偏灰 否则会偏灰
            fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

            //修改我们的 Alpha返回值 决定颜色的透明度
            return fixed4(color.rgb, texColor.a * _AlphaScale);
        }
        ENDCG
    }
    
    Pass
    {
        Tags
        {
            "LightMode" = "ForwardBase"
        }
        
        //剔除背面 只渲染正面
        Cull Back

        //对于处理半透明效果 需要
        //关闭深度写入
        ZWrite Off
        //将混合因子 设置为半透明效果的 搭配
        Blend SrcAlpha OneMinusSrcAlpha

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

        //纹理贴图对应的映射成员
        
        //映射对应纹理属性的图片颜色相关数据
        sampler2D _MainTex;
        
        //映射对应纹理属性的 缩放 平(偏)移数据
        float4 _MainTex_ST; //xy代表缩放 zw代表平移
        
        //漫反射颜色、高光反射颜色、光泽度
        fixed4 _MainColor;
        fixed4 _SpecularColor;
        float _SpecularNum;
        
        //用于控制对象整体透明度的值
        fixed _AlphaScale;

        struct v2f
        {
            //裁剪空间下的顶点坐标
            float4 pos:SV_POSITION;
            //UV坐标
            float2 uv:TEXCOORD0;
            //世界空间下的法线
            float3 wNormal:NORMAL;
            //世界空间下的顶点坐标
            float3 wPos:TEXCOORD1;
        };

        v2f vert(appdata_base appdata_base)
        {
            v2f v2f;
            
            //把模型空间下的顶点转换到裁剪空间下
            v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
            
            //uv坐标计算
            //appdata_base.texcoord.xy //代表uv坐标
            //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
            //先缩放 后平移 这个是一个固定的算法 规则如此
            //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
            //因为缩放默认值是1和1 ,平移默认值是0和0
            v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
            
            //世界空间下的法线
            v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
            
            //世界空间下的顶点坐标
            v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex);
            
            return v2f;
        }

        fixed4 frag(v2f v2f) : SV_Target
        {
            //取出纹理当中的颜色RGBA
            fixed4 texColor = tex2D(_MainTex, v2f.uv);
            
            //新知识点:纹理颜色需要和漫反射材质颜色叠加(乘法) 共同决定最终的颜色
            fixed3 albedo = texColor.rgb * _MainColor.rgb;
            
            //光的方向(指向光源方向)
            float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
            
            //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
            //新知识点:兰伯特光照模型计算时,漫反射材质颜色使用 1 中的叠加颜色计算
            fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(v2f.wNormal, lightDir));

            // 视角方向
            //float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
            float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.wPos));
            
            //半角向量 = 视角方向 + 光的方向
            float3 halfA = normalize(viewDir + lightDir);
            
            //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
            fixed3 specularColor = _LightColor0.rgb * _SpecularColor * pow(
                max(0, dot(v2f.wNormal, halfA)), _SpecularNum);

            //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
            //新知识点:最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 最终的颜色 进行乘法叠加
            // * albedo为了避免最终的渲染效果偏灰 否则会偏灰
            fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

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

}


---

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏