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

Тебе может показаться, что тени — это ни кому
не нужная роскошь, а лишь очередной шанс
выпендрится своим движком, тогда могу
посоветовать тебе взглянуть на скрины
русского движка X-Tend, который лег в основу не
менее русской игры Kreed,
может хоть эти картинки тебя переубедят.
Остальным же могу порекомендовать
продолжить умиротворенное чтение моей
рукописи. Давай-ка, рассмотрим самые
популярные способы рендеринга
динамических теней.

Фейковые тени

Самый простой способ показать, что ты
серьезный человек и заботишься о проблемах
распространения света в пространстве — это
использование фейковых теней (fake — подделка).
Фейковая тень — это всего лишь полигон с
изображением полупрозрачного кружочка или
еще какой глупости, который "кладется"
на землю прямо под моделью.

Выглядит не очень впечатляюще, но все равно
лучше, чем ни чего, ну и порядком добавляет
ощущения "трехмерности". Короче, это
программа-минимум, которую ты обязан
выполнить.

Метод проекции вершин

Этот метод также очень прост, но позволяет
добиться куда более реалистичных
результатов, нежели предыдущий. Суть метода
состоит в том в том, что мы проецируем
каждый полигон модели на плоскую землю, а
точнее горизонтальную плоскость. Вполне
очевидно, что такая тень не будет падать на
посторонние объекты сцены и будет верна
только для абсолютно ровного пола, типа
того, что мы можем наблюдать в спортивных
игрушках от EA Sports.

Для источника параллельных лучей
вычисления будут очень просты:

Листинг 5.1
/*
point — вершина, которую нужно спроецировать;
lvec — единичный вектор, задающий направление
параллельных лучей
*/

point.x -= lvec.x * point.z;
point.y -= lvec.y * point.z;
point.z = 0;

Для точечного источника будет несколько
сложнее:

Листинг 5.2
/*
point — вершина, которую нужно спроецировать;
light — координаты источника света
*/

i = light.z/(light.z-point.z);

point.x -= light.x;
point.x *= i;
point.x += light.x;

point.y -= light.y;
point.y *= i;
point.y += light.y;

point.z = 0;

Stencil-тени

Для реализации stencil-теней необходимо
прибегнуть к помощи так называемого stencil-буфера,
который, к счастью, поддерживается
аппаратно во всех современных
видеокарточках. Stencil-буфер позволяет задать
для каждого пикселя экрана какое-нибудь
числовое значение (обычно от 0 до 255), которое
затем можно использовать, чтобы
идентифицировать точки, над которыми
необходимо совершить ту или иную операцию,
например, изменение цвета. Так с помощью
stencil-буфера можно определить пиксели,
которые волею судеб попали в область
геометрической тени отбрасываемой 3D-объектом
и сделать их соответственно темнее.

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

Процесс построения stencil-теней выглядит
следующим образом.

1. Рисуем всю сцену без теней.
2. Запрещаем запись в z-буфер и в буфер кадра.
3. Рисуем обратные, т.е. невидимые грани
конуса тени, при этом увеличивая на единицу
значения в stencil-буфере для тех точек,
которые лежат ближе выводимых граней.
4. Рисуем передние, т.е. видимые грани конуса
тени и уменьшаем на единицу значения stencil-буфера
для точек, которые лежат ближе выводимых
граней. Теперь в stencil-буфере мы имеем маску
для тех точек, которые попали в область
конуса тени.
5. Рисуем серый полупрозрачный
прямоугольник на весь экран, разрешая
закраску только тех пикселей, для которых
значения stencil-буфера остались увеличенными
на единицу.

Карты затенения

Аналогично тому, как мы задавали освещение
уровня с помощью карт освещенности (light map),
мы можем нарисовать динамические тени с
помощью карт затенения (shadow map). 

Чтобы создать наш shadow map, мы должны
переместить камеру в точку, в которой
расположен источник света, и ориентировать
ее так, чтобы был виден объект, тень
которого мы хотим построить. Очевидно, что
из такого положения тень отбрасываемая
объектом не будет видна вообще, т.к. она
загорожена самим объектом.

Затем мы рисуем наш объект, так как он виден
из установленной камеры, но только черным
цветом на белом фоне, получив, таким образом,
картинку — shadow map, которая является как бы
поперечным срезом конуса тени всего
объекта. 

Последнее, что остается сделать, так это
наложить полученную карту затенения на
другие объекты 3D-сцены. Делается это
обычным проецированием полигонов.

Метод теневых z-буферов

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

И так, первое, что тебе необходимо сделать,
так это построить два z-буфера: один при
рендеринге сцены из обычной камеры (z-буфер
камеры), а другой при рендеринге из точки, в
которой установлен направленный источник
света (z-буфер источника), так же как это
делалось в предыдущем примере. 

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

И затем мы сравниваем значения z-буфера
камеры и z-буфера источника для данной точки,
и если значение z-буфера источника
оказалось меньше, чем значение z-буфера
камеры, то точка лежит в тени.

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

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

На сегодня это все!

Оставить мнение

Check Also

SHA 2017. Репортаж с самого яркого хакерского ивента этого лета

Still Hacking Anyway 2017 — это фестиваль, на котором собрались четыре тысячи хакеров со в…