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