【笔记】《WebGL编程指南》学习-第7章进入三维世界(1-视点和视线)

来源:互联网 发布:js随机数 不重复 编辑:程序博客网 时间:2024/04/29 22:21

立方体由三角形构成

到目前位置,前几章的示例程序绘制了各种各样的三角形。之前讨论过,三维物体也是由二维图形组成的,如下图,12个三角形组成了一个立方体。

这里写图片描述

既然三维物体是由三角形组成的,那我们只需像前几章那样,逐个绘制组成物体的每个三角形,最终就可以绘制出整个三维物体了。但是,三维与二维还有一个显著区别:在绘制二维图形时,只需要考虑顶点的x 和 y 坐标,而绘制三维物体时,还得考虑它们的深度信息。那就开始吧,首先我们来研究一下如何定义三维世界的观察者:在什么地方、朝哪里看、视野有多宽、能看多远。为了简单起见,我们暂时不去绘制立方体,还是绘制几个简单的三角形,因为不管绘制立方体还是三角形,三维空间的规则是一样的。

视点和视线

三维物体与二维图形的最显著区别就是,三维物体具有深度,也就是Z轴。因此,你会遇到一些之前不曾考虑过的问题。事实上,我们最后还是得把三维场景绘制到二维屏幕上,即绘制观察者看到的世界,而观察者可以处在任意位置观察。为了定义一个观察者,你需要考虑以下两点:

  • 观察方向,即观察者自己在什么位置,在看场景的哪一个部分?
  • 可视距离,即观察者能够看多远?

我们将观察者所处的位置视为视点,从视点出发沿着观察方向的射线称作视线、本节将研究如何通过视点和视线来描述观察者。到下一节我们再来研究“观察者能看多远”的问题。

在 WebGL 系统中,默认情况下的视点处于原点(0, 0, 0),视线为Z轴负半轴。在这一节中,我们把视点从默认位置移动到另一个位置,以观察场景中的三角形。

我们来创建一个新的示例程序 LookAtTriangles。在程序中,视点位于(0.20. 0.25. 0.25),视线沿着原点(0, 0, 0)方向,可以看到原点附近有三个三角形,程序中的这三个三角形错落摆放,以帮助你理解三维场景中深度的概念。

这里写图片描述
这里写图片描述

三角形的颜色比之前的程序中的柔和了一些,这样看上去眼睛会比较舒服。

视点、观察目标点和上方向

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

这里写图片描述

视点:观察者所在的三维空间中位置,视线的起点。在接下来的几节中,视点坐标都用(eyeX, eyeY, eyeZ)表示。

观察目标点:被观察目标所在的点。视线从视点出发,穿过观察目标点并继续延伸。注意,观察目标点是一个点,而不是视线方向,只有同时知道观察目标点和视点,才能算出视线方向。观察目标点的坐标用(atX, atY, atZ)表示。

上方向:最终绘制在屏幕上的影像中的向上的方向。试想,如果仅仅确定了视点和观察点,观察者还是可能以视线为轴旋转的。所以,为了将观察者固定住,我们还需要指定上方向。上方向是具有3个分量的矢量,用(upX, upY, upZ)表示。

这里写图片描述

在 WebGL 中,我们可以用上述三个矢量创建一个视图矩阵,然后将该矩阵传给顶点着色器。试图矩阵可以表示观察者的状态,含有观察者的视点,观察目标点,上方向等信息。之所以被成为视图矩阵,是因为它最终影响了显示在屏幕上的视图,也就是观察者观察到的场景。 Matrix4.setLookAt()函数可以根据上述三个矢量:视点、观察点和上方向,来创建出视图矩阵。

这里写图片描述

在WebGL 中,观察者的默认状态应该是这样的:

  • 视点位于坐标系统原点(0, 0, 0)。
  • 视线为 Z 轴负方向,观察点为(0, 0, -1),上方向为为Y 轴正方向, 即(0, 1, 0)。

如果将上方向改为 X 轴正半轴方向(1, 0, 0),你将看到场景旋转了90度。

创建这样一个矩阵,你只需要简单地使用如下代码。

这里写图片描述

示例程序

我们修改了视点,然后绘制了3个三角形。

LookAtTriangles.js

//顶点着色器程序var VSHADER_SOURCE =    'attribute vec4 a_Position;'+    'attribute vec4 a_Color;'+    'uniform mat4 u_ViewMatrix;'+    'varying vec4 v_Color;'+    'void main(){'+    'gl_Position = u_ViewMatrix * a_Position;'+    'v_Color = a_Color;'+    '}';//片元着色器程序 var FSHADER_SOURCE=    '#ifdef GL_ES\n' +    'precision mediump float;\n' +    '#endif\n' +    'varying vec4 v_Color;' +    'void main() {'+   'gl_FragColor = v_Color;'+    '}';function main() {    //获取canvas元素    var canvas = document.getElementById("webgl");    if(!canvas){        console.log("Failed to retrieve the <canvas> element");        return;    }    //获取WebGL绘图上下文    var gl = getWebGLContext(canvas);    if(!gl){        console.log("Failed to get the rendering context for WebGL");        return;    }    //初始化着色器    if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){        console.log("Failed to initialize shaders.");        return;    }    //设置顶点位置    var n = initVertexBuffers(gl);    if (n < 0) {        console.log('Failed to set the positions of the vertices');        return;    }    //指定清空<canvas>颜色    gl.clearColor(0.0, 0.0, 0.0, 1.0);    //获取 u_ViewMatrix 变量的存储位置    var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');    if(u_ViewMatrix < 0){        console.log("Failed to get the storage location of u_ViewMatrix");        return;    }    //设置视点、视线和上方向    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();    if(!vertexColorBuffer){        console.log("Failed to create thie buffer object");        return -1;    }    //将缓冲区对象保存到目标上    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');    if(a_Position < 0){        console.log("Failed to get the storage location of a_Position");        return -1;    }    gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE*6, 0);    gl.enableVertexAttribArray(a_Position);    var a_Color = gl.getAttribLocation(gl.program, 'a_Color');    if(a_Color < 0){        console.log("Failed to get the storage location of a_Color");        return -1;    }    gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE*6, FSIZE*3);    gl.enableVertexAttribArray(a_Color);    gl.bindBuffer(gl.ARRAY_BUFFER, null);    return n;}

本例基于第5章中的 ColoredTriangle.js 改编。片元着色器、传入定点数据的方式等与 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 变量。

 //设置视点、视线和上方向    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);

JS部分的修改就到这里,下面来看着色器部分的修改:

//顶点着色器程序var VSHADER_SOURCE =    'attribute vec4 a_Position;'+    'attribute vec4 a_Color;'+    'uniform mat4 u_ViewMatrix;'+    'varying vec4 v_Color;'+    'void main(){'+    'gl_Position = u_ViewMatrix * a_Position;'+    'v_Color = a_Color;'+    '}';

与ColoredTriangle.js 相比,低昂点着色器有两处改动:定义 uniform 变量 u_viewMatrix;将视图矩阵与顶点坐标相乘再赋值给 gl_Position。看上去差不多,不是吗?那么这样的改动会怎样影响观察到的景象呢?接着来看。

LookAtTriangle.js 与 RotatedTriangle_Matrix.js

仔细观察示例中的顶点着色器,你会发现它和第4章的 RotatedTriangle_Matrix4.js 很像。后者在顶点着色器中创建了一个 Matrix4 类型的旋转矩阵对象,用它去旋转三角形。我们来回顾一下这个着色器:

//顶点着色器程序var VSHADER_SOURCE =    'attribute vec4 a_Position;'+    'uniform mat4 u_xformMatrix;'+    'void main(){'+        'gl_Position = a_Position * u_xformMatrix;'+    '}';

本例 LookAtTriangle.js 的顶点着色器程序如下所示:

var VSHADER_SOURCE =    'attribute vec4 a_Position;'+    'attribute vec4 a_Color;'+    'uniform mat4 u_ViewMatrix;'+    'varying vec4 v_Color;'+    'void main(){'+    'gl_Position = u_ViewMatrix * a_Position;'+    'v_Color = a_Color;'+    '}';

可见,后者与前者相比增加了 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)”的效果。视点移动的方向与被观察对象移动的方向正好相反。对于视点的旋转,也可以采用类似的方式。

“改变观察者的状态”与“对整个世界进行平移和旋转变换”,本质上是一样的,它们都可以用矩阵来描述。接下来,我们将从一个指定的视点来观察旋转后的三角形。

从指定视点观察旋转后的三角形

第4章 RotatedTriangle_Matrix 程序绘制了一个绕Z轴旋转一定角度后的三角形。本节将修改 LookAtTriangles 程序来绘制一个从指定位置看过去的旋转后的三角形。这时,我们需要两个矩阵:旋转矩阵和视图矩阵。首先有一个问题是,以怎样的顺序相乘这两个矩阵。

我们知道,矩阵乘以顶点坐标,得到的结果是顶点经过矩阵变换之后的新坐标。也就是说,用旋转矩阵乘以顶点坐标,就可以得到旋转后的顶点坐标。

用视同矩阵乘以顶点坐标会把顶点变换到合适的位置,使得观察者(以默认状态)观察新位置的顶点,就好像在观察处在视图矩阵描述的视点上观察观察原始顶点一样。现在要在某个视点处观察旋转后的三角形,我们需要先旋转三角形,然后从这个视点来观察他。换句话说,我们需要先对三角形进行旋转变换,再对旋转后的三角形进行与”移动视点“等效的变换。我们按照上述顺序相乘两个矩阵。具体看一下等式。

我们知道,如果想旋转图形,就需要用旋转矩阵乘以旋转前的顶点坐标:

<旋转后顶点坐标>=<旋转矩阵>x<原始顶点坐标>

用视图矩阵乘以旋转后的顶点坐标,就可以获得”从视点看上去“的旋转后的顶点坐标:

<”从顶点看上去“的旋转后顶点坐标>=<视图矩阵>x<旋转后顶点坐标>

将1个式子带入第2个,可得:

<”从顶点看上去“的旋转后顶点坐标>=<视图矩阵>x<旋转矩阵>x<原始顶点坐标>

除了旋转矩阵,你还可以使用平移、缩放等基本变换矩阵或它们的组合,这时矩阵被成为模型矩阵。这样,上式就可以写成:

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

示例程序在着色器中实现了该式。很简单,直接照着该式修改顶点着色器。修改后的 LookAtTriangles 程序实现了上述变换。

示例程序(LookAtRotatedTriangles.js)

LookAtTriangles.js

//顶点着色器程序var VSHADER_SOURCE =    'attribute vec4 a_Position;'+    'attribute vec4 a_Color;'+    'uniform mat4 u_ViewMatrix;'+    'uniform mat4 u_ModelMatrix;'+    'varying vec4 v_Color;'+    'void main(){'+    'gl_Position = u_ViewMatrix * u_ModelMatrix * a_Position;'+    'v_Color = a_Color;'+    '}';//片元着色器程序var FSHADER_SOURCE=    '#ifdef GL_ES\n' +    'precision mediump float;\n' +    '#endif\n' +    'varying vec4 v_Color;' +    'void main() {'+    'gl_FragColor = v_Color;'+    '}';function main() {    //获取canvas元素    var canvas = document.getElementById("webgl");    if(!canvas){        console.log("Failed to retrieve the <canvas> element");        return;    }    //获取WebGL绘图上下文    var gl = getWebGLContext(canvas);    if(!gl){        console.log("Failed to get the rendering context for WebGL");        return;    }    //初始化着色器    if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){        console.log("Failed to initialize shaders.");        return;    }    //设置顶点位置    var n = initVertexBuffers(gl);    if (n < 0) {        console.log('Failed to set the positions of the vertices');        return;    }    //指定清空<canvas>颜色    gl.clearColor(0.0, 0.0, 0.0, 1.0);    //获取 u_ViewMatrix 、u_ModelMatrix 变量的存储位置    var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');    if(u_ViewMatrix < 0){        console.log("Failed to get the storage location of u_ViewMatrix");        return;    }    var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');    if(u_ModelMatrix < 0){        console.log("Failed to get the storage location of u_ModelMatrix");        return;    }    //设置视点、视线和上方向    var viewMatrix = new Matrix4();    viewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0);    //计算旋转矩阵    var modelMatrix = new Matrix4();    modelMatrix.setRotate(-10, 0, 0, 1);    //将视图矩阵传递给 u_ViewMatrix 变量    gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);    gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.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();    if(!vertexColorBuffer){        console.log("Failed to create thie buffer object");        return -1;    }    //将缓冲区对象保存到目标上    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');    if(a_Position < 0){        console.log("Failed to get the storage location of a_Position");        return -1;    }    gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE*6, 0);    gl.enableVertexAttribArray(a_Position);    var a_Color = gl.getAttribLocation(gl.program, 'a_Color');    if(a_Color < 0){        console.log("Failed to get the storage location of a_Color");        return -1;    }    gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE*6, FSIZE*3);    gl.enableVertexAttribArray(a_Color);    gl.bindBuffer(gl.ARRAY_BUFFER, null);    return n;}

首先,顶点着色器中添加了 uniform 变量 u_ModelMatrix,该变量从 JS 中接受模型矩,以实现等式。

'gl_Position = u_ViewMatrix * u_ModelMatrix * a_Position;'+

JS 的 main()函数已经有了与视图矩阵相关的代码,只需要添加几行计算和传入旋转矩阵的代码,将三角形绕Z轴旋转10度。

运行示例程序,顶点坐标依次与旋转矩阵和视图矩阵相乘,最终获得了预期的效果。即先用 u_ModelMatrix 旋转三角形,再将旋转后的坐标用 u_ViewMatrix 变换到正确的位置,使其看上去就像是从指定观点出观察一样。

这里写图片描述

利用键盘改变视点

这一节将在LookAtTriangles 的基础上记性修改,使得当键盘上的方向键被按下时,观察者的视点也随之移动。在新程序 LookAtTrianglesWithKeys 中,如果右方向键被按下,视点 X 坐标将增大 0.01; 如果左方向键被按下,视点的 X 坐标将减少 0.01。

LookAtTrianglesWithKeys.js

//顶点着色器程序var VSHADER_SOURCE =    'attribute vec4 a_Position;'+    'attribute vec4 a_Color;'+    'uniform mat4 u_ViewMatrix;'+    'varying vec4 v_Color;'+    'void main(){'+    'gl_Position = u_ViewMatrix * a_Position;'+    'v_Color = a_Color;'+    '}';//片元着色器程序var FSHADER_SOURCE=    '#ifdef GL_ES\n' +    'precision mediump float;\n' +    '#endif\n' +    'varying vec4 v_Color;' +    'void main() {'+    'gl_FragColor = v_Color;'+    '}';function main() {    //获取canvas元素    var canvas = document.getElementById("webgl");    if(!canvas){        console.log("Failed to retrieve the <canvas> element");        return;    }    //获取WebGL绘图上下文    var gl = getWebGLContext(canvas);    if(!gl){        console.log("Failed to get the rendering context for WebGL");        return;    }    //初始化着色器    if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){        console.log("Failed to initialize shaders.");        return;    }    //设置顶点位置    var n = initVertexBuffers(gl);    if (n < 0) {        console.log('Failed to set the positions of the vertices');        return;    }    //指定清空<canvas>颜色    gl.clearColor(0.0, 0.0, 0.0, 1.0);    //获取 u_ViewMatrix 变量的存储位置    var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');    if(u_ViewMatrix < 0){        console.log("Failed to get the storage location of u_ViewMatrix");        return;    }    //设置视点、视线和上方向    var viewMatrix = new Matrix4();    // 注册键盘事件响应函数    document.onkeydown = function(ev){        keydown(ev, gl, n, u_ViewMatrix, viewMatrix);    };    draw(gl, n, u_ViewMatrix, viewMatrix);}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();    if(!vertexColorBuffer){        console.log("Failed to create thie buffer object");        return -1;    }    //将缓冲区对象保存到目标上    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');    if(a_Position < 0){        console.log("Failed to get the storage location of a_Position");        return -1;    }    gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE*6, 0);    gl.enableVertexAttribArray(a_Position);    var a_Color = gl.getAttribLocation(gl.program, 'a_Color');    if(a_Color < 0){        console.log("Failed to get the storage location of a_Color");        return -1;    }    gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE*6, FSIZE*3);    gl.enableVertexAttribArray(a_Color);    gl.bindBuffer(gl.ARRAY_BUFFER, null);    return n;}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);    //将视图矩阵传递给 u_ViewMatrix 变量    gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);    gl.clear(gl.COLOR_BUFFER_BIT);    gl.drawArrays(gl.TRIANGLES, 0, n);}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);}

在本例中,我们注册了键盘事件响应函数。每当左方向键或右方仙剑被按下时,就会改变视点的位置,然后调用 draw()函数重绘场景。在研究键盘事件响应函数前,先来看一下 draw()函数。

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 属性上去。我们定义了一个匿名函数作为键盘事件响应函数:

document.onkeydown = function(ev){        keydown(ev, gl, n, u_ViewMatrix, viewMatrix);    };

匿名函数调用了 keydown()函数,并传入了相关的参数。让我们来看一下 keydown()函数的实现。

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()函数绘制三角形。

运行程序,每当你按下左或右方向键时,三角形都会改变以下方向,实际上这时因为观察者的位置发生了变化。

这里写图片描述
这里写图片描述

阅读全文
0 0
原创粉丝点击