16.光照模型-综合光照模型-Phong光照模型-逐顶点光照
16.1 知识点
利用Phong光照模型实现光照效果(逐顶点光照)
Phong光照模型是将兰伯特光照模型与高光反射光照模型结合,能够模拟物体表面的光泽反射效果,计算公式如下:
物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色
关键步骤:
- 计算兰伯特光照模型。
- 计算Phong式高光反射光照模型。
创建Shader并保留必要骨架
我们首先创建一个Shader,并保留必要的骨架。在这个Shader中,我们需要结合兰伯特光照和Phong高光反射,所以需要声明漫反射颜色、高光反射颜色和光泽度。顶点函数将传递裁剪空间下的顶点坐标信息和对应顶点的漫反射光照颜色给片元函数。
Shader "Unlit/Lesson16_Phong_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;
};
v2f vert(appdata_base appdata_base)
{
}
fixed4 frag(v2f v2f) : SV_Target
{
}
ENDCG
}
}
}
创建计算兰伯特光照模型函数
接下来,我们创建一个用于计算兰伯特光照模型颜色的函数。该函数接收模型空间下的法线并计算其在世界空间下的漫反射光照颜色。
//计算兰伯特光照模型 颜色 相关函数
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;
}
创建计算Phong高光反射光照模型函数
该函数用于计算Phong式高光反射光照模型颜色,外部传入模型空间下的顶点和法线。我们将顶点转换到世界空间,再通过观察方向和反射方向计算高光反射。
//计算Phong高光反射光照模型 颜色 相关函数
fixed3 getSpecularColor(in float4 objVertex, in float3 objNormal)
{
//标准化后观察方向向量
//将模型空间下的顶点位置 转换到 世界空间下
//由于没有提供直接转换的api 所以要使用内置矩阵运算进行转换
// UNITY_MATRIX_M : 从模型空间下 转到 世界空间下的转换矩阵
float3 worldPos = mul(UNITY_MATRIX_M, objVertex);
//用观察者的位置(摄像机的位置)减去世界空间下顶点坐标 得到的就是视角方向
float3 viewDir = _WorldSpaceCameraPos.xyz - worldPos;
//单位化(归一化)
viewDir = normalize(viewDir);
//标准化后的反射方向
//世界空间下的光位置的方向向量
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
//法线在世界空间下的向量
float3 normal = UnityObjectToWorldNormal(objNormal);
//reflect(入射向量,顶点法向量) 返回反射向量 反射光线向量
float3 reflectDir = reflect(-lightDir, normal);
//套公式
//高光反射光照颜色 = 光源的颜色 * 材质高光反射颜色 * max(0, 标准化后观察方向向量· 标准化后的反射方向)幂
fixed3 color = _LightColor0.rgb * _SpecularColor.rgb * pow(
max(0, dot(viewDir, reflectDir)), _SpecularNum);
return color;
}
顶点函数调用兰伯特和Phong函数
在顶点着色器中,我们调用前面创建的兰伯特和Phong函数,利用公式计算顶点的光照颜色:
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//把模型空间下的 顶点转换到裁剪空间下
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//计算兰伯特光照模型所得颜色
fixed3 lambertColor = getLambertColor(appdata_base.normal);
//计算Phong式高光反射光照模型所得颜色
fixed3 specularColor = getSpecularColor(appdata_base.vertex, appdata_base.normal);
//利用Phong光照模型公式 进行颜色计算
//物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色
v2f.color = UNITY_LIGHTMODEL_AMBIENT.rgb + lambertColor + specularColor;
return v2f;
}
片元函数返回颜色
在片元着色器中,我们直接返回顶点着色器传递的颜色,并设置透明度为1:
fixed4 frag(v2f v2f) : SV_Target
{
// 返回顶点着色器计算好的光照颜色,透明度为1
return fixed4(v2f.color.rgb, 1);
}
对比效果
将完成的Shader赋值到材质上,并应用到场景中的物体,逐顶点光照的过渡效果和高光反射可以使物体呈现更加真实的光照表现。

16.2 知识点代码
Lesson16_光照模型_综合光照模型_Phong光照模型_逐顶点光照
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson16_光照模型_综合光照模型_Phong光照模型_逐顶点光照 : MonoBehaviour
{
void Start()
{
#region 知识点 利用Phong光照模型实现光照效果(逐顶点光照)
//公式
//物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色
//关键步骤:
//1.计算兰伯特光照模型
//2.计算Phong式高光反射光照模型
#endregion
}
}
Lesson16_Phong_Vertex.shader
Shader "Unlit/Lesson16_Phong_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;
}
//计算Phong高光反射光照模型 颜色 相关函数
fixed3 getSpecularColor(in float4 objVertex, in float3 objNormal)
{
//标准化后观察方向向量
//将模型空间下的顶点位置 转换到 世界空间下
//由于没有提供直接转换的api 所以要使用内置矩阵运算进行转换
// UNITY_MATRIX_M : 从模型空间下 转到 世界空间下的转换矩阵
float3 worldPos = mul(UNITY_MATRIX_M, objVertex);
//用观察者的位置(摄像机的位置)减去世界空间下顶点坐标 得到的就是视角方向
float3 viewDir = _WorldSpaceCameraPos.xyz - worldPos;
//单位化(归一化)
viewDir = normalize(viewDir);
//标准化后的反射方向
//世界空间下的光位置的方向向量
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
//法线在世界空间下的向量
float3 normal = UnityObjectToWorldNormal(objNormal);
//reflect(入射向量,顶点法向量) 返回反射向量 反射光线向量
float3 reflectDir = reflect(-lightDir, normal);
//套公式
//高光反射光照颜色 = 光源的颜色 * 材质高光反射颜色 * max(0, 标准化后观察方向向量· 标准化后的反射方向)幂
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);
//计算兰伯特光照模型所得颜色
fixed3 lambertColor = getLambertColor(appdata_base.normal);
//计算Phong式高光反射光照模型所得颜色
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