目录透视投影透视投影可视空间可视空间构造效果图Matrix4.setPerspective三角形与可视化空间的相对位置示例代码代码详解示例效果投影矩阵的作用透视投影矩阵对物体进行了两次变换透视投影变换示意图透视投影在透视投影下产生的三维场景看上去更是有深度感更加自然因为我们平时观察真实世界用的也是透视投影。在大多数情况下比如三维射击类游戏中我们都应当采用透视投影。在下图的场景中道路两边都有成排的树木。树应该都是差不多高的但是在照片上越远的树看上去越矮。同样道路尽头的建筑看上去比近处的树矮但实际上那座建筑比树高很多。这种“远处的东西看上去小”的效果赋予了照片深度感或称透视感。我们的眼睛就是这样观察世界的。有趣的是孩童的绘画往往会忽视这一点。在正射投影的可视空间中不管三角形与视点的距离是远是近它有多大那么画出来就有多大。为了打破这条限制我们可以使用透视投影可视空间它将使场景具有图上图那样的深度感。本例PerspectiveView使用了一个透视投影可视空间视点在005视线沿着Z轴负方向。下图显示了程序的运行效果以及程序的场景中各三角形的位置。如上图右所示沿着Z轴负半轴也就是视线方向在轴的左右侧各依次排列着3个相同大小的三角形场景与本例第一张图中的道路和树木有一点相似。在使用透视投影矩阵后WebGL就能够自动将距离远的物体缩小显示从而产生上图左中的深度感。透视投影可视空间透视投影可视空间如下图所示。就像盒状可视空间那样透视投影可视空间也有视点、视线、近裁剪面和远裁剪面这样可视空间内的物体才会被显示可视空间外的物体则不会显示。那些跨越可视空间边界的物体则只会显示其在可视空间内的部分。可视空间构造效果图不论是透视投影可视空间还是盒状可视空间我们都用投影矩阵来表示它但是定义矩阵的参数不同。Matrix4对象的setPerspective方法可用来定义透视投影可视空间。WebGL矩阵变换库_山楂树の的博客-CSDN博客Matrix4.setPerspective定义了透视投影可视空间的矩阵被称为透视投影perspective projection matrix。注意第2个参数aspect是近裁剪面的宽高比而不是水平视角第1个参数是垂直视角。比如说如果近裁剪面的高度是100而宽度是200那么宽高比就是2。在本例中各个三角形与可视空间的相对位置如下图所示。我们指定了near1.0far100aspect1.0宽度等于高度与画面相同以及fov30.0。三角形与可视化空间的相对位置示例代码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 v_Color a_Color;\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); var gl getWebGLContext(canvas); if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) return; // 设置顶点坐标和颜色蓝色三角形在最前面 var n initVertexBuffers(gl); gl.clearColor(0, 0, 0, 1); var u_ViewMatrix gl.getUniformLocation(gl.program, u_ViewMatrix); var u_ProjMatrix gl.getUniformLocation(gl.program, u_ProjMatrix); var viewMatrix new Matrix4(); // 视图矩阵 var projMatrix new Matrix4(); // 模型矩阵 viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0); // 视点、被观察点、正方向 projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); // 视角顶底面夹角、近裁面宽高比、near、far // 将视图和投影矩阵传递给u_ViewMatrix、u_ProjectMatrix gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements); 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([ // 顶点坐标、颜色 0.75, 1.0, -4.0, 0.4, 1.0, 0.4, // 后面的绿色 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, // 中间的黄色 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, // 前面的蓝色 0.25, -1.0, 0.0, 0.4, 0.4, 1.0, 1.25, -1.0, 0.0, 1.0, 0.4, 0.4, // 左侧有三个三角形 -0.75, 1.0, -4.0, 0.4, 1.0, 0.4, // 后面的绿色 -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, // 中间的黄色 -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, // 前面的蓝色 -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; // 六个三角形18个顶点 var vertexColorbuffer gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorbuffer); 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); return n; }代码详解main函数中首先调用initVertexBuffers函数向缓冲区对象中写入这6个三角形的顶点坐标和颜色数据第26行在这6个三角形中右侧3个的数据从第44行开始左侧3个的数据从第54行开始。而且需要绘制的顶点的个数为18第75行6个三角形3×618。接着我们获取了着色器中视图矩阵和透视投影矩阵uniform变量的存储地址第28行和第29行并创建了两个对应的矩阵对象第30和第31行。然后我们计算了视图矩阵第32行视点设置在005视线为Z轴负方向上方向为Y轴正方向。最后我们按照金字塔状的可视空间建立了透视投影矩阵第33行。其中第2个参数aspect宽高比近裁剪面的宽度与高度的比值应当与canvas保持一致我们根据canvas的width和height属性来计算出该参数这样如果canvas的大小发生变化也不会导致显示出来的图形变形。接下来将准备好的视图矩阵和透视投影矩阵传给着色器中对应的uniform变量第35和第36行。最后将三角形绘制出来第38行就获得了如下图所示的效果。示例效果到目前为止还有一个重要的问题没有完全解释那就是矩阵为什么可以用来定义可视空间。接下来我们尽量避开其中复杂的数学过程稍做一些探讨。投影矩阵的作用首先看一下本例的运行效果如上图可以看到运用透视投影矩阵后场景中的三角形有了两处变化透视投影矩阵对物体进行了两次变换首先距离较远的三角形看上去变小了其次三角形被不同程度地平移以贴近中心线即视线使得它们看上去在视线的左右排成了两列。实际上如下图左所示这些三角形的大小是完全相同的透视投影矩阵对三角形进行了两次变换1根据三角形与视点的距离按比例对三角形进行了缩小变换2对三角形进行平移变换使其贴近视线如下图右所示。经过了这两次变换之后就产生了上图0那张照片中的深度效果。透视投影变换示意图这表明可视空间的规范对透视投影可视空间来说就是近、远裁剪面垂直视角宽高比可以用一系列基本变换如缩放、平移来定义。Matrix4对象的setPerspective方法自动地根据上述可视空间的参数计算出对应的变换矩阵。换一个角度来看透视投影矩阵实际上将金字塔状的可视空间变换为了盒状的可视空间这个盒状的可视空间又称规范立方体Canonical View Volume如上图右所示。注意正射投影矩阵不能产生深度感。正射投影矩阵的工作仅仅是将顶点从盒状的可视空间映射到规范立方体中。顶点着色器输出的顶点都必须在规范立方体中这样才会显示在屏幕上。