[recovery mode] Использование различных простых шейдеров в 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, без модификаций.


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


Your browser does not support HTML5 video.

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

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

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

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

hjrahxk0b0wpfoloukyyv94_5xa.png


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


Your browser does not support HTML5 video.

Мультисэмплинг (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…

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


Your browser does not support HTML5 video.

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

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


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


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

myjwkjmzbheoeryqp7-c2ljsuks.png

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

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

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


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

erol7mw2jpgeo5h3dhfqnwvf1bi.png

В 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 — контур у фиолетового щита.

czcdpij1oyr0gocgtyeur2chzca.png

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

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


Area lights:

pkjk1wid9ainv1qejlxtek0vsl4.png

sabtlmvjimad7tt_tsf1-nr3xvu.png

Используя материал 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 пикселей.
В нем храниться состояние анимации всех квадратов, и передается положение камеры и волка, двух персонажей.

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


Your browser does not support HTML5 video.

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

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

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


Конец статьи

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

© Habrahabr.ru