Освещение в играх. Deferred shading. G-буфер

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

Статья о методах динамического освещения.

G-buffer (геометрический буфер) — набор render targets хранящих в себе информацию о сцене. Причем каждый из этих render targets хранит информацию именно о текущей сцене и только для одной сцены. То есть G-буфер может быть только real-time (во время исполнения).

Все render targets G-буфера одинаковы в размерах (ширина, высота, обычно по размерам back buffer), но могут быть разных форматов.

Для тех кто не помнит что такое render targets — это текстуры, в которые можно рендерить (кратко).

G-буфер является достаточно узким местом в плане производительности, потому что рендер происходит в несколько render targets. Поэтому желательно его хорошенько скомпоновать,а также они занимает достаточно много видео памяти для своего хранения. В нашем движке SkyXEngine мы используем 4 render targets (поддерживается многими видео картами, даже частью устаревших):

  • color — цвет rgba (сцена с текстурами), здесь же отражения, детальный текстуры, формат rgba8
  • normals — нормали (и микрорельеф в том числе) rgb, тип материала — освещаемый/не освещаемый и непрозрачный/полупрозрачный (a), формат rgb10a2
  • parameters — параметры материала rgb, номер полупрозрачной поверхности материала (a), формат rgba8
  • depth — линейная глубина r, формат r16 или r32

От упаковки данных отказались, потому что в общем профита не увидели (может быть что-то не так делали), и думаю для первой пробы вполне сойдет) Упаковка нормали.

Со всеми render targets вроде ясно, но что такое parameters? У меня это вызывало массу вопрос и недоумения)) А оказалось все просто, каждый материал имеет какие то параметры, в нашем случае материал имеет:

  • roughness — коэффициент шероховатости (на сколько поверхность гладкая)
  • f0 — отражательная способность поверхности (сколько энергии света отразится от поверхности, остльное поглотится)
  • thickness — коэффициент толщины (просвечиваемости, в основном для растительности, что листья не полностью затеняли)

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

Вот так выглядит G-буфер в нашем случае:

Первый render target - color (сцена только с цветом) (G-буфер SkyXEngine 0.9.0)
Первый render target — color (сцена только с цветом) (G-буфер SkyXEngine 0.9.0)
Второй render target - normals (нормали сцены) (G-буфер SkyXEngine 0.9.0)
Второй render target — normals (нормали сцены) (G-буфер SkyXEngine 0.9.0)
Третий render target - parameters (параметры материалов сцены) (G-буфер SkyXEngine 0.9.0)
Третий render target — parameters (параметры материалов сцены) (G-буфер SkyXEngine 0.9.0)
Четвертый render target - depth (глубина сцены) (G-буфер SkyXEngine 0.9.0)
Четвертый render target — depth (глубина сцены) (G-буфер SkyXEngine 0.9.0)

Примечание: со второго и третьего render targets я убрал альфа канал для того чтобы было понятнее, иначе бы эти изображения были бы почти прозрачные, а четвертый сделал черно-белым, ибо там был только один цвет и наглядность терялась))

А вот так будет выглядеть просчитанное освещение:

Просчитанное освещение на основании данных света и G-буфера (SkyXEngine 0.9.0)
Просчитанное освещение на основании данных света и G-буфера (SkyXEngine 0.9.0)

Упаковка нормалей в шейдере при построении G-буфера:

OUT.Normal.xyz = 0.5f * IN.Normal + 0.5f;

Распаковка нормалей при просчете освещения:

Normal.xyz = 2.f * Normal.xyz - 1.f;

Просчет линейной глубины:

depth = (pos.z + near)/far;
  • pos — мировая позиция пикселя умножения на WorldViewProjection матрицу
  • near — ближняя плоскость отсечения (у нас 0.025)
  • far — дальняя плоскость отсечения (мы делаем от 200-400)

Хотя раньше делали так:

depth = (pos.z)/far;

И в этом случае восстановленные мировые координаты немного ехали из стороны в сторону, а pos.z + near это предотвращает. Бился я с этой проблемой очень долго, в течении года то забрасывал, то пытался решить, а решение оказалось чрезвычайно простым))


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

Поделиться:

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

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

*

*

code