建网站与发布网站,优秀网站管理员,徐州有哪些制作网站的公司吗,计算机专业主要学什么课程目录
鼠标控制物体旋转
如何实现物体旋转
示例程序#xff08;RotateObject.js#xff09;
代码详解
示例效果 鼠标控制物体旋转
有时候#xff0c;WebGL程序需要让用户通过鼠标操作三维物体。这一节来分析示例程序RotateObject#xff0c;该程序允许用户通过拖动RotateObject.js
代码详解
示例效果 鼠标控制物体旋转
有时候WebGL程序需要让用户通过鼠标操作三维物体。这一节来分析示例程序RotateObject该程序允许用户通过拖动即按住左键移动鼠标旋转三维物体。为了简单示例程序中的三维物体是一个立方体但拖曳鼠标旋转物体的方法却适用于所有物体。下图显示了程序的运行效果立方体上贴有纹理图像。 如何实现物体旋转
我们已经知道如何旋转二维图形或三维物体了就是使用模型视图投影矩阵来变换顶点的坐标。现在需要使用鼠标来控制物体旋转就需要根据鼠标的移动情况创建旋转矩阵更新模型视图投影矩阵并对物体的顶点坐标进行变换。
我们可以这样来实现在鼠标左键按下时记录鼠标的初始坐标然后在鼠标移动的时候用当前坐标减去初始坐标获得鼠标的位移然后根据这个位移来计算旋转矩阵。显然我们需要监听鼠标的移动事件并在事件响应函数中计算鼠标的位移、旋转矩阵从而旋转立方体。下面看一下示例程序。
示例程序RotateObject.js
如下显示了示例程序的代码如你所见着色器部分没什么特别的。顶点着色器使用模型视图投影矩阵变换顶点坐标第7行并向片元着色器传入纹理坐标以映射纹理第8行。
var VSHADER_SOURCE attribute vec4 a_Position;\n attribute vec2 a_TexCoord;\n uniform mat4 u_MvpMatrix;\n varying vec2 v_TexCoord;\n void main() {\n gl_Position u_MvpMatrix * a_Position;\n v_TexCoord a_TexCoord;\n }\n;
var FSHADER_SOURCE #ifdef GL_ES\n precision mediump float;\n #endif\n uniform sampler2D u_Sampler;\n varying vec2 v_TexCoord;\n void main() {\n gl_FragColor texture2D(u_Sampler, v_TexCoord);\n }\n;function main() {var canvas document.getElementById(webgl);var gl getWebGLContext(canvas);if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) returnvar n initVertexBuffers(gl);gl.clearColor(0.0, 0.0, 0.0, 1.0);gl.enable(gl.DEPTH_TEST);var u_MvpMatrix gl.getUniformLocation(gl.program, u_MvpMatrix);var viewProjMatrix new Matrix4();viewProjMatrix.setPerspective(30.0, canvas.width / canvas.height, 1.0, 100.0);viewProjMatrix.lookAt(3.0, 3.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);// 注册事件处理函数var currentAngle [0.0, 0.0]; // 绕z轴旋转角度绕y轴旋转角度initEventHandlers(canvas, currentAngle);if (!initTextures(gl)) return // 设置纹理var tick function() { // Start drawingdraw(gl, n, viewProjMatrix, u_MvpMatrix, currentAngle);requestAnimationFrame(tick, canvas);};tick();
}function initVertexBuffers(gl) {// v6----- v5// /| /|// v1------v0|// | | | |// | |v7---|-|v4// |/ |/// v2------v3var vertices new Float32Array([ // Vertex coordinates1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0,-1.0, 1.0, 1.0,-1.0, 1.0, // v0-v1-v2-v3 front1.0, 1.0, 1.0, 1.0,-1.0, 1.0, 1.0,-1.0,-1.0, 1.0, 1.0,-1.0, // v0-v3-v4-v5 right1.0, 1.0, 1.0, 1.0, 1.0,-1.0, -1.0, 1.0,-1.0, -1.0, 1.0, 1.0, // v0-v5-v6-v1 up-1.0, 1.0, 1.0, -1.0, 1.0,-1.0, -1.0,-1.0,-1.0, -1.0,-1.0, 1.0, // v1-v6-v7-v2 left-1.0,-1.0,-1.0, 1.0,-1.0,-1.0, 1.0,-1.0, 1.0, -1.0,-1.0, 1.0, // v7-v4-v3-v2 down1.0,-1.0,-1.0, -1.0,-1.0,-1.0, -1.0, 1.0,-1.0, 1.0, 1.0,-1.0 // v4-v7-v6-v5 back]);var texCoords new Float32Array([ // Texture coordinates1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v1-v2-v3 front0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, // v0-v3-v4-v5 right1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, // v0-v5-v6-v1 up1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v1-v6-v7-v2 left0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // v7-v4-v3-v2 down0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 // v4-v7-v6-v5 back]);// Indices of the verticesvar indices new Uint8Array([0, 1, 2, 0, 2, 3, // front4, 5, 6, 4, 6, 7, // right8, 9,10, 8,10,11, // up12,13,14, 12,14,15, // left16,17,18, 16,18,19, // down20,21,22, 20,22,23 // back]);var indexBuffer gl.createBuffer();if (!initArrayBuffer(gl, vertices, 3, gl.FLOAT, a_Position)) return -1; // Vertex coordinatesif (!initArrayBuffer(gl, texCoords, 2, gl.FLOAT, a_TexCoord)) return -1;// Texture coordinatesgl.bindBuffer(gl.ARRAY_BUFFER, null);gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);return indices.length;
}function initEventHandlers(canvas, currentAngle) {var dragging false; // 是否在拖动var lastX -1, lastY -1; // 鼠标的开始位置canvas.onmousedown function(ev) { // Mouse is pressedvar x ev.clientX, y ev.clientY;// 如果鼠标在canvas内就开始拖动var rect ev.target.getBoundingClientRect();if (rect.left x x rect.right rect.top y y rect.bottom) {lastX x; lastY y;dragging true;}};canvas.onmouseup function(ev) { dragging false; }; // Mouse is releasedcanvas.onmousemove function(ev) { // Mouse is movedvar x ev.clientX, y ev.clientY;if (dragging) {var factor 100/canvas.height; // The rotation ratiovar dx factor * (x - lastX);var dy factor * (y - lastY);// 将x轴旋转角度限制为-90到90度currentAngle[0] Math.max(Math.min(currentAngle[0] dy, 90.0), -90.0);currentAngle[1] currentAngle[1] dx; // 拿y轴举例鼠标水平移动物体会以Y轴旋转所以水平的移动距离直接影响y轴要转动角度}lastX x, lastY y;};
}var g_MvpMatrix new Matrix4(); // 模型视图投影矩阵
function draw(gl, n, viewProjMatrix, u_MvpMatrix, currentAngle) {// 计算模型视图投影矩阵并将其传递给u_MvpMatrixg_MvpMatrix.set(viewProjMatrix);g_MvpMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // 绕x轴旋转g_MvpMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // 绕y轴旋转gl.uniformMatrix4fv(u_MvpMatrix, false, g_MvpMatrix.elements);gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 清除颜色|深度缓冲gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); // 画画
}function initArrayBuffer(gl, data, num, type, attribute) {var buffer gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, buffer);gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);var a_attribute gl.getAttribLocation(gl.program, attribute);gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);gl.enableVertexAttribArray(a_attribute);return true;
}function initTextures(gl) {var texture gl.createTexture(); // 创建温丽丽对象var u_Sampler gl.getUniformLocation(gl.program, u_Sampler); // 获取uSampler的存储位置_Svar image new Image();image.onload function(){ loadTexture(gl, texture, u_Sampler, image); };image.src ../resources/sky.jpg;return true;
}function loadTexture(gl, texture, u_Sampler, image) {gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 翻转图像Y坐标// 激活纹理单元0gl.activeTexture(gl.TEXTURE0);// 将纹理对象绑定到2维目标先绑定到纹理单元gl.bindTexture(gl.TEXTURE_2D, texture);// 设置纹理参数gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);// 将图像设置为纹理设置图像参数gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);// 将纹理单元0传递到uSamplergl.uniform1i(u_Sampler, 0);
}
代码详解 首先main函数计算出了初始的模型视图投影矩阵第2931行。程序将根据鼠标位移来实时更新该矩阵。 然后鼠标移动事件响应函数实现了用鼠标旋转三维物体的逻辑。currentAngle变量表示当前的旋转角度它是一个数组因为物体的旋转需要被分解为绕x轴旋转和绕y轴旋转两步因此需要两个角度值第34行。真正注册事件响应函数的过程发生在initEventHandlers函数中第35行。真正绘制的过程发生在tick函数中第37行。 initEventHandler函数的任务是注册鼠标响应事件函数第87行包括鼠标左键按下事件第91行、鼠标左键松开事件第101行以及鼠标移动事件第103行。
当鼠标左键被按下时首先检查鼠标是否在canvas元素内部第95行如果是就将鼠标左键按下时的位置坐标保存到lastX和lastY变量中第96行并将dragging变量赋值为true表示拖曳操作即按住鼠标左键移动开始了。
鼠标左键被松开时表示拖曳操作结束了将dragging变量赋值为false第101行。
鼠标移动事件响应函数最为重要第103行首先检查dragging变量判断当前是否处于拖动状态。如果不在拖曳状态说明是鼠标的正常移动左键松开状态下的移动那就什么都不做。如果处于拖曳状态就计算出当前鼠标相对于上次鼠标移动事件触发时的移动距离即位移值并将结果保存在dx和dy变量中第107108行。注意位移值在存入变量前按比例缩小了这样dx和dy的值就与canvas自身的大小无关了。有了鼠标当前的位移dx和dy就可以根据这两个值计算出当前三维物体相对于上次鼠标移动事件触发时在x轴和y轴上的旋转角度值第110和111行。而且程序还将物体在y轴上的旋转角度限制在正负90度之间这样做的原因仅仅是为了展示技巧你也可以将其删掉。最后把当前鼠标的位置坐标赋值给lastX和lastY。 一旦成功地将鼠标的移动转化为旋转矩阵我们就可以用旋转矩阵更新物体的状态第121122行。当程序再次调用tick函数进行绘制时就绘制出了旋转后的物体。
示例效果