对应《WebGL编程指南》第七章 22-LookAtTriangles、23-LookAtRotatedTriangles、24-LookAtRotatedTriangles_mvMatrix、25-LookAtTrianglesWithKeys
要点:视点和视线、景深、观察者视图、模型视图矩阵
知识点
一、立方体由三角形构成
1.1 深度
之前讨论过,三维物体也是由二维图形(特别是三角形)组成的。既然如此,是不是我们只需要像前几章一样逐个绘制组成物体的每个三角形,最终就可以绘制出整个三维物体了呢?
实际上,三维与二维还有一个显著区别:在绘制三维物体时,还得考虑它们的深度信息。
1.2 三维世界的观察者
定义:三维世界的观察者:在什么地方、朝哪里看、视野有多宽、能看多远。
二、视点和视线
三维物体与二维物体的显著区别:三维具有深度,也就是Z轴。事实上,我们最后还是把三维场景绘制到二维的屏幕上,即绘制观察者看到的世界,而观察者可以处在任意位置观察。
2.1 定义
定义一个观察者需要考虑两点:
- 观察方向,即观察者自己在什么位置,在看场景的哪一部分。
- 可视距离,即观察者能够看多远。
我们将观察者所处的位置称为视点,从视点出发沿着观察方向的射线称作视线。
在WebGL系统中,视点默认处于原点(0, 0, 0),视线为Z轴负半轴(指向屏幕内部),这一节,我们将视点移动到另一个位置,来观察三维场景。
2.2 程序示例
该程序修改了视点,位于(0.20. 0.25. 0.25),视线沿着原点(0, 0, 0)方向,可以看到原点附近有三个三角形,程序中的这三个三角形错落摆放,以帮助你理解三维场景中深度的概念。
1
| viewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0);
|


2.3 视点、观察目标点和上方向
为了确定观察者的状态,你需要获取两项信息:视点,即观察者的位置;观察目标点,即被观察目标所在的点,它可以用来确定视线。此外,因为我们最后要把观察到的景象绘制到屏幕上,还需要知道上方向。有了这三项信息,就可以确定观察者的状态了。

视点:观察者所在的三维空间中位置,视线的起点。在接下来的几节中,视点坐标都用$(eyeX, eyeY, eyeZ)$表示。
观察目标点:被观察目标所在的点。视线从视点出发,穿过观察目标点并继续延伸。注意,观察目标点是一个点,而不是视线方向,只有同时知道观察目标点和视点,才能算出视线方向。观察目标点的坐标用$(atX, atY, atZ)$表示。
上方向:最终绘制在屏幕上的影像中的向上的方向。试想,如果仅仅确定了视点和观察点,观察者还是可能以视线为轴旋转的。所以,为了将观察者固定住,我们还需要指定上方向。上方向是具有3个分量的矢量,用$(upX, upY, upZ)$表示。

