Get to know MDN better
This page was translated from English by the community. Learn more and join the MDN Web Docs community.
Вам уже должно быть понятно,что у WebGL нет много "встроенного знания". Он просто выполняет две функции, которые вы написали - вершинный шейдер и фрагментный шейдер, а функции, которые рисуют сцену должны написать вы сами. Другими словами, если вы хотите добавить освещение, то вы должны рассчитать его самостоятельно. К счастью, сделать это не сложно. В этой статье мы опишем некоторые основы.
Хотя детали теории, лежащей в основе симуляции освещения в 3D-графике лежат далеко за пределами этой статьи, будет полезным немного узнать о том, как это работает. Посмотрите статью Затенение по Фонгу в Википедии, чтобы получить хороший обзор наиболее часто используемых моделей освещения. А в этой статье вы можете посмотреть объяснение, основанное на WebGL.
Существует три основных типа источников света:
Окружающий свет освещает всю сцену. Он не направленный и освещает все грани всех объектов одинаково, не зависимо от ориентации граней.
Направленный свет исходит из определённого направления. Этот свет приходит от настолько удалённого источника, что все фотоны летят параллельно друг другу. К примеру, солнечный свет можно считать.
Точечный свет исходит из одной точки во всех направлениях. В реальном мире многие источники освещения являются точечными, например электрическая лампочка.
В этой статье мы упростим модель освещения и будем использовать только простой направленный и окружающий свет. Мы не будем создавать блики на поверхности объектов и точечные источники света. Вместо этого мы добавим окружающий свет и направленный свет в сцену с вращающимся кубом из предыдущего примера.
Если оставить в стороне блики и точечные источники света, то останутся два пункта, которые нужно изучить по порядку:
Затем мы обновим вершинный шейдер, чтобы скорректировать цвет каждой вершины в зависимости от окружающего и направленного освещения с учётом угла падения на грань. Мы увидим, как это делается, когда посмотрим на код шейдера.
Сначала нам нужно создать массив нормалей для всех вершин, из которых состоит наш куб. Это будет просто, потому что куб очень простой объект. Очевидно, что для более сложных объектов расчёт нормалей будет более затратным.
Код уже должен выглядеть узнаваемо. Мы создаём новый буфер, связываем его с рабочим буфером и записываем в него массив нормалей к вершинам при помощи bufferData().
Затем добавим в drawScene() код, который свяжет массив нормалей с атрибутом шейдера. Таким образом шейдер сможет получить к нему доступ:
В конце нужно обновить код, который строит матрицы для uniform-переменных, чтобы создать и передать в шейдер матрицу нормалей, которая используется для трансформации нормалей при расчёте ориентации куба относительно направления на источник света:
Теперь у нас есть все данные для шейдеров. Пора обновить код самих шейдеров.
Сначала обновим вершинный шейдер, чтобы он рассчитывал значение освещения для каждой вершины на основе окружающего и направленного света. Посмотрим на код:
После расчёта позиции вершины мы передаём координаты текселя (texel), соответствующего вершине, во фрагментный шейдер, и начинаем расчёт освещения вершины.
Сначала нужно преобразовать нормаль, основываясь на текущей ориентации куба - умножив нормаль вершины на матрицу нормалей. Затем мы можем рассчитать количество света от направленного источника, которое приходит в вершину, посчитав скалярное произведение преобразованной нормали и вектора направления (направления, с которого приходит свет). Если скалярное произведение меньше нуля, то мы принимаем его за ноль, потому что количество света не может быть меньше 0.
После расчёта количества падающего направленного света мы можем посчитать финальное освещение, сложив окружающий свет и произведение количества направленного света на его цвет. В результате получается значение RGB, которое используется фрагментным шейдером для изменения цвета каждого пикселя.
Фрагментный шейдер должен быть обновлён таким образом, чтобы он учитывал в значение освещения, рассчитанное в вершинном шейдере:
Здесь мы получаем цвет текселя, как и в предыдущем примере, но перед тем, как установить цвет фрагмента, мы умножаем цвет текселя на значение освещения, чтобы учесть влияние источников света.
Осталось только посмотреть на определение атрибута aVertexNormal и uniform-переменной uNormalMatrix.
И это всё!
Посмотреть код примера полностью | Открыть демо в новом окне
Очевидно, что это простой пример, показывающий базовое вершинное освещение. В более продвинутой графике вам наверняка захочется сделать попиксельное освещение.
Также вы можете поэкспериментировать с направлением на источник света, цветами окружающего и направленного света, и т. д.
This page was last modified on 4 дек. 2024 г. by MDN contributors.
Your blueprint for a better internet.
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998–2026 by individual mozilla.org contributors. Content available under a Creative Commons license.