Как работает WebGL? - PullRequest
       4

Как работает WebGL?

30 голосов
/ 07 сентября 2011

Я ищу глубокое понимание того, как работает WebGL. Я хочу получить знания на уровне, который больше не волнует большинство людей, потому что эти знания не являются необходимыми для обычного программиста WebGL. Например, какую роль каждая часть (браузер, графический драйвер и т. Д.) Общей системы рендеринга играет в получении изображения на экране? Должен ли каждый браузер создавать движок / среду javascript / html для запуска WebGL в браузере? Почему Chrome является главой всех остальных с точки зрения совместимости с WebGL?

Итак, какие есть хорошие ресурсы для начала? Спецификации kronos не хватает (из того, что я видел, просматривая ее в течение нескольких минут) для того, что я хочу. В основном я хочу узнать, как это делается / реализуется в браузерах и что еще нужно изменить в вашей системе, чтобы это стало возможным.

Ответы [ 2 ]

43 голосов
/ 10 сентября 2011

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

Архитектура

Браузер - это всего лишь браузер. Все, что он делает, это представляет API WebGL (через JavaScript), с которым программист делает все остальное.

Насколько я могу судить, API WebGL, по сути, представляет собой просто набор (предоставляемых браузером) функций JavaScript, которые обертывают спецификацию OpenGL ES. Поэтому, если вы знакомы с OpenGL ES, вы можете довольно быстро освоить WebGL. Не путайте это с чистым OpenGL. «ES» важен.

Спецификация WebGL была намеренно оставлена ​​на очень низком уровне, оставляя многое для быть повторно реализован из одного приложения в другое. Это до сообщество, чтобы написать рамки для автоматизации, и до разработчика выбрать, какой фреймворк использовать (если есть). Это не совсем сложно бросить свой собственный, но это означает много накладных расходов, потраченных на заново изобретать колесо. (FWIW, я работал над своим WebGL рамки под названием Jax на некоторое время Теперь.)

Графический драйвер обеспечивает реализацию OpenGL ES, которая фактически выполняет ваш код. На данный момент он работает на оборудовании машины, даже ниже кода C. Хотя это то, что делает WebGL возможным в первую очередь, это также обоюдоострый меч, потому что ошибки в драйвере OpenGL ES (о котором я уже упоминал довольно много) будут появляться в вашем веб-приложении, и вы не будете Обязательно знайте об этом, если только вы не можете рассчитывать на свою пользовательскую базу для составления согласованных отчетов об ошибках, включая ОС, видеооборудование и версии драйверов. Вот как выглядит процесс отладки для таких проблем.

В Windows существует дополнительный слой, который существует между API WebGL и аппаратным обеспечением: ANGLE, или «Почти собственный движок графического уровня» . Поскольку драйверы OpenGL ES в Windows, как правило, отстой, ANGLE принимает эти вызовы и вместо этого переводит их в вызовы DirectX 9.

Рисование в 3D

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

JavaScript

Сначала код JavaScript получает трехмерный контекст из элемента HTML5 canvas . Затем он регистрирует набор шейдеров, которые написаны на GLSL ([Open] GL Shading Language) и по сути напоминают код C.

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

JavaScript устанавливает исходные структуры данных и отправляет их в API WebGL, который отправляет их либо в ANGLE, либо в OpenGL ES, что в конечном итоге отправляет их графическому оборудованию.

Вершинные шейдеры

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

gl_Position = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * VERTEX_POSITION

, где VERTEX_POSITION - это четырехмерный вектор (x, y, z и w, который обычно равен 1); VIEW_MATRIX - матрица 4x4, представляющая взгляд камеры на мир; MODEL_MATRIX - матрица 4x4, которая преобразует координаты пространства объекта (то есть, координаты, локальные для объекта до применения поворота или перемещения) в координаты мирового пространства; и PROJECTION_MATRIX, который представляет объектив камеры.

Чаще всего VIEW_MATRIX и MODEL_MATRIX предварительно вычисляются и называются MODELVIEW_MATRIX.Иногда все 3 предварительно вычисляются в MODELVIEW_PROJECTION_MATRIX или просто MVP.Они обычно подразумевают оптимизацию, хотя я хотел бы найти время для некоторых тестов.Вполне возможно, что предварительные вычисления на самом деле медленнее в JavaScript, если они выполняются каждый кадр, потому что сам JavaScript не так уж и быстр.В этом случае аппаратное ускорение, обеспечиваемое математическим анализом на графическом процессоре, может быть быстрее, чем на процессоре в JavaScript.Мы, конечно, можем надеяться, что будущие реализации JS разрешат эту потенциальную ошибку, просто быстрее.

