Если вы изучили HTML, CSS и JavaScript, возможно, вы подумаете: Могу ли я сделать что-то более интересное, чем отображение текста и изображений? Конечно, ДА! Например, вы можете использовать WebGL для создания интерактивной двухмерной или трехмерной графики.
Звучит сложно и вызывающе? Давайте начнем с чего-то простого: рисования 2D-фигуры.
Предварительные требования: обязательны HTML, CSS и JavaScript. Если вы знаете GLSL, это лучше, но я объясню код GLSL.
Репозиторий: https://github.com/yexiaosu/Intro-to-WebGL
Примечание. Чтобы увидеть результат, нельзя просто открыть HTML-файл. Вместо этого вам нужно запустить локальный сервер. Если вы используете VS Code, вы можете просто установить расширение под названием Live Server и нажать «Go Live» в нижней части окна VS Code.
Что такое WebGL?
WebGL (библиотека веб-графики) — это API-интерфейс JavaScript, полностью соответствующий OpenGL, который можно использовать в элементах HTML <canvas>
, чтобы мы могли отображать 2D- или 3D-графику на холсте. WebGL может использовать аппаратное ускорение графики, предоставляемое устройством пользователя, что означает, что данные могут быть отправлены на графический процессор для рендеринга графики. ¹
Начало работы с WebGL
Как мы упоминали ранее, API используется в элементах HTML <canvas>
, поэтому сначала нам нужен файл HTML с именем index.html
с элементом <canvas>
:
Здесь style
просто используется, чтобы сделать холст более четким. Вы можете определить стиль холста по своему усмотрению, а также добавить дополнительные элементы в HTML-файл, если хотите. Файл JavaScript, который мы включили сюда, будет создан позже.
В этом уроке я нарисую вторичную отметку Мичиганского университета ².
Прежде чем рисовать фигуру, нам нужно узнать координаты границы фигуры путем моделирования. Мы можем найти бумагу с сетками, подогнать метку к сеткам и вычислить координаты следующим образом:
Примечания:
- Координаты в WebGL имеют диапазон от -1 до 1, поэтому нам нужно сделать некоторое преобразование;
- Вы могли заметить, что я делю фигуру на несколько треугольников. Это связано с тем, что WebGL отображает фигуры, рисуя треугольники или полосу треугольников. Я выберу режим рисования треугольников, который будет проще при моделировании.
Поскольку мы смоделировали метку, мы можем сформировать информацию о ее геометрии. Информация о геометрии включает координаты вершин, цвет каждой вершины и порядок прохождения вершин при рисовании треугольников. Мы можем собрать эту информацию в файле JSON:
Примечания:
- Здесь я помещаю позиции и цвета в
attributes
, который отделен отtriangles
. Но вы можете определить структуру самостоятельно. triangles
— это список индексов вершин в спискеposition
. Вы можете видеть, что каждые три индекса сгруппированы, что означает, что соответствующие три вершины являются вершинами одного треугольника.- Поскольку метка имеет только один цвет, каждая вершина имеет один и тот же цвет. Цвет указан в процентах RGBA. Когда WebGL визуализирует фигуру, он окрашивает каждую вершину, а цвета всех остальных пикселей вычисляются с использованием интерполяции ³.
Вершинный шейдер и фрагментный шейдер
Нам также нужно подготовить два шейдера для рендеринга формы с информацией о геометрии: вершинный шейдер и фрагментный шейдер. Как следует из названия, они имеют дело с вершинами и фрагментами между вершинами. Когда форма визуализируется:
- Вершинный шейдер преобразует позиции в координаты, используемые WebGL. Он также будет выполнять другие преобразования по мере необходимости. Например, вы можете применить матрицу преобразования к каждой вершине, чтобы сделать анимацию в шейдере.
- Фрагментный шейдер будет использовать информацию, совместно используемую вершинным шейдером, для вычисления цвета, света или любых других вещей, которые необходимы для рендеринга пикселя с помощью программы, представленной в этом шейдере. Шейдер будет вызываться один раз для каждого пикселя.
Я знаю, что это сложно, если у вас раньше не было соответствующих знаний… Но не волнуйтесь! Мы здесь не делаем сложных вещей! У нас нет ни анимации, ни цветового градиента, ни освещения. Нам просто нужны два простых шейдера, чтобы использовать нашу информацию о геометрии для рендеринга статической 2D-формы:
Шейдеры написаны на GLSL. Если вы хорошо его знаете, то можете пропустить следующее объяснение кода выше и перейти к следующему разделу.
#version 300 es
указывает версию языка GLSL.precision highp float
(во фрагментном шейдере) указывает точность типа float.- Ключевые слова
in
иout
в GLSL определяют входные и выходные данные шейдера.vec4
объявляет 4-компонентный вектор. - В вершинном шейдере у нас есть два входа:
position
иcolor
.position
— координата вершины, аcolor
— цвет этой вершины. Эти входные данные будут поступать из нашей геометрической информации. - В вершинном шейдере у нас также есть вывод с именем
vColor
. Это будет передано фрагментному шейдеру, в котором мы можем видеть ввод, также называемыйvColor
. В нашем примере, поскольку нам не нужно менять цвет, мы просто используем исходную информацию о цвете и передаем ее на выход фрагментного шейдера. Таким образом, каждый пиксель отображается как тот же цвет, что и все вершины. - В вершинном шейдере мы видим, что значение ввода
position
присваиваетсяgl_Position
. Это встроенная переменная, которая содержит информацию о положении, которая будет использоваться для визуализации вершины.
Более подробную информацию о GLSL можно найти в документе: OpenGL Wiki⁴
Используйте API WebGL в JavaScript для рендеринга формы
Последний шаг — написать JavaScript! Давайте сначала создадим файл с именем webgl.js
, который мы включили в наш файл HTML, и проверим файлы, которые у нас есть:
. ├── fragment.glsl ├── geometry.json ├── index.html ├── vertex.glsl └── webgl.js
В webgl.js
нам нужно сначала настроить среду WebGL и загрузить нужные нам ресурсы:
Как вы могли заметить, на самом деле vs
и fs
являются строками. На самом деле шейдеры могут быть определены в файлах JavaScript в виде таких строк:
const fs = ` #version 300 es precision highp float; in vec4 vColor; out vec4 fragColor; void main() { fragColor = vColor; } `;
Но если шейдер сложный, делать это не рекомендуется.
После этого нам нужно скомпилировать шейдеры:
Шейдеры также должны быть связаны с программой:
Давайте вызовем функцию compileAndLinkGLSL
после загрузки исходников:
Затем мы собираемся иметь дело с информацией о геометрии. Нам нужно загрузить информацию в VAO (объект массива вершин), чтобы позже ее можно было использовать в нашей программе.
Обратите внимание, что gl.bindVertexArray()
НЕ используется для привязки данных. Он фактически объявляет, какие VAO используются. Например, gl.bindVertexArray(triangleArray);
означает, что мы используем triangleArray
, а позже всякий раз, когда мы изменяем VAO, мы фактически изменяем triangleArray
, пока не будет привязан другой VAO.
После этого нам нужно создать буферы и хранить в них информацию о геометрии, потому что при вычислениях GPU и рендеринге графики он фактически считывает данные из буферов.
VAO на самом деле похож на ⁵:
// pseudo code // reference: https://webglfundamentals.org/webgl/lessons/webgl-attributes.html vertexArray = { attributes: [ { enable: ?, type: ?, size: ?, normalize: ?, stride: ?, offset: ?, buffer: ?, divisor: 0, }, // ... other attributes ], elementArrayBuffer: null, }
Вот почему я поставил positions
и colors
в attributes
из geometry.json
. Фактически мы сохраняем их в буфере и присваиваем его атрибуту VAO, чтобы к буферам можно было получить доступ по расположению атрибутов.
Для этого мы можем обновить функцию setupGeometry()
:
Обратите внимание, что gl.bindBuffer()
точно так же, как gl.bindVertexArray()
, который только объявляет буфер, который будет использоваться позже. В то время как gl.bufferData()
— это функция, используемая для загрузки данных в буфер.
Если вы хорошо разберетесь, вы можете вспомнить, что у нас также есть список triangle
в нашей информации о геометрии, и мы не можем рисовать форму только с помощью position
и color
. Мы также должны создать буфер и сообщить WebGL, как рисовать треугольники. Вспомните псевдокод VAO, на этот раз мы будем использовать в нем elementArrayBuffer
. Добавьте следующий код после того, как мы переберем атрибуты и привяжем буферы:
Обратите внимание, что на этот раз, когда мы связываем буфер, мы используем gl.ELEMENT_ARRAY_BUFFER
вместо gl.ARRAY_BUFFER
в качестве точки привязки, которая всегда используется для индексов. ⁶ Кроме того, мы добавляем некоторую другую информацию к возвращаемому объекту. Эта информация будет использоваться позже, чтобы указать GPU, как считывать данные из буферов при рендеринге фигур.
После этого давайте вызовем функцию setupGeometry()
после компиляции и компоновки шейдеров. Чтобы использовать информацию и VAO позже, мы можем присвоить объект, возвращаемый этой функцией, переменной окна:
Последний шаг — рисование, которое очень просто. Все, что нам нужно сделать, это использовать нашу программу и вызвать функцию gl.drawElements
, предоставляемую WebGL:
И вызовите функцию в функции setup()
:
Поскольку мы уже добавили эту функцию в окно в качестве прослушивателя событий загрузки, когда мы запускаем сервер и открываем веб-страницу, мы можем видеть метку на нашем холсте!
Ссылка
[1] WebGL: 2D- и 3D-графика для Интернета — веб-API: MDN. веб-API | МДН. (н.д.). Получено 19 апреля 2023 г. с https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API.
[2] Рекомендации по использованию логотипа UM. Вице-президент по коммуникациям Мичиганского университета. (н.д.). Получено 19 апреля 2023 г. с https://brand.umich.edu/logos/u-m-logo/.
[3] Использование шейдеров для применения цвета в webgl — веб-API: MDN. веб-API | МДН. (н.д.). Получено 20 апреля 2023 г. с https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Using_shaders_to_apply_color_in_WebGL.
[4] Язык затенения OpenGL. Язык шейдеров OpenGL — OpenGL Wiki. (н.д.). Получено 20 апреля 2023 г. с https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language.
[5] Атрибуты Webgl. Атрибуты WebGL. (н.д.). Получено 20 апреля 2023 г. с https://webglfundamentals.org/webgl/lessons/webgl-attributes.html.
[6] Индексированные вершины WebGL. Индексированные вершины WebGL. (н.д.). Получено 20 апреля 2023 г. с https://webglfundamentals.org/webgl/lessons/webgl-indexed-vertices.html.