Использование различных простых шейдеров в Godot 3.2

от автора

Еще один мой проект в Godot 3 с использованием разных шейдеров, все шейдеры довольно простые.

Ссылка для запуска на itch.io, требуется WebGL2.
Исходный код проекта на github, проект graphic_demo_3d.

Статья разбита на такие разделы:

  1. Статические текстуры, генерация и плавная смена для эффекта освещения.
  2. Сглаживание и мультисэмплинг.
  3. Про используемые шейдеры и их логику.
  4. Немного про логику скриптов.

О чем это:

В статье описание используемых шейдеров, и ссылки на первоисточник там где использовался сторонний материал.

Скачать готовые сборки для Linux и Windows ссылка на itch.io.
Исходный код проекта ссылка на github.

Для разработки использовался официальный Godot 3.2.1, без модификаций.

Статические текстуры

Ray-tracer и убирание шума:
Для генерации всех текстур освещения, как не сложно догадаться, использовался raytracer. В основном это Blender-cycles.
В качестве деноизера я использовал этот код шейдера glslSmartDeNoise.

Разные цвета освещения:
В каталоге проекта graphic_demo_3d/game/models/objects/arc/ есть папки orig+цвет где все текстуры освещения для этой геометрии(меша).
И эти текстуры синхронно меняются через логику скрипта arc.gd

Панорамы:
Для создания текстур панорам, которые используются как текстура отражения на сферах, делал cubemap снимок и трансформировал в панораму, в Godot такое сделать быстрее чем в Blender.

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

Сглаживание и мультисэмплинг

Мультисэмплинг(MSAA) на весь экран очень дорогой, и в этом проекте он не используется.
На видео выше шейдер рисует линии анимации используя smoothstep, и для сглаживания радиус smoothstep увеличивается в зависимости от положения фрагмента шейдера, на этом видеоролике справа этаже анимация без сглаживания.

На фиолетовых сферах используется мультисэмплинг текстуры, код шейдера:

    const int AA=4;     for (int mx = 0; mx < AA; mx++)         for (int nx = 0 ; nx < AA; nx++) {             vec2 o = vec2(float(mx), float(nx)) / float(AA) - 0.5;             o=tuv+o*baa;             tot+=texture(p_o,o).rgb;         }     tot /= float(AA * AA);

Где baa это удаление фрагмента от камеры.
Это работает достаточно хорошо для этого конкретного случая.
В этом случае mipmaps нельзя использовать, так как эта панорама и сфера(размер пикселей не равномерный), кубемап на сферу тоже не выйдет натянуть без потери mipmaps…

Еще один случай, где нужно сглаживание — когда с удалением яркие объекты стали слишком маленькими, и пикселей не хватает чтоб их нарисовать:

Справа рисуется без фокусов честная геометрия(меш), и с удалением объект сверху на темной пирамиде становится слишком маленький и начинает прыгать по пикселям.

Я просто взял точки(GL_POINT) и поставил их внутрь этих мелких объектов, и с удалением точка сохраняет свой размер и заменяет пропадающий объект. Вариант с точками на левой части видео.

Про используемые шейдеры и их логику.

Шейдер outline для одного объекта:

Корректный способ такое сделать описан по ссылке в разделе Silhouette Effect.
В Godot 3 нельзя, без модификации кода движка, записывать ни ID материала ни какие-либо дополнительные данные в процессе создания кадра.

Поэтому единственный способ это использовать дополнительную framebuffer(Viewport) где еще раз рендерить все нужные объекты, и пока объектов мало можно не волноваться о производительности, но это плохой способ в любом случае, ждем Godot 4.

В моем случае я поставил еще одну камеру на сцену, и эта камера видит только один объект — волка, поэтому всегда контур будет, камера в Viewport разрешение которого в 2 раза меньше текущего экрана.

Шейдер для освещения и теней:

В Godot довольно мало возможностей влиять на освещение и тени, и некоторые из заявленных возможностей в Godot 3 просто не реализованы и перенесены в Godot 4.
Код этого шейдера в файле box/floor.shader

