11.Phong式高光反射逐片元光照

11.光照模型-高光反射光照模型-Phong式高光反射模型-逐片元光照


11.1 知识点

利用Phong式高光反射光照模型实现光照效果(逐片元光照)

关键步骤

  • 基本和逐顶点一致
  • 区别:
    1. 在顶点着色器中计算顶点和法线相关数据
    2. 在片元着色器中计算Phong式高光反射光照

创建着色器

我们将首先创建一个基本的Phong光照模型着色器,并删除所有默认的无用代码,保留最基础的顶点和片元着色器的骨架:

Shader "Unlit/Lesson11_Phong_Specular_Pixel"
{
    Properties
    {
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"
        }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            v2f vert(appdata appdata)
            {
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
            }
            ENDCG
        }
    }
}

声明颜色和光泽度属性,设置Tags

我们接下来添加高光反射的颜色和光泽度属性,并为渲染模式设置Tags。注意Tags可以放置在Pass内,这样可以避免对多个Pass产生影响。此外,引用必要的内置文件以便后续使用。

Shader "Unlit/Lesson11_Phong_Specular_Pixel"
{
    Properties
    {
        // 高光反射颜色
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)

        // 光泽度
        _SpecularNum("SpecularNum", Range(0, 20)) = 0.5
    }

    SubShader
    {
        Pass
        {
            // 如果有多个Pass渲染通道时,Tags应放到对应的Pass中,避免影响其他Pass
            // 设置光照模式为 ForwardBase,主要用于处理不透明物体的光照渲染
            Tags
            {
                "LightMode"="ForwardBase"
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 引用内置文件,主要用于内置结构体和变量的使用
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            // 对应属性中的颜色和光泽度
            fixed4 _SpecularColor;
            float _SpecularNum;

            v2f vert(appdata_base appdata_base)
            {

            }

            fixed4 frag(v2f v2f) : SV_Target
            {

            }
            ENDCG
        }
    }
}

声明结构体,传递顶点数据

声明顶点传递给片元的结构体。该结构体需要包含裁剪空间中的顶点坐标、世界空间的法线和世界空间的顶点坐标,这样片元函数才能正确地进行光照计算。

// 顶点着色器传递给片元着色器的内容
struct v2f
{
    // 裁剪空间下的顶点坐标信息
    float4 pos:SV_POSITION;
    // 世界空间下的法线信息
    float3 wNormal:NORMAL;
    // 世界空间下的顶点坐标 
    float3 wPos:TEXCOORD0;
};

顶点着色器中进行空间转换

在顶点着色器中,我们计算裁剪空间的顶点坐标、世界空间的法线和世界空间的顶点坐标。这些信息将传递给片元着色器进行进一步的计算。

v2f vert(appdata_base appdata_base)
{
    v2f v2f;
    
    //1.顶点裁剪空间变换 把模型空间下的法线从变成裁剪空间下的法线信息
    v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
    
    //2.进行法线空间变换 把模型空间下的法线从变成世界空间下的法线信息
    v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
    
    //3.顶点转到世界空间 把模型空间下的顶点变成世界空间下的顶点坐标 
    //以下是三种把模型空间坐标转换成世界空间坐标的方法 但是有些可能已经过时了
    //data.wPos = mul(UNITY_MATRIX_M, appdata_base.vertex).xyz;
    //data.wPos = mul(_Object2World, appdata_base.vertex).xyz;
    v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
    
    return v2f;
}

在片元着色器中进行Phong光照计算

在片元着色器中,我们使用Phong光照模型进行计算,包括视角向量、反射向量和最终的高光反射计算。

fixed4 frag(v2f v2f) : SV_Target
{
    //1.视角单位向量
    //用观察者的位置(摄像机的位置)减去世界空间下顶点坐标 得到的就是视角方向 并且进行单位化
    float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);

    //2.光的反射单位向量
    //光的方向 进行单位化
    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
    //光反射光线的单位向量 reflect(入射向量,顶点法向量) 返回反射向量 
    float3 reflectDir = reflect(-lightDir, v2f.wNormal);

    //3.套公式计算
    //color = 光源颜色 * 材质高光反射颜色 * pow( max(0, dot(视角单位向量, 光的反射单位向量)), 光泽度 )
    fixed3 color = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, reflectDir)), _SpecularNum);

    return fixed4(color.rgb, 1);
}

比较逐顶点与逐片元实现

与逐顶点光照模型相比,逐片元光照模型能够带来更加平滑的光照过渡。顶点光照模型由于使用插值运算,会出现明显的锯齿状效果,而逐片元光照则能避免这种现象。


11.2 知识点代码

Lesson11_光照模型_高光反射光照模型_Phong式高光反射模型_逐片元光照

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

public class Lesson11_光照模型_高光反射光照模型_Phong式高光反射模型_逐片元光照 : MonoBehaviour
{
    void Start()
    {
        #region 知识点 利用Phong式高光反射光照模型实现光照效果(逐片元光照)

        //关键步骤
        //基本和逐顶点一致
        //区别:
        //1.在顶点着色器中计算顶点和法线相关数据
        //2.在片元着色器中计算Phong式高光反射光照

        #endregion
    }
}

Lesson11_Phong_Specular_Pixel.shader

Shader "Unlit/Lesson11_Phong_Specular_Pixel"
{
    Properties
    {
        //高光反射颜色
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)

        //光泽度
        _SpecularNum("SpecularNum", Range(0, 20)) = 0.5
    }

    SubShader
    {
        Pass
        {
            //如果有多个Pass渲染通道时 一般情况下会把光照模式的 Tags放到对应的Pass中 以免影响其他Pass
            //设置我们的光照模式 ForwardBase这种向前渲染模式 主要是用来处理 不透明物体的 光照渲染的
            Tags
            {
                "LightMode"="ForwardBase"
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            //引用对应的内置文件 
            //主要是为了之后 的 比如内置结构体使用,内置变量使用
            #include "UnityCG.cginc"
            #include "Lighting.cginc"


            //对应属性当中的颜色和光泽度
            fixed4 _SpecularColor;
            float _SpecularNum;


            //顶点着色器传递给片元着色器的内容
            struct v2f
            {
                //裁剪空间下的顶点坐标信息
                float4 pos:SV_POSITION;
                //世界空间下的 法线信息
                float3 wNormal:NORMAL;
                //世界空间下的 顶点坐标 
                float3 wPos:TEXCOORD0;
            };


            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                
                //1.顶点裁剪空间变换 把模型空间下的法线从变成裁剪空间下的法线信息
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                
                //2.进行法线空间变换 把模型空间下的法线从变成世界空间下的法线信息
                v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
                
                //3.顶点转到世界空间 把模型空间下的顶点变成世界空间下的顶点坐标 
                //以下是三种把模型空间坐标转换成世界空间坐标的方法 但是有些可能已经过时了
                //data.wPos = mul(UNITY_MATRIX_M, appdata_base.vertex).xyz;
                //data.wPos = mul(_Object2World, appdata_base.vertex).xyz;
                v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
                
                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //1.视角单位向量
                //用观察者的位置(摄像机的位置)减去世界空间下顶点坐标 得到的就是视角方向 并且进行单位化
                float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);

                //2.光的反射单位向量
                //光的方向 进行单位化
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                //光反射光线的单位向量 reflect(入射向量,顶点法向量) 返回反射向量 
                float3 reflectDir = reflect(-lightDir, v2f.wNormal);

                //3.套公式计算
                //color = 光源颜色 * 材质高光反射颜色 * pow( max(0, dot(视角单位向量, 光的反射单位向量)), 光泽度 )
                fixed3 color = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, reflectDir)), _SpecularNum);

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


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

×

喜欢就点赞,疼爱就打赏