Оживление объектов при помощи WebGL. Библиотеки для работы с WebGL

03.04.2019

WebGL технологии открывают для разработчиков целый мир возможностей, которые можно реализовать прямо в окне браузера. Сегодня я хочу представить Вам 9 крутых примеров, которые Вам точно понравятся и Вы их ни в коем случае не должны пропустить!

WebGL вода

Это имитация воды, которая сделана с помощью WebGL технологий. Вы можете поднять белый мяч и бросить его обратно в воду. Таким образом можно наблюдать на сколько реалистичны круги на воде и брызги от мяча.

Белые квадраты — Fizzy Cam

Это приложение использует веб — камеру на Вашем устройстве. Таким образом вырисовывается картина которую видит камера из множество блоков, которые находятся на странице, довольно очень интересно смотрится.

Листья

Реалистичные листья которые подхватывает и уносит вдаль ветер. Вроде бы просто всё смотрится, но узнавая, что это реализовано в веб браузере, становится довольно интересно и необычно.

Научи меня летать

Мини игра, которая сделан в веб браузере на WebGL технологиях. В прочем очень просто, и сделано для того чтобы показать, на что способны новейшие технологии. Очень круто.

Большой глаз

В этом демо показан большой глаз, который следит за курсором. А если у Вас ещё и играет музыка, он пульсирует и изменяет цвета. Немного приводит в шок:) но и впечатляет не меньше.

Частицы

Крутая анимация частиц в веб — браузере, смотрится очень красиво и необычно. Также Вы можете добавить количество частиц и покрутить анимацию.

Глобус

Смотрится необычно и очень красиво, плюс присутствует анимация. Как будто из земного шара торчат иголки указывая нам на разнообразные события.

Наш код оставшийся от предыдущего примера уже насроен на перерисовку нашей WebGL сцены каждые 15 миллисекунд. До сих пор мы просто отрисовывали в точности одно и то же каждый раз. Пришло время изменить это так, чтобы наш квадрат начал двигаться.

В этом примере мы заставим наш двумерный квадрат вращаться и двигаться во всех трёх направлениях. Это докажет, что не смотря на то, что мы создавали всего-лишь двумерный объект, он всё ещё существует в трёхмерном пространстве.

Заставим квадрат вращаться

Давайте начнём с того, что заставим квадрат вращаться. Для начала нам понадобится переменная значение в которой будет отражать текущий угол поворота нашего квадрата:

Var squareRotation = 0.0;

Теперь нам надо изменить процедуру drawScene() и добавить в неё код, который бы учитывал этот угол поворота при отрисовке квадрата в нашей сцене. После первоначального рассчёта матрицы положения для квадрата мы применяем условия поворота следующим образом:

MvPushMatrix(); mvRotate(squareRotation, );

Этот код сохраняет матрицу текущего положения, затем поворачивает матрицу на текущее значение переменной squareRotation вокруг осей X и Z.

После отрисовки мы восстанавливаем изначальное состояние матрицы положения:

MvPopMatrix();

Мы сохраняем и восстанавливаем состояние матрицы положения чтобы избежать влияние поворота на другие объекты, которые мы можем захотеть отобразить.

Чтобы заставить квадрат крутиться нам нужно добавить в программу код, который будет изменять значение переменной squareRotation во времени. Для этого мы заведём ещё одну переменную в которую будем записывать время последней отрисовки (давайте назовём её lastSquareUpdateTime), затем добавим следующий код в конец нашей процедуры drawScene() :

Var currentTime = (new Date).getTime(); if (lastSquareUpdateTime) { var delta = currentTime - lastSquareUpdateTime; squareRotation += (30 * delta) / 1000.0; } lastSquareUpdateTime = currentTime;

Этот код использует количество времени прошедшего с момента, когда мы в последний раз изменяли значение переменной squareRotation чтобы определить как сильно нужно повернуть квадрат.

Сделаем так, чтобы квадрат начал двигаться

Мы можем также заставить наш квадрат двигаться изменяя его позицию перед отрисовкой. Этим примером мы собираемся продемонстрировать самые основные движения, очевидно, что в реальном мире вы бы сделали что-нибудь менее безумное.

Давайте отслеживать сдвиг положения квадрата по каждой оси в новых переменных:

Var squareXOffset = 0.0; var squareYOffset = 0.0; var squareZOffset = 0.0;

И расстояние на которое нужно сместить квадрат по каждой из осей в следующих переменных:

Var xIncValue = 0.2; var yIncValue = -0.4; var zIncValue = 0.3;

Теперь мы можем просто добавить следующий код к уже написанному, который обновляет положение угла поворота:

SquareXOffset += xIncValue * ((30 * delta) / 1000.0); squareYOffset += yIncValue * ((30 * delta) / 1000.0); squareZOffset += zIncValue * ((30 * delta) / 1000.0); if (Math.abs(squareYOffset) > 2.5) { xIncValue = -xIncValue; yIncValue = -yIncValue; zIncValue = -zIncValue; }

И, наконец, мы добавляем в процедуру drawScene() следующий код:

MvTranslate();

Это заставит наш квадрат двигаться по экрану, приближаясь и удаляясь от наблюдателя и при этом ещё и вращаться. Это похоже на работу заставки

Если ваш браузер поддерживает работу с WebGL, в действии.

Больше операций с матрицами

Этот пример использует несколько дополнительных операций над матрицами включая две утилиты для работы со стэком состояния матриц и одну, поворачивающую матрицу положения на заданное количество градусов. Далее приведён код для вашего удобства:

Var mvMatrixStack = ; function mvPushMatrix(m) { if (m) { mvMatrixStack.push(m.dup()); mvMatrix = m.dup(); } else { mvMatrixStack.push(mvMatrix.dup()); } } function mvPopMatrix() { if (!mvMatrixStack.length) { throw("Can"t pop from an empty matrix stack."); } mvMatrix = mvMatrixStack.pop(); return mvMatrix; } function mvRotate(angle, v) { var inRadians = angle * Math.PI / 180.0; var m = Matrix.Rotation(inRadians, $V()).ensure4x4(); multMatrix(m); }

Эти утилиты были заимствованы из примера разработанного Владом Вукицевичем (Vlad Vukićević).

Почти все современные компьютеры и большинство смартфонов имеют мощные графические процессоры, чаще всего даже мощнее, чем основные процессоры. Но до недавнего времени вся эта мощь не использовалась в веб-страницах. Причина проста: не было нормальных графических библиотек, низкая скорость передачи данных и низкое качество графики, в основном используемой только в 2D .

Но все изменилось с выходом WebGL в последних версиях Firefox и Chrome . API основан на хорошо известном графическом стандарте , дающим доступ через JavaScript плагины к графическому аппаратному обеспечению. Сама 3D графика выводится с помощью элемента canvas из HTML5 .

Так как в Apple следят за стандартами, то ожидаем выхода Safari с поддержкой WebGL . В компании Opera уже тестируют свою собственную версию, ну и как всегда тормозит Microsoft .

И так, если у вас есть последний браузер (Chrome или Firefox ), вы можете посмотреть подборку лучших WebGL сайтов.

1. ROME: "3 Dreams of Black"

— это тот же самый YouTube , только для 3D видео. По умолчанию сервис показывает видео в 2D , но если нажать кнопку Current 3D mode , то можно попробовать различные режимы 3D , в том числе и красно-синий «анаглиф».


18. three.js

И последние три сайта о том, как создавать свои WebGL страницы. У WebGL очень урезанный программный интерфейс, что делает его намного проще во внедрении для создатели браузеров, но для веб-разработчиков это настоящий кошмар, особенно для тех, кто впервые столкнулся с этой технологией.

Большое количество времени было потрачено на создание фреймворков, которые бы облегчили жизнь программистам сайтов, и самый известный из них — это three.js, созданный человеком Mr.doob .

Этот фреймворк использовался при создании "3 Dreams of Black" (в котором Mr.doob был техническим директором), и сейчас это самый популярный выбор веб-программистов, изучающих 3D



Идея данной статьи родилась после нескольких мучительных недель изучения WebGL. На личном примере выяснилось, что люди, не сталкивающиеся до этого с 3D-графикой, имеют в основном ошибочные представления о работе данной технологии. К тому же была проблема с поиском информации в интернете.

WebGL, в отличие от Javascript, имеет высокий порог вхождения, его до сих пор мало кто использует, а ещё меньше тех, кто об этом пишет. Большинство руководств или статей перепрыгивают сразу на использование какой-нибудь библиотеки. Но мы-то с вами знаем, что большие универсальные инструменты не всегда пригодны для наших задач или, возможно, делают это на неприемлемом уровне: проигрывают в скорости, поставляются с ненужным багажом и т.д.

Этой статьёй хочется облегчить порог вхождения в чистый WebGL, дать начальное представление и подсказать, куда двигаться дальше.

Технология WebGL позволяет рисовать графику в браузере, используя возможности видеокарты, тогда как раньше мы могли использовать только процессор. Если вы не понимаете, что это даёт, советую посмотреть эту небольшую демонстрацию.

WebGL основан на OpenGL ES 2.0, который, в свою очередь, является специальной версией для работы на мобильных устройствах. Спецификация WebGL была выпущена в 2011 году, разрабатывается и поддерживается некоммерческой организацией Kronos Group, сайт которой частенько лежит, что ещё более усложняет изучение. Известно, что в настоящее время идёт разработка спецификации версии 2.0.


Статистика поддержки WebGL разными браузерами с сайта caniuse.com

WebGL доступен в большинстве современных браузеров и поддерживается у 83% пользователей. Приятным бонусом разработки на WebGL является то, что вы будете поддерживать только современные браузеры и забудете о кошмарах ECMAScript 3.

Если вы думаете, что WebGL рисует 3D, вы ошибаетесь. WebGL ничего не знает о 3D, это скорее низкоуровневый 2D API, и всё что он умеет делать, это рисовать треугольники. Но он умеет рисовать их очень много и очень быстро.

Хотите нарисовать квадрат? Пожалуйста, соедините два треугольника. Нужна линия? Без проблем, всего лишь несколько последовательно соединенных треугольников.

Как нарисовать треугольник

Поскольку все фигуры в WebGL состоят из треугольников, поэтапно разберём, как отобразить один треугольник.

В отличие от OpenGL, в WebGL для отрисовки используются только шейдеры. Шейдеры никак не связаны, как вы могли бы подумать, с тенями или затенениями. Возможно, задумывались они именно для этого, но теперь используются для рисования всего и вся повсеместно.

Шейдер - это программа, выполняемая на видеокарте и использующая язык GLSL. Этот язык достаточно простой, и его изучение не представляет проблемы.

Всего есть два вида шейдеров: вершинный и фрагментый, и для отрисовки абсолютно любой фигуры всегда используются оба. Разберёмся с каждым по очереди.

Чтобы понять суть работы вершинного шейдера, абстрагируемся от задачи с треугольником и предположим, что вы хотите нарисовать куб или любую другую фигуру со множеством вершин. Для этого вам нужно задать её геометрию, а геометрия в свою очередь задаётся с помощью указания координат вершин. Было бы накладно самим каждый раз вычислять новые координаты всех вершин при изменении положения куба в пространстве. Такую работу лучше переложить с процессора на видеокарту, для этого и существует вершинный шейдер.

В него передаются координаты вершин фигуры и положение локальной системы координат, в которой эти вершины заданы. Вершинный шейдер вызывается для каждой из вершин, он вычисляет их положение в глобальной системе координат и передаёт дальше для работы фрагментного шейдера.

Вершинный шейдер всегда вычисляет положение вершин, но попутно он может выполнять и другую работу, например, подсчёт угла падения света. Энтузиасты делают потрясающие вещи , используя возможности вершинных шейдеров.

Знания положения фигуры недостаточно, чтобы её нарисовать. Необходима также информация о том, как должна быть раскрашена фигура, для этого служит фрагментный шейдер. Он вызывается для каждой точки поверхности фигуры и на основе переданной информации вычисляет цвет пикселя на экране.


Если вершинный шейдер определяет геометрию фигуры, то фрагментный - её цвет

Как уже было сказано выше, код шейдеров пишется на языке GLSL. Рассмотрим код шейдеров для треугольника:

Пример вершинного шейдера:
attribute vec3 a_position; attribute vec3 a_color; uniform vec3 u_position; varying vec3 v_color; void main(void) { v_color = a_color; gl_Position = vec4(u_position + a_position, 1.0); }
Пример фрагментного шейдера:
precision mediump float; varying vec3 v_сolor; void main(void) { gl_FragColor = vec4(v_color.rgb, 1.0); }
Код состоит из переменных и главной функции, возвращающей основной результат работы шейдера: gl_Position передаёт координаты, а gl_FragColor устанавливает цвет.

Шейдеры имеют три типа переменных, которые передаются из основной программы:

  1. attributes - доступны только в вершинном шейдере, разные для каждой из вершин;
  2. uniforms - доступны в обоих шейдерах и одинаковы для всех вызовов шейдера;
  3. varying - служат для передачи информации от вершинного шейдера к фрагментному.
При вызове фрагментого шейдера для конкретной точки, значения varying переменных линейно интерполируются между вершинами треугольника, которому принадлежит данная точка.


Значения varying переменных внутри треугольника вычисляются
на основе значений этих переменных в вершинах

Попробуем инициализировать данные шейдеры. Для начала получим контекст WebGL:
var gl = canvas.getContext(‘webgl’);
Код шейдеров представляется обычной строкой и для использования его нужно скомпилировать:
var vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, document.getElementById("vertexShader").text); gl.compileShader(vertexShader); var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, document.getElementById("fragmentShader").text); gl.compileShader(fragmentShader);
Для связывания двух типов шейдеров вместе используется шейдерная программа:
var program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program);
Если uniform-переменные связываются напрямую с переменными из js, то для атрибутов нужно использовать ещё одну сущность - буферы. Данные буферов хранятся в памяти видеокарты, что даёт значительный прирост в скорости рендеринга.

В нашем случае нам понадобятся:

  1. буфер вершин, который хранит всю информацию о расположению вершин геометрии;
  2. буфер цветов с информацией о цвете вершин.
Зададим буфер вершин:

Геометрия нашего треугольника

Вершины имеют координаты:

  • (0, 0, 0);
  • (0.5, 1, 0);
  • (1, 0, 0).
Стоит отметить, что при работе с буферами следует учитывать несколько особенностей:
  1. данные в буфер передаются одним массивом без вложенности, в случае нашего треугольника данные будут выглядеть следующим образом: ;
  2. передаваться должны только типизированные массивы ;
  3. прежде чем передать данные, вы должны точно указать, какой буфер будет использоваться, методом gl.bindBuffer.
Как это будет выглядеть в программе:
var vertexBuffer = gl.createBuffer(); var vertices = ; gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
Создадим аналогичным образом буфер цветов. Цвет указываем для каждой из вершин в формате RGB, где каждая компонента цвета от 0 до 1:
var colorBuffer = gl.createBuffer(); var colors = ; gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
Всё, что нам осталось, чтобы нарисовать треугольник, - это связать данные с переменными шейдерной программы и вызвать методы отрисовки. Для этого:
// Получим местоположение переменных в программе шейдеров var uPosition = gl.getUniformLocation(program, "u_position"); var aPosition = gl.getAttribLocation(program, "a_position"); var aColor = gl.getAttribLocation(program, "a_color"); // Укажем какую шейдерную программу мы намерены далее использовать gl.useProgram(program); // Передаем в uniform-переменную положение треугольника gl.uniform3fv(uPosition, ); // Связываем данные цветов gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); gl.enableVertexAttribArray(aColor); // Вторым аргументом передаём размерность, RGB имеет 3 компоненты gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0); // И вершин gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.enableVertexAttribArray(aPosition); gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0); // Очищаем сцену, закрашивая её в белый цвет gl.clearColor(1.0, 1.0, 1.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); // Рисуем треугольник // Третьим аргументом передаём количество вершин геометрии gl.drawArrays(gl.TRIANGLES, 0, 3);
Наш треугольник готов:

Полный код примера можно
посмотреть

Как я и говорил, цвет пикселей внутри треугольника линейно интерполируется между разноцветными вершинами. Мы смогли нарисовать самую простейшую фигуру с помощью WebGL и познакомились с шейдерами и буферами. Перейдём к следующему этапу.

Как нарисовать куб и заставить его вращаться

Попробуем усложнить задачу и нарисуем трёхмерный вращающийся куб. Куб будет состоять из шести граней, в каждой по два треугольника:

Нам придётся прописать каждую вершину каждого треугольника. Есть способы использовать более короткую запись, но для начала сделаем по-простому:

Это - просто

var vertexBuffer = gl.createBuffer(); var vertices = [ // Передняя грань -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, // Задняя грань -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, // Нижняя грань -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, // Верхняя грань -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, // Левая грань -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, // Правая грань 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1 ]; gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);


Аналогично запишем буфер цветов, раскрасив грани куба в три цвета:
  1. (1, 0.5, 0.5)
  2. (0.5, 0.7, 1)
  3. (0.3, 1, 0.3)

Тоже простой пример

var colorBuffer = gl.createBuffer(); var colors = [ // Передняя грань 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, // Задняя грань 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, // Нижняя грань 0.5, 0.7, 1, 0.5, 0.7, 1, 0.5, 0.7, 1, 0.5, 0.7, 1, 0.5, 0.7, 1, 0.5, 0.7, 1, // Верхняя грань 0.5, 0.7, 1, 0.5, 0.7, 1, 0.5, 0.7, 1, 0.5, 0.7, 1, 0.5, 0.7, 1, 0.5, 0.7, 1, // Левая грань 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, // Правая грань 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3, 0.3, 1, 0.3 ]; gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);


Положение треугольника в пространстве задавалось с помощью вектора размерности три. Но фигура может не только менять положение, она может ещё вращаться и масштабироваться. Поэтому в трёхмерной графике используются не вектор положения, а матрица.

Известно, что матрица поворота в трёхмерном пространстве задаётся с помощью матрицы размером 3?3. К этой матрице добавляется вектор положения, таким образом, в итоге используется матрица 4?4.

WebGL никак не помогает нам работать с матрицами, поэтому, чтобы не тратить на них много времени, будем использовать довольно известную библиотеку glMatrix . Создадим с помощью неё единичную матрицу положения:
var cubeMatrix = mat4.create();
Чтобы отрисовать трёхмерный объект, нам нужно ввести понятие камеры. Камера, как и любой объект, имеет своё положение в пространстве. Она также определяет, какие объекты будут видны на экране, и отвечает за преобразование фигур так, чтобы на экране у нас создалась иллюзия 3D.


Перспектива куба на экране

За это преобразование отвечает матрица перспективы. C glMatrix она создаётся в две строчки:
var cameraMatrix = mat4.create(); mat4.perspective(cameraMatrix, 0.785, window.innerWidth / window.innerHeight, 0.1, 1000);
Метод mat4.perspective(matrix, fov, aspect, near, far) принимает пять параметров:

  1. matrix - матрица, которую необходимо изменить;
  2. fov - угл обзора в радианах;
  3. aspect - cоотношение сторон экрана;
  4. near - минимальное расстояние до объектов, которые будут видны;
  5. far - максимальное расстояние до объектов, которые будут видны.
Чтобы изображение куба попало в камеру, сдвинем камеру по оси Z:
mat4.translate(cameraMatrix, cameraMatrix, );
В отличие от треугольника, в шейдерах для куба дополнительно используется матрица положения и матрица камеры:

Вершинный шейдер:
attribute vec3 a_position; attribute vec3 a_color; uniform mat4 u_cube; uniform mat4 u_camera; varying vec3 v_color; void main(void) { v_color = a_color; gl_Position = u_camera * u_cube * vec4(a_position, 1.0); }
Фрагментый шейдер:
precision mediump float; varying vec3 v_color; void main(void) { gl_FragColor = vec4(v_color.rgb, 1.0); }
Инициализация шейдеров происходит точно так же, как и в случае треугольника:
var vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, document.getElementById("vertexShader").text); gl.compileShader(vertexShader); var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, document.getElementById("fragmentShader").text); gl.compileShader(fragmentShader); var program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); var uCube = gl.getUniformLocation(program, "u_cube"); var uCamera = gl.getUniformLocation(program, "u_camera"); var aPosition = gl.getAttribLocation(program, "a_position"); var aColor = gl.getAttribLocation(program, "a_color");
Чтобы куб не стоял на месте, а вращался, необходимо постоянно менять его положение и обновлять кадр. Обновление происходит по средствам вызова встроенной функции requestAnimationFrame.

В отличие от других подобных методов, requestAnimationFrame вызывает переданную функцию только когда видеокарта свободна и готова к отрисовке следующего кадра.
// Создадим единичную матрицу положения куба var cubeMatrix = mat4.create(); // Запомним время последней отрисовки кадра var lastRenderTime = Date.now(); function render() { // Запрашиваем рендеринг на следующий кадр requestAnimationFrame(render); // Получаем время прошедшее с прошлого кадра var time = Date.now(); var dt = lastRenderTime - time; // Вращаем куб относительно оси Y mat4.rotateY(cubeMatrix, cubeMatrix, dt / 1000); // Вращаем куб относительно оси Z mat4.rotateZ(cubeMatrix, cubeMatrix, dt / 1000); // Очищаем сцену, закрашивая её в белый цвет gl.clearColor(1.0, 1.0, 1.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Включаем фильтр глубины gl.enable(gl.DEPTH_TEST); gl.useProgram(program); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.enableVertexAttribArray(aPosition); gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); gl.enableVertexAttribArray(aColor); gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0); gl.uniformMatrix4fv(uCube, false, cubeMatrix); gl.uniformMatrix4fv(uCamera, false, cameraMatrix); gl.drawArrays(gl.TRIANGLES, 0, 36); lastRenderTime = time; } render();
Получаем вращающийся куб:

Полный код примера можно
посмотреть

Мы научились рисовать простой куб, поняли, как заставить его вращаться, и познакомились с понятиями матрицы положения и камеры.

Как отлаживать

Поскольку при работе с WebGL часть программы исполняется на стороне видеокарты, процесс отладки значительно усложняется. Нет привычных инструментов в виде DevTools и даже console.log. В интернете есть множество статей и докладов на эту тему, здесь же приведу лишь основные способы.

Чтобы понять, что код шейдеров был написан с ошибкой, после компиляции шейдеров можно использовать следующий метод:
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { console.log(gl.getShaderInfoLog(vertexShader)); } if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { console.log(gl.getShaderInfoLog(fragmentShader)); } if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.log("Could not initialize shaders"); }
Также есть специальное расширение для браузеров WebGL-Inspector . Оно позволяет отследить загруженные шейдеры, буферы, текстуры в видеокарту, и вызовы методов WebGL.

Ещё есть Shader Editor, в Firefox DevTools этот функционал уже встроен, а для Chrome есть расширение , которое позволяет редактировать код шейдеров прямо в процессе работы приложения.

Куда двигаться дальше

В статье я попробовал осветить основные моменты, которые могут вызвать трудности во время изучения WebGL. Несмотря на то, что в работе требуется использовать разные векторы, матрицы и проекции, знать, как всё устроено внутри, необязательно. WebGL - отличный инструмент для решения целого ряда задач, и использовать его можно не только в геймдеве. Не бойтесь пробовать что-то новое, открывать для себя новые технологии и экспериментировать.

Напоследок - список полезных ресурсов, где можно продолжить изучение WebGL.

  • Полный код примеров с треугольником  

Браузер в компьютере, браузер в телефоне или умных часах, а может даже у кого-то в чайнике? Веб-технологии окружают нас со всех сторон и по мере развития железной начинки, становятся все более зрелищными и совершенными. Всего несколько лет назад о WebGL говорили, как о забавной игрушке с туманными перспективами. Но многое изменилось и на смену ранее популярному flash приходят более продвинутые технологии HTML5/WebGL.

Прошло время, когда демонстрации WebGL представляли собой примитивные, кустарные поделки или показательные демо от разработчиков софта. Сейчас многие известные бренды, используют эту технологию для презентаций и многие из них выглядят очень убедительными.

Указанный сайт ничто иное, как отличная замануха на просмотр фильма “Терминатор: Генезис”. Помимо стандартных постеров, трейлеров, тизеров - создатели фильма придумали вот такое великолепное интерактивное шоу.

Изюминка задумки заключается в привязке трехмерной графики к реальным кадрам из фильма. Посетитель SkyNet может узнать интересные подробности о главных персонажах, как роботов, так и живых актерах. Например, SpIder Tank. Что за монстр, непонятно? А так, покрутил модельку (кстати, качественно сделанную), почитал информацию о ключевых узлах, да еще и посмотрел видео, относящееся к возможностям монстра.

Честно, такая подача информации о фильме выглядит новаторски и очень завлекающе. WebGL/HTML5 поднимают планку на совсем другой уровень восприятия.

BioDigital Human

А это уже совсем иной уровень. Если в первых трех случаях технология WebGL использовалась больше в развлекательных целях, то здесь разработчики предлагают свои наработки в обучающем контексте.

Данный проект ориентирован на организации, связанные с медицинской отраслью. Качественно выполненные модели различных органов, точная визуализация происходящих процессов, специальные режимы просмотра - все это позволяет упростить процесс обучения или продемонстрировать особенности протекания заболевания.

И здесь дело даже не в качестве моделей, а в точности реализации. Например, при демонстрации работы коленного сустава, вы никак не сможете выгнуть его больше положенного.

Кроме того, есть очень интересный режим “рентгена”, включаемый специальной кнопкой в плеере. В этом случае, модель становится полупрозрачной с возможностью рассмотрения некоторых внутренних особенностей. Причем, при необходимости можно выделить часть объекта на фоне “рентгеновского снимка”. Так, на скриншоте ниже можно отчетливо рассмотреть разрыв связки.

Как уверяют разработчики, всего создано около 5000 объектов, а также сделано несколько полноценных уроков по анатомии человека. Думается, такой подход к обучению будет гораздо результативнее, нежели по обычным бумажным атласам.

Идея проста до безобразия, но именно благодаря WebGL удалось совместить необычную визуальную картинку (нужно смотреть в “живую”, так как скриншот эффект не передает) с некой массовой доской сообщений.

В действительности, частички образующие форму шлема из знаменитой игры - это пожелания пользователей. Сотни и сотни сообщений, оставленных на странице сайта, постепенно образуют цельную модель. Первоначально шлем представлял собой простую, примитивную сетку. В этом можно убедиться, если передвинуть слайдер на шкале времени внизу экрана. Интересно, как будет выглядеть шлем, после достижения некоторого порога. А пока, вы можете также присоединиться к ожидающим игру и черкануть несколько строк на этой необычной “доске объявлений”.

Постскриптум

Как, видите, все выбранные презентации показывают нужность и простоту использования технологии WebGL. Всё ближе и ближе смыкаются персональные компьютеры с мобильными системами. Настанет момент, когда браузерные приложения станут по-настоящему кроссплатформенными.