void light() {     vec3 col=texture(floor_img,UV).rgb;     if(col.r>0.001){         float dif = clamp(dot(NORMAL,LIGHT), 0.0, 1.0);         vec3 hal = normalize(VIEW+LIGHT);         float spe = pow(clamp(dot(hal, NORMAL), 0.0, 1.0), 32.0);         vec3 ta=ATTENUATION;         vec2 tuv=UV*vec2(15.,10.)*50.;         vec2 ddx = dFdx(tuv);          vec2 ddy = dFdy(tuv);         float tx=filteredSquares(tuv, ddx, ddy);         float tx2=filteredCrosses(tuv, ddx, ddy);         DIFFUSE_LIGHT = dif*co*spe*4.* ta*col;         DIFFUSE_LIGHT = clamp(DIFFUSE_LIGHT,0.,1.);         SPECULAR_LIGHT = mix(ta,vec3(0.)+DIFFUSE_LIGHT,clamp(dif*spe*5.,0.,1.))*tx+0.5*((1.-tx2)*(clamp(1.-ta*5.,0.,1.)));         SPECULAR_LIGHT = clamp(SPECULAR_LIGHT*col,0.,1.);     }     else{         DIFFUSE_LIGHT=vec3(0.);         SPECULAR_LIGHT=vec3(0.);     } }

Логика такая что, берется значение текстуры floor_img чтобы ограничить тень и свет в пределах белого прямоугольника текстуры, функции filteredSquares и filteredCrosses от iquilezles.org.
Эти функции выводят свой узор(патерн), и этот узор накладывается в зависимости от ATTENUATION, и дальше разделение цвета SPECULAR_LIGHT от DIFFUSE_LIGHT чтоб один сделать белым другой желтого цвета.

Depth — контур у фиолетового щита.

Вся логика работы с глубиной(depth) отсюда godot_force_shield_shader
Шейдер шума на основе этого ссылка на shadertoy
Сам код шейдера в файле shield.shader.

Этот эффект рисуется двумя слоями, внутренним и внешним, внутренний слой после внешнего чтобы не влиять на SCREEN_TEXTURE на основе которого строится смещение(волны) снаружи.

Area lights:

Используя материал Real-Time Polygonal-Light Shading with Linearly Transformed Cosines этот код на shadertoy ссылка, для прямоугольных источников.
И код этого шейдера для сферы и трубы ссылка на shadertoy.

Код этих шейдеров в этих файлах area_lights/floor.shader и area_lights2/floor_area.shader

Никакой сложности их переноса в Godot нет, все так же как во многих других движках, где этот эффект уже встроенный.

Немного про логику скриптов.

Навигация модели волка — использует встроенную в Godot возможность Navigation, логика скопирована из туториала Godot 3D Navigation Mesh. Волк бежит к камере по клику правой кнопкой мыши.

Пирамиды-лампы вокруг поля сделаны частицами, не сильно портят производительность.
Смена цвета через шейдер, определение расстояния до камеры.

Анимация квадратов на полу — используется отдельный framebuffer(Viewport) разрешения 20×20 пикселей.
В нем храниться состояние анимации всех квадратов, и передается положение камеры и волка, двух персонажей.

Бесконечное поле:

Перемещение игрока в симметричную позицию на другой стороне при достижении определенной позиции. (да есть немного заметный лаг, я уже не исправлял это)

Используются две сторонние модели с анимациями.
Взяты из sketchfab, ссылки на оригинал — модель волка и модель робота.

Производительность — я запускал на Nvidia 750 и Vega 8 картах, работает на 60fps в 1080p разрешении, думаю производительность в пределах нормы. Проверил на Windows и Linux — работает. В WebGL2 также работает очень хорошо.

Конец статьи

Кто прочитал — спасибо Вам что уделили столько времени этому тексту.

ссылка на оригинал статьи https://habr.com/ru/post/506120/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *