19.透明度混合物体阴影

19.阴影-透明物体阴影-透明度混合物体阴影


19.1 知识点

透明度混合

透明度测试只能处理极端效果(完全透明和完全不透明),而透明度混合可以帮助我们实现半透明效果。其基本原理如下:

  • 关闭深度写入。
  • 开启混合,使片元颜色和颜色缓冲区中的颜色进行混合计算。

准备工作

主要步骤

  1. 新建一个 Shader,取名为 Lesson19_Transparent_Blend_Both_Shadow
  2. 复制 Shader 开发入门中的 文件 Lesson43_Transparent_Blend_Both.shader(透明效果实现双面渲染的透明效果)。
  3. 创建材质并赋值为新建的 Shader,同时为材质贴上一个透明混合贴图。
  4. 在场景中创建一个立方体对象用于测试,可以观察到此透明混合立方体是没有阴影的。

复制的透明度混合代码

Shader "Unlit/Lesson19_Transparent_Blend_Both_Shadow"
{
    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
        }
    }
}

可以看到透明混合立方体没有投射阴影

透明度混合 Shader 处理阴影

投射阴影的尝试

为使物体能够投射阴影,可以在 Shader 中添加 Fallback 设置。例如:

Fallback "Transparent/VertexLit"

然而,在 Unity 中查看效果时,并不能实现阴影投射。同样,即使按上节课的方式加入各种接受阴影的宏和计算阴影衰减值的相关代码,也无法看到接受阴影的效果。

问题原因

  • 由于透明度混合需要关闭深度写入。
  • 阴影相关的处理依赖于深度值的参与计算。
  • 因此Unity中从性能方面考虑(要计算半透明物体的的阴影表现效果是相对复杂的),Unity 内置的半透明 Shader(如 Transparent/VertexLit)不会产生阴影效果。

结论

Unity 不会直接为透明度混合 Shader 处理阴影。

透明混合 Shader 想要投射阴影时

无论在 Fallback 中写入哪种内置的半透明混合 Shader,都不会产生投射阴影的效果,因为深度值不会写入。

透明混合 Shader 想要接受阴影时

Unity 内置的关于阴影接收计算的宏也不会处理透明混合 Shader。这是因为半透明物体的深度值和遮挡关系无法直接用传统的深度缓冲和阴影贴图进行处理。

强制生成阴影

思路

我们可以尝试让透明混合 Shader 强制投射阴影:

  • Fallback 中设置一个非透明 Shader(如 VertexLitDiffuse)。
  • 使用其中灯光模式为阴影投射的渲染通道来参与阴影映射纹理的计算,将该物体当成一个实体物体处理。
  • 但是,这种效果并不真实,不建议使用

添加代码

//Fallback "Transparent/VertexLit"
//强制生成阴影 效果不好 没有透明阴影的感觉
Fallback "VertexLit"

效果

强制生成阴影的效果不真实,无法体现透明阴影的感觉,因此不建议使用。


深度写入与半透明效果

如果使用的是 Shader开发入门中的 Lesson42_透明_效果实现_开启深度写入的半透明效果中 的Lesson42_Transparent_Open_Depth_Writing_Translucent,会不会产生阴影相关呢?

答案是不会的,开启深度写入的半透明Shader只是会多一个 Pass,但这并不会影响阴影投射和接受的相关处理。

//主要就是进行深度写入 但是不输出颜色
Pass
{
    ZWrite On
    ColorMask 0
}

这是因为阴影投射需要灯光模式为阴影投射的渲染通道,接受阴影则需要单独计算。隐藏要实现透明混合物体的阴影效果,需要学习更高级的 Shader 知识。


19.2 知识点代码

Lesson19_阴影_透明物体阴影_透明度混合物体阴影

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

public class Lesson19_阴影_透明物体阴影_透明度混合物体阴影 : MonoBehaviour
{
    void Start()
    {
        #region 知识回顾 透明度混合

        //透明度测试只能处理极端效果(完全透明和完全不透明)
        //而透明度混合可以帮助我们实现半透明效果
        //它的基本原理:
        //关闭深度写入,开启混合,让片元颜色和颜色缓冲区中颜色进行混合计算

        #endregion

        #region 知识点一 准备工作

        //新建一个Shader 取名为Lesson19_Transparent_Blend_Both_Shadow
        //复制Shader开发入门 Lesson43_透明_效果实现_双面渲染的透明效果 Lesson43_Transparent_Blend_Both.shader
        //创建材质赋值shader 并给材质贴上一个透明混合贴图
        //在场景中创建一个和之前透明度混合一样的立方体对象 用于测试
        //可以看到现在这个透明混合立方体是没有阴影的
        
        #endregion

        #region 知识点二 透明度混合Shader处理阴影

        //根据上节课的思路
        //我们如果想要物体产生阴影效果无非就两步:
        //1.投射阴影
        //  在FallBack中关联内置的对应Shader("Transparent/VertexLit")
        //2.接受阴影
        //  在Shader中书写计算阴影衰减值的相关代码
        //因此对于透明度混合的Shader也会使用同样的思路去制作

        //但是!!!
        //由于透明度混合需要关闭深度写入
        //而阴影相关的处理需要用到深度值参与计算
        //因此Unity中从性能方面考虑(要计算半透明物体的的阴影表现效果是相对复杂的)
        //所有的内置半透明Shader都不会产生阴影效果(比如 Transparent/VertexLit)
        //因此
        //2-1.透明混合Shader想要 投射阴影时
        //    不管你在FallBack中写入哪种自带的半透明混合Shader
        //    都不会有投射阴影的效果,因为深度不会写入

        //2-2.透明混合Shader想要 接受阴影时
        //    Unity内置关于阴影接收计算的相关宏
        //    不会计算处理 透明混合Shader
        //    混合因子 设置为半透明效果(Blend SrcAlpha OneMinusSrcAlpha)的Shader 
        //    因为透明混合物体的深度值和遮挡关系无法直接用传统的深度缓冲和阴影贴图来处理

        //结论:
        //Unity中不会直接为透明度混合Shader处理阴影

        #endregion

        #region 知识点三 强制生成阴影

        //我们可以尝试让透明混合Shader强制投射阴影

        //在FallBack中设置一个非透明Shader,比如VertexLit、Diffuse等
        //用其中的灯光模式设置为阴影投射的渲染通道来参与阴影映射纹理的计算
        //把该物体当成一个实体物体处理

        //但是,这种效果并不真实,不建议使用

        #endregion
    }
}

Lesson19_Transparent_Blend_Both_Shadow

Shader "Unlit/Lesson19_Transparent_Blend_Both_Shadow"
{
    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
        }
    }

    Fallback "Transparent/VertexLit"
    //强制生成阴影 效果不好 没有透明阴影的感觉
    //Fallback "VertexLit"
}


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

×

喜欢就点赞,疼爱就打赏