17.Phong光照模型逐片元光照

17.光照模型-综合光照模型-Phong光照模型-逐片元光照


17.1 知识点

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

Phong光照模型通过结合环境光、漫反射光(兰伯特光照模型),以及高光反射光来实现更真实的光照效果。

公式:
物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色

核心的计算步骤包括:

  1. 计算兰伯特光照模型,用于处理物体表面的漫反射光。
  2. 计算Phong式高光反射光照模型,用于模拟物体表面的高光反射效果。

创建Shader

首先,我们创建一个Shader,用于实现Phong光照效果。为了结合兰伯特光照和Phong高光反射模型,需要声明一些属性,如漫反射颜色、高光反射颜色和光泽度。同时,顶点函数需要传递给片元函数的信息包括:裁剪空间下的顶点坐标、世界空间的顶点信息以及世界空间的法线信息。

Shader "Unlit/Lesson17_Phong_Pixel"
{
    Properties
    {
        //材质的漫反射光照颜色
        _MainColor("MainColor", Color) = (1,1,1,1)
        //高光反射颜色
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        //光泽度
        _SpecularNum("SpecularNum", Range(0, 20)) = 0.5
    }
    SubShader
    {
        Pass
        {
            //设置我们的光照模式 ForwardBase这种向前渲染模式 主要是用来处理 不透明物体的 光照渲染的
            Tags
            {
                "LightMode"="ForwardBase"
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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


            //材质的漫反射颜色
            fixed4 _MainColor;
            //对应属性当中的颜色和光泽度
            fixed4 _SpecularColor;
            float _SpecularNum;

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


            v2f vert(appdata_base appdata_base)
            {
    
            }

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

顶点函数计算裁剪空间、世界空间顶点和法线

在顶点着色器中,我们需要将模型空间的顶点和法线转换为世界空间信息,并传递给片元函数。以下是具体的代码实现:

v2f vert(appdata_base appdata_base)
{
    v2f v2f;
    
    //将模型空间中的顶点转换为裁剪空间中的顶点
    v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
    
    //将模型空间中的法线转换为世界空间中的法线
    v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
    
    //将模型空间中的顶点转换为世界空间中的顶点
    v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;

    return v2f;
}

计算兰伯特光照模型

我们通过以下函数计算基于兰伯特光照模型的漫反射光照效果,只需要传入世界空间的法线即可:

//得到兰伯特光照模型计算的颜色 (逐片元)
fixed3 getLambertColor(in float3 wNormal)
{
    //_WorldSpaceLightPos0.xyz 世界坐标系下光源方向 _WorldSpaceLightPos0是四维向量 只需要xyz即可
    //normalize 把向量归一化
    //lightDir 得到光源单位向量
    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
    
    //_LightColor0光照颜色
    //.rgb 代表只使用颜色进行计算 透明度不考虑 
    //color 计算出了兰伯特光照的漫反射颜色
    fixed3 color = _LightColor0.rgb * _MainColor.rgb * max(0, dot(wNormal, lightDir));

    return color;
}

计算Phong高光反射光照模型

使用以下函数来计算Phong高光反射模型的颜色,所需的参数是世界空间中的顶点和法线:

//得到Phong式高光反射模型计算的颜色(逐片元)
fixed3 getSpecularColor(in float3 wPos, in float3 wNormal)
{
    //1.视角单位向量
    //用观察者的位置(摄像机的位置)减去世界空间下顶点坐标 得到的就是视角方向 并且进行单位化
    float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - wPos);

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

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

    return color;
}

片元函数中的光照计算

在片元函数中,调用兰伯特光照模型和Phong高光反射模型函数,最终通过以下公式计算出物体表面的光照颜色:

fixed4 frag(v2f v2f) : SV_Target
{
    //计算兰伯特光照颜色
    fixed3 lambertColor = getLambertColor(v2f.wNormal);
    
    //计算Phong式高光反射颜色
    fixed3 specularColor = getSpecularColor(v2f.wPos, v2f.wNormal);
    
    //物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色
    fixed3 phongColor = UNITY_LIGHTMODEL_AMBIENT.rgb + lambertColor + specularColor;

    return fixed4(phongColor.rgb, 1);
}

结果对比

将该Shader应用到场景中的材质球体对象上,与逐顶点光照效果相比,逐片元光照能带来更加平滑的光照过渡和细腻的高光效果,尤其在模拟高光反射时效果更为明显。


17.2 知识点代码

Lesson17_光照模型_综合光照模型_Phong光照模型_逐片元光照

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

public class Lesson17_光照模型_综合光照模型_Phong光照模型_逐片元光照 : MonoBehaviour
{
    void Start()
    {
        #region 知识点 利用Phong光照模型实现光照效果(逐片元光照)

        //公式
        //物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色

        //关键步骤:
        //1.计算兰伯特光照模型
        //2.计算Phong式高光反射光照模型

        #endregion
    }
}

Lesson17_Phong_Pixel.shader

Shader "Unlit/Lesson17_Phong_Pixel"
{
    Properties
    {
        //材质的漫反射光照颜色
        _MainColor("MainColor", Color) = (1,1,1,1)
        //高光反射颜色
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        //光泽度
        _SpecularNum("SpecularNum", Range(0, 20)) = 0.5
    }
    SubShader
    {
        Pass
        {
            //设置我们的光照模式 ForwardBase这种向前渲染模式 主要是用来处理 不透明物体的 光照渲染的
            Tags
            {
                "LightMode"="ForwardBase"
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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


            //材质的漫反射颜色
            fixed4 _MainColor;
            //对应属性当中的颜色和光泽度
            fixed4 _SpecularColor;
            float _SpecularNum;

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


            //得到兰伯特光照模型计算的颜色 (逐片元)
            fixed3 getLambertColor(in float3 wNormal)
            {
                //_WorldSpaceLightPos0.xyz 世界坐标系下光源方向 _WorldSpaceLightPos0是四维向量 只需要xyz即可
                //normalize 把向量归一化
                //lightDir 得到光源单位向量
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                //_LightColor0光照颜色
                //.rgb 代表只使用颜色进行计算 透明度不考虑 
                //color 计算出了兰伯特光照的漫反射颜色
                fixed3 color = _LightColor0.rgb * _MainColor.rgb * max(0, dot(wNormal, lightDir));

                return color;
            }

            //得到Phong式高光反射模型计算的颜色(逐片元)
            fixed3 getSpecularColor(in float3 wPos, in float3 wNormal)
            {
                //1.视角单位向量
                //用观察者的位置(摄像机的位置)减去世界空间下顶点坐标 得到的就是视角方向 并且进行单位化
                float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - wPos);

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

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

                return color;
            }

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                
                //转换模型空间下的顶点到裁剪空间中
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                
                //转换模型空间下的法线到世界空间下
                v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
                
                //转换模型空间下的顶点到世界空间
                v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;

                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //计算兰伯特光照颜色
                fixed3 lambertColor = getLambertColor(v2f.wNormal);
                
                //计算Phong式高光反射颜色
                fixed3 specularColor = getSpecularColor(v2f.wPos, v2f.wNormal);
                
                //物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色
                fixed3 phongColor = UNITY_LIGHTMODEL_AMBIENT.rgb + lambertColor + specularColor;

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


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

×

喜欢就点赞,疼爱就打赏