Строим 3D проекты – Шаг 4
Продолжим изучение основ низкоуровневого WebGL и усовершенствуем тестовую программу. Установим 3D движение за счет заметного изменения положения точек по оси Z в модели программы.
Анимация
Движение объекта, особенно вращение, позволяет понять его свойства, в частности, является ли он трехмерным.
Чтобы увидеть изменения положения объекта по оси Z, мы должны посмотреть на движение из точечной камеры. Для этого необходимо задать расположение камеры.
Изначально объект находится в центре координат, а объектив камеры – перпендикулярно плоскости XY. При этом, можно сказать, что объектив расфокусирован (вся моделируемая область прямоугольно проецируется на матрицу, равную размеру этой области). Это модельные координаты:
При переходе в мировые координаты, камера располагается в центре координат:
При переходе в видовые координаты, объектив камеры смотрит на объект:
Для преобразования координат XYZ их удобно записать в виде матрицы. Процесс трансформации матрицы координат из модельных в видовые, и далее в проекцию на экран называется модель-вид проекция, МВП (Model-View-Projection, MVP).
Проекция на экран может быть ортогональной или перспективной:
Для работы с матрицами координат лучше подключить библиотеку gl-matrix.js (скачать тут github.com/toji/gl-matrix). Скачиваем, подключаем в шапке программы:
<script src="js_std/gl-matrix-min.js"></script>
Подготовка программы к 3D анимации.
Заведем две новые переменные для хранения матриц модель-вид и проекции.
var mvMatrix = mat4.create(); //To store our model-view and projection matrices. var pMatrix = mat4.create();
И заведем в вершинном шейдере две униформы для приёма данных этих матриц:
<script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 aVertexPosition; attribute vec3 aVertexColor; uniform mat4 uMVMatrix; uniform mat4 uPMatrix; varying highp vec4 vColor; void main(void) { //gl_Position = vec4(aVertexPosition, 1.0); gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); vColor = vec4(aVertexColor, 1.0); } </script>
Теперь, каждая вершина vec4 (aVertexPosition, 1.0) в пространстве будет проецироваться на экран умножением произведения матриц на вектор uPMatrix * uMVMatrix * vec4 (умножение некоммутативно, то есть важен порядок умножения именно в такой последовательности) .
Для установки наших двух матриц mvMatrix и pMatrix, перепишем функцию setupWebGL:
function setupWebGL() { //The default value to set the color buffer. gl.clearColor(0, 0.5, 0.5, 1); gl.clear(gl.COLOR_BUFFER_BIT); gl.viewport(0, 0, canvas.width, canvas.height); mat4.identity(mvMatrix); mat4.translate(mvMatrix, [0, 0, -2.0]); mat4.rotate(mvMatrix, 0.50, [1.0, 0.0, 0.0]); mat4.rotate(mvMatrix, -0.50, [0.0, 1.0, 0.0]); mat4.perspective(45, canvas.width / canvas.height, 0.1, 100.0, pMatrix); }
Для mvMatrix в identity мы создаем единичную матрицу. В translate мы смещаем ее по оси Z умножением матрицы на вектор [0, 0, -2.0]. А в rotate поворачиваем относительно оси X на 0.5, а относительно оси Y на -0.5.
Для pMatrix в perspective мы задали для рабочей области угол обзора, соотношение ширины и высоты, переднюю и заднюю границы пространства, в котором помещается модель.
Далее, нам необходимо связать наши матрицы mvMatrix и pMatrix в JavaScript с униформами uPMatrix и uMVMatrix в шейдере. Для этого добавим в конец программы функцию getMatrixUniforms:
function getMatrixUniforms() { glProgram.mvMatrixUniform = gl.getUniformLocation(glProgram, "uMVMatrix"); glProgram.pMatrixUniform = gl.getUniformLocation(glProgram, "uPMatrix"); }
После отработки getMatrixUniforms для отправки матриц в шейдер, будем использовать еще одну функцию — setMatrixUniforms, ее тоже добавим в конец программы:
function setMatrixUniforms() { gl.uniformMatrix4fv( glProgram.mvMatrixUniform, false, mvMatrix); gl.uniformMatrix4fv( glProgram.pMatrixUniform, false, pMatrix); }
Настройка программы.
Все приготовления для работы с матрицами преобразования координат сделаны. Теперь перепишем основной цикл:
if(gl) { initShaders(); //[2] setupBuffers(); //[3].1 getMatrixUniforms(); (function animLoop() { setupWebGL(); //[1] setupDynamicBuffers(); //[3].2 setMatrixUniforms(); drawScene(); //[4] requestAnimationFrame( animLoop, canvas); })(); }
И будем теперь менять в цикле программы координату Z каждой вершины в setupDynamicBuffers:
//3D-Move. var triangleVertices = [ //Triangle's points coodinates. -0.5 , 0.5, -0.5 + x_delta, 0.0 , 0.0, -0.5 + x_delta, -0.5 , -0.5, -0.5 + x_delta ];
Наша камера теперь в виде точки в пространстве, поэтому мы различаем приближение и удаление треугольника-примитива.
На следующем шаге мы планируем нарисовать объемную фигуру, сделать динамическое вращение, добавить тест глубины для прорисовки только видимой части.