Координаты обрезки

Когда все они будут применены, переменная gl_Position будетиметь набор координат XYZ в пределах [-1, 1] и компонент W.Они называются координатами клипа.

Стоит отметить, что координаты клипа - это единственное, что действительно необходимо для создания вершинного шейдера.Вы можете полностью пропустить преобразования матрицы, выполненные выше, при условии, что вы получите результат координат клипа.(Я даже экспериментировал с заменой матриц для кватернионов; это работало просто отлично, но я отказался от проекта, потому что не получил улучшения производительности, на которое я надеялся.)

После того, как вы поставили клипкоординаты gl_Position WebGL делит результат на gl_Position.w, получая так называемые нормализованные координаты устройства.Оттуда, проецируя пиксель на экран, нужно просто умножить на 1/2 размера экрана и затем добавить 1/2 размера экрана. [1] Вот несколько примеров координат клипа, переведенных в2D координаты на дисплее 800x600:

clip = [0, 0]
x = (0 * 800/2) + 800/2 = 400
y = (0 * 600/2) + 600/2 = 300

clip = [0.5, 0.5]
x = (0.5 * 800/2) + 800/2 = 200 + 400 = 600
y = (0.5 * 600/2) + 600/2 = 150 + 300 = 450

clip = [-0.5, -0.25]
x = (-0.5  * 800/2) + 800/2 = -200 + 400 = 200
y = (-0.25 * 600/2) + 600/2 = -150 + 300 = 150

Пиксельные шейдеры

Как только определено, где должен быть нарисован пиксель, пиксель передается в пиксельный шейдер, который выбирает фактический цветпиксель будет.Это можно сделать множеством способов, начиная от простого жесткого кодирования определенного цвета до поиска текстур и заканчивая более сложным отображением нормалей и параллакса (которые по сути являются способами «обмана» поиска текстур для получения различных эффектов).

Глубина и буфер глубины

Теперь, пока мы проигнорировали компонент Z координат клипа.Вот как это работает.Когда мы умножили матрицу проекции, третий компонент клипа привел к некоторому числу.Если это число больше 1,0 или меньше -1,0, то это число выходит за пределы диапазона просмотра матрицы проекции, что соответствует значениям матрицы zFar и zNear соответственно.

Так что, если оно не находится в диапазоне[-1, 1] тогда оно полностью обрезается.Если в этом диапазоне равен , то значение Z масштабируется от 0 до 1 [2] и сравнивается с буфером глубины [3] .Буфер глубины равен размерам экрана, поэтому при использовании проекции 800x600 буфер глубины имеет ширину 800 пикселей и высоту 600 пикселей.У нас уже есть координаты X и Y пикселя, поэтому они подключены к буферу глубины, чтобы получить текущее сохраненное значение Z.Если значение Z больше, чем новое значение Z, то новое значение Z на ближе , чем то, что было нарисовано ранее, и заменяет его [4] .В этот момент можно осветить рассматриваемый пиксель (или, в случае WebGL, нарисовать пиксель на холсте) и сохранить значение Z в качестве нового значения глубины.

Если значение Z больше , чем сохраненное значение глубины, тогда оно считается "позади" того, что уже было нарисовано, и пиксель отбрасывается.

[1] фактическое преобразование использует настройки gl.viewport для преобразования из нормализованных координат устройства в пиксели.

[2] Фактически оно масштабируется до настроек gl.depthRange.По умолчанию от 0 до 1.

[3] Предполагается, что у вас есть буфер глубины и вы включили тестирование глубины с помощью gl.enable(gl.DEPTH_TEST).

[4] Вы можете установить, как значения Z сравниваются с gl.depthFunc

9 голосов
/ 23 октября 2012

Я бы прочитал эти статьи

http://webglfundamentals.org/webgl/lessons/webgl-how-it-works.html

Предполагая, что эти статьи полезны, остальная часть картины такова, что WebGL работает в браузере. Это рендеринг к тегу canvas. Вы можете думать о холст-теге как о теге img, за исключением того, что вы используете API WebGL для создания изображения вместо его загрузки.

Как и другие теги HTML5, тег canvas может быть стилизован с помощью CSS, находиться под или над другими частями страницы. Составляется (смешивается) с другими частями страницы. Преобразуйте, поворачивайте, масштабируйте с помощью CSS вместе с другими частями страницы. Это большая разница от OpenGL или OpenGL ES.

...