Вращение объекта
Для динамичности сцены добавим вращения нашему объекту. Для этого рассмотрим несколько матричных функций.
var mvMatStack = []; function mvPushMatrix(m) { if (m) { mvMatStack.push(m.dup()); mvMatrix = m.dup(); } else { mvMatStack.push(mvMatrix.dup()); } } function mvPopMatrix() { if (!mvMatStack.length) { throw('Стек пуст.'); } mvMatrix = mvMatStack.pop(); return mvMatrix; } function mvRotate(angle, v) { var inRadians = angle * Math.PI / 180.0; var m = Matrix.Rotation(inRadians, $V([v[0], v[1], v[2]])).ensure4x4(); multMatrix(m); }
Чтобы преобразование нашего объекта не влияло на другие объекты сцены, мы добавили функции mvPushMatrix() и mvPopMatrix(). mvPushMatrix() вызывается перед тем как мы начнём работать с объектом, а mvPopMatrix() вызывается после того как мы нарисуем наш объект. Функция mvRotate() будет вращать наш объект относительно осей, которые передаются в вектор v.
Посмотрим, как изменится функция drawScene().
function drawScene(delta) { r = r + 0.005; gl.clearColor(r, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); perspectiveMatrix = makePerspective(45, 600.0/400.0, 0.1, 100.0); loadIdentity(); mvPushMatrix(); mvTranslate([-0.0, 0.0, -6.0]); mvRotate(sqrRot, [0, 0, 1]); gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); setMatrixUniforms(); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); mvPopMatrix(); sqrRot -= delta * 10; }
Переменная sqrRot содержит значение угла поворота, на который мы поворачиваем наш объект. В конце функции drawScene() мы меняем этот угол на 10 градусов умноженных на время рендеринга кадра. Ниже представлен полный код и результат работы программы.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title> WebGL </title> <script src="sylvester.js" type="text/javascript"></script> <script type="text/javascript" src="glUtils.js"></script> <script id="shader-fs" type="x-shader/x-fragment"> void main(void) { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); } </script> <script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 aVertexPosition; uniform mat4 uMVMatrix; uniform mat4 uPMatrix; void main(void) { gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); } </script> <script> var gl; function start() { var canvas = document.getElementById("glcanvas"); gl = initWebGL(canvas); if (gl) { gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT); initShaders(); initBuffers(); r = 0.0; sqrRot = 0.0; requestAnimationFrame(render); } } var then = 0; function render(now) { now *= 0.001; const deltaTime = now - then; then = now; drawScene(deltaTime); requestAnimationFrame(render); } function initWebGL(canvas) { gl = null; try { gl = canvas.getContext("webgl2") ; } catch(e) {} if (!gl) { alert("Unable to initialize WebGL. Your browser may not support it."); gl = null; } return gl; } function initShaders() { var fragmentShader = getShader(gl, "shader-fs"); var vertexShader = getShader(gl, "shader-vs"); shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Не получилось инициилизировать шейдерную программу."); } gl.useProgram(shaderProgram); vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(vertexPositionAttribute); } function getShader(gl, id) { var shaderScript, theSource, currentChild, shader; shaderScript = document.getElementById(id); if (!shaderScript) { return null; } theSource = ""; currentChild = shaderScript.firstChild; while(currentChild) { if (currentChild.nodeType == currentChild.TEXT_NODE) { theSource += currentChild.textContent; } currentChild = currentChild.nextSibling; } if (shaderScript.type == "x-shader/x-fragment") { shader = gl.createShader(gl.FRAGMENT_SHADER); } else if (shaderScript.type == "x-shader/x-vertex") { shader = gl.createShader(gl.VERTEX_SHADER); } else { return null; } gl.shaderSource(shader, theSource); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert("Ошибка компиляции шейдера: " + gl.getShaderInfoLog(shader)); return null; } return shader; } function initBuffers() { squareVerticesBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); var vertices = [ 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); } function drawScene(delta) { r = r + 0.005; gl.clearColor(r, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); perspectiveMatrix = makePerspective(45, 640.0/480.0, 0.1, 100.0); loadIdentity(); mvPushMatrix(); mvTranslate([-0.0, 0.0, -6.0]); mvRotate(sqrRot, [0, 0, 1]); gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); setMatrixUniforms(); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); mvPopMatrix(); sqrRot -= delta * 10; } function loadIdentity() { mvMatrix = Matrix.I(4); } function multMatrix(m) { mvMatrix = mvMatrix.x(m); } function mvTranslate(v) { multMatrix(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4()); } function setMatrixUniforms() { var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); gl.uniformMatrix4fv(pUniform, false, new Float32Array(perspectiveMatrix.flatten())); var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten())); } var mvMatStack = []; function mvPushMatrix(m) { if (m) { mvMatStack.push(m.dup()); mvMatrix = m.dup(); } else { mvMatStack.push(mvMatrix.dup()); } } function mvPopMatrix() { if (!mvMatStack.length) { throw('Стек пуст.'); } mvMatrix = mvMatStack.pop(); return mvMatrix; } function mvRotate(angle, v) { var inRadians = angle * Math.PI / 180.0; var m = Matrix.Rotation(inRadians, $V([v[0], v[1], v[2]])).ensure4x4(); multMatrix(m); } </script </head> <body onload="start()"> <canvas id="glcanvas" width="600" height="400"> Используйте браузер который поддерживает "canvas" </canvas> </body> </html>