2.4 视图矩阵—Matrix4.setLookAt()
在 WebGL 中,我们可以用上述三个矢量创建一个视图矩阵,然后将该矩阵传给顶点着色器。试图矩阵可以表示观察者的状态,含有观察者的视点,观察目标点,上方向等信息。之所以被成为视图矩阵,是因为它最终影响了显示在屏幕上的视图,也就是观察者观察到的场景。 Matrix4.setLookAt()
函数可以根据上述三个矢量:视点、观察点和上方向,来创建出视图矩阵。
参数 |
描述 |
eyeX, eyeY, eyeZ |
指定视点 |
atX, atY, atZ |
指定观察点 |
upX, upY, upZ |
指定上方向,如果Y轴是上方向,那么就是(0, 1, 0) |
在WebGL 中,观察者的默认状态应该是这样的:
-
视点位于坐标系统原点(0, 0, 0)
。
-
视线为 Z 轴负方向,观察点为(0, 0, -1)
,上方向为为Y 轴正方向, 即(0, 1, 0)
。
如果将上方向改为 X 轴正半轴方向(1, 0, 0),你将看到场景旋转了90度。创建这样一个矩阵,你只需要简单地使用如下代码。
1 2 3
| var viewMatrix = new Matrix4(); viewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0);
|
三、代码分析
3.1 LookAtTriangles 与 ColoredTriangle
本节代码1根据【WebGL之巅】17-颜色与纹理-绘制彩色与渐变三角形实例改编,片元着色器、传入定点数据的方式等与 ColoredTriangle.js 中的一样,主要有以下三点区别:
-
视图矩阵
被传给顶点着色器,并与顶点坐标相乘。
-
initVertexBuffers()
函数创建了3个三角形的顶点坐标和颜色数据,并在 main()函数中调用。
-
main()
函数计算了视图矩阵并传给了顶点着色器的 uniform 变量 u_viewMatrix
。视点坐标为(0.25, 0.25, 0.25),观察点坐标为(0, 0, 0),上方向为(0, 1, 0)。
首先,来看一下上述第2点中提到 initVertexBuffers()
函数。该函数与 ColorTriangle.js 中的区别在于 verticesColors 数组
。原先,该数组中只有一个三角形的顶点坐标和颜色数据,修改后数组包含了3个三角形共计9个顶点的数据,而且顶点坐标的 z 分量也不再是0了。接着我们创建了缓冲区对象,并将数组中的数据填了进去。此外,我们还把 gl.drawArrays()
的第3个参数改成了9
,因为这里共有9个顶点。
然后,根据上述第3点,需要建立视图矩阵(包含了视点、视线和上方向信息)并传给顶点着色器。为此,我们先创建了一个 Matrix4 对象 viewMatrix,然后用 setLookAt()
方法将其设置为视图矩阵,最后将视图矩阵中的元素传给顶点着色器中的 u_viewMatrix
变量。
1 2 3 4 5
| var viewMatrix = new Matrix4(); viewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
|
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_ViewMatrix;\n' + 'varying vec4 v_Color;\n' + 'void main() {\n' + ' gl_Position = u_ViewMatrix * a_Position;\n' + ' gl_PointSize = 10.0;\n' + ' v_Color = a_Color;\n' + '}\n';
|
与ColoredTriangle.js 相比,顶点着色器有两处改动:定义 uniform 变量 u_viewMatrix
;将视图矩阵与顶点坐标相乘
再赋值给 gl_Position。那么这样的改动会怎样影响观察到的景象呢?接着来看。
3.2LookAtTriangles 与 RotatedTriangle_Matrix4
仔细观察示例中的顶点着色器,你会发现它和第4章的 RotatedTriangle_Matrix4.js 很像。后者在顶点着色器中创建了一个 Matrix4 类型的旋转矩阵对象,用它去旋转三角形。我们来回顾一下这个着色器:
1 2 3 4 5 6
| var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'uniform mat4 u_xformMatrix;\n' + 'void main() {\n' + 'gl_Position = u_xformMatrix * a_Position;\n' + '}\n';
|
本例 LookAtTriangle.js 的顶点着色器程序如下所示:
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_ViewMatrix;\n' + 'varying vec4 v_Color;\n' + 'void main() {\n' + ' gl_Position = u_ViewMatrix * a_Position;\n' + ' gl_PointSize = 10.0;\n' + ' v_Color = a_Color;\n' + '}\n';
|
可见,后者与前者相比增加了 attribute 变量 a_Color 以存储顶点颜色值,增加了 varying 变量 v_Color把颜色传给片元着色器,uniform 变量由 u_RotMatrix
改成了 u_ViewMatrix
。尽管存在上述这些差异,但是在两个着色器中,使用 mat4 对象乘以顶点坐标再赋值给 gl_Position 的行为却非常相似。
实际上,“根据自定义的观察者状态,绘制观察者看到的景象”与“使用默认的观察状态,但是对三维对象进行平移,旋转等变换,再绘制观察者看到的景象”,这两种行为是等价的。
举个例子,默认情况下视点在原点,视线沿着Z轴负方向进行观察。假如我们将点移动到(0, 0, 1),如下图所示。这时,视点与被观察的三角形在Z轴上的距离增加了 1.0 个单位。实际上,如果我们使三角形沿着Z轴负方向1.0个单位,也可以达到同样的效果,因为观察者看上去是一样的。

事实上,上述过程就发生在示例程序 LookAtTriangles.js 中。根据视点、观察点和上方向参数,setLookAt()方法计算出的视图矩阵恰恰就是“沿着Z轴负方向移动1.0个单位”的变换矩阵。所以,把这个矩阵与顶点坐标相乘,就相当于获得了“将视点设置在(0.0, 0.0, 1.0)”的效果。视点移动的方向与被观察对象移动的方向正好相反。对于视点的旋转,也可以采用类似的方式。
“改变观察者的状态”与“对整个世界进行平移和旋转变换”,本质上是一样的,它们都可以用矩阵来描述。接下来,我们将从一个指定的视点来观察旋转后的三角形。
3.3 从指定视点观察旋转后的三角形
第4章 RotatedTriangle_Matrix 程序绘制了一个绕Z轴旋转一定角度后的三角形。本节将修改 LookAtTriangles 程序来绘制一个从指定位置看过去的旋转后的三角形。这时,我们需要两个矩阵:旋转矩阵和视图矩阵。首先有一个问题是,以怎样的顺序相乘这两个矩阵。
我们知道,矩阵乘以顶点坐标,得到的结果是顶点经过矩阵变换之后的新坐标。也就是说,用旋转矩阵乘以顶点坐标,就可以得到旋转后的顶点坐标。
用视图矩阵乘以顶点坐标会把顶点变换到合适的位置,使得观察者(以默认状态)观察新位置的顶点,就好像在观察者处在(视图矩阵描述的)视点上观察原始顶点一样。现在要在某个视点处观察旋转后的三角形,我们需要先旋转三角形,然后从这个视点来观察他。换句话说,我们需要先对三角形进行旋转变换,再对旋转后的三角形进行与”移动视点“等效的变换。我们按照上述顺序相乘两个矩阵。具体看一下等式。
我们知道,如果想旋转图形,就需要用旋转矩阵乘以旋转前的顶点坐标:
<旋转后顶点坐标> = <旋转矩阵> * <原始顶点坐标>
用视图矩阵乘以旋转后的顶点坐标,就可以获得”从视点看上去“的旋转后的顶点坐标:
<"从顶点看上去"的旋转后顶点坐标> = <视图矩阵> * <旋转后顶点坐标>
将第1个式子带入第2个,可得:
<"从顶点看上去"的旋转后顶点坐标>=<视图矩阵>*<旋转矩阵>*<原始顶点坐标>
除了旋转矩阵,你还可以使用平移、缩放等基本变换矩阵或它们的组合,这时矩阵被称为模型矩阵。这样,上式就可以写成:
<视图矩阵>*<模型矩阵>*<原始顶点坐标>
代码2着色器代码实现了这一等式,以下是代码核心部分截取:
1 2 3 4 5 6 7 8 9 10 11
| var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute vec4 a_Color;\n' + 'uniform mat4 u_ViewMatrix;\n' + 'uniform mat4 u_ModelMatrix;\n' + 'varying vec4 v_Color;\n' + 'void main() {\n' + ' gl_Position = u_ViewMatrix * u_ModelMatrix * a_Position;\n' + ' gl_PointSize = 10.0;\n' + ' v_Color = a_Color;\n' + '}\n';
|
1 2 3 4 5 6 7 8 9 10 11
| var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix'); var viewMatrix = new Matrix4(); viewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0); gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix'); var modelMatrix = new Matrix4(); modelMatrix.setRotate(-10, 0, 0, -1); gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
|
复习:gl.uniformMatrix4fv、setRotate(angle, x, y, z)
3.4 模型视图矩阵
上一节实现了<视图矩阵>*<模型矩阵>*<原始顶点坐标>
,这样,程序对每个顶点都要计算<视图矩阵>*<模型矩阵>
,如果顶点数量很多,就会造成不必要的开销。这是因为,无论对哪个顶点而言,两个矩阵相乘的结果都是一样的。
所以我们可以在js中事先把这两个矩阵相乘的结果计算出来,再传给顶点着色器。这两个矩阵相乘得到的结果被称为模型视图矩阵,如下:
<模型视图矩阵> = <视图矩阵>*<模型矩阵>
这样,上一节的式子就可以改成:
<模型视图矩阵>*<原始顶点坐标>
代码3实现了这一等式,以下是代码核心部分截取:
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_ModelViewMatrix;\n' + 'varying vec4 v_Color;\n' + 'void main() {\n' + ' gl_Position = u_ModelViewMatrix * a_Position;\n' + ' gl_PointSize = 10.0;\n' + ' v_Color = a_Color;\n' + '}\n';
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 方法一相乘
方法二相乘 var u_ModelViewMatrix = gl.getUniformLocation(gl.program, 'u_ModelViewMatrix');
var modelViewMatrix = new Matrix4(); modelViewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0).rotate(-10, 0, 0, -1); gl.uniformMatrix4fv(u_ModelViewMatrix, false, modelViewMatrix.elements);
|
复习:rotate(angle, x, y, z)
3.5 利用键盘改变视点
这一节将在LookAtTriangles 的基础上进行修改,使得当键盘上的方向键被按下时,观察者的视点也随之移动。在新程序 LookAtTrianglesWithKeys 中,如果右方向键被按下,视点 X 坐标将增大 0.01; 如果左方向键被按下,视点的 X 坐标将减少 0.01。
代码4实现了这一效果,以下是代码核心部分截取。
在本例中,我们注册了键盘事件响应函数。每当左方向键或右方向键被按下时,就会改变视点的位置,然后调用draw()
函数重绘场景。在研究键盘事件响应函数前,先来看一下 draw()函数。
1 2 3 4 5 6 7 8 9 10 11 12 13
| var g_eyeX = 0.20, g_eyeY = 0.25, g_eyeZ = 0.25;
function draw(gl, n, u_ViewMatrix, viewMatrix) { viewMatrix.setLookAt(g_eyeX, g_eyeY, g_eyeZ, 0, 0, 0, 0, 1, 0);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
gl.clear(gl.COlOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n); }
|
draw()
函数的流程十分直接:首先根据全局变量 g_eyeX
、g_eyeY
、g_eyeZ
计算视图矩阵,这三个变量的初始值分别是0.2、0.25、0.25;然后将计算得到的视图矩阵传给顶点着色器中的 u_ViewMatrix 变量。注意 main()
函数调用 draw()
函数时以参数的形式传入了之前获取的着色器中 u_ViewMatrix 的存储地址,和一个新创建的 Matrix4 对象。这样做的目的是为了提高 draw()
函数的效率,否则我们就得在每次调用 draw()函数时都重新获取 u_ViewMarix 的地址并新建 Matrix4 对象。
全局变量 g_eyeX、g_eyeY、g_eyeZ
中存储着视点的坐标,键盘事件响应函数将更新 g_eyeX 的值。为了在按键被按下时调用该函数,我们必须把函数注册到 document 对象的 onkeydown 属性上去。我们定义了一个匿名函数作为键盘事件响应函数:
1 2 3 4 5
|
document.onkeydown = function (ev) { keydown(ev, gl, n, u_ViewMatrix,viewMatrix); };
|
匿名函数调用了 keydown()
函数,并传入了相关的参数。让我们来看一下 keydown()函数的实现。
1 2 3 4 5 6 7 8 9 10
| function keydown(ev, gl, n, u_ViewMatrix, viewMatrix) { if(ev.keyCode == 39){ g_eyeX += 0.01; }else if(ev.keyCode == 37){ g_eyeX -= 0.01; }else { return ; } draw(gl, n, u_ViewMatrix, viewMatrix); }
|
keydown()
函数的第1个参数 ev
是一个事件对象,该函数的逻辑很直接,首先根据 ev.keyCode
属性检查哪个案件被按下,然后更新 g_eyeX
。如果是右方向键,就令 g_eyeX
增加0.01,如果是左方向键,就令 g_eyeX
减少0.01。最后调用 draw()
函数绘制三角形。
运行程序,每当你按下左或右方向键时,三角形都会改变一下方向,实际上这时因为观察者的位置发生了变化。( 预览效果(可交互))
仔细观察运行效果,你会注意到当视点在极右/左的位置时,三角形会缺少一部分,下一节可视范围将详细讲解。
实例
代码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
| var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute vec4 a_Color;\n' + 'uniform mat4 u_ViewMatrix;\n' + 'varying vec4 v_Color;\n' + 'void main() {\n' + ' gl_Position = 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.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n); }
function initVertexBuffers(gl) { var verticesColors = new Float32Array( [ 0.0, 0.5, -0.4, 0.4, 1.0, 0.4, -0.5, -0.5, -0.4, 0.4, 1.0, 0.4, 0.5, -0.5, -0.4, 1.0, 0.4, 0.4,
0.5, 0.4, -0.2, 1.0, 0.4, 0.4, -0.5, 0.4, -0.2, 1.0, 1.0, 0.4, 0.0, -0.6, -0.2, 1.0, 1.0, 0.4,
0.0, 0.5, 0.0, 0.4, 0.4, 1.0, -0.5, -0.5, 0.0, 0.4, 0.4, 1.0, 0.5, -0.5, 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'); gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE *6, 0); 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; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>LookAtTriangles</title> </head> <body onload="main()"> <canvas id="webgl" width="400" height="400"> Please use the browser supporting "canvas". </canvas>
<script src="../lib/webgl-utils.js"></script> <script src="../lib/webgl-debug.js"></script> <script src="../lib/cuon-utils.js"></script> <script src="../lib/cuon-matrix.js"></script> <script src="LookAtTriangles.js"></script> </body> </html>
|
效果

代码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
| var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute vec4 a_Color;\n' + 'uniform mat4 u_ViewMatrix;\n' + 'uniform mat4 u_ModelMatrix;\n' + 'varying vec4 v_Color;\n' + 'void main() {\n' + ' gl_Position = 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' + '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.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0); gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix'); var modelMatrix = new Matrix4(); modelMatrix.setRotate(-10, 0, 0, -1); gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n); }
function initVertexBuffers(gl) { var verticesColors = new Float32Array( [ 0.0, 0.5, -0.4, 0.4, 1.0, 0.4, -0.5, -0.5, -0.4, 0.4, 1.0, 0.4, 0.5, -0.5, -0.4, 1.0, 0.4, 0.4,
0.5, 0.4, -0.2, 1.0, 0.4, 0.4, -0.5, 0.4, -0.2, 1.0, 1.0, 0.4, 0.0, -0.6, -0.2, 1.0, 1.0, 0.4,
0.0, 0.5, 0.0, 0.4, 0.4, 1.0, -0.5, -0.5, 0.0, 0.4, 0.4, 1.0, 0.5, -0.5, 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'); gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE *6, 0); 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
| var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute vec4 a_Color;\n' + 'uniform mat4 u_ModelViewMatrix;\n' + 'varying vec4 v_Color;\n' + 'void main() {\n' + ' gl_Position = u_ModelViewMatrix * 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_ModelViewMatrix = gl.getUniformLocation(gl.program, 'u_ModelViewMatrix'); var modelViewMatrix = new Matrix4(); modelViewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0).rotate(-10, 0, 0, -1); gl.uniformMatrix4fv(u_ModelViewMatrix, false, modelViewMatrix.elements);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n); }
function initVertexBuffers(gl) { var verticesColors = new Float32Array( [ 0.0, 0.5, -0.4, 0.4, 1.0, 0.4, -0.5, -0.5, -0.4, 0.4, 1.0, 0.4, 0.5, -0.5, -0.4, 1.0, 0.4, 0.4,
0.5, 0.4, -0.2, 1.0, 0.4, 0.4, -0.5, 0.4, -0.2, 1.0, 1.0, 0.4, 0.0, -0.6, -0.2, 1.0, 1.0, 0.4,
0.0, 0.5, 0.0, 0.4, 0.4, 1.0, -0.5, -0.5, 0.0, 0.4, 0.4, 1.0, 0.5, -0.5, 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');
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE *6, 0); 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; }
|
效果

代码4
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
| var VSHADER_SOURCE = 'attribute vec4 a_Position;\n' + 'attribute vec4 a_Color;\n' + 'uniform mat4 u_ViewMatrix;\n' + 'varying vec4 v_Color;\n' + 'void main() {\n' + ' gl_Position = 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();
document.onkeydown = function (ev) { keydown(ev, gl, n, u_ViewMatrix,viewMatrix); };
draw(gl, n, u_ViewMatrix, viewMatrix); }
var g_eyeX = 0.20, g_eyeY = 0.25, g_eyeZ = 0.25; function keydown(ev, gl, n, u_ViewMatrix, viewMatrix) { if(ev.keyCode == 39){ g_eyeX += 0.01; }else if(ev.keyCode == 37){ g_eyeX -= 0.01; }else { return ; } draw(gl, n, u_ViewMatrix, viewMatrix); }
function draw(gl, n, u_ViewMatrix, viewMatrix) { viewMatrix.setLookAt(g_eyeX, g_eyeY, g_eyeZ, 0, 0, 0, 0, 1, 0);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
gl.clear(gl.COlOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n); }
function initVertexBuffers(gl) { var verticesColors = new Float32Array( [ 0.0, 0.5, -0.4, 0.4, 1.0, 0.4, -0.5, -0.5, -0.4, 0.4, 1.0, 0.4, 0.5, -0.5, -0.4, 1.0, 0.4, 0.4,
0.5, 0.4, -0.2, 1.0, 0.4, 0.4, -0.5, 0.4, -0.2, 1.0, 1.0, 0.4, 0.0, -0.6, -0.2, 1.0, 1.0, 0.4,
0.0, 0.5, 0.0, 0.4, 0.4, 1.0, -0.5, -0.5, 0.0, 0.4, 0.4, 1.0, 0.5, -0.5, 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');
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE *6, 0); 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; }
|
效果4
模拟观察者(视点)改变自身位置(围绕观察物水平旋转)的场景。
尝试按下你键盘的←
键或→
键,或点击下面的按钮~
LookAtTriangles
Tips:
Please indicate the source and original author when reprinting or quoting this article.