пятница, 4 февраля 2011 г.

Как ковалось железо, или "параллакс меппинг и использование изнутри".

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

Шаг1:
В рендертаргет (в примере равный размеру окна) рендерится диффузная карта местности.

Шаг2:
В тот же рендертаргет через multiply рендерятся "тени" от объектов. Пока они плоские, также как и сама карта диффуза, но она исказится чуть позже.
Шаг3:
Во второй рендертаргет рендерится карта высот для всего ландшафта.
Шаг4:
В третий рендертаргет рендерится карта нормалей для всего ландшафта.
  
Шаг5:
Тут начинается все самое интересное. Сразу забыл оговориться, что три рендертаргета используемых в предыдущих шагах это три текстуры в трех разных стейтах и ипользуются они одновременно.
Эти три текстуры прогоняются через шейдер (о нём позже), который создает эффект "вдавливания" текстуры. Но так как в особо сильно вдавленных местах информация о текстуре заканчивается, мы получаем неприятные артефакты вокруг результата, в зависимости от степени вдавленности (в нашем случае максимальное вдавливание равно 128 пикселям). К сожалению, избавиться от эффекта невозможно, однако можно немножко считерить. 
Вариантов решения проблемы два. Первый — научить шейдер не вдавливать текстуру, а выдавливать. Второй — затекстурировать полигон так, чтобы текстурные координаты были не 0,0 - 1,1, а что-то вроде 0.1,0.1 - 0.9,0.9 или же просто увеличить полигон за пределы экрана в каждую сторону на 128 пикселей. Я избрал метод вдавливания так как при вдавленном методе не искажаются самые высокие участки, которые больше всего и видны игроку. Поэтому использовал второй метод решения проблемы.
Собственно что получится без коррекции  при попытки "вдавить":
 Когда текстурные координаты поправлены и артефактов нет, получаем то, что хотели, но чуточку побольше размером. А так как мы в диффузный рендертаргет нарисовали и тени — получаем еще и тени, причем с учетом искажения по рельефу:
Шаг 6:
Осталось дорисовать сами объекты поверх ландшафта, делая это привычным методом. Тут уже никаких секретов нет.

Шейдер:
Шейдер заслуживает отдельной статьи так как достаточно объемен. Сначала в программе высчитывается TBN для вершин (в quad-engine делается автоматом), далее TBN, матрица проекции и позиция источника света передаются в вершинный шейдер. Вершинный шейдер переводит все полученные параметры в тангент-спейс полигона и дальнейшая работа уже идет с ним. На выхлопе для каждого пикселя вектор света и вектор вида.
Пиксельный шейдер. Из карты высот берется значение высоты и учитывая вектор вида в данной точке ищется пересечение луча вида с плоскостью с учетом поправки на высоту (взятую из карты высот). Далее идет 8 итераций на корректировку текстурных координат, точнее на поиск более близкого значения к желаемому. После нахождения производится ленейная интерполяция в пределах 1\8 глубины (для этого и делается 8 проходов). Таким образом получается достаточно точное значение, которое и берется как реальные текстурные координаты. Пиксель из этих текстурных координат уже и выводится. Также по этим текстурным координатам берется вектор из карты нормалей и производится затенение картинки, согласно расположению источника света (в примере ровно по центру экрана). Добавляется спекуляр и эмбиент по вкусу и результат готов.

Подробно с кодом почитать о Parallax mapping можно вот здесь.

7 комментариев:

  1. Помню, что на steps3d читал эту статью, но там меня скриншоты и демки не впечатляют :) Не знал, что этими технологиями можно добиться такого результата.

    А тень в итоге искажается корректно или как получится?

    ОтветитьУдалить
  2. Корректно. В предыдущем посте линк на демо с "ползающей" тенью глянь.

    ОтветитьУдалить
  3. Я помню как там выглядит, но на глаз это определить не всегда можно :)

    ОтветитьУдалить
  4. Скажем так.. настолько же корректно, насколько корректно отрехмеривается ландшафт.

    ОтветитьУдалить
  5. Я понял, тень действительно именно так падает, если источник света расположен высоко в бесконечности. Например, солнце.

    Это если источник где-то близко или под наклоном, то тень уже будет как-то нетривиально падать.

    ОтветитьУдалить
  6. ну и есстественно нужны шейдеры 3.0 чтоб оценить это да? :)

    ОтветитьУдалить
  7. Да, в 2.0 попросту нехватает количества инструкций. А упрощение алгоритма сильно портит картинку.

    ОтветитьУдалить