Войти
ПрограммированиеТерминыГрафика

Теневой объём (Shadow Volume)

Теневой объём (Shadow Volume) — один из методов построения теней, заключающийся в создании объекта, определяющего объём, внутри которого точки находятся в тени.

Для нахождения границ определяющих теневой объём, следует вытянуть силуэтные рёбра объекта вдаль от источника света. Затем следует найти способ отрисовки этого объёма для классификации точек находящихся внутри него и за его пределами.

Первая задача — нахождение объёма и вытягивание может быть произведено на шейдерах. Можно рассмотреть несколько вариантов:

а) силуэтные рёбра находятся на процессоре, а вытягивание производиться в шейдере. Для этого следует составить "квадрат" из двух треугольников с шириной равной нулю, и отметить числом нуждается ли точка в вытягивании. В результате получиться квадрат, две точки которого помечены и после вертексого шейдера будут образовывать грань теневого объёма. Этот способ хорошо дружит с анимированными моделями, производя точный результат, но имеет проблемы со скоростью - много расчётов остаются на CPU и постоянно требуется обновлять буфера, находящиеся в видеокарте.

б) создается вспомогательный буфер из всех рёбер модели. Каждое ребро является опять же четырехугольником. Но теперь вместо того чтобы отмечать точки которые нужно вытягивать мы храним в каждой точке нормаль треугольника, у двух точек - одного, у двух других - второго. Далее в вертексном шейдере мы проверяем знак скалярного произведения между нормалью и вектором к источнику света. Если нормаль повёрнута от источника света, мы вытягиваем вершину с этой нормалью. В результате лицевые к свету грани остаются на месте, а задние вытягиваются и мы вновь получаем ограничивающую объём грань в месте, где нормали четырёхугольника были повёрнуты в разные стороны относительно света. Этот способ переносит все расчеты теневого объёма в вертексный шейдер, но имеет свои минусы. Во-первых, требуется дополнительная память для буфера граней у каждой модели. Во-вторых способ испытывает трудности с анимированными моделями, где он будет допускать ошибки при изменении нормали, что не может быть произведено точно.

в) новый подход стал возможен после прихода новой стадии конвейера - геометрического шейдера. Теперь не нужен дополнительный буфер - для нахождения объёма можно использовать саму модель. В геометрическом шейдере можно получить данные о соседних треугольниках, найти их нормали и произвести создание грани теневого объёма там где это нужно. Такой подход не требует лишней памяти, производит расчет полностью на GPU и совместим с анимированными моделями. Единственный минус который приходит в голову - очень малая распространённость видеокарт нового поколения.

После того как был выбран способ для расчёта граней теневого объема, встаёт вопрос о том, как следует с его помощью определить, какие точки находятся в тени, а какие - нет.
Для этого этапа отлично подходит буфер трафарета (stencil buffer). Настраивая его параметры определённым образом, и рисуя задние/передние грани найденного теневого объёма, мы можем отметить нужные точки. Для данного этапа тоже существует несколько методов, но в отличии от предыдущего они все по умолчанию выполняются на GPU. Вот три из них:

а) Depth pass/Z-pass
Задние и передние грани теневого объёма рисуются в два прохода. В каждом проходе различаются настройки рендера. Итак, что мы делаем:
1. Отключим запись в буфер глубины и цвета
2. Включим отсечение задних граней. Установим операцию трафарета в инкрементирование при прохождении теста глубины. Нарисуем объём.
3. Включим отсечение передних граней. Установим операцию в уменьшение на единицу при прохождении теста глубины. Нарисуем объём.

Все освещённые точки будут иметь значение 0 в буфере трафарета.

Метод даёт ошибку, когда камера находится внутри объёма. В этом случае камера не видит часть объёма, и все значения в буфере трафарета сдвигаются на -1, что приводит к инвертации теней. Для обхода этой проблемы на картах NVidia было введено расширение GL_NV_depth_clamp [spec] с помощью которого можно отключить обрезание геометрии дальней и ближней плоскостями отсечения.

б) Depth fail/Z-fail (аля Carmack's Reverse)
Метод исправляем ситуацию, когда камера находится внутри объёма.
1. Отключим запись в буфер глубины и цвета
2. Включим отсечение передних граней. Установим операция в инкрементирование на единицу при провале теста глубины. Нарисуем объём.
3. Включим отсечение задних граней. Установим операцию трафарета в уменьшение на единицу при провале теста глубины. Нарисуем объём.
Все освещённые точки будут иметь значение 0 в буфере трафарета.
Для нормальной работы метода может потребоваться отрисовка всех задних граней модели, вытянутых в бесконечность, но в большинстве случаев этого не требуется, так как эти грани в любом случае были бы закрыты геометрией.

в)XOR (исключающее ИЛИ)
Классификация точек двумя предыдущими способами может быть приближённо выполнена данным методом, который плохо справляется с пересекающимися теневыми объёмами, но сохраняет один проход отрисовки (экономим fillrate) и требует всего лишь 1-битный буфер трафарета (сомнительное преимущество учитывая стандарт 8бит на большинстве видеокарт). Установки для Depth Pass вариации данного метода:
1. Отключим запись в буфер глубины и цвета
2. Установим операцию трафарета в XOR при проходе теста глубины.
3. Нарисуем теневые объёмы

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

Данный метод построения теней требует большой скорости отрисовки (fillrate) и очень тяжёл для больших высокополигональных моделей. Для экономии fillrate можно вытягивать точки не в бесконечность, а на величину радиуса света. В таком случае могут возникнуть проблемы с методом (б), но их не будет видно так как за пределами радиуса источника света и так не должно быть освещения. Для высокополигональных моделей можно использовать низкополигональные приближения, но это может сильно испортить качество изображения и производить неприятные ошибки.

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

Метод даёт совершенно точные тени на любом расстоянии. Для их смягчения можно рисовать найденную тень во временную текстуру и её там размывать, но такой метод дает разную степень размытия в зависимости от расстояния, так как является фильтром в экранном пространстве (screen-space). Второй способ называется wedges. Он заключается в создании дополнительной геометрии, которая будет описывать не только теневой объём, но и объём зоны перехода из света в тень. Построение этой геометрии является довольно тяжёлой задачей, но существует множество методов реального времени.

Ссылки:
Стенсельные тени изнутри by Семён Козлов
Realistic Soft Shadows by Penumbra-Wedges Blending

Что такое Теневой объём (Shadow Volume)?

#тени, #stencil

29 марта 2007 (Обновление: 17 фев 2011)