Системы частиц — один из самых простых способов добавить визуальное богатство 3D-сцене.
В одном из наших приложений для Android 3D Будда Живые Обои Довольно простая сцена, которую стоило бы добавить немного больше деталей.
И когда мы думали о том, как добавить разнообразия изображению, самым очевидным решением заполнить пустое пространство вокруг статуи Будды было добавление клубов дыма или тумана.
Благодаря использованию мягких частиц мы добились неплохих результатов.
В этой статье мы подробно опишем реализацию мягких частиц на чистом WebGL/OpenGL ES без использования сторонних библиотек и готовых 3D-движков.
Разница между старым и обновленным приложением даже превзошла наши ожидания.
Простые частицы дыма значительно улучшили сцену, сделав ее богаче и насыщеннее.
Клубы дыма — это дополнительные детали, которые бросаются в глаза, а также способ сделать переход между основными объектами и фоном более плавным:
Мягкие частицы
Так что же это за мягкие частицы? Возможно, вы помните, что во многих старых играх (времён Quake 3 и CS 1.6) эффекты дыма и взрывов имели очень чёткие плоские границы на пересечении частиц с другой геометрией.Во всех современных играх таких артефактов больше нет из-за использования мягких частиц — то есть частиц с размытыми, «мягкими» краями вокруг соседних объектов.
Рендеринг
Что необходимо, чтобы частицы стали мягкими? Во-первых, информация о глубине сцены необходима, чтобы определить места пересечения частиц с другими объектами и смягчить их.Затем нам нужно определить, где пересекаются геометрии сцены и частицы, сравнивая глубины сцены и частицы во фрагментном шейдере — пересечения, где глубины одинаковы.
Далее мы рассмотрим процесс рендеринга шаг за шагом.
Обе реализации сцены для Android OpenGL ES и WebGL одинаковы, основная разница заключается только в загрузке ресурсов.
Реализация WebGL имеет открытый исходный код, и вы можете получить ее здесь — https://github.com/keaukraine/webgl-buddha .
Рендеринг карты глубины
Чтобы визуализировать карту глубины сцены, нам сначала нужно создать текстуры для карт глубины и цветов и назначить их определенному FBO. Это делается в методе initOffscreen() в файле Буддарендерер.js
.Фактический рендеринг объектов Actual сцены в карту глубины выполняется в методе рисоватьDepthObjects() , который отображает статую Будды и плоскость пола.
Однако есть один трюк, позволяющий улучшить производительность.
Поскольку на этом этапе рендеринга нам не нужна информация о цвете, а только глубина, рендеринг в цветовой буфер отключается вызовом gl.colorMask(ложь, ложь, ложь, ложь) , а затем снова включил, позвонив gl.colorMask(истина, правда, правда, правда) .
Функция глколормаск() может включать и выключать запись красного, зеленого, синего и альфа-компонентов по отдельности, поэтому, чтобы полностью отключить запись в цветовой буфер, мы устанавливаем для всех компонентов значение false, а затем разрешаем им отображать на экране, устанавливая для них всех значение true. Результат рендеринга в текстуру глубины можно увидеть, раскомментировав вызов drawTestDepth() в методе рисоватьСцену() .
Поскольку текстура карты глубины имеет только один канал, она воспринимается как имеющая только красный, синий и зеленый каналы, равные нулю.
Визуализация карты глубины нашей сцены выглядит так:
Рендеринг частиц
Код шейдера, который используется для рисования частиц, находится в файле.Давайте разберемся, как это работает. Основная идея поиска пересечения геометрий частиц и сцены заключается в сравнении текущего значения глубины фрагмента с сохраненным значением из карты глубины.
Первым шагом при сравнении глубин является линеаризация значений глубины, поскольку исходные значения являются экспоненциальными.
Это делается с помощью функции Calc_Deep() .
Эта техника хорошо описана здесь - https://community.khronos.org/t/soft-blending-do-it-yourself-solved/58190 .
Чтобы линеаризовать значения, нам нужна юниформ-переменная vec2 uCameraRange , компоненты x и y которого содержат значения ближней и дальней плоскостей отсечения камеры.
Затем шейдер вычисляет линейную разницу между глубиной частицы и сцены — это значение сохраняется в переменной a. Однако если мы применим это значение к цвету фрагмента, мы получим слишком прозрачные частицы — цвет будет линейно исчезать из любой геометрии позади частицы и исчезать довольно быстро.
Вот как выглядит визуализация линейной разницы глубины (вы можете раскомментировать соответствующую строку кода в шейдере и увидеть ее):
Чтобы сделать частицы более прозрачными только вблизи границы пересечения (в области a=0), воспользуемся функцией GLSL плавный шаг() к значению переменной а со значением перехода от 0 к коэффициенту, указанному в униформе uTransitionSize , который определяет ширину прозрачного перехода.
Если вы хотите узнать больше о том, как работает функция плавный шаг() и увидеть пару интересных примеров его использования, рекомендуем прочитать эту статью — http://www.fundza.com/rman_shaders/smoothstep/ .
Итоговый коэффициент сохраняется в переменной б .
Для режима смешивания цветов, используемого в нашей сцене, достаточно просто умножить цвет частицы, взятый из текстуры, на этот коэффициент; В других реализациях частиц вам может потребоваться изменить, например, только альфа-канал.
Если раскомментировать строку кода в шейдере для визуализации этого коэффициента, результат будет выглядеть так:
Сравнение разных значений коэффициента «мягкости» частиц:
Оптимизация рендеринга спрайтов
В этой сцене маленькие пылинки нарисованы в виде точечных спрайтов (примитивы вроде GL_POINTS ).Этот режим удобен тем, что автоматически создает готовую квадратную геометрию частиц с текстурными координатами.
Однако они имеют и недостатки, делающие их использование нецелесообразным для крупных частиц туманных облаков.
В первую очередь они отсекаются секущими плоскостями матрицы камеры по координатам центра спрайта.
Это приводит к тому, что они внезапно исчезают из поля зрения по краям экрана.
Также квадратная форма спрайта не очень оптимальна для фрагментного шейдера, так как она вызывается и в тех местах, где текстура частиц пуста, что вызывает заметную чрезмерную перерисовку.
Мы используем оптимизированную форму частиц — с обрезанными краями в местах, где текстура полностью прозрачна:
Такие модели частиц обычно называют рекламными щитами.
Конечно, их нельзя отобразить как примитивы.
GL_POINTS , поэтому каждая частица рисуется отдельно.
Это не создает слишком много проблем.
рисовать элементы , во всей сцене всего 18 частиц тумана.
Их необходимо разместить в произвольных координатах, масштабировать, но повернуть так, чтобы они всегда были перпендикулярны камере, независимо от ее положения.
Это достигается путем изменения матрицы, описанной в этом ответе, на Переполнение стека .
В файле Буддарендерер.
js есть метод вычислитьMVPMatrixForSprite() который создает матрицы MVP для моделей рекламных щитов.
Он выполняет все обычные преобразования перевода и масштабирования, а затем использует сбросMatrixRotations() для сброса компонента вращения матрицы представления модели перед его умножением на матрицу проекции.
Полученная матрица выполняет преобразование, в результате которого модель всегда направлена точно в камеру.
Результат
Окончательный результат можно увидеть вживую здесь .Вы можете изучить и повторно использовать исходный код с Github для своих проектов.
.
Теги: #JavaScript #webgl #OpenGL ES
-
Как Избежать Перегорания Печатающей Головки
19 Oct, 24 -
Почему Стоит Выбрать Наряд
19 Oct, 24 -
Вы Понимаете, Что Такое Веб-Хостинг?
19 Oct, 24 -
Восемь Способов Демотивировать Сотрудников
19 Oct, 24 -
10 Лет Онлайн-Безопасности
19 Oct, 24 -
Школы Перешли На Opensource
19 Oct, 24