【WebGL之巅】28-光照-光照原理-点光源

By yesmore on 2021-08-08
阅读时间 32 分钟
文章共 6k
阅读量

对应《WebGL编程指南》第八章 40-PointLightedCube、41-PointLightedCube_animation、42-PointLightedCube_perFragment

要点:点光源光、逐片元光照

知识点

一、点光源

与平行光相比,点光源光发出的光,在三维空间的不同位置上其方向也不同,如下图所示。所以,在对点光源光下的物体进行着色时,需要在每个入射点计算点光源光在该处的方向。

点光源光的方向随位置变化

前一节根据每个顶点的法向量和平行光入射方向来计算反射光的颜色,这一节还是采用该方法,只不过点光源光的方向不再是恒定不变的,而是根据每个顶点的位置逐一计算。着色器需要知道点光源光自身的所在位置,而不是光的方向

示例程序 PointLightedCube 是前一节 LightedCube_ambient 示例程序的点光源光版本,显示了一个点光源下的红色立方体。立方体表面仍然是漫反射,环境光保持不变,程序的效果如下图:

PointLightedCube

旋转示例:

LookAtTriangles
Please use the browser supporting "canvas".

二、程序分析

LightedCube_ambient 相比,顶点着色器中新增加了 u_ModelMatrix 变量和 u_LightPosition 变量,前者表示模型矩阵,后者表示点光源的位置。本例中的光是点光源光而非平行光,所以我们需要用到定光源光的位置,而不是光线方向。为了让你看的更清楚,本例将立方体稍做放大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' + //表面基底色
'attribute vec4 a_Normal;\n' + //法向量
'uniform mat4 u_MvpMatrix;\n' +
'uniform mat4 u_ModelMatrix;\n' + //模拟矩阵
'uniform mat4 u_NormalMatrix;\n' + //用来变换法向量的矩阵
'uniform vec3 u_LightColor;\n' + //光线颜色
'uniform vec3 u_LightPosition;\n' + //光源位置(世界坐标系)
'uniform vec3 u_AmbientLight;\n' + //环境光颜色
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_Position = u_MvpMatrix * a_Position;\n' +
' vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' + //计算变化后的法向量进行归一化
' vec4 vertexPosition = u_ModelMatrix * a_Position;\n' + //计算顶点的世界坐标
' vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n' + //计算光线方向并归一化
' float nDotL = max(dot(lightDirection, normal), 0.0);\n' + //计算光线方向和法向量的点积
' vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;\n' + //计算漫反射光的颜色
' vec3 ambient = u_AmbientLight * vec3(a_Color);\n' + //计算环境光产生的反射光颜色
' v_Color = vec4(diffuse + ambient, a_Color.a);\n' +
'}\n';
1
2
3
4
5
6
7
8
...
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
...
var u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
...
gl.uniform3f(u_LightPosition, 2.3, 4.0, 3.5);//设置光线位置(在世界坐标系下)
...
var modelMatrix = new Matrix4(); // Model matrix

2.1 顶点着色器

首先使用模型矩阵变换顶点坐标,获得顶点在世界坐标系中的坐标,以便计算点光源光在顶点处的方向。点光源向四周发射光线,所以顶点处的光线方向是由点光源光坐标减去顶点坐标而得到的矢量

点光源在世界坐标系中的坐标已经传给了着色器中的 u_LightPosition,而前面也已经计算出了顶点在世界坐标系中的坐标,这样就将计算出了光线方向矢量 lightDirection

注意,需要使用 normaliza()进行归一化,以保证光线方向矢量的长度为1.0。

最后, 计算光线方向矢量与法向量的点积,从而计算处每个顶点的颜色。

2.2 问题

运行程序,你会发现效果更加逼真了但是,如果仔细观察还是能发现一个问题:立方体表面有不自然的线条。

出现该现象的原因在第5章讨论过的内插过程中提到过。你应该还记得,WebGL 系统会根据顶点的颜色,内插出表面上每个片元的颜色。实际上,点光源光照射到一个表面上,所产生的效果(即每个片元获得的颜色)与简单使用4个顶点颜色内插出的效果并不完全相同,所以为了使效果更加逼真,我们需要对表面的每一个点,而不仅仅是4个顶点计算光照效果。如果使用一个球体,二者的差异可能会更加明显。
点光源下的球体

三、逐片元光照

乍一听,要在表面的每一点上计算光照产生的颜色,似乎是个不可能完成的任务。但实际上,我们只需要逐片元地进行计算。片元着色器总算要派上用场了。

示例程序是 代码2-PointLightedCube_perFragment,效果如下图所示:

PointLightedCube_perFragment

为了逐片元地计算光照,你需要知道:

  • 片元在世界坐标下的坐标

  • 片元处表面的法向量。

可以在顶点着色器中,将顶点的世界坐标和法向量以 varying变量的形式传入片元着色器,片元着色器中的同名变量就已经是内插后的逐片元值了。

顶点着色器使用模型矩阵乘以顶点坐标计算处顶点的世界坐标,将其赋值给 v_Position 变量,经过内插过程后,片元着色器就获得了逐片元的 v_Position 变量,也就是片元的世界坐标。类似的,顶点着色器将顶点的法向量赋值给 v_Normal 变量,经过内插,片元着色器就获得了逐片元的 v_Normal 变量,即片元的法向量。

片元着色器计算光照效果的方法与 PointLightedCube.js 相同。首先对法向量 v_Normal 进行归一化,因为内插后法向量可能不再是 1.0了;然后,计算片远处的光线方向并对其归一化;接着计算法向量与光线方向的点积;最后分别计算点光源光和环境光产生的反射光颜色,并将两个结果加起来,赋值给 gl_FragColor,片元就会显示为这个颜色。

如果场景中有超过一个点光源,那么就需要在片元着色器中计算每一个点光源,环境光对片元的颜色贡献,并将它们全部加起来。

核心代码:

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
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' + //表面基底色
'attribute vec4 a_Normal;\n' + //法向量
'uniform mat4 u_MvpMatrix;\n' +
'uniform mat4 u_ModelMatrix;\n' + //模拟矩阵
'uniform mat4 u_NormalMatrix;\n' + //用来变换法向量的矩阵
'varying vec3 v_Normal;\n' +
'varying vec3 v_Position;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
'gl_Position = u_MvpMatrix * a_Position;\n' +
'v_Position = vec3(u_ModelMatrix * a_Position);\n' +
'v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' + //计算变化后的法向量进行归一化
'v_Color = a_Color;\n' +
'}\n';

var FSHADER_SOURCE=
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'uniform vec3 u_LightColor;\n' + // 光的颜色
'uniform vec3 u_LightPosition;\n' + // 光源位置
'uniform vec3 u_AmbientLight;\n' + // 环境光颜色
'varying vec3 v_Normal;\n' +
'varying vec3 v_Position;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
// 对法线进行归一化,因为内插后法向量可能不再是 1.0
' vec3 normal = normalize(v_Normal);\n' +
// 计算光线方向并归一化
' vec3 lightDirection = normalize(u_LightPosition - v_Position);\n' +
// 计算光线方向和法向量的点积
' float nDotL = max(dot(lightDirection, normal), 0.0);\n' +
// 计算 diffuse、ambient 以及最终的颜色
' vec3 diffuse = u_LightColor * v_Color.rgb * nDotL;\n' +
' vec3 ambient = u_AmbientLight * v_Color.rgb;\n' +
' gl_FragColor = vec4(diffuse + ambient, v_Color.a);\n' +
'}\n';

实例

代码1

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//PointLightedCube.js
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' + //表面基底色
'attribute vec4 a_Normal;\n' + //法向量
'uniform mat4 u_MvpMatrix;\n' +
'uniform mat4 u_ModelMatrix;\n' + //模拟矩阵
'uniform mat4 u_NormalMatrix;\n' + //用来变换法向量的矩阵
'uniform vec3 u_LightColor;\n' + //光线颜色
'uniform vec3 u_LightPosition;\n' + //光源位置(世界坐标系)
'uniform vec3 u_AmbientLight;\n' + //环境光颜色
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_Position = u_MvpMatrix * a_Position;\n' +
' vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' + //计算变化后的法向量进行归一化
' vec4 vertexPosition = u_ModelMatrix * a_Position;\n' + //计算顶点的世界坐标
' vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n' + //计算光线方向并归一化
' float nDotL = max(dot(lightDirection, normal), 0.0);\n' + //计算光线方向和法向量的点积
' vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;\n' + //计算漫反射光的颜色
' vec3 ambient = u_AmbientLight * vec3(a_Color);\n' + //计算环境光产生的反射光颜色
' v_Color = vec4(diffuse + ambient, a_Color.a);\n' +
'}\n';

var FSHADER_SOURCE=
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'varying vec4 v_Color;\n' +
'void main(){\n'+
' gl_FragColor = v_Color;\n'+
'}\n';

function main() {

var canvas = document.getElementById("webgl");
if (!canvas) {
console.log("Failed to retrieve the <canvas> element");
return;
}

var gl = getWebGLContext(canvas);
if (!gl) {
console.log("Failed to get the rendering context for WebGL");
return;
}

if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log("Failed to initialize shaders.");
return;
}

//设置顶点位置
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}

// Set the clear color and enable the depth test
// gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);

var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');//模型视图投影矩阵
var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
var u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
var u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
if (!u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight) {
console.log('Failed to get the storage location');
return;
}

gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0); //设置光线颜色为白色
gl.uniform3f(u_LightPosition, 2.3, 4.0, 3.5);//设置光线位置(在世界坐标系下)
gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2); //设置环境光颜色


var modelMatrix = new Matrix4(); // Model matrix
var mvpMatrix = new Matrix4(); // Model view projection matrix
var normalMatrix = new Matrix4(); // Transformation matrix for normals

modelMatrix.setRotate(90, 0, 1, 0);
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1 ,100);
mvpMatrix.lookAt(6, 6, 14, 0, 0, 0, 0, 1, 0);
mvpMatrix.multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

normalMatrix.setInverseOf(modelMatrix);
normalMatrix.transpose();
gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);


gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}

function initVertexBuffers(gl) {
// Create a cube
// v6----- v5
// /| /|
// v1------v0|
// | | | |
// | |v7---|-|v4
// |/ |/
// v2------v3
var vertices = new Float32Array([ // Vertex coordinates
2.0, 2.0, 2.0, -2.0, 2.0, 2.0, -2.0,-2.0, 2.0, 2.0,-2.0, 2.0, // v0-v1-v2-v3 front
2.0, 2.0, 2.0, 2.0,-2.0, 2.0, 2.0,-2.0,-2.0, 2.0, 2.0,-2.0, // v0-v3-v4-v5 right
2.0, 2.0, 2.0, 2.0, 2.0,-2.0, -2.0, 2.0,-2.0, -2.0, 2.0, 2.0, // v0-v5-v6-v1 up
-2.0, 2.0, 2.0, -2.0, 2.0,-2.0, -2.0,-2.0,-2.0, -2.0,-2.0, 2.0, // v1-v6-v7-v2 left
-2.0,-2.0,-2.0, 2.0,-2.0,-2.0, 2.0,-2.0, 2.0, -2.0,-2.0, 2.0, // v7-v4-v3-v2 down
2.0,-2.0,-2.0, -2.0,-2.0,-2.0, -2.0, 2.0,-2.0, 2.0, 2.0,-2.0 // v4-v7-v6-v5 back
]);

var colors = new Float32Array([ // Colors
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v1-v2-v3 front
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v5-v6-v1 up
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v1-v6-v7-v2 left
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v7-v4-v3-v2 down
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0  // v4-v7-v6-v5 back
]);

var normals = new Float32Array([ // Normal
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, // v7-v4-v3-v2 down
0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0 // v4-v7-v6-v5 back
]);

var indices = new Uint8Array([ // Indices of the vertices
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // right
8, 9,10, 8,10,11, // up
12,13,14, 12,14,15, // left
16,17,18, 16,18,19, // down
20,21,22, 20,22,23 // back
]);

// Write the vertex coordinates and color to the buffer object
if (!initArrayBuffer(gl, vertices, 3, gl.FLOAT, 'a_Position'))
return -1;

if (!initArrayBuffer(gl, colors, 3, gl.FLOAT, 'a_Color'))
return -1;

if (!initArrayBuffer(gl, normals, 3, gl.FLOAT, 'a_Normal'))
return -1;

// Create a buffer object
var indexBuffer = gl.createBuffer();
if (!indexBuffer) {
console.log('Failed to create the buffer object');
return false;
}

// Write the indices to the buffer object
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

return indices.length;
}

function initArrayBuffer(gl, data, num, type, attribute) {
var buffer = gl.createBuffer(); // Create a buffer object
if (!buffer) {
console.log('Failed to create the buffer object');
return false;
}
// Write date into the buffer object
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
// Assign the buffer object to the attribute variable
var a_attribute = gl.getAttribLocation(gl.program, attribute);
if (a_attribute < 0) {
console.log('Failed to get the storage location of ' + attribute);
return false;
}
gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
// Enable the assignment of the buffer object to the attribute variable
gl.enableVertexAttribArray(a_attribute);

gl.bindBuffer(gl.ARRAY_BUFFER, null);

return true;
}

效果

见顶部

代码2

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//PointLightedCube_perFragment.js
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' + //表面基底色
'attribute vec4 a_Normal;\n' + //法向量
'uniform mat4 u_MvpMatrix;\n' +
'uniform mat4 u_ModelMatrix;\n' + //模拟矩阵
'uniform mat4 u_NormalMatrix;\n' + //用来变换法向量的矩阵
'varying vec3 v_Normal;\n' +
'varying vec3 v_Position;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
'gl_Position = u_MvpMatrix * a_Position;\n' +
'v_Position = vec3(u_ModelMatrix * a_Position);\n' +
'v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' + //计算变化后的法向量进行归一化
'v_Color = a_Color;\n' +
'}\n';

var FSHADER_SOURCE=
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'uniform vec3 u_LightColor;\n' + // Light color
'uniform vec3 u_LightPosition;\n' + // Position of the light source
'uniform vec3 u_AmbientLight;\n' + // Ambient light color
'varying vec3 v_Normal;\n' +
'varying vec3 v_Position;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
// Normalize the normal because it is interpolated and not 1.0 in length any more
' vec3 normal = normalize(v_Normal);\n' +
// Calculate the light direction and make its length 1.
' vec3 lightDirection = normalize(u_LightPosition - v_Position);\n' +
// The dot product of the light direction and the orientation of a surface (the normal)
' float nDotL = max(dot(lightDirection, normal), 0.0);\n' +
// Calculate the final color from diffuse reflection and ambient reflection
' vec3 diffuse = u_LightColor * v_Color.rgb * nDotL;\n' +
' vec3 ambient = u_AmbientLight * v_Color.rgb;\n' +
' gl_FragColor = vec4(diffuse + ambient, v_Color.a);\n' +
'}\n';

function main() {

var canvas = document.getElementById("webgl");
if (!canvas) {
console.log("Failed to retrieve the <canvas> element");
return;
}

var gl = getWebGLContext(canvas);
if (!gl) {
console.log("Failed to get the rendering context for WebGL");
return;
}

if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log("Failed to initialize shaders.");
return;
}

//设置顶点位置
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}

// Set the clear color and enable the depth test
// gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);

var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');//模型视图投影矩阵
var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
var u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
var u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
if (!u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight) {
console.log('Failed to get the storage location');
return;
}

gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0); //设置光线颜色为白色
gl.uniform3f(u_LightPosition, 2.3, 4.0, 3.5);//设置光线位置(在世界坐标系下)
gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2); //设置环境光颜色


var modelMatrix = new Matrix4(); // Model matrix
var mvpMatrix = new Matrix4(); // Model view projection matrix
var normalMatrix = new Matrix4(); // Transformation matrix for normals

modelMatrix.setRotate(90, 0, 1, 0);
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1 ,100);
mvpMatrix.lookAt(6, 6, 14, 0, 0, 0, 0, 1, 0);
mvpMatrix.multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

normalMatrix.setInverseOf(modelMatrix);
normalMatrix.transpose();
gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);


gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}

function initVertexBuffers(gl) {
// Create a cube
// v6----- v5
// /| /|
// v1------v0|
// | | | |
// | |v7---|-|v4
// |/ |/
// v2------v3
var vertices = new Float32Array([ // Vertex coordinates
2.0, 2.0, 2.0, -2.0, 2.0, 2.0, -2.0,-2.0, 2.0, 2.0,-2.0, 2.0, // v0-v1-v2-v3 front
2.0, 2.0, 2.0, 2.0,-2.0, 2.0, 2.0,-2.0,-2.0, 2.0, 2.0,-2.0, // v0-v3-v4-v5 right
2.0, 2.0, 2.0, 2.0, 2.0,-2.0, -2.0, 2.0,-2.0, -2.0, 2.0, 2.0, // v0-v5-v6-v1 up
-2.0, 2.0, 2.0, -2.0, 2.0,-2.0, -2.0,-2.0,-2.0, -2.0,-2.0, 2.0, // v1-v6-v7-v2 left
-2.0,-2.0,-2.0, 2.0,-2.0,-2.0, 2.0,-2.0, 2.0, -2.0,-2.0, 2.0, // v7-v4-v3-v2 down
2.0,-2.0,-2.0, -2.0,-2.0,-2.0, -2.0, 2.0,-2.0, 2.0, 2.0,-2.0 // v4-v7-v6-v5 back
]);

var colors = new Float32Array([ // Colors
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v1-v2-v3 front
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v5-v6-v1 up
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v1-v6-v7-v2 left
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v7-v4-v3-v2 down
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0  // v4-v7-v6-v5 back
]);

var normals = new Float32Array([ // Normal
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, // v7-v4-v3-v2 down
0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0 // v4-v7-v6-v5 back
]);

var indices = new Uint8Array([ // Indices of the vertices
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // right
8, 9,10, 8,10,11, // up
12,13,14, 12,14,15, // left
16,17,18, 16,18,19, // down
20,21,22, 20,22,23 // back
]);

// Write the vertex coordinates and color to the buffer object
if (!initArrayBuffer(gl, vertices, 3, gl.FLOAT, 'a_Position'))
return -1;

if (!initArrayBuffer(gl, colors, 3, gl.FLOAT, 'a_Color'))
return -1;

if (!initArrayBuffer(gl, normals, 3, gl.FLOAT, 'a_Normal'))
return -1;

// Create a buffer object
var indexBuffer = gl.createBuffer();
if (!indexBuffer) {
console.log('Failed to create the buffer object');
return false;
}

// Write the indices to the buffer object
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

return indices.length;
}

function initArrayBuffer(gl, data, num, type, attribute) {
var buffer = gl.createBuffer(); // Create a buffer object
if (!buffer) {
console.log('Failed to create the buffer object');
return false;
}
// Write date into the buffer object
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
// Assign the buffer object to the attribute variable
var a_attribute = gl.getAttribLocation(gl.program, attribute);
if (a_attribute < 0) {
console.log('Failed to get the storage location of ' + attribute);
return false;
}
gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
// Enable the assignment of the buffer object to the attribute variable
gl.enableVertexAttribArray(a_attribute);

gl.bindBuffer(gl.ARRAY_BUFFER, null);

return true;
}
效果

见知识点三


Tips: Please indicate the source and original author when reprinting or quoting this article.