Computer Graphics Experiment III Part.2 LightModel in OpenGL (Inplemented via GLSL)

图形学实验三第二部分——GLSL实现的三种光照模型。 此次试验用GLSL(OpenGL Shading Language)着色语言实现OpenGL自带的光照模型,比较不同的着色方法带来的不同。

点的着色过程

首先,对某个点的计算光照的方法如下:

环境光(Ambient)与漫反射(Diffuse)

即环境光直接是环境光,漫反射经过反射(由于是单位向量直接取内积)后反射到所有方向。 其中$k$是衰减因子,在实验中忽略了。

1
2
3
4
5
6
7
8
9
// Ambient
float ambientStrength = 0.1f;
vec3 ambient = ambientStrength * lightColor;

// Diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

镜面反射

如图,镜面反射需要先计算反射光的方向,再乘以与视线向量的夹角的余弦值,其中余弦值以高光系数$n_{shiny}$作幂次。 反射光$R$的方向可以由下图求得

也可以直接在GLSL中调用relect()函数,该函数接收入射光方向和法向量,返回反射后的光线方向:

1
2
3
4
5
6
// Specular
float specularStrength = 0.5f;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;

Gouraud着色

Gouraud着色为只对三角面片的顶点计算着色,其他的点通过插值计算颜色,GLSL中的顶点和片段着色器分别如下:

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
//gouraudShader.vs
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;

out vec3 LightingColor; // resulting color from lighting calculations

uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);

// gouraud shading
// ------------------------
vec3 Position = vec3(model * vec4(position, 1.0));
vec3 Normal = mat3(transpose(inverse(model))) * normal;

// ambient
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;

// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - Position);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

// specular
float specularStrength = 1.0; // this is set higher to better show the effect of Gouraud shading
vec3 viewDir = normalize(viewPos - Position);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;

LightingColor = ambient + diffuse + specular;
}

顶点着色器计算之后传入片段着色器后会自动对顶点着色器的输出进行插值。

1
2
3
4
5
6
7
8
9
10
11
12
//gouraudShader.frag
#version 330 core
out vec4 FragColor;

in vec3 LightingColor;

uniform vec3 objectColor;

void main()
{
FragColor = vec4(LightingColor * objectColor, 1.0);
}

结果演示如下,可以看出明显的区域和插值结果:

Phong着色

和Gouraud着色不同,Phong着色则是对法向量进行插值,之后对每个点计算光照,因此,光照的计算是在片段着色器中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//PhongShader.vs
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;

out vec3 Normal;
out vec3 FragPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
FragPos = vec3(model * vec4(position, 1.0f));
Normal = mat3(transpose(inverse(model))) * normal;
}

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
//PhongShader.frag
#version 330 core
out vec4 color;

in vec3 FragPos;
in vec3 Normal;

uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform vec3 objectColor;

void main()
{
// Ambient
float ambientStrength = 0.1f;
vec3 ambient = ambientStrength * lightColor;

// Diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

// Specular
float specularStrength = 0.5f;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;

vec3 result = (ambient + diffuse + specular) * objectColor;
color = vec4(result, 1.0f);
}

结果是最好的:

Flat着色

Flat就比较Naive了,每一个三角面片只计算一个光照,并将这个光照当做这个三角面片的光照:

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
//FlatShader.vs

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;

flat out vec3 LightingColor;

uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);

// flat shading
// ------------------------
vec3 Position = vec3(model * vec4(position, 1.0));
vec3 Normal = mat3(transpose(inverse(model))) * normal;

// ambient
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;

// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - Position);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;

// specular
float specularStrength = 1.0;
vec3 viewDir = normalize(viewPos - Position);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;

LightingColor = ambient + diffuse + specular;
}

1
2
3
4
5
6
7
8
//FlatShader.frag
#version 330 core
flat in vec3 LightingColor;
layout( location = 0 ) out vec4 FragColor;
uniform vec3 objectColor;
void main() {
FragColor = vec4(LightingColor * objectColor, 1.0);
}

结果有明显的马赫带(x

三种着色方法比较