Если вы изучили 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 визуализирует фигуру, он окрашивает каждую вершину, а цвета всех остальных пикселей вычисляются с использованием интерполяции ³.

Вершинный шейдер и фрагментный шейдер

Нам также нужно подготовить два шейдера для рендеринга формы с информацией о геометрии: вершинный шейдер и фрагментный шейдер. Как следует из названия, они имеют дело с вершинами и фрагментами между вершинами. Когда форма визуализируется:

  1. Вершинный шейдер преобразует позиции в координаты, используемые WebGL. Он также будет выполнять другие преобразования по мере необходимости. Например, вы можете применить матрицу преобразования к каждой вершине, чтобы сделать анимацию в шейдере.
  2. Фрагментный шейдер будет использовать информацию, совместно используемую вершинным шейдером, для вычисления цвета, света или любых других вещей, которые необходимы для рендеринга пикселя с помощью программы, представленной в этом шейдере. Шейдер будет вызываться один раз для каждого пикселя.

Я знаю, что это сложно, если у вас раньше не было соответствующих знаний… Но не волнуйтесь! Мы здесь не делаем сложных вещей! У нас нет ни анимации, ни цветового градиента, ни освещения. Нам просто нужны два простых шейдера, чтобы использовать нашу информацию о геометрии для рендеринга статической 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.