更新于 

立方体

使用drawArrays绘制立方体

一个立方体一共8个顶点,6个面,12个三角形。

ModeVertex NumberDraw Time
TRIANGLES361
TRIANGLE_FAN246
TRIANGLE_STRIP141

通过顶点索引绘制物体

drawElements
立方体绘制所需建立的数据结构
立方体绘制所需建立的数据结构
drawArrays和drawElements区别

最重要的区别就在于drawElements需要指定顶点的索引值,
因此使用的是 gl.ELEMENT_ARRAY_BUFFER 而不是gl.ARRAY_BUFFER。

  • drawArrays就像班级里老师直接叫名字给同学安排座位
  • drawElements更像学校里通过学号安排考场
关于第三个参数type的注意点

需要注意的是type参数gl.ELEMENT_ARRAY_BUFFER的数据类型不一致,也不会报错,
但是可能会绘制出一些不可名状之物

HelloCubes Code
HelloCubes运行效果
HelloCubes运行效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_MvpMatrix;
varying vec4 v_Color;
void main(){
gl_Position = u_MvpMatrix * a_Position;
v_Color = a_Color;
}
`;

let FSHADER_SOURCE = `
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_Color;
void main(){
gl_FragColor = v_Color;
}
`;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function main() {
let canvas = document.getElementById('webgl');
let gl = getWebGLContext(canvas);
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('failed to init shaders');
return;
}

let n = initVertexBuffers(gl);
let u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
let mvpMatrix = new Matrix4();
mvpMatrix.setPerspective(30, 1, 1, 100);
mvpMatrix.lookAt(
3, 3, 7,
0, 0, 0,
0, 1, 0
);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 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
function initVertexBuffers(gl) {
let verticesColors = new Float32Array([
// Vertex coordinates and color
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // v0 White
-1.0, 1.0, 1.0, 1.0, 0.0, 1.0, // v1 Magenta
-1.0, -1.0, 1.0, 1.0, 0.0, 0.0, // v2 Red
1.0, -1.0, 1.0, 1.0, 1.0, 0.0, // v3 Yellow
1.0, -1.0, -1.0, 0.0, 1.0, 0.0, // v4 Green
1.0, 1.0, -1.0, 0.0, 1.0, 1.0, // v5 Cyan
-1.0, 1.0, -1.0, 0.0, 0.0, 1.0, // v6 Blue
]);
let FSIZE = verticesColors.BYTES_PER_ELEMENT;
let indices = new Uint8Array([
0, 1, 2, 0, 2, 3, //前
0, 3, 4, 0, 4, 5, //右
0, 5, 6, 0, 6, 1, //上
1, 6, 7, 1, 7, 2, //左
7, 4, 3, 7, 3, 2, //下
4, 7, 6, 4, 6, 5, //后
]);
let a_Position = gl.getAttribLocation(gl.program, 'a_Position');
let a_Color = gl.getAttribLocation(gl.program, 'a_Color');

let vertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 6 * FSIZE, 0);
gl.enableVertexAttribArray(a_Position);
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 6 * FSIZE, 3 * FSIZE);
gl.enableVertexAttribArray(a_Color);

// write index buffer
let indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

return indices.length;
}
顶点索引的写入
顶点索引数组的类型是怎么确定的?
1
2
3
4
5
6
7
8
let indices = new Int8Array([
0, 1, 2, 0, 2, 3,
0, 1, 6, 0, 5, 6,
0, 3, 4, 0, 4, 5,
1, 2, 7, 1, 6, 7,
5, 6, 7, 5, 4, 7,
2, 3, 7, 4, 3, 7
]);

答: 需要顶点个数来确定,Int8Array类型可以用来绘制256个顶点。

gl.ELEMENT_ARRAY_BUFFER和gl.ARRAY_BUFFER的内容是如何产生联系的?
顶点索引和顶点关系数据结构图
顶点索引和顶点关系数据结构图
通过索引获取顶点数据示意图
通过索引获取顶点数据示意图
gl.drawElements的n参数表示的是什么的个数?
1
gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_BYTE,0);

答: n表示顶点索引数组的长度。

通过索引来访问顶点数据的优缺点是什么?

优点
能够循环利用顶点信息,控制内存的开销。

缺点
需要通过索引来间接地访问,某种程度上使程序复杂化了。

为立方体的每个表面指定颜色

需要创建多个坐标相同,颜色不同的顶点

立方体的一个顶点同时在三个平面的交点处,因此需要设置3个坐标相同,颜色不同的顶点。
这样一共就需要创建3*8=24个顶点。

ColoredCube
ColoredCube运行效果
ColoredCube运行效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_MvpMatrix;
varying vec4 v_Color;
void main(){
gl_Position = u_MvpMatrix * a_Position;
v_Color = a_Color;
}
`;

let FSHADER_SOURCE = `
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_Color;
void main(){
gl_FragColor = v_Color;
}
`;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function main() {
let canvas = document.getElementById('webgl');
let gl = getWebGLContext(canvas);
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('failed to init shaders');
return;
}

let n = initVertexBuffers(gl);

let u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
let mvpMatrix = new Matrix4();
mvpMatrix.setPerspective(
30, 1, 1, 100
).lookAt(
3, 3, 7, 0, 0, 0, 0, 1, 0
);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT | gl.FEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 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
function initVertexBuffers(gl) {
// v6------ v5
// /| /|
// v1--------v0|
// | | | |
// | |v7-----|-|v4
// |/ |/
// v2-------v3

let vertices = new Float32Array([
// font-back-left-right-up-down
1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, //front v0-v1-v2-v3
1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1,//back v5-v6-v7-v4
-1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, //left v1-v2-v7-v6
1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1,//right v0-v3-v4-v5
1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1,//up v0-v1-v6-v5
1, -1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1//down v3-v2-v7-v4
]);
let colors = new Float32Array([
0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, //front
0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, //back
1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, //left
0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, //right
1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, //up
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, //down
]);
let indices = new Int8Array([
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // back
8, 9,10, 8,10,11, // left
12,13,14, 12,14,15, // right
16,17,18, 16,18,19, // up
20,21,22, 20,22,23 // down
]);
let indexBuffer = gl.createBuffer();
initArrayBuffer(gl, vertices, 3, gl.FLOAT, 'a_Position');
initArrayBuffer(gl, colors, 3, gl.FLOAT, 'a_Color');
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
return indices.length;
}
1
2
3
4
5
6
7
8
function initArrayBuffer(gl, data, num, type, attribute) {
let buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
let a_attribute = gl.getAttribLocation(gl.program, attribute);
gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0,);
gl.enableVertexAttribArray(a_attribute);
}
纯白盒子
1
2
3
4
5
6
7
8
let colors = new Float32Array([
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0
]);
ColoredCube_singleColor运行效果
ColoredCube_singleColor运行效果