[图形学] 经典光照模型

Lambert模型,Phong模型,Blinn-Phong模型,全局光照模型

光源:

  • 环境光(Ambient Light):从物体表面所产生的反射光的统一照明,称为环境光或背景光(计算机图形学第二版 389 页)。通常我们认为理想的环境光具有如下特性:没有空间或方向性;在所有方向上和所有物体表面上投射的环境光强度是统一的恒定值。
  • 环境光是对光照现象的最简单抽象,局限性很大。它仅能描述光线在空间中无方向并均匀散布时的状态。真实的情况是:光线通常都有方向。点光源是发光体的最简单的模型,光线由光源出发向四周发散。还有一种是平行光,即光线都从同一个方向照射。通过模拟方向光和物体表面的交互模式,可以渲染出具有高真实感(明暗变化、镜面反射等)的三维场景。

漫反射与 Lambert 模型:

定义:

  1. 漫反射:粗糙的物体表面向各个方向等强度地反射光,这种等同地向各个方向散射的现象称为光的漫反射(diffuse reflection)。
  2. 产生光的漫反射现象的物体表面称为理想漫反射体,也称为朗伯(Lambert)反射体

仅暴露在环境光下:

对于仅暴露在环境光下的朗伯反射体,可以用公式1表示某点处漫反射的光强:

公式1:$I_{ambdiff} = K_dI_a$

其中$I_a$表示环境光强度(简称光强),$K_d (0< K_d <1)$为材质对环境光的反射系数,$I_{ambdiff}$ 是漫反射体与环境光交互反射的光强。

即使一个理想的漫反射体在所有方向上具有等量的反射光线,但是表面光强还依赖于光线的入射方向(方向光)。例如,入射光方向垂直的表面与入射光方向成斜角的表面相比,其光强要大的多。这种现象可以用 Lambert 定律进行数学上的量化。

Lambert 定律

当方向光照射到朗伯反射体上时,漫反射光的光强与入射光的方向和入射点表面法向夹角的余弦成正比。

  • Lambert漫反射模型:

    公式2:$I_{ldiff} = k_dI_lcosθ$

    $I_l$ 是点光源强度,$θ$ 是入射光方向与顶点法线的夹角,称为入射 $(0≤θ≤90°)$,$I_{ldiff}$ 是漫反射体与方向光交互反射的光强。入射角为零时,说明光线垂直于物体表面,漫反射光强最大;90°时光线与物体表面平行,物体接收不到任何光线。

    若 N 为顶点单位法向量,L 表示从顶点指向光源的单位向量(注意,是由顶点指向光源,不要弄反了),则cosθ 等价于N 与L的点积。所以公式2可以表示为公式3:

    公式3:$I_{ldiff} = k_dI_l(N·L)$

  • Lambert光照模型:结合环境光与漫反射模型

    $I_{diff} = I_{ambdiff} + I_{ldiff} = K_dI_a + k_dI_l(N·L)$

    在实际应用中,为了防止点积结果为负值,需要用max函数对其进行筛选。

Unity Shader实现漫反射光照模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 仅为核心实现
Properties{
// 属性,用于存储我们想得到并控制的漫反射颜色
_Diffuse("Diffuse",Color) = (1,1,1,1)
}

SubShader{
Pass{
Tags{"LightMode" = "ForwardBase"}
}

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "Lighting.cginc"

fixed4 _Diffuse;

struct a2v{
float4 vertex : POSITION;
// 模型顶点的法线信息
float3 normal : NORMAL;
};

struct v2f{
float4 pos : SV_POSITION;
fixed3 color : COLOR;
}

v2f vert(a2v v)
{
v2f o;
// 将顶点位置从模型空间转换到裁剪空间
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);

// 内置环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// 将法线转换到世界空间下与光源方向做点积(只有在同一坐标空间下的点积才有意义)
fixed3 worldNormal = normalize(mul(v.normal,(float3x3)_World2Object))
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
// 即上述I_diff模型公式
fixed diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight));

o.color = ambient + diffuse;

return o;
}

fixed4 frag(v2f i):SV_Target{
return fixed4(i.color,1.0);
}

Fallback "Diffuse"
}

半兰伯特(Half Lambert)光照模型:

$I_{diffuse} = (I_{light}·k_{diffuse})(α(n·l)+β)$

对点积结果进行一个α倍的缩放与大小为β的偏移。通过这样的方式,我们可以将点积的结果从[-1,1]映射到[0,1]范围内,对于模型的背光面,兰伯特光照模型将统一映射到0处,而半兰伯特光照模型中,背光面也可以有明暗变化,不同的点积结果将会映射到不同的值上。

Unity Shader 实现半兰伯特模型:

1
2
3
4
5
6
7
8
9
10
11
12
// ...环境光照与上大致相同
fixed4 frag(v2f i):SV_Target{
...

// Compute diffuse term
fixed halfLambert = dot(worldNormal,worldLightDir) * 0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;

fixed3 color = ambient + diffuse;

return fixed4(color,1.0);
}

镜面反射与 Phong 模型:

定义:

  • 镜面反射:一个光滑物体被光照射时,可以在某个方向上看到很强的反射光,这是因为在接近镜面反射角的一个区域内,反射了入射光的全部或绝大部分光强,该现象称为镜面反射
  • phong模型:Phong Bui Tuong 提出一个计算镜面反射光强的经验模型,称为phong模型,认为镜面反射的光强与反射光线和视线的夹角相关。

Phong模型的数学表达:

$I_{spec} = k_sI_l(V·R)^{n_s}$

$k_s$ 为材质的镜面反射系数,$n_s$ 是高光指数,$V$ 表示从顶点到视点的观察方向, $R$ 代表反射光方向。

高光指数反映了物体表面的光泽程度。${n_s}$越大,反射光越集中,当偏离反射方向时,光线衰减的越厉害,只有当视线方向与反射光线方向非常接近时才能看到镜面反射的高光现象,此时,镜面反射光将会在反射方向附近形成亮且小的光斑;$n_s $越小,表示物体越粗糙,反射光分散,观察到的光斑区域小,强度弱。

反射光的方向$R$可以通过入射光方向$L$(从顶点指向光源)和物体法向量 $N$ 求出:

$R+L = (2N·L)N$

故:$R = (2N·L)N-L$

Unity Shader实现Phong模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ...仅给出核心实现代码
v2f vert(a2v v){
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

fixed3 worldNormal = normalize(mul(v.normal,(float3x3)_World2Object));
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLightDir));

fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal));

fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(_Object2World,v.vertex).xyz);

fixed3 specular = _LightColor.rgb * _Specular.rgb * pow(saturate(dot(reflectDir,viewDir)),_Gloss);

o.color = ambient + diffuse + specular;

return o;
}

Blinn-Phong 光照模型:

定义:

  • Blinn-Phong 光照模型:又称为 Blinn-phong 反射模型(Blinn–Phong reflectionmodel)或者 phong 修正模型(modified Phong reflection model)和传统 phong 光照模型相比,Blinn-phong 光照模型混合了 Lambert 的漫射部分和标准的高光,渲染效果有时比 Phong 高光更柔和、更平滑,此外它在速度上相当快,因此成为许多 CG 软件中的默认光照渲染方法。

Blinn-Phong模型的数学表达:

phong 光照模型中,必须计算$V • R$ 的值,其中 $R$ 为反射光线方向单位向量,$V$ 为视线方向单位向量,但是在 Blinn-phong 光照模型中,用 $N • H$ 的值取代了$V • R$ 。Blinn-phong 光照模型公式为:

$I_{spec} = k_sI_l(N·H)^{n_s}$

其中 N 是入射点的单位法向量, $H$是“光入射方向 $L$ 和视点方向$V$ 的中间向量”,通常也称之为半角向量。注意:半角向量被广泛用于各类光照模型,原因不但在于半角向量蕴含的信息价值,也在于计算半角向量是一件简单、耗时不多的工作。

$H = {L+V}/{|L+V|}$

通常情况下,使用 Blinn-phong 光照模型渲染的效果和 phong 模型渲染的效果没有太大的区别,有些艺术工作者认为 phong 光照模型比 blinn-phong 更加真实,实际上也是如此。Blinn-phong 渲染效果要更加柔和一些,Blinn-phong 光照模型省去了计算反射光线方向向量的两个乘法运算,速度更快。由于 Blinn-phong和 phong 模型的唯一区别一个使用半角向量,一个使用反射光方向向量。

Unity Shader 实现Blinn-Phong模型:

1
2
3
4
5
6
7
8
9
10
11
12
// ...

fixed4 frag(v2f i):SV_Target{
...

fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);

fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);

return fixed4(ambient + diffuse + specular,1.0);
}

全局光照模型与 Rendering Equation:

数学表达:

$L_o (x, w_o ) = L_e (x, w_o ) + ∫ f_r (x, w_i , w_o )L_i (x, w_i )(n • w_i )dw_i$

$x$表示入射点,$L_o (x, w_o ) $即从物体表面$x$点,沿方向$w_o$反射的光强;$L_e (x, w_o )$表示从物体表面$x$以方向$w_o$发射出去的光强,该值仅对自发光体有效;$ f_r (x, w_i , w_o )$为入射光线方向为$w_i$,照射到点x上,然后从$w_o$方向发射出去的BRDF值;$L_i (x, w_i )$为入射方向为$w_i$,照射到点$x$上的入射光强;$n$表示点$x$处的法向量。然后对入射方向进行积分(因为光线入射的方向是四面八方的,积分的意义是对每个方向进行一遍计算后进行相加),计算的结果就是“从观察方向上看到的辐射率”。

该公式基于物理光学,对观察方向上的辐射率进行了本质上的量化,前面所讲的漫反射光照模型以及phong高光模型其实是公式在单一光源,特定BRDF下的推导。

  • 对于单个点光源照射到不会自发光的物体上,公式可以简化为:

    $L_o (x, w_o ) = f_r (x, w_i , w_o )L_i (x, w_i )(n • w_i )$

  • 对于漫反射表面,公式可以写成:

    $L_o (x, w_o ) = I_{diff}+ f_r (x, w_i , w_o )L_i (x, w_i )(n • w_i )$

  • 对于phong高光而言:

    $f_{rs}(x,w_i,w_o)=k_s(n·h)^{n_s}/n·w_i$

参考书目:

  • 《Unity Shader入门精要》冯乐乐
  • 《GPU 编程与CG 语言之阳春白雪下里巴人》康玉之