Строим 3D проекты – Шаг 3
Продолжим изучать основы низкоуровневого WebGL и совершенствовать тестовую программу. Установим цвет простейшего объекта, и зададим его движение в 2D по двум осям XY.
Добавляем цвет
Примитив-треугольник на прошлом шаге был белым. Изменим шейдеры так, чтобы можно было принимать в вершинный шейдер цвета вершин, и далее из него передавать цвета в пиксельный шейдер:
<script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 aVertexPosition; attribute vec3 aVertexColor; varying highp vec4 vColor; void main(void) { gl_Position = vec4(aVertexPosition, 1.0); vColor = vec4(aVertexColor, 1.0); } </script> <script id="shader-fs" type="x-shader/x-fragment"> varying highp vec4 vColor; void main(void) { //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); gl_FragColor = vColor; } </script>
Цвета мы будем передавать в aVertexColor, формировать на его основе vColor, а его использовать в пиксельном шейдере.
В функции setupBuffers мы аналогично вершинам добавим цвета вершин в массив triangleVerticeColors и создадим на его основе буфер trianglesColorBuffer, который будем передавать в шейдер в переменную-атрибут aVertexColor:
var triangleVerticeColors = [ 1.0, 0.0, 0.0, //Red 0.0, 1.0, 0.0, //Green 0.0, 0.0, 1.0 //Blue ]; trianglesColorBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, trianglesColorBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( triangleVerticeColors), gl.STATIC_DRAW);
Передачу цветов в шейдер пропишем в функции drawScene аналогично вершинам:
function drawScene() { vertexPositionAttribute = gl.getAttribLocation(glProgram, "aVertexPosition"); gl.enableVertexAttribArray( vertexPositionAttribute); gl.bindBuffer(gl.ARRAY_BUFFER, trianglesVerticeBuffer); gl.vertexAttribPointer( vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); vertexColorAttribute = gl.getAttribLocation(glProgram, "aVertexColor"); gl.enableVertexAttribArray( vertexColorAttribute); gl.bindBuffer(gl.ARRAY_BUFFER, trianglesColorBuffer); gl.vertexAttribPointer( vertexColorAttribute, 3, gl.FLOAT, false, 0, 0); gl.drawArrays(gl.TRIANGLES, 0, 3); }
В результате мы видим не только цвета вершин, но и градиентный переход цветов между вершинами:
Анимация объекта
Есть два варианта создания иллюзии движения – или движется объект, или движется камера вокруг объекта. Для начала попробуем двигать объект.
Переписываем главную программу с добавлением функции цикла animloop ():
if(gl) { initShaders(); //[2] setupBuffers(); //[3].1 (function animLoop() { setupWebGL(); //[1] setupDynamicBuffers(); //[3].2 drawScene(); //[4] requestAnimFrame( animLoop, canvas); })(); }
Наша функция animLoop будет вызывать себя бесконечно, пока работает программа.
2 Мы вынесли установку буфера вершин в отдельную функцию setupDynamicBuffers, в которой будем изменять его содержимое в процессе работы программы.
function setupDynamicBuffers() { //Limit translation amount to -0.5 to 0.5. var x_delta = Math.sin(angle_x) / 2.0; //Move. var triangleVertices = [ //Triangle's points coodinates. -0.5 + x_delta, 0.5, 0.0, 0.0 + x_delta, 0.0, 0.0, -0.5 + x_delta, -0.5, 0.0 ]; angle_x += 0.01; trianglesVerticeBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, trianglesVerticeBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.DYNAMIC_DRAW); }
Мы также поменяли STATIC_DRAW на DYNAMIC_DRAW.
И завели глобальную переменную var angle_x = 0; в файле vars.js
Мы можем таким образом менять координаты XY любой вершины. Осталось разобраться с осью Z, тогда мы сможем создать 3D вращение. Сейчас, если изменять Z вершин, то мы не заметим их движения, потому что прямоугольная проекция на экран не зависит от расстояния до вершин по оси Z, так как ось Z перпендикулярна нашему экрану.
//Rotate. var triangleVertices = [ //Triangle's points coodinates. -0.5 + x_delta, 0.5, 0.0, 0.0 - x_delta, 0.0, 0.0, -0.5 + x_delta, -0.5, 0.0 ];
Этот шаг небольшой, но повозится и разобраться придется, чтобы не задавать вопросы в дальнейшем. На следующем шаге мы переместим камеру, чтобы увидеть движение не только в 2D мире, но и в 3D.