【WebGL之巅】23-三维世界-可视空间(透视投影)

By yesmore on 2021-08-03
阅读时间 22 分钟
文章共 5k
阅读量

对应《WebGL编程指南》第七章 28-perspectiveview,29-perspectiveview_mvp,30-perspectiveview_mvpMatrix

要点:可视空间(透视投影)、模型视图投影矩阵

知识点

一、可视空间(投影透视)

在正射投影的可视空间中,不管三角形与视点的距离是远是近,它由多大,那么画出来就有多大。为了打破这条限制,我们可以使用透视投影可视空间,它将使场景具有上图那样的深度感。

示例程序PerspectiveView 就使用了一个透视投影可视空间,视点在(0, 0, 5),视线沿着Z轴负方向。

PerspectiveView 场景中三角形的位置示意图

从上图所示,沿着Z轴负半轴,在轴的左右两侧各一次排列着个相同大小的三角形。在使用透视投影矩阵后,WebGL 就能够自动将距离远的物体缩小显示,从而产生深度感。

1.1 定义透视投影可视空间——透视投影矩阵

投影透视可视空间如下图所示。就像盒状可视空间那样,透视投影可视空间也有视点、视线、近裁剪面和远裁剪面,这样可视空间内的物体才会被显示,可视空间外的物体则不会显示。那些跨越可视空间边界的物体则只会显示其在可是空间内的部分。

透视投影可视空间

​ 不论是透视投影可视空间还是盒状可视空间,我们都用投影矩阵来表示它,但是定义矩阵的参数不同。Matrix4 对象的setPerspective()方法可用来定义透视投影可视空间。

参数 Matrix4.setPerspective()
fov 指定垂直视角,即可视空间顶面和底面间的夹角,必须大于0
aspect 指定近裁剪面的宽高比(宽度/高度)
near,far 指定近裁剪面和远裁剪面的位置,即可视空间的近边界和远边界,必须大于0

定义了透视投影可视空间的矩阵别成为透视投影矩阵

注意,第2个参数 aspect 是近裁剪面的宽高比,而不是水平视角。比如说,如果近裁剪面的高度是100而宽度是200,那么宽高比就是2。

在本例中,各个三角形与可视空间的相对位置如下图所示。我们指定了 near = 1.0far = 100aspect = 1.0,以及 fov = 30.0

三角形与可视空间相对位置

1.2 代码分析

本案例核心代码

1
2
3
4
// 投影矩阵
var projMatrix = new Matrix4();
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);

代码中的顶点着色器和片元着色器,与 LookAtTrianglesWithKeys_ViewVolume.js 中的变量的命名都完全一致。

main()函数的执行流程也差不多:首先调用 initVertexBuffers()函数,向缓冲区对象中写入这6个三角形的顶点坐标和颜色数据。

接着我们获取了着色器中视图矩阵和透视投影矩阵 uniform 变量的存储地址,并创建了两个对应的矩阵对象。

然后,我们计算了视图矩阵,视点设置在(0, 0, 5)视线为Z轴负方向上方向为Y轴正方向,最后我们按照金字塔状的可视空间建立了透视投影矩阵。

1
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);

其中,第2个参数 aspect 宽高比(近裁剪面的宽度与高度的比值)应当与 canvas 保持一致,我们根据 canvas 的 width 和 height 属性来计算出该参数,这样如果 canvas 的大小发生变化,也不会导致显示出来的图形变形。

接下来,将准备好的视图矩阵和透视投影矩阵传给着色器中对应的 uniform变量。最后将三角形绘制出来。

到目前为止,还有一个很重要的问题没有完全解释,那就是矩阵为什么可以用来定义可视空间。

1.3 投影矩阵的作用——规范立方体

首先来看透视投影矩阵。可以看到,运用透视投影矩阵后,场景中的三角形有了两个变化。

首先,距离较远的三角形看上去变小了;其次,三角形被不同程度地平移以贴近中心线,使得它们看上去在视线的左右排成了两列。实际上,如下图左所示,这些三角形的大小是完全相同的,透视投影矩阵对三角形进行了两次变换:

  • 根据三角形与视点的距离,按比例对三角形进行了缩小变换
  • 对三角形进行平移变换,使其贴近视线,如下图右所示。经过了这两次变换之后,就产生了深度效果。

透视投影变换示意图及规范立方体

这表明,可视空间的规范可以用一系列基本变换(如缩放、平移)来定义。Matrix4 对象的 setPerspective()方法自动地根据上述可视空间的参数计算出对应的变换矩阵。

换一个角度来看,透视投影矩阵实际上将金字塔状的可视空间变换为了盒状的可视空间,这个盒装的可视空间又称规范立方体,如上图右所示。

注意,正射投影矩阵不能产生深度感。正射投影矩阵的工作仅仅是将顶点从盒状的可视空间映射到规范立方体中。顶点着色器输出的顶点都必须在规范立方体中,这样才会显示在屏幕上。

有了投影矩阵、模型矩阵和视图矩阵,我们就能够处理顶点需要经过的所有集合变换,最终达到具有深度感的视觉效果。在下面几小节中,我们就把这三个矩阵结合起来,建立一个简单的示例程序。

二、模型矩阵、视图矩阵和投影矩阵综合案例

2.1 综合表达式

PerspectiveView.js 的一个问题是,我们用了一大段枯燥的代码来定义所有顶点和数据。示例中只有6个三角形,我们还可以手动管理这些数据,但是如果三角形的数量进一步增加的话,那可真就是一团糟了。幸运的是,对于这个问题,确实还有跟高效的方法。

仔细观察下图,你会发现左右两组三角形的大小、位置、颜色都是对应的。如果在虚线标识处也有这样3个三角形,那么将它们向X轴正方向平移0.75单位就可以得到右侧的三角形,向X轴负方向平移0.75单位就可以得到左侧三角形。

平移然后绘制三角形

利用这一点,我们只需按照下面的步骤,就能获得 PerspectiveView 的效果了:

  • 在虚线处,即沿着Z轴准备3个三角形的顶点数据;
  • 将其沿X轴正方向平移0.75单位,绘制这些三角形;
  • 将其沿X轴负方向平移0.75单位,绘制这些三角形。

示例程序 代码2-PerspectiveView_mvp 就尝试这样做。

PerspectiveView 程序使用投影矩阵定义可视空间,使用视图矩阵定义观察者,而 PerspectiveView_mvp 程序又加入了模型矩阵,用来对三角形进行变换。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 顶点着色器
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'uniform mat4 u_ModelMatrix;\n' + // 模型矩阵
'uniform mat4 u_ViewMatrix;\n' + // 视图矩阵
'uniform mat4 u_ProjMatrix;\n' + // 投影矩阵
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;\n' +
' gl_PointSize = 10.0;\n' +
' v_Color = a_Color;\n' +
'}\n';

复习矩阵变换:LookAtTriangles,该程序允许观察者从自定义的位置观察旋转后的三角形。下式描述了三角形顶点的变换过程:

<视图矩阵> x <模型矩阵> x <顶点坐标>

后来的 LookAtTriangles_ViewVolume 程序(该程序修复了三角形的一个角被切掉的错误)使用下式来计算最终的额顶点坐标,其中投影矩阵有可能是正射投影矩阵或透视投影矩阵:

<投影矩阵> x <视图矩阵> x <顶点坐标>

可以从上述两式推断出:

<投影矩阵> x <视图矩阵> x <模型矩阵> x <顶点坐标>

上式表示,在 WebGL 中,你可以使用投影矩阵、视图矩阵、模型矩阵这3种矩阵计算出最终的顶点坐标(即顶点在规范立方体中的坐标)。

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
// 模型矩阵------------------------------
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
var modelMatrix = new Matrix4();
modelMatrix.setTranslate(0.75, 0, 0); //平移0.75单位
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
//---------------------------------------

// 视图矩阵------------------------------
var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
// 设置视点、视线、上方向
var viewMatrix = new Matrix4();
viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
//---------------------------------------

// 投影矩阵--------------------------
var u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
var projMatrix = new Matrix4();
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);

gl.clear(gl.COLOR_BUFFER_BIT);

gl.drawArrays(gl.TRIANGLES, 0, n);// 绘制右侧一组三角形

modelMatrix.setTranslate(-0.75, 0, 0);
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
gl.drawArrays(gl.TRIANGLES, 0, n);// 绘制左侧一组三角形

2.2 分析

顶点着色器中新增加的 u_ModelMatrix 变量参与了 gl_Position 的计算:

1
'   gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;\n' +

main()函数调用 initVertexBuffers()函数,定义将要传给缓冲区对象的三角形定点数据。我们只定义了3个三角形,其中心都在Z轴上。而在 PerpspectiveView.js 中,我们在Z轴两侧共定义了6个三角形。前面说过,这时因为这3个三角形将与平移变换结合使用。

接着,我们获取了顶点着色器 u_ModelMatrix变量的存储地址,然后新建了模型矩阵 modelMatrix 对象,并根据参数将其计算出来。此时,该模型矩阵会将三角形向X轴正方向平移0.75个单位。

1
2
3
4
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
var modelMatrix = new Matrix4();
modelMatrix.setTranslate(0.75, 0, 0); //平移0.75单位
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

除了计算模型矩阵,计算视图矩阵和投影矩阵的过程与 PerspectiveView.js 中一样。模型矩阵被传给 u_ModelMatrix 并进行绘制,绘制了Z轴右侧的3个三角形。

下面以相似的方式来绘制左侧的三角形:首先重新计算模型矩阵,使之将初始的三角形沿X轴负方向平移0.75单位。算出新的模型矩阵后,传给着色器,再调用 gl.drawArrays()进行绘制,就画出了左侧的三角形。视图矩阵和投影矩阵不需要变化,不需要管它们。

1
2
3
4
// 为另一侧的三角形重新计算模型矩阵
modelMatrix.setTranslate(-0.75, 0, 0); // 平移-0.75单位
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
gl.drawArrays(gl.TRIANGLES, 0, n);// 绘制左侧一组三角形

2.3 改进——模型视图投影矩阵

代码2-PerspectiveView_mvp直接在着色器中计算<投影矩阵> x <视图矩阵> x <模型矩阵> x <顶点坐标>。这个式子其实和顶点没有关系,没必要在每个顶点处理时都计算一遍。我们可以在js中计算出三个矩阵相乘得到的单个矩阵的结果,传给顶点着色器,就像在LookAtRotatedTriangles_mvMatrix一样。

传入的这个矩阵被称为模型视图投影矩阵,代码中将其命名为u_MvpMatrix

1
2
3
4
5
6
7
8
9
10
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'uniform mat4 u_MvpMatrix;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_Position = u_MvpMatrix * a_Position;\n' +
' gl_PointSize = 10.0;\n' +
' v_Color = a_Color;\n' +
'}\n';

设置好各个矩阵后,相乘:

1
2
// 计算模型视图投影矩阵
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);

2.4 总结

目前为止,已经掌握编写代码移动视点,设置可视空间,从不同的角度观察三维对象,处理三角形缺一角的情况。但是,仍然还存在一个问题:在移动视点的过程中,有时候前面的三角形会“躲”到后面的三角形之后,下一节将会研究这一问题。

实例

代码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
//perspectiveview.js
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'uniform mat4 u_ViewMatrix;\n' +
'uniform mat4 u_ProjMatrix;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_Position = u_ProjMatrix * u_ViewMatrix * a_Position;\n' +
' gl_PointSize = 10.0;\n' +
' v_Color = a_Color;\n' +
'}\n';

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

function main() {

var canvas = document.getElementById("webgl");

var gl = getWebGLContext(canvas);

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

//设置顶点位置
var n = initVertexBuffers(gl);

var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');

// 视图矩阵,设置视点、视线、上方向
var viewMatrix = new Matrix4();
viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
var u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');

// 投影矩阵
var projMatrix = new Matrix4();
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);

gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n);
}

function initVertexBuffers(gl) {
var verticesColors = new Float32Array(
[
// Three triangles on the right side
0.75, 1.0, -4.0, 0.4, 1.0, 0.4, // The back green one
0.25, -1.0, -4.0, 0.4, 1.0, 0.4,
1.25, -1.0, -4.0, 1.0, 0.4, 0.4,

0.75, 1.0, -2.0, 1.0, 1.0, 0.4, // The middle yellow one
0.25, -1.0, -2.0, 1.0, 1.0, 0.4,
1.25, -1.0, -2.0, 1.0, 0.4, 0.4,

0.75, 1.0, 0.0, 0.4, 0.4, 1.0, // The front blue one
0.25, -1.0, 0.0, 0.4, 0.4, 1.0,
1.25, -1.0, 0.0, 1.0, 0.4, 0.4,

// Three triangles on the left side
-0.75, 1.0, -4.0, 0.4, 1.0, 0.4, // The back green one
-1.25, -1.0, -4.0, 0.4, 1.0, 0.4,
-0.25, -1.0, -4.0, 1.0, 0.4, 0.4,

-0.75, 1.0, -2.0, 1.0, 1.0, 0.4, // The middle yellow one
-1.25, -1.0, -2.0, 1.0, 1.0, 0.4,
-0.25, -1.0, -2.0, 1.0, 0.4, 0.4,

-0.75, 1.0, 0.0, 0.4, 0.4, 1.0, // The front blue one
-1.25, -1.0, 0.0, 0.4, 0.4, 1.0,
-0.25, -1.0, 0.0, 1.0, 0.4, 0.4,
]);
var n = 18; //点的个数


//创建缓冲区对象
var verteColorBuffer = gl.createBuffer();

//将缓冲区对象保存到目标上
gl.bindBuffer(gl.ARRAY_BUFFER, verteColorBuffer);

//向缓存对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

var FSIZE = verticesColors.BYTES_PER_ELEMENT;

var a_Position = gl.getAttribLocation(gl.program, 'a_Position');

//将缓冲区对象分配给a_Postion变量
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE *6, 0);
//连接a_Postion变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);

var a_Color = gl.getAttribLocation(gl.program, 'a_Color');

gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);

gl.bindBuffer(gl.ARRAY_BUFFER, null);//取消绑定的缓冲区对象
return n;
}

代码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
//perspectiveview-mvp.js
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'uniform mat4 u_ModelMatrix;\n' + // 模型矩阵
'uniform mat4 u_ViewMatrix;\n' + // 视图矩阵
'uniform mat4 u_ProjMatrix;\n' + // 投影矩阵
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;\n' +
' gl_PointSize = 10.0;\n' +
' v_Color = a_Color;\n' +
'}\n';

var FSHADER_SOURCE=
'precision mediump float;\n' +//!!! 需要声明浮点数精度,否则报错No precision specified for (float)
'varying vec4 v_Color;\n' +
'void main(){\n'+
' gl_FragColor = v_Color;\n'+
'}\n';

function main() {

var canvas = document.getElementById("webgl");

var gl = getWebGLContext(canvas);

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

// 设置顶点位置
var n = initVertexBuffers(gl);

//gl.clearColor(0, 0, 0, 1);

// 模型矩阵------------------------------
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
var modelMatrix = new Matrix4();
modelMatrix.setTranslate(0.75, 0, 0); //平移0.75单位
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
//---------------------------------------

// 视图矩阵------------------------------
var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
// 设置视点、视线、上方向
var viewMatrix = new Matrix4();
viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
//---------------------------------------

// 投影矩阵--------------------------
var u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
var projMatrix = new Matrix4();
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
//----------------------------------

gl.clear(gl.COLOR_BUFFER_BIT);

gl.drawArrays(gl.TRIANGLES, 0, n);// 绘制右侧一组三角形

// 为另一侧的三角形重新计算模型矩阵
modelMatrix.setTranslate(-0.75, 0, 0); // 平移-0.75单位
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
gl.drawArrays(gl.TRIANGLES, 0, n);// 绘制左侧一组三角形


}

function initVertexBuffers(gl) {
var verticesColors = new Float32Array(
[
// Three triangles on the right side
0.0, 1.0, -4.0, 0.4, 1.0, 0.4, // The back green one
-0.5, -1.0, -4.0, 0.4, 1.0, 0.4,
0.5, -1.0, -4.0, 1.0, 0.4, 0.4,

0.0, 1.0, -2.0, 1.0, 1.0, 0.4, // The middle yellow one
-0.5, -1.0, -2.0, 1.0, 1.0, 0.4,
0.5, -1.0, -2.0, 1.0, 0.4, 0.4,

0.0, 1.0, 0.0, 0.4, 0.4, 1.0, // The front blue one
-0.5, -1.0, 0.0, 0.4, 0.4, 1.0,
0.5, -1.0, 0.0, 1.0, 0.4, 0.4,
]);
var n = 9; // 点的个数

// 创建缓冲区对象
var verteColorBuffer = gl.createBuffer();

// 将缓冲区对象保存到目标上
gl.bindBuffer(gl.ARRAY_BUFFER, verteColorBuffer);

//向缓存对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

var FSIZE = verticesColors.BYTES_PER_ELEMENT;

var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
// 将缓冲区对象分配给a_Postion变量
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE *6, 0);
// 连接a_Postion变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);

var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);

gl.bindBuffer(gl.ARRAY_BUFFER, null);// 取消绑定的缓冲区对象
return n;
}

代码3

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
//perspectiveview_mvpMatrix.js
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'uniform mat4 u_MvpMatrix;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_Position = u_MvpMatrix * a_Position;\n' +
' gl_PointSize = 10.0;\n' +
' v_Color = a_Color;\n' +
'}\n';

var FSHADER_SOURCE=
'precision mediump float;\n' +//!!! 需要声明浮点数精度,否则报错No precision specified for (float)
'varying vec4 v_Color;\n' +
'void main(){\n'+
' gl_FragColor = v_Color;\n'+
'}\n';

function main() {

var canvas = document.getElementById("webgl");

var gl = getWebGLContext(canvas);

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

//设置顶点位置
var n = initVertexBuffers(gl);

//gl.clearColor(0, 0, 0, 1);

// 模型视图投影矩阵
var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
var mvpMatrix = new Matrix4();

//模型矩阵------------------------------
// var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
var modelMatrix = new Matrix4();
modelMatrix.setTranslate(0.75, 0, 0); //平移0.75单位
//gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
//---------------------------------------

//视图矩阵------------------------------
// var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
//设置视点、视线、上方向
var viewMatrix = new Matrix4();
viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0);
// gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
//---------------------------------------

//投影矩阵--------------------------
// var u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
var projMatrix = new Matrix4();
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
//gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
//----------------------------------

// 计算模型视图投影矩阵
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

gl.clear(gl.COLOR_BUFFER_BIT);

gl.drawArrays(gl.TRIANGLES, 0, n);//绘制右侧一组三角形

// 计算模型视图投影矩阵
modelMatrix.setTranslate(-0.75, 0, 0);
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
gl.drawArrays(gl.TRIANGLES, 0, n);//绘制左侧一组三角形
}

function initVertexBuffers(gl) {
var verticesColors = new Float32Array(
[
// Three triangles on the right side
0.0, 1.0, -4.0, 0.4, 1.0, 0.4, // The back green one
-0.5, -1.0, -4.0, 0.4, 1.0, 0.4,
0.5, -1.0, -4.0, 1.0, 0.4, 0.4,

0.0, 1.0, -2.0, 1.0, 1.0, 0.4, // The middle yellow one
-0.5, -1.0, -2.0, 1.0, 1.0, 0.4,
0.5, -1.0, -2.0, 1.0, 0.4, 0.4,

0.0, 1.0, 0.0, 0.4, 0.4, 1.0, // The front blue one
-0.5, -1.0, 0.0, 0.4, 0.4, 1.0,
0.5, -1.0, 0.0, 1.0, 0.4, 0.4,
]);
var n = 9; //点的个数


//创建缓冲区对象
var verteColorBuffer = gl.createBuffer();

//将缓冲区对象保存到目标上
gl.bindBuffer(gl.ARRAY_BUFFER, verteColorBuffer);

//向缓存对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

var FSIZE = verticesColors.BYTES_PER_ELEMENT;

var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
//将缓冲区对象分配给a_Postion变量
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE *6, 0);
//连接a_Postion变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);

var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);

gl.bindBuffer(gl.ARRAY_BUFFER, null);//取消绑定的缓冲区对象
return n;
}

效果

效果


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