网站自助建站,学校网站的建立,网站手机网页如何做,个人网站seo目录
立方体由三角形构成
视点和视线
视点、观察目标点和上方向
视点#xff1a;
观察目标点#xff1a;
上方向#xff1a;
在WebGL中#xff0c;观察者的默认状态应该是这样的#xff1a;
视图矩阵程序#xff08;LookAtTriangles.js#xff09; 实际上…目录
立方体由三角形构成
视点和视线
视点、观察目标点和上方向
视点
观察目标点
上方向
在WebGL中观察者的默认状态应该是这样的
视图矩阵程序LookAtTriangles.js 实际上“根据自定义的观察者状态绘制观察者看到的景象”与“使用默认的观察状态但是对三维对象进行平移、旋转等变换再绘制观察者看到的景象”这两种行为是等价的。归根结底不论视图怎样变化无疑就是旋转、平移等操作最终本质上都是对物体进行相反方向的旋转平移默认的视角
移动视点和移动被观察对象等效
从指定视点观察旋转后的三角形
旋转后顶点坐标旋转矩阵×原始顶点坐标
“从视点看上去”的旋转后顶点坐标视图矩阵×旋转后顶点坐标
“从视点看上去”的旋转后顶点坐标视图矩阵×旋转矩阵×原始顶点坐标
视图矩阵×模型矩阵×原始顶点坐标
模型视图矩阵程序LookAtRotatedTriangles.js
模型视图矩阵
模型视图矩阵视图矩阵×模型矩阵
模型视图矩阵×顶点坐标 立方体由三角形构成
三维物体也是由二维图形特别是三角形组成的。如下图所示12个三角形组成了一个立方体。 既然三维物体是由三角形组成的那么则需要逐个绘制组成物体的每个三角形最终就可以绘制出整个三维物体了。但是三维与二维还有一个显著区别在绘制二维图形时只需要考虑顶点的x和y坐标而绘制三维物体时还得考虑它们的深度信息depth information。那就开始吧首先我们来研究一下如何定义三维世界的观察者在什么地方、朝哪里看、视野有多宽、能看多远。
视点和视线
三维物体与二维图形的最显著区别就是三维物体具有深度也就是Z轴。因此你会遇到一些之前不曾考虑过的问题。事实上我们最后还是得把三维场景绘制到二维的屏幕上即绘制观察者看到的世界而观察者可以处在任意位置观察。为了定义一个观察者你需要考虑以下两点 ● 观察方向即观察者自己在什么位置在看场景的哪一部分 ● 可视距离即观察者能够看多远 我们将观察者所处的位置称为视点eye point从视点出发沿着观察方向的射线称作视线viewing direction。本次将研究如何通过视点和视线来描述观察者。到下面我们再来研究“观察者能看多远”的问题。
我们来创建一个新的示例程序LookAtTriangles。在程序中视点位于0.200.250.25视线向着原点000方向可以看到原点附近有三个三角形。程序中的这三个三角形前后错落摆放以帮助你理解三维场景中深度的概念。下图显示了LookAtTriangles的运行结果。
视点、观察目标点和上方向
为了确定观察者的状态你需要获取两项信息视点即观察者的位置观察目标点look-at point即被观察目标所在的点它可以用来确定视线。此外因为我们最后要把观察到的景象绘制到屏幕上还需要知道上方向up direction。有了这三项信息就可以确定观察者的状态了。下面将逐一进行解释见下图。 视点 观察者所在的三维空间中位置视线的起点。在接下来的几节中视点坐标都用eyeXeyeYeyeZ表示。 观察目标点 被观察目标所在的点。视线从视点出发穿过观察目标点并继续延伸。注意观察目标点是一个点而不是视线方向只有同时知道观察目标点和视点才能算出视线方向。观察目标点的坐标用atXatYatZ表示。 上方向 最终绘制在屏幕上的影像中的向上的方向。试想如果仅仅确定了视点和观察点观察者还是可能以视线为轴旋转的如下图所示头部偏移会导致观察到的场景也偏移了。所以为了将观察者固定住我们还需要指定上方向。上方向是具有3个分量的矢量用upXupYupZ表示。 在WebGL中我们可以用上述三个矢量创建一个视图矩阵view matrix然后将该矩阵传给顶点着色器。视图矩阵可以表示观察者的状态含有观察者的视点、观察目标点、上方向等信息。之所以被称为视图矩阵是因为它最终影响了显示在屏幕上的视图也就是观察者观察到的场景。根据提供的Matrix4.setLookAt函数可以根据上述三个矢量视点、观察点和上方向来创建出视图矩阵 矩阵库 WebGL矩阵变换库_山楂树の的博客-CSDN博客。
在WebGL中观察者的默认状态应该是这样的
● 视点位于坐标系统原点000。
● 视线为Z轴负方向观察点为00-1
如果将上方向改为X轴正半轴方向100你将看到场景旋转了90度。
创建这样一个矩阵你只需要简单地使用如下代码见下图。 现在你已经了解了setLookAt函数下面来看示例程序的代码。
视图矩阵程序LookAtTriangles.js
程序显示了LookAtTriangles.js的代码我们修改了视点然后绘制了3个三角形如上图三角形所示。实际上这3个三角形的颜色分别为蓝色到红色、黄色到红色和绿色到红色的渐变色。
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 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)) {console.log(Failed to intialize shaders.);return;}// 设置顶点坐标和颜色蓝色三角形在最前面var n initVertexBuffers(gl);gl.clearColor(0, 0, 0, 1);// 获取u_ViewMatrix变量的存储地址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);// 将视图矩阵传给u_ViewMatrix变量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 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);gl.bindBuffer(gl.ARRAY_BUFFER, null);return n;
}首先来看一下initVertexBuffers函数第42行。verticesColors数组包含了3个三角形共计9个顶点的数据而且顶点坐标的z分量也不再是0了。接着我们创建了缓冲区对象第57行并将数组中的数据填了进去第5859行。此外我们还把gl.drawArrays的第3个参数改成了9第39行因为这里共有9个顶点。
然后需要建立视图矩阵包含了视点、视线和上方向信息并传给顶点着色器。为此我们先创建了一个Matrix4对象viewMatrix第33行然后用setLookAt方法将其设置为视图矩阵第34行最后将视图矩阵中的元素传给顶点着色器中的u_ViewMatrix变量第36行。 实际上“根据自定义的观察者状态绘制观察者看到的景象”与“使用默认的观察状态但是对三维对象进行平移、旋转等变换再绘制观察者看到的景象”这两种行为是等价的。归根结底不论视图怎样变化无疑就是旋转、平移等操作最终本质上都是对物体进行相反方向的旋转平移默认的视角
举个例子默认情况下视点在原点视线沿着Z轴负方向进行观察。假如我们将视点移动到001如下图左所示。这时视点与被观察的三角形在Z轴上的距离增加了1.0个单位。实际上如果我们使三角形沿Z轴负方向移动1.0个单位也可以达到同样的效果因为观察者看上去是一样的如下图右所示。
移动视点和移动被观察对象等效 事实上上述过程就发生在示例程序LookAtTriangles.js中。根据视点、观察点和上方向参数setLookAt方法计算出的视图矩阵恰恰就是“沿着Z轴负方向移动1.0个单位”的变换矩阵。所以把这个矩阵与顶点坐标相乘就相当于获得了“将视点设置在0.00.01.0”的效果。视点移动的方向与被观察对象也就是整个世界移动的方向正好相反。对于视点的旋转也可以采用类似的方式。
“改变观察者的状态”与“对整个世界进行平移和旋转变换”本质上是一样的它们都可以用矩阵来描述。接下来我们将从一个指定的视点来观察旋转后的三角形。
从指定视点观察旋转后的三角形
现在将修改LookAtTriangles程序来绘制一个从指定位置看过去的旋转后的三角形。这时我们需要两个矩阵旋转矩阵表示三角形的旋转和视图矩阵表示观察世界的方式。首先有一个问题是以怎样的顺序相乘这两个矩阵。
我们知道矩阵乘以顶点坐标得到的结果是顶点经过矩阵变换之后的新坐标。也就是说用旋转矩阵乘以顶点坐标就可以得到旋转后的顶点坐标。
用视图矩阵乘以顶点坐标会把顶点变换到合适的位置使得观察者以默认状态观察新位置的顶点就好像在观察者处在视图矩阵描述的视点上观察原始顶点一样。现在要在某个视点处观察旋转后的三角形我们需要先旋转三角形然后从这个视点来观察它。换句话说我们需要先对三角形进行旋转变换再对旋转后的三角形进行与“移动视点”等效的变换。我们按照上述顺序相乘两个矩阵。具体看一下等式。
我们知道如果想旋转图形就需要用旋转矩阵乘以旋转前的顶点坐标
旋转后顶点坐标旋转矩阵×原始顶点坐标
用视图矩阵乘以旋转后的顶点坐标就可以获得“从视点看上去”的旋转后的顶点坐标
“从视点看上去”的旋转后顶点坐标视图矩阵×旋转后顶点坐标
将第1个式子代入第2个可得
“从视点看上去”的旋转后顶点坐标视图矩阵×旋转矩阵×原始顶点坐标
除了旋转矩阵你还可以使用平移、缩放等基本变换矩阵或它们的组合这时矩阵被称为模型矩阵model matrix。这样上式就可以写成
视图矩阵×模型矩阵×原始顶点坐标
示例程序在着色器中实现了该式。很简单直接照着该式修改顶点着色器。修改后的LootAtRotatedTriangles程序实现了上述变换如下图所示。图中白色虚线为旋转前三角形的所在位置可以看到三角形确实被旋转过了。 模型视图矩阵程序LookAtRotatedTriangles.js
LookAtRotatedTriangles.js与LookAtTriangles.js相比只有几处小改动加入了uniform变量u_ModelMatrixJavaScript中的main函数将模型矩阵传给该变量。相应的代码如下所示。 首先顶点着色器中添加了uniform变量u_ModelMatrix第7行该变量从JavaScript中接收模型矩阵以实现等式视图矩阵×模型矩阵×原始顶点坐标。 JavaScript的main函数已经有了与视图矩阵相关的代码只需添加几行计算和传入旋转矩阵的代码将三角形绕Z轴旋转10度。第53行代码实现了获取u_ModelMatrix变量的存储地址第64行实现了创建一个新的矩阵对象modelMatrix调用Matrix.setRotate将其设为旋转矩阵第65行然后传给顶点着色器中的u_ModelMatrix第69行。
运行示例程序顶点坐标依次与旋转矩阵和视图矩阵相乘最终获得了预期的效果。如上图所示即先用u_ModelMatrix旋转三角形再将旋转后的坐标用u_ViewMatrix变换到正确的位置使其看上去就像是从指定视点处观察一样。
模型视图矩阵
在LookAtRotatedTriangle.js中着色器实现了式视图矩阵×模型矩阵×原始坐标。这样程序对每个顶点都要计算视图矩阵×模型矩阵。如果顶点数量很多这一步操作就会造成不必要的开销。这是因为无论对哪个顶点而言式视图矩阵×模型矩阵×原始坐标中的两个矩阵相乘的结果都是一样的。所以我们可以在JavaScript中事先把这两个矩阵相乘的结果计算出来再传给顶点着色器。这两个矩阵相乘得到的结果被称为模型视图矩阵model view matrix如下所示
模型视图矩阵视图矩阵×模型矩阵
式视图矩阵×模型矩阵×原始坐标可以重写为下式
模型视图矩阵×顶点坐标
新的示例程序LookAtRotatedTriangles_mvMatrix.js按照式模型视图矩阵×顶点坐标重写了LookAtRotatedTriangles中的代码如下所示。 顶点着色器中出现了新的uniform变量u_mvMatrix它参与了对gl_Position的计算第9行。顶点着色器执行的流程与最初的LookAtTriangles.js中一样除了uniform变量的名称。
在JavaScript代码中我们分别计算出了视图矩阵viewMatrix和模型矩阵modelMatrix第5963行就像在LookAtTriangle.js中一样。然后我们调用Matrix4.multiply方法使这两个矩阵相乘并将结果赋值给modelviewMatrix第66行。注意我们是在viewMatrix上调用了multiply方法并传入modelMatrix为参数所以这样做的结果实际上就是modelViewMatrixviewMatrixmodelMatrix。因为在JavaScript中我们不能像在GLSL ES中那样直接使用号来进行矩阵相乘而需要用矩阵库所提供的方法。
得到了modelViewMatrix后就将它传给着色器的u_ModelMatrix变量第69行。运行程序效果如下图所示。 最后还需要指出示例程序显式地把视图矩阵和模型矩阵单独计算出来再相乘为模型视图矩阵第5966行是为了更好地表达模型视图矩阵的由来。实际上只需要一行代码就可以计算出模型视图矩阵了计算出视图矩阵再将视图矩阵与模型矩阵(当前只有旋转)相乘