Проекционные полу-тени на полу.

Jan 07, 2021 00:30

Прямо перед новогодними праздниками впервые за где-то год перепали задачи по программированию графики. Была рекомендация от дизайнера: нужны «мягкие вторичные тени» (ака полутени) в точке касания манекеном пола. Сразу понятно, что манекен у нас может касаться пола не только ногами, но и платьем. Поэтому отскочить статической текстурой на полу не выйдет.

На момент старта задачи рендер (если без текстур, с монохромной тканью) выглядел так:





У нас уже были т.н. «эмбиентные» тени (ambient occlusion) в локальных впуклостях геометрии (1). Но я их специально для пола отключил (2), так как теневое гало появлялось не просто в точке контакта ног с полом, но и выше: в пространстве экрана линия раздела ног и пола - это одна большая «впуклость». Понятно, что в алгоритме была включена проверка на разрыв функции: не затенять места со слишком большим перепадом высот. Но если начать адаптировать этот «порог» под устранение гало вокруг ног, то может пострадать качество картинки в других местах. Тем более что на данный момент куча констант уже неплохо была подогнана. То есть, надо было изобретать что-то другое.

Ок, пол у нас плоский. Идеальный способ рисовать на таком тени - проекция. Но просто взять и сделать проекционные тени не выйдет. Как они программируются обычно? Берём матрицу проекции из точки (или по направлению для бесконечно удалённого источника света) на плоскость, действуем ей на все треугольники сцены, получившуюся плоскую кашу треугольников рисуем полупрозрачным чёрным цветом. Там надо ещё чутка поприседать с буфером шаблона (stencil buffer), что б не было перекрытий треугольников тени, но это мелочи.
Это нам не подходит, потому как тень нужна не от всей геометрии (мы её и так через PCSS рисуем (3)), а только от её тонкого слоя вблизи пола. К примеру, для ног нам фактически нужно чутка размыть отпечатки стоп. Вторая проблема проекционных теней - их принципиальная немягкость. Мы получим на полу треугольники с резкой границей, которую непонятно, как «размывать».

Ну ок. Давайте шаг один: как получить геометрию из тонкого слоя над полом? Нам нужно при проецировании на пол игнорировать всю геометрию, которая выше от него, чем некий заданный порог расстояния. В теории, эту проверку можно написать, хоть в пиксельном шейдере, хоть в вершинном. Но можно сделать круче. Мы же проецировать будем ровно сверху вниз! Можем обрезать всё ненужное матрицей проецирования на фикрованном : устанавливаем ближнюю плоскость отсечения на нужном нам расстоянии и ништяк.




Ок, а что с размытием? Шаг два: давайте геометрию мы спроецируем на плоскость пола (с означенным выше отсечением), но будем рисовать не прямо в буфер кадра, а в текстуру. Камера у нас будет смотреть вертикально вниз на пол. Как организовать матрицу проекции (не перспективной, а ортографической), я написал в предыдущем абзаце. По итогу мы получим текстуру очень похожую на shadow map, но только там будут не значения глубины, видимой из источника света поверхности, а непосредственно наша тень. Осталось теперь при отрисовке почитать из этой текстуры с фильтрацией, которая даст нужное нам размытие. В текстуре запишем 0 там, где есть полу-тень, 1 - в остальных местах. Фильтрация даст нам промежуточные значения. Код на самом деле почти не отличается от любых теневых карт за тем лишь исключением, что мы не делаем никаких тестов глубины в пространстве источника света: взяли мировую координату пикселя, умножили на матрицу проекции, которой сплющивали всю геометрию, получили текстурные координаты, считали из текстуры.

После реализации выяснилось, что таки надо регулировать черноту тени по расстоянию до пола. А то ткань в сантиметре от пола пятна под собой непонятные оставляла. Поэтому в текстуру с тенью стал рисовать не 0/1, а градиент от 0 к 1. Он был уже автоматом - нормализованная Z-координата пикселя в системе координат проекции теневой.

Итог работы:





Бонус:

1) Схему можно допилить и до работы с произвольной функцией двух аргументов (ака поверхностью), а не только с плоскостью. Надо только будет окрестность сцены рисовать в карту высот и читать из ней для более точного отсечения, нежели z-near и z-far плоскостями.

2) Под желания (а они 100% будут) дизайнера можно сделать разную черноту у теней от разных объектов: от ткани одну, от манекена - другую.

3) В текстуру тени, как и в StarCraft 2 (раздел 5.8), можно писать ещё и цвет, если хотим тени от полу-прозрачных объектов.

компьютерная графика, cadwise

Previous post Next post
Up