sonyps4.ru

Webgl примеры. Пять презентаций WebGL, которые поражают

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

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

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

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

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

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

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

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

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

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

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

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

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

Здравствуйте, уважаемые читатели!

В продолжении статей про Canvas сегодня хотел бы вам рассказать про WebGL .

Чтобы лучше понимать, как мы работаем с WebGL , рекомендую освежить знания по Canvas:

WebGL позволяет web-контенту использовать API , основанный на OpenGLES 2.0 , для визуализации 3D графики в HTML элементе canvas в браузерах.

WebGL программы состоят из кода управления (JS ) и шейдерного кода для эффектов, который выполняется на графическом процессоре.

Сразу пример интересных возможностей WebGL : ссылка

Кроссбраузерность WebGL

На момент написания статьи (12.08.16) статистика поддержки браузерами следующая:

Как видно из таблицы современные браузеры уже поддерживают WebGL , поэтому можно смело разрабатывать свои крутые приложения 🙂

Принцип работы WebGL

Принцип работы в общих чертах хорошо иллюстрирует схема ниже:

Небольшое пояснение:

  • Процесс начинается с создания массива вершин. Этот массив содержит атрибуты вершин: расположение в 3D пространстве, информацию о текстуре, цвете или нормалей (освещение). Данная информация создается в JavaScript из файлах описания 3D моделей (.obj файлы) или из библиотеки, которая описывает массив вершин геометрических фигур
  • Затем вершины отправляются на GPU. Вместе с этим мы должны так же передать массив индексов вершин для контроля преобразования вершин в треугольники
  • GPU читает каждую вершину из буфера вершин и прогоняет ее через вершинные шейдеры. Вершинные шейдеры вычисляют положение вершины на экране, цвет, текстурные координаты
  • GPU соединяет по 3 вершины в треугольники используя массив индексов
  • Затем происходит растеризация, приведение изображения к пиксельном фрагментам
  • Сформированные пиксели проходят через пиксельные шейдеры. Пиксельный шейдер рассчитывает цвет и глубину каждого пикселя. Там же на этом этапе происходит наложение текстуры и расчет освещения. Рассчитанные пиксели помещаются в буфер фреймов (кадров).
  • Буфер кадров — последняя стадия отрисовки. Результатом его работы является 2D изображение на экране с эффектами глубины
  • Библиотеки для работы с WebGL

    WebGL API очень низкоуровневый. Писать довольно сложно. Рекомендуется использовать библиотеки, которые написали умельцы с разных уголков земли.

    Ниже приведены несколько популярных библиотек:

    • Three.js (Github) – рассмотрим данную библиотеку
    • PhiloGL (Github) — фокус на лучших практиках JavaScript
    • GLGE (Github) — имеет некоторые свои особенности, например, скелетную анимацию
    • J3D (Github) — позволяет экспортировать сцены из Unity в WebGL

    Рассмотрим подробнее Three.js и попробуем воспроизвести простой пример

    Three.js

    Для того, чтобы начать работать с three.js нужно подключить библиотеку:

    Var scene, camera, renderer; var geometry, material, mesh; init(); animate();

    Определим наши функции:

    Function init() { scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000); camera.position.z = 1000; geometry = new THREE.BoxGeometry(200, 200, 200); material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true }); mesh = new THREE.Mesh(geometry, material); scene.add(mesh); renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); } function animate() { requestAnimationFrame(animate); mesh.rotation.x += 0.01; mesh.rotation.y += 0.02; renderer.render(scene, camera); }

    Результат отработки скрипта:

    Последнее обновление: 1.11.2015

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

    Первый пример

    Создадим какую-нибудь веб-страничку со следующим содержимым:

    Привет WebGL! Ваш браузер не поддерживает элемент canvas void main(void) { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); } attribute vec3 aVertexPosition; void main(void) { gl_Position = vec4(aVertexPosition, 1.0); } var gl; var shaderProgram; var vertexBuffer; // установка шейдеров function initShaders() { // получаем шейдеры var fragmentShader = getShader(gl.FRAGMENT_SHADER, "shader-fs"); var vertexShader = getShader(gl.VERTEX_SHADER, "shader-vs"); //создаем объект программы шейдеров shaderProgram = gl.createProgram(); // прикрепляем к ней шейдеры gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); // связываем программу с контекстом webgl gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Не удалсь установить шейдеры"); } gl.useProgram(shaderProgram); // установка атрибута программы shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); // делаем доступным атрибут для использования gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); } // Функция создания шейдера по типу и id источника в структуре DOM function getShader(type,id) { var source = document.getElementById(id).innerHTML; // создаем шейдер по типу var shader = gl.createShader(type); // установка источника шейдера gl.shaderSource(shader, source); // компилируем шейдер gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert("Ошибка компиляции шейдера: " + gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; } // установка буфера вершин function initBuffers() { // установка буфера вершин vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); // массив координат вершин объекта var triangleVertices = [ 0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.STATIC_DRAW); // указываем кол-во точек vertexBuffer.itemSize = 3; vertexBuffer.numberOfItems = 3; } // отрисовка function draw() { // установка области отрисовки gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); gl.clear(gl.COLOR_BUFFER_BIT); // указываем, что каждая вершина имеет по три координаты (x, y, z) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0); // отрисовка примитивов - треугольников gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.numberOfItems); } window.onload=function(){ // получаем элемент canvas var canvas = document.getElementById("canvas3D"); try { // Сначала пытаемся получить стандартный контекст WegGL // Если не получится, обращаемся к экспериментальному контексту gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); } catch(e) {} // Если контекст не удалось получить, выводим сообщение if (!gl) { alert("Ваш браузер не поддерживает WebGL"); } if(gl){ // установка размеров области рисования gl.viewportWidth = canvas.width; gl.viewportHeight = canvas.height; // установка шейдеров initShaders(); // установка буфера вершин initBuffers(); // покрасим фон в бледно-розовый цвет gl.clearColor(1.0, 0.0, 0.0, 0.5); // отрисовка сцены draw(); } }

    Я не буду останавливаться на этом коде - основные моменты подробнее мы рассмотрим детально позднее, но, я думаю, комментарии помогут разобраться. Но если вкратце: у нас есть элемент canvas , который представляет полотно для отрисовки сцены WebGL. Далее в виде скриптов javascript объявляются две мини-программы - фрагментный и вершинный шейдер.

    Весь код программы находится в обработчике функции window.onload . Вначале нам надо получить контекст gl , через который и буду вестись все основные действия. Далее программа содержит три основные части: установка шейдеров, установка буфера вершин и сама отрисовка.

    А пока можно запустить веб-страничку в браузере. И браузер явит нам белый треугольник на красном фоне.

    Всего лишь? Это только начало. Позже мы подробнее поговорим о шейдерах, вершинах, геометрических примитивах и т.д. А теперь перейдем ко второму примеру.

    Пример 2

    Второй пример представляет собой хрестоматийный пример вращающегося куба. Вначале загрузим специальную библиотеку Three.js . Ее минимизированную версию можно найти по адресу Three.js . Перейдем по этому адресу и сохраним весь код на жесткий диск в файл под названием three.min.js . Эта библиотека не является необходимой для работы с WebGL, однако она упрощает работу.

    В одном каталоге с загруженной библиотекой создадим файл index.html со следующим содержанием:

    Привет WebGL! window.onload=function(){ var camera, scene, renderer; var geometry, material, mesh; init(); animate(); // инициализация начальных значений function init() { // создаем камеру - перспективная проекция camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000); // установка z-координаты камеры camera.position.z = 600; // настройка сцены scene = new THREE.Scene(); // настройка геометрии - в качестве геометрии будет куб // настроим его ширину, высоту и длину по оси z geometry = new THREE.CubeGeometry(200, 200, 200); // настройка материала - установка цвета material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true}); // настраиваем меш, который будет отображать куб mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // создаем объект для рендеринга сцены renderer = new THREE.WebGLRenderer(); // установка размеров renderer.setSize(window.innerWidth, window.innerHeight); // встраиваем в DOM-структуру страницы document.body.appendChild(renderer.domElement); } // функция анимации function animate() { requestAnimationFrame(animate); // вращение меша вокруг осей mesh.rotation.x += 0.01; mesh.rotation.y += 0.02; // рендеринг сцены - метод, производящий по сути отрисовку renderer.render(scene, camera); } }

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



    Идея данной статьи родилась после нескольких мучительных недель изучения 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 устанавливает цвет.

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

  • attributes - доступны только в вершинном шейдере, разные для каждой из вершин;
  • uniforms - доступны в обоих шейдерах и одинаковы для всех вызовов шейдера;
  • 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, то для атрибутов нужно использовать ещё одну сущность - буферы. Данные буферов хранятся в памяти видеокарты, что даёт значительный прирост в скорости рендеринга.

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

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

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

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

    • (0, 0, 0);
    • (0.5, 1, 0);
    • (1, 0, 0).
    Стоит отметить, что при работе с буферами следует учитывать несколько особенностей:
  • данные в буфер передаются одним массивом без вложенности, в случае нашего треугольника данные будут выглядеть следующим образом: ;
  • передаваться должны только типизированные массивы ;
  • прежде чем передать данные, вы должны точно указать, какой буфер будет использоваться, методом 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, 0.5, 0.5)
  • (0.5, 0.7, 1)
  • (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) принимает пять параметров:

  • matrix - матрица, которую необходимо изменить;
  • fov - угл обзора в радианах;
  • aspect - cоотношение сторон экрана;
  • near - минимальное расстояние до объектов, которые будут видны;
  • 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.

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


    Загрузка...