冯氏光照模型

冯氏光照模型

现实生活中的光照效果是得长复杂的,要精确模拟这种效果是不现实的,所以需要一种能够近似现实光照效果的简化模型。比较著名的是冯氏光照模型(Phong Lighting Model)。

在冯氏光照模型中,生活中的光照有三种类型:环境光(Ambient)、漫反射(Diffuse)和镜面高光(Specular)

1
总光照效果 = 环境光 + 漫反射 + 镜面高光

环境光

某些光从光源发出后,被多个物体(墙壁、镜子等)反射 ,最终照射到物体上。对于场景中这种经过反射多次以至于无法确定来自某个方向的光,被称为环境光。在冯氏模型中,环境光从各个角度照射物体,其强度都是一致。

通常情况我们使用一个较小的光线因子乘以光源颜色来模拟。

1
环境光颜色 = 光源颜色 * 光线因子

漫反射

漫反射是为了模拟平行光源点光源对物体的方向性影响。

我们知道,当一束光线照射到物体表面时,光线的入射角越小,该表面的亮度就越大,看上去也就越亮。反之,该表面的亮度就越小,看上去越暗。

入射角的表示:

要定义入射角,需要先定义一个法向量,即垂直于物体表面,并且朝向平面外部的向量。

image-20211121232434452
1
漫反射光照 = 入射光颜色 *  表面基颜色  *  入射角的余弦值

几何体在旋转、缩放时,每个表面的法向量也会随之变化。

image-20211122173649029

  • 平移变换不会改变法向量,因为平移不会改变物体的方向。
  • 旋转变换会改变法向量,因为旋转改变了物体的方向。

  • 拉伸的情况比较复杂,如果在所有轴上的缩放比例都是一样的,那么法向量不会发生变化

如何计算变换之后的法向量呢?

只要将变换之前的法向量乘以模型矩阵的逆转置矩阵(inverse transpose matrix)即可 。具体操作就是:对模型矩阵先求逆,再转置。

模型矩阵即:对顶点进行变换(缩放、位移、旋转)的矩阵 。

平行光源

在现实中平行光漫反射可以模拟遥远的光源,比如太阳光,由于太阳距离地球过于遥远,所以太阳光照射在物体各个点的方向可以看做是平行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
attribute vec3 a_Color;
attribute vec4 a_Normal; //法向量

uniform mat4 u_NormalMatrix; //法向量变换矩阵
uniform vec3 u_LightDirection; //入射光线

varying vec4 v_Color;


void main(){

.......

vec4 color = vec4(a_Color.xyz * 1.0, 1.0);
vec4 normal = u_NormalMatrix * a_Normal; //对发向量进行变换

//计算光线入射角与法向量的余弦值,即计算改点的漫反射强度
float nDotL = max(dot(u_LightDirection, normalize(normal.xyz)), 0.0);


v_Color = vec4(color.xyz * nDotL, 1.0);
}

点光源

如果光源距离物体比较近,那就不能把光源看作平行的了,照在物体不同点时,入射角也会不一样,在一个平面上照强度也会有差别,距离光源近的部分比较亮,距离光源远的部分比较暗。

对比平行光源,点光源光的方向不再是恒定不变的,而要根据每个顶点的位置逐一计算。着色器需要知道点光源光自身的所在位置,而不是光的方向。

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
<script id="vertexShader" type="x-shader/x-vertex">

precision mediump float;


attribute vec3 a_Position;
attribute vec3 a_Color;
attribute vec4 a_Normal;

uniform mat4 u_MvpMatrix;
uniform mat4 u_NormalMatrix;
uniform mat4 u_ModelMatrix;
uniform vec3 u_LightPosition;
uniform vec3 u_LightColor;
uniform vec3 u_AmbientLight;

varying vec4 v_Color;


void main(){
gl_Position = u_MvpMatrix * vec4(a_Position,1.0);
vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));
vec4 worldPositon = u_ModelMatrix * vec4 (a_Position ,1.0); //顶点的世界坐标

vec3 lightDirection = normalize(u_LightPosition - vec3(worldPositon.xyz)) ;//光照方向

float nDotL = max(dot(lightDirection , normalize(normal.xyz)), 0.0);

vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;

vec3 ambient = u_AmbientLight * a_Color.rgb;

v_Color = vec4(diffuse + ambient, 1);

}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">

precision mediump float;


varying vec4 v_Color;

void main(){
gl_FragColor = v_Color;
}
</script>

上面的效果也不完美,可以看到有很明显的明暗分界线。这是因为,上面代码只计算了6个点的颜色,其他颜色都是通过插值生成的。

image-20211126142927662

下面可以试一下逐片加载的方式,处理点光源。也就是把计算颜色的逻辑放到片元着色器上面。

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

<script id="vertexShader" type="x-shader/x-vertex">

precision mediump float;


attribute vec3 a_Position;
attribute vec3 a_Color;
attribute vec4 a_Normal;

uniform mat4 u_MvpMatrix;
uniform mat4 u_NormalMatrix;
uniform mat4 u_ModelMatrix;


varying vec4 v_Color;
varying vec3 v_Normal;
varying vec3 v_Position;


void main(){
gl_Position = u_MvpMatrix * vec4(a_Position,1.0);
v_Color = vec4(a_Color , 1.0);

v_Position = vec3(u_ModelMatrix * vec4 (a_Position ,1.0)); //顶点的世界坐标
v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));


}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">

precision mediump float;

uniform vec3 u_LightPosition;
uniform vec3 u_LightColor;
uniform vec3 u_AmbientLight;


varying vec4 v_Color;
varying vec3 v_Normal;
varying vec3 v_Position;


void main(){
vec3 normal = normalize(v_Normal);
vec3 lightDirection = normalize(u_LightPosition - v_Position);
float nDotL = max(dot(lightDirection, normal), 0.0);
vec3 diffuse = u_LightColor * v_Color.rgb * nDotL;
vec3 ambient = u_AmbientLight * v_Color.rgb;
gl_FragColor = vec4(diffuse + ambient, v_Color.a);
}
</script>

这时候再看,过渡就自然很多了。

image-20211126150645379


冯氏光照模型
http://example.com/2024/05/23/webgl/冯氏光照模型/
Author
John Doe
Posted on
May 23, 2024
Licensed under