17.光照模型-综合光照模型-Phong光照模型-逐片元光照
17.1 知识点
利用Phong光照模型实现光照效果(逐片元光照)
Phong光照模型通过结合环境光、漫反射光(兰伯特光照模型),以及高光反射光来实现更真实的光照效果。
公式:
物体表面光照颜色 = 环境光颜色 + 兰伯特光照模型所得颜色 + Phong式高光反射光照模型所得颜色
核心的计算步骤包括:
- 计算兰伯特光照模型,用于处理物体表面的漫反射光。
- 计算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