在讲Unity光照模型之前,先介绍图形学中的两个基础光照模型原理,将会更利于我们理解和使用Unity中的光照模型。
物体表面的光照颜色由入射光。物体材质,以及材质和光的交互规律共同决定的。
1、图形学中的两个基础光照模型
(1)漫反射与Lambert光照模型
粗糙的物体表面向各个方向等强度地反射光,这种等同度地散射现象称为光的漫反射(Diffuse Reflection)。产生光的漫反射现象的物体表面称为理想漫反射体,也称为Lambert反射体。
对于暴露在环境光下的Lambert反射体,可以用以下公式表示某点处的漫发射光强:
Iad= K*Ia
(约定:小写的字母表示下标,ad = ambient diffuse)
其中Ia表示环境光强度,k(0<k<1)为材质对环境光的反射系数,Iad是漫发射体与环境光交互时反射的光强。
理想的环境光是无序的,但是表面光强还依赖于光线的入射方向,这种现象可以用Lambert定律进行数学上的量化。
Lambert定律:当方向光照射到理想反射体上时,漫反射光的光强与入射光方向和入射点便面法向量夹角的余弦成正比。
Ild = K*Il*Cosa
Iid是漫反射体与方向光交互反射的光强,Il是方向光的光源强度,a是入射光线与定点法向量的夹角,k是材质的反射系数。
当入射角为零时,说明光线垂直于物体表面,漫反射光强最大;九十度时光线与物体表面平行,物体接收不到任何光线。
若N表示顶点单位法向量,L表示从顶点指向光源位置的单位向量(主意指向,不要弄反了),那么Cosa等价于N与L的点积。
即:
Ild = k*I*(N·L)
那么综合考虑环境光和方向光,Lambert光照模型可写为:
Idiff = Iad + Ild = k*Ia + k*Il*(N·L)
(****2****)镜面反射与****Phong****光照模型
Lambert模型较好的表现了粗糙表面的光照现象,如墙壁,纸张等,但是在用于诸如金属等有光泽效果的材质上则会显得呆板,表现不出光泽,主要原因是Lambert光照模型没有考虑表现这些表面的镜面反射效果。
一个光滑物体被光照射时,可以早某个方向上看到很强的反射光,这是因为在接近镜面反射角的一个区域内,反射了入射光的全部或大部分光强,这种现象称为镜面发射。
故此,Phong Bui Tuong提出一个计算镜面反射光强的光照模型,称为Phong模型,认为镜面反射的光强与反射光线和视线的夹角相关。
Phong模型数学表达式:
Ispec = Ks*Il*(V·R)^n
注:((V·R)^n
表示(V·R)的n次方即(V·R)^n 。
Ks表示材质的镜面反射系数,n指高光指数,V表示从顶点到视点观察方向,R表示反射光方向。
高光指数n反映了物体表面的光泽程度。n越大,反射越集中,当偏离发射方向时,光线衰减的越厉害,只有当视线方向与反射光线非常接近时,才能看到镜面反射的高光现象。此时,镜面反射光将会在反射方向附近形成亮且小的光斑。n越小,表示物体表面越粗糙,反射光越分散,观察到的光斑区域越小,强度弱。
反射光方向向量R可以通过入射方向L(从顶点指向光源)和物体表面法向量L求出。
R+L = (2N·L)N
下面是一段Phong模型的cg代码:
void main_f( VertexScreen posIn,
out float4 color : COLOR,
uniform float4x4 worldMatrix,
uniform float4x4 worldMatrix_IT,
uniform float3 globalAmbient,
uniform float3 eyePosition,
uniform float3 lightPosition,
uniform float3 lightColor,
uniform float3 Kd,
uniform float3 Ks,
uniform float shininess)
{
float3 worldPos = mul(worldMatrix, posIn.objectPos).xyz;
float3 N = mul(worldMatrix_IT, posIn.objectNormal).xyz;
N = normalize(N);
//计算入射光方向、视线方向、反射光线方向
float3 L = normalize(lightPosition - worldPos);
float3 V = normalize(eyePosition - worldPos);
float3 R = 2*max(dot(N, L), 0)*N-L;
R = normalize(R);
// 计算漫反射分量
float3 diffuseColor = Kd * globalAmbient +Kd*lightColor*max(dot(N, L), 0);
//计算镜面反射分量
float3 specularColor = Ks * lightColor*pow(max(dot(V, R), 0), shininess);
color.xyz = diffuseColor + specularColor;
color.w = 1;
}
(3)BlinnPhong光照模型
BlinnPhong光照模型混合和了Lambert的漫反射和标准的高光,渲染小股票有时比Phong高光更柔和、更平滑,此外它的处理速度相当快,因此成为许多CG软件中默认的光照渲染方法。
在Phong模型中,必须计算V·R
的值,其中R为反射光线的单位向量,V为视线方向的单位向量,但是在BlinnPhong模型中,用N·H
的值来取代V·R
。
BlinnPhong光照模型公式:
Ibp = Ks*Il(N·H)^n
其中N是入射点的单位法向量,H是“光入射方向L和视点方向V的中间向量”,通常也称为半角向量。
注:半角向量被广泛运用于各类光照模型,因为其既含有各种信息价值(光线入射方向以及视点方向),而且计算快速方便。
H = (L+V)/(|L+V|)
同样还有Cook-Torrence、BRDF等高级光照模型,可以自己去查资料拓展。
2、Unity中自定义光照模型
(1)、光照模型声明
光照模型函数的声明一般以“Lighting”开头的几个模板,可以被定义在你的Shader里的任何位置。
1、half4 Lighting<name> (SurfaceOutput s,half3 lightDir,half atten);
这个函数模板一般用于正向渲染路径(forward rendering path),并且是视线方向无关的。
2、half4 Lighting<name>(SurfaceOutput s,half3 lightDir,half3 viewDir,half atten);
一般用于forward rendering path,并且是视线方向相关的。
3、half4 Lighting<name>_OrePass(SurfaceOutput s,half4 light);
用于deferred lighting path。
你不需要声明所有的函数,一个光照模型是否使用view direction。如果一个光照模型不在deferred lighting起作用,你就不需要声明_PrePass函数,并且所有的Shader都会被编译成forward rendering path。
(2)、解码光照贴图
对于forward 和 deferred 光照,解码光照贴图可以被定制为何光照函数相似的方式。使用下面的函数取决于你是否使用了视线方向相关的光照模型。
解码基础的Unity光照贴图数据(通过color,totalcolor,indirectOnlyColor和scale参数)使用内置的DecodeLightmap
函数。
定制的光照贴图解码函数会自动处理forward和deferred渲染路径。但是在deferred case Lighting<name> _PrePass
函数将会在光照贴图解码之后被调用,并且光照参数将会包含实时光照和光照贴图。
如果需要的需要的话你可以用Unity内置的宏UNITY_PASS_PREPASSFINAL
来区分forward和deferred渲染路径。
对于解码单个光照贴图的自定义函数:
1、half4 Lighting<Name> _SingleLingtingMap(SurfaceOutput s,fixed4 color);
用于不依赖视线方向的光照模型。
2、half4 Lighting<Name>(SurfaceOutput s,fixed4 color,half3 vieDir);
用于依赖视线方向的光照模型。
对于解码双光照贴图的自定义函数:
1、half4 Lighting<Name> _DualLightMap(SurfaceOurput s,fixed4 totalColor,fixed4 indirectionOnlyColor,half indirectFade);
用于不依赖视线方向的光照模型。
2、half4 Lighting<Name> _DualLightMap(SurfaceOurput s,fixed4 totalColor,fixed4 indirectionOnlyColor,half indirectFade,half3 viewDir);
用于依赖视线方向的光照模型。
对于解码定向光照贴图的自定义函数:
1、half4 Lighting<Name> _DirLightMap(SurfaceOutput s,fixed4 color,fixed4 scale,bool surfFuncWritesNormal);
用于不依赖视线方向的光照模型。
2、half Lighting<Name> _DirLightMap(SurfaceOutput s,fixed4 color,fixed4 scale,half3 viewDir,bool suefFuncWritesNormal,out half3 specColor);
用于依赖视线方向的光照函数。