33.菲涅尔反射结合漫反射及阴影

33.高级纹理-立方体纹理-菲涅尔反射-菲涅尔反射结合漫反射及阴影


33.1 知识点

菲涅尔反射结合漫反射

主要步骤

  • 菲涅尔反射是基于反射的,因此在之前实现的反射结合漫反射的基础上进行修改。
  1. 新建 Shader Lesson33_FresnelDiffuseShadow ,复制Lesson27_ReflectDiffuseShadow
  2. 修改属性:
    • 去掉反射颜色。
    • 修改反射率为菲涅耳相关的 R0 反射率。
  3. 修改 v2f 结构体,加入视角方向(需要在片元着色器中使用菲涅耳近似公式进行计算)。
  4. 修改顶点着色器:
    • 在顶点着色器中修改视角方向临时变量。
  5. 修改片元着色器:
    • 计算菲涅尔反射率。
    • 用菲涅尔反射率参与 lerp 计算。

新建 Shader Lesson33_FresnelDiffuseShadow ,复制Lesson27_ReflectDiffuseShadow

Shader "Unlit/Lesson33_FresnelDiffuseShadow"
{
    Properties
    {
        //漫反射颜色
        _Color("Color", Color) = (1,1,1,1)
        //反射颜色
        _ReflectColor("ReflectColor", Color) = (1,1,1,1)
        //立方体纹理
        _Cube("Cubemap", Cube) = ""{}
        //反射率
        _Reflectivity("Reflectivity", Range(0,1)) = 1
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque" "Queue"="Geometry"
        }

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

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //Shader变体
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            //灯光内置文件
            #include "AutoLight.cginc"

            fixed4 _Color;
            fixed4 _ReflectColor;
            samplerCUBE _Cube;
            float _Reflectivity;

            struct v2f
            {
                float4 pos:SV_POSITION; //裁剪空间下的顶点坐标
                //世界空间下法线
                fixed3 worldNormal:NORMAL;
                //世界空间下的顶点位置
                float3 worldPos:TEXCOORD0;
                //世界空间下的反射向量
                //我们将把反射向量的计算放在顶点着色器函数中 节约性能 表现效果也不会太差 肉眼几乎分辨不出来
                float3 worldRefl:TEXCOORD1;
                //阴影相关
                SHADOW_COORDS(2)
            };

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;

                //顶点坐标转换
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);

                //计算反射光向量
                //1.计算世界空间下法线向量
                v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
                //2.世界空间下的顶点坐标
                v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
                //3.计算视角方向 内部是用摄像机位置 - 世界坐标位置 
                fixed3 worldViewDir = UnityWorldSpaceViewDir(v2f.worldPos);
                //4.计算反射向量
                v2f.worldRefl = reflect(-worldViewDir, v2f.worldNormal);

                //阴影相关处理(接受阴影)
                TRANSFER_SHADOW(v2f);

                return v2f;
            }

            fixed4 frag(v2f v2f):SV_TARGET
            {
                //漫反射光照相关计算
                //传入世界空间顶点 得到世界空间下光的方向并单位化 
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v2f.worldPos));
                //漫反射颜色
                fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(normalize(v2f.worldNormal), worldLightDir));

                //对立方体纹理利用对应的反射向量进行采样
                fixed3 cubemapColor = texCUBE(_Cube, v2f.worldRefl).rgb * _ReflectColor.rgb;

                //得到光照衰减以及阴影相关的衰减值
                UNITY_LIGHT_ATTENUATION(atten, v2f, v2f.worldPos);

                //利用lerp 在漫反射颜色和反射颜色之间 进行插值
                //_Reflectivity为0或1就是极限状态
                //_Reflectivity=0 没有反射效果
                //_Reflectivity=1只有反射效果
                //_Reflectivity在0~1之间就是两者叠加
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse, cubemapColor, _Reflectivity) * atten;

                //用采样颜色 * 反射率 决定最终的颜色效果
                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }

    //内置的阴影投射的Pass
    FallBack "Reflective/VertexLit"
}

修改属性,去掉反射颜色,修改反射率命名为菲涅耳相关反射率以及相关映射

Properties
{
    //漫反射颜色
    _Color("Color", Color) = (1,1,1,1)
    //立方体纹理
    _Cube("Cubemap", Cube) = ""{}
    //反射率
    _FresnelScale("FresnelScale", Range(0,1)) = 1
}
fixed4 _Color;
samplerCUBE _Cube;
float _FresnelScale;

修改v2f结构体,加入视角方向,因为需要在片元着色器中用菲涅耳近似公式参与计算

struct v2f
{
    //裁剪空间下的顶点坐标
    float4 pos:SV_POSITION; 
    //世界空间下法线
    fixed3 worldNormal:NORMAL;
    //世界空间下的顶点位置
    float3 worldPos:TEXCOORD0;
    //世界空间下视角的方向
    float3 worldViewDir:TEXCOORD1;
    //世界空间下的反射向量
    //我们将把反射向量的计算放在顶点着色器函数中 节约性能 表现效果也不会太差 肉眼几乎分辨不出来
    float3 worldRefl:TEXCOORD2;
    //阴影相关
    SHADOW_COORDS(3)
};

顶点函数中,把临时变量视角方向赋值给结构体变量

v2f vert(appdata_base appdata_base)
{
    v2f v2f;
    //顶点坐标转换
    v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
    //计算反射光向量
    //1.计算世界空间下法线向量
    v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
    //2.世界空间下的顶点坐标
    v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
    //3.计算视角方向 内部是用摄像机位置 - 世界坐标位置 
    v2f.worldViewDir = UnityWorldSpaceViewDir(v2f.worldPos);
    //4.计算反射向量
    v2f.worldRefl = reflect(-v2f.worldViewDir, v2f.worldNormal);

    //阴影相关处理
    TRANSFER_SHADOW(v2f);

    return v2f;
}

片元函数中,对立方体纹理不需要乘上反射颜色了。使用菲涅耳公式进行计算菲涅耳反射率,同时和漫反射插值时也使用菲涅耳反射率

fixed4 frag(v2f v2f):SV_TARGET
{
    //漫反射光照相关计算
    //得到光的方向
    fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v2f.worldPos));
    //漫反射颜色
    fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(normalize(v2f.worldNormal), worldLightDir));

    //对立方体纹理利用对应的反射向量进行采样
    fixed3 cubemapColor = texCUBE(_Cube, v2f.worldRefl).rgb;

    //得到光照衰减以及阴影相关的衰减值
    UNITY_LIGHT_ATTENUATION(atten, v2f, v2f.worldPos);

    //菲涅耳反射率计算
    //  R(θ) = R0 + ( 1− R0 )( 1 − V·𝑵)^𝟓 
    //      R(θ):对应角度的菲涅耳反射率
    //      R0:介质反射率
    //      V:视角单位向量
    //      N:法线单位向量
    fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(
        1 - dot(normalize(v2f.worldViewDir), normalize(v2f.worldNormal)), 5);

    //我们利用lerp 在漫反射颜色和反射颜色之间 进行插值 0和1就是极限状态 0 没有反射效果 1只有反射效果 0~1之间就是两者叠加
    fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse, cubemapColor, fresnel) * atten;

    //用采样颜色 * 反射率 决定最终的颜色效果
    return fixed4(color, 1.0);
}

创建shader进行对比,修改反射程度后,可以看到菲涅尔反射边缘和普通反射的不同,类似玻璃杯的效果



33.2 知识点代码

Lesson33_高级纹理_立方体纹理_菲涅尔反射_菲涅尔反射结合漫反射及阴影.cs

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

public class Lesson33_高级纹理_立方体纹理_菲涅尔反射_菲涅尔反射结合漫反射及阴影 : MonoBehaviour
{
    void Start()
    {
        #region 知识点 菲涅尔反射结合漫反射

        //由于菲涅耳反射是基于反射的
        //因此我们在之前实现的反射结合漫反射的基础上修改即可

        //1.新建Shader命名为Lesson33_FresnelDiffuseShadow,复制Lesson27_ReflectDiffuseShadow

        //2.修改属性
        //  2-1:去掉反射颜色
        //  2-2:修改反射率为菲涅耳相关的R0反射率

        //3.修改v2f结构体
        //  加入视角方向(需要再片元着色器中用菲涅耳近似公式参与计算)

        //4.顶点着色器
        //  修改视角方向临时变量

        //5.片元着色器
        //  5-1:计算菲涅耳反射率
        //  5-2:用菲涅耳反射率参与lerp计算

        #endregion
    }
}

Lesson33_FresnelDiffuseShadow.shader

Shader "Unlit/Lesson33_FresnelDiffuseShadow"
{
    Properties
    {
        //漫反射颜色
        _Color("Color", Color) = (1,1,1,1)
        //立方体纹理
        _Cube("Cubemap", Cube) = ""{}
        //反射率
        _FresnelScale("FresnelScale", Range(0,1)) = 1
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque" "Queue"="Geometry"
        }

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

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            fixed4 _Color;
            samplerCUBE _Cube;
            float _FresnelScale;

            struct v2f
            {
                //裁剪空间下的顶点坐标
                float4 pos:SV_POSITION; 
                //世界空间下法线
                fixed3 worldNormal:NORMAL;
                //世界空间下的顶点位置
                float3 worldPos:TEXCOORD0;
                //世界空间下视角的方向
                float3 worldViewDir:TEXCOORD1;
                //世界空间下的反射向量
                //我们将把反射向量的计算放在顶点着色器函数中 节约性能 表现效果也不会太差 肉眼几乎分辨不出来
                float3 worldRefl:TEXCOORD2;
                //阴影相关
                SHADOW_COORDS(3)
            };

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                //顶点坐标转换
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                //计算反射光向量
                //1.计算世界空间下法线向量
                v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
                //2.世界空间下的顶点坐标
                v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
                //3.计算视角方向 内部是用摄像机位置 - 世界坐标位置 
                v2f.worldViewDir = UnityWorldSpaceViewDir(v2f.worldPos);
                //4.计算反射向量
                v2f.worldRefl = reflect(-v2f.worldViewDir, v2f.worldNormal);

                //阴影相关处理
                TRANSFER_SHADOW(v2f);

                return v2f;
            }

            fixed4 frag(v2f v2f):SV_TARGET
            {
                //漫反射光照相关计算
                //得到光的方向
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v2f.worldPos));
                //漫反射颜色
                fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(normalize(v2f.worldNormal), worldLightDir));

                //对立方体纹理利用对应的反射向量进行采样
                fixed3 cubemapColor = texCUBE(_Cube, v2f.worldRefl).rgb;

                //得到光照衰减以及阴影相关的衰减值
                UNITY_LIGHT_ATTENUATION(atten, v2f, v2f.worldPos);

                //菲涅耳反射率计算
                //  R(θ) = R0 + ( 1− R0 )( 1 − V·𝑵)^𝟓 
                //      R(θ):对应角度的菲涅耳反射率
                //      R0:介质反射率
                //      V:视角单位向量
                //      N:法线单位向量
                fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(
                    1 - dot(normalize(v2f.worldViewDir), normalize(v2f.worldNormal)), 5);

                //我们利用lerp 在漫反射颜色和反射颜色之间 进行插值 0和1就是极限状态 0 没有反射效果 1只有反射效果 0~1之间就是两者叠加
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse, cubemapColor, fresnel) * atten;

                //用采样颜色 * 反射率 决定最终的颜色效果
                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Reflective/VertexLit"
}


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

×

喜欢就点赞,疼爱就打赏