19.BlinnPhong光照模型逐顶点光照

19.光照模型-综合光照模型-BlinnPhong光照模型-逐顶点光照


19.1 知识点

利用Blinn Phong光照模型实现光照效果(逐顶点光照)

在Blinn Phong光照模型中,物体表面光照颜色是由环境光、兰伯特光照模型的漫反射颜色以及Blinn Phong的高光反射光照颜色共同决定的。

公式

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

关键步骤

  • 计算兰伯特光照模型:用于模拟物体表面的漫反射光照效果。
  • 计算Blinn Phong式高光反射光照模型:用于模拟物体表面的高光反射效果。

基础骨架复用

我们可以复用Phong式光照模型逐顶点实现的基础骨架,只需修改 getSpecularColor 函数,使其适应Blinn Phong高光反射模型。以下是计算Blinn Phong高光反射的关键函数:

//计算BlinnPhong高光反射光照模型 颜色 相关函数
fixed3 getSpecularColor(in float4 objVertex, in float3 objNormal)
{
    //1.求出标准化后顶点法线方向向量
    //求出世界空间下的单位法线向量
    //UnityObjectToClipPos 把模型空间下的顶点转换到裁剪空间下
    float3 worldNormal = UnityObjectToWorldNormal(objNormal);


    //2.标准化后半角向量方向向量
    //使用矩阵变换求出世界坐标下的顶点位置

    // UNITY_MATRIX_M : 从模型空间下 转到 世界空间下的转换矩阵
    float3 worldPos = mul(UNITY_MATRIX_M, objVertex);
    // float3 worldPos = mul(unity_ObjectToWorld, objVertex);//用这个也可以
    //用摄像机位置减顶点位置 得到在世界坐标下视角方向向量 并转换成单位向量 得到的就是视角方向
    float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos);
    //得到的光位置的方向向量(世界空间下的)
    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
    //半角方向向量的单位向量
    float3 halfA = normalize(viewDir + lightDir);

    //3.套公式
    //高光反射光照颜色 = 光源的颜色 * 材质高光反射颜色 * max(0, 标准化后观察方向向量· 标准化后的反射方向)幂
    fixed3 color = _LightColor0.rgb * _SpecularColor.rgb * pow(
        max(0, dot(worldNormal, halfA)), _SpecularNum);

    return color;
}

对比分析

Blinn Phong光照模型与Phong光照模型相比,主要的区别在于计算高光反射时使用的半角向量。
在Blinn Phong模型中,半角向量是光线方向与视角方向的中间向量,通常能得到更好的高光效果,尤其在平滑物体的表面上。
在相同条件下Blinn-Phong的高光范围要比Phong更大,写实效果Phong光照模型更好,周围会有光晕的感觉。同时算法简单,运行速度快也是Blinn-Phong光照模型的优点。


19.2 知识点代码

Lesson19_光照模型_综合光照模型_BlinnPhong光照模型_逐顶点光照

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

public class Lesson19_光照模型_综合光照模型_BlinnPhong光照模型_逐顶点光照 : MonoBehaviour
{
    void Start()
    {
        #region 知识点 利用Blinn Phong光照模型实现光照效果(逐顶点光照)

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

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

        #endregion
    }
}

Lesson19_BlinnPhong_Vertex.shader

Shader "Unlit/Lesson19_BlinnPhong_Vertex"
{
    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;
                //对应顶点的漫反射光照颜色
                fixed3 color:COLOR;
            };

            //计算兰伯特光照模型 颜色 相关函数
            fixed3 getLambertColor(in float3 objNormal)
            {
                //传入在模型空间下的法线
                //UnityObjectToWorldNormal 将法线从模型空间转换成世界空间 获取到相对于世界坐标系下的法线信息 
                float3 normal = UnityObjectToWorldNormal(objNormal);

                //_WorldSpaceLightPos0.xyz 世界坐标系下光源方向 _WorldSpaceLightPos0是四维向量 只需要xyz即可
                //normalize 把向量归一化
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

                //有了相关的参数 就可以用公式来进行计算了
                //公式:
                //漫反射光照颜色 = 光源的颜色 * 材质的漫反射颜色 * max(0, 标准化后物体表面法线向量· 标准化后光源方向向量)         
                //_LightColor0光照颜色
                //.rgb 代表只使用颜色进行计算 透明度不考虑 
                fixed3 color = _LightColor0.rgb * _MainColor.rgb * max(0, dot(normal, lightDir));

                return color;
            }

            //计算BlinnPhong高光反射光照模型 颜色 相关函数
            fixed3 getSpecularColor(in float4 objVertex, in float3 objNormal)
            {
                //1.求出标准化后顶点法线方向向量
                //求出世界空间下的单位法线向量
                //UnityObjectToClipPos 把模型空间下的顶点转换到裁剪空间下
                float3 worldNormal = UnityObjectToWorldNormal(objNormal);


                //2.标准化后半角向量方向向量
                //使用矩阵变换求出世界坐标下的顶点位置

                // UNITY_MATRIX_M : 从模型空间下 转到 世界空间下的转换矩阵
                float3 worldPos = mul(UNITY_MATRIX_M, objVertex);
                // float3 worldPos = mul(unity_ObjectToWorld, objVertex);//用这个也可以
                //用摄像机位置减顶点位置 得到在世界坐标下视角方向向量 并转换成单位向量 得到的就是视角方向
                float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos);
                //得到的光位置的方向向量(世界空间下的)
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                //半角方向向量的单位向量
                float3 halfA = normalize(viewDir + lightDir);

                //3.套公式
                //高光反射光照颜色 = 光源的颜色 * 材质高光反射颜色 * max(0, 标准化后观察方向向量· 标准化后的反射方向)幂
                fixed3 color = _LightColor0.rgb * _SpecularColor.rgb * pow(
                    max(0, dot(worldNormal, halfA)), _SpecularNum);

                return color;
            }
            
            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                
                //把模型空间下的 顶点转换到裁剪空间下
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                
                //计算兰伯特光照模型所得颜色
                fixed3 lambertColor = getLambertColor(appdata_base.normal);
                
                //计算BlinnPhong式高光反射光照模型所得颜色
                fixed3 specularColor = getSpecularColor(appdata_base.vertex, appdata_base.normal);

                //利用Phong光照模型公式 进行颜色计算
                //物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + 布林Phong式高光反射光照模型所得颜色
                v2f.color = UNITY_LIGHTMODEL_AMBIENT.rgb + lambertColor + specularColor;

                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //把计算好的兰伯特光照的颜色 传递出去就可以了
                //返回v2f中的颜色 透明度传1
                return fixed4(v2f.color.rgb, 1);
            }
            ENDCG
        }
    }
}


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

×

喜欢就点赞,疼爱就打赏