Steep parallax что такое
Точный учет микрорельефа при помощи parallax и relief mapping’а
Характерной чертой развития 3D-графики является все возрастающая степень детализации объектов. Первым шагом в такой детализации было применение bumpmapping-а (впервые предложенное Блинном).
Сейчас это стало повсеместно распространенным использовать bumpmapping, т.е. попиксельное задание вектора нормали, для моделирования неровностей поверхности (ее микрорельефа). За счет этого удается не усложняя геометрию объектов создать иллюзию микрорельфа, т.е. высокой степени детализации.
Рис 1. Пример использования bumpmapping-а.
Однако bumpmapping дает четко видимые результаты либо для освещения небольшим количеством точечных источников света (при этом желательно чтобы поверхности имели сильно выраженную бликовую компоненту как в DooM 3), либо при отражении (EMBM).
В данной статье мы рассмотрим моделирование подобных явлений начиная с самых простых и заканчивая наиболее продвинутыми методами.
Проще всего задавать микрорельеф поверхности при помощи так называемой карты высот (height map) h(s,t). При этом возможны два варианта трактовки этой карты. Значения из нее могут рассматриваться либо как высота точки над гранью (рис 2а), либо как углубление внутрь грани (рис 2b). Мы далее будем придерживаться первой интерпретации.
Рис 2. Возможные интерпретации карты высот.
Parallax Mapping
Когда неровная поверхность (задаваемая картой высот h(s,t)) рассматривается с направления v, то если мы не учитываем микрорельефа поверхности, в качестве точки на ней будет взята точка А (точнее, соответствующие этой точке текстурные координаты). Однако точному пересечению луча от наблюдателя соответствует точка В (и соответствующая ей точка T * в текстурных координатах).
Рис. 3. Приближенное вычисление точки на поверхности.
Можно использовать значение высоты в точке А для получения более точного значения текстурных координат (T1):
Коррекцию текстурных координат по приведенной формуле можно довольно легко реализовать при помощи простых вершинных и фрагментных шейдеров.
Steep Parallex Mapping
Рис 6. Нахождение пересечения путем перебора слоев.
В этом случае сначала проверяется точка A0, затем A1, затем A2 и так далее до тех пор, пока очередная точка не окажется ниже соответствующего значения высоты (для рис. 6 такой точкой является A3). Фактически осуществляется трассировка объема, разбитого на слои одинаковой толщины.
Однако поскольку не для всех графических ускорителей поддерживается цикл while во фрагментном шейдере, ниже приводится вариант шейдера для для конкретного числа слоев данный цикл развернут в цепочку условных операторов.
Рис 7. Изображение, получаемое при помои перебора набора слоев.
На следующем рисунке приводится изображение, построенное при помощи этого подхода. Однако использование небольшого числа проверяемых слоев может давать заметные погрешности, хорошо заметные на рисунке.
Рис 8. Артефакты, возникающие при переборе слоев.
Эта погрешность свзяана с тем, что проверка на пересечение луча проводится высего лишь в небольшом числе слоев, соответствующих дискретному набору высот. В ислу этого при резких изменениях высоты и/или при направлении взгляда, близкому к касательному, подобная «слоистость» поверхности становится явно видимой.
Также на форуме gamedev.ru предлагалось использовать вместо линейной интерполяции экспоненциальную.
Relief Mapping
Кроме того для устранения этих погрешностей можно использовать найденную путем перебора слоев точку как начальное приближения и произвести уточнение точки пересечения при помощи метода половинного деления.
Рис 9. Уточнение точки при помощи метода половинного деления.
Так для случая с рис. 8 точное значение пересечения лежит между точками A* и A1, поэтому в качестве начального приближения можно взять точку B1=(A1+A*)/2. Если эта точка лежит выше поверхности, задаваемой картой высоты, то корень находится на отрезке [B1, A*], в противном случае корень находится на [A1,B1].
Далее на получившемся отрезке вновь выбираем середину и сравниваем значение в ней со значением высоты. Это сравнение определяет какую половину отрезка следует оставить (т.к. она содержит корень).
Так проводится несколько итераций уточнения корня, при этом на каждой итерации точность удваивается (поскольку длина отрезка, содержащего корень сокращается в два раза на каждой итерации). После проведения фиксированного числа итераций середина последнего отрезка берется в качестве корня. Этот подход получил название relief mapping. Ниже приводится соответствующая фрагментная программа.
В качестве иллюстрации к этому подходу возьмем текстуру из NVSDK, задающую одновременно и карту нормалей (в RGB части) и карту высот (в альфа-канале). Далее построим изображение куба, на каждую сторону которого накладывается текстура камня и карта нормалей(высот). При построении изображения будет не только использовать relief mapping, но и проводить попиксельное вычисление диффузной и бликовой освещенности.
На следующих двух изображениях приведены изображения куба с использованием relief mapping и без него (только попиксельное вычисление освещенности).
Обратите внимание на то, что наложение текстуры на поверхность отличается от обычного bumpmapping‘а.
Рис. 10. Попиксельное освещение с использованием relief mapping-а
Рис. 11. Попиксельное освещение без использования relief mapping-а
Путем небольшой модификации шейдера можно использовать relief mapping не только для определения точной точки на поверхности, но также и для проверки ее затененности (закрывания микрорельефом от источника света).
Для этого поиск пересечения луча с поверхностью начинается от найденной точки пересечения и ведется в направлении вектора на источник света. Перебор слоев в этом случае осуществляется в обратном направлении (т.е. соответствующем увеличению высоты).
Parallax Occlusion Mapping
Существует еще один вариант развития steep parallax mapping‘а, получивший название parallax occlusion mapping.
Рис 12. Линейное приближение.
Тогда для приближенного нахождения точки пересечения мы можем просто найти точку пересечения из этого допущения вообще не прибегая к бинарному поиску (и вообще больше не обращаясь к текстуре). Данный способ оказывается не только более быстрым, но и дающим лучшие изображения.
Ниже приводится фрагментный шейдер, реализующий данный подход.
На следующих скриншотах приводятся результаты применения данного подхода. Обратите внимание на то, как чтеко виден рельеф поверхности.
Использование карты расстояний до поверхности
Существует еще один интересный способ для определения видимой точки с учетом карты высот. Он описан в главе 8 книги GPU Gems 2 (данная глава может быть скачана с сайта компании NVIDIA).
Данный способ также является итерационным способом нахождения пересечения луча от наблюдателя с поверхностью объекта. Однако для нахождения величины очередного шага он использует вспомогательную трехмерную текстуру.
Эта текстура строится по карте высот, где в качестве третьего измерения выступает высота. При этом значением каждого тексела является ближайшее расстояние до поверхности, задаваемой картой высот (см рис. 15).
Рис 14. Функция расстояния до поверхности.
Рис 15. Вспомогательная трехмерная текстура.
Этот подход также, как и предыдущий, осуществляет трассировку объема, однако в качестве величины очередного шага вдоль луча в направлении поверхности используется значение из этой вспомогательной текстуры.
Поскольку оно является ближайшим расстоянием до поверхности, то делая шаг этой длины, мы гарантированно не пересечем поверхность (что может произойти с ранее рассмотренным методом).
Рис 16. Последовательное приближение к точному решению.
Таким образом выполнение ряда шагов вдоль луча подобным образом дает нам приближенное значение точки пересечения луча с поверхностью. Чем больше шагов будет выполнено, тем выше будет точность.
Однако важным моментом является правильное согласование размеров вспомогательной текстуры со значениями внутри нее.
К сожалению данным метод требует специальным образом созданной 3D-текстуры и целого ряда зависимых (dependent) чтений из текстуры, что весьма отрицательно сказывается на быстродействии.
Cone Step Mapping, Relaxed Cone Step Mapping
Рис 17. Конусы на поверхности.
Тогда в силу построения такого конуся внутри него не может быть точек поверхности и можно не проверять нет ли пересечений луча с поверхностью внутри конуса, а сразу перейти к пересечению с границей конуса.
Поэтому для трассировки луча можно в начальной точке взять конус и найти пересечение луча с конусом, в точке пересечения луча с конусом берем новый конус и ищем пересечение луча с ним и та далее. В результате мы поулчаем последовательность точек, гарантированно сходящихся к точному пересечению луча с поверхностью.
Рис 18. Построение приближений к точке пересечения.
При этом как и ранее строится итерационная последовательность, но для каждой точки пересечения луча и конуса, проверяется не находится ли полученная точка под поверхностью. Если это так, то значит отрезок между полученной точкой последовательности и предыдущей содержит ровно одно пересечение, которое легко может быть поулчено либо при помощи половинного деления, либо через линейную интерполяцию.
Рис 20. Нахождение пересечения.
Таким образом полседний вариант (relaxed cone step mapping) отличается более высокой скоростью сходимости и, как и метод с функций расстояния, гарантирует, что даже мелкие детали не будут пропущены (что может иметь место при проверке отдельных слоев). Следующий рисунок иллюстрирует каким образом находится пересечение луча и конуса.
Рис 21. Нахождение пересечения луча и конуса.
Таким образом, для применения данного метода необходимо заранее по карте высот построить двухмерную тектсуру, содержащую тангенсы половинных углов разворота конусов. Следующий фрагмент псевдокода иллюстрирует алгоритм, который может быть использован для построения такой текстуры.
По этой ссылке можно скачать весь исходный код к этой статье. Также доступны для скачивания откомпилированные версии для M$ Windows, Linux и Mac OS X.
ИГРЫ И РАЗВЛЕЧЕНИЯ
3D-графика в играх: развитие и перспективы
Развитие персонажей
Уровень детализации игр реального времени или приключенческих игр долго не мог сравниться. В 2004 году Half Life 2 установила новый стандарт выражений лица и анимации персонажей. Как раз в то время началась кампания nVidia по продвижению шейдеров, которые позволили создать реалистичные оттенки кожи и индивидуальные выражения лиц. В 2006 году стратегии и ролевые/приключенческие игры стали настолько детализованными, что даже вплотную приблизив камеру, вы вряд ли найдёте разницу между ними и настоящими 3D-играми.
Будучи сиквелом Morrowind, Oblivion тоже стала знаковым событием в мире графики. Впервые в игре стал использоваться HDR-рендеринг (Shader Model 3), а броня и мечи стали действительно сиять. Сложность отрисовки лиц персонажа очень впечатлила; многие движки регулировки включали дополнительно индивидуальный разрез и цвет глаз, форму губ, подбородка, рта и головы, в результате чего герой мог стать различимым, с собственным лицом. Однако столь широкая регулировка опций в игре не приводила к какому-либо эффекту. В Oblivion можно играть только за себя в одиночку, а компьютерных персонажей (NPC) ваш внешний вид и лицо совершенно не интересуют.
Новые игры, подобно Hellgate London, должны выиграть от подобного усложнения, поскольку персонажи могут встречаться через Интернет, и здесь индивидуальная внешность отнюдь не помешает. Разница ограничена размером фигуры, волосами, цветом кожи и различным оснащением, которое позволяет персонажу выделиться среди толпы. Если вы посмотрите на развитие, которое произошло между играми Oblivion и Drakensang, то наверняка заметите современную ситуацию стагнации. Эффекты окружения развиваются, но детализация героев остаётся прежней.
На следующей иллюстрации показаны дополнительные персонажи и различия, которые даёт используемая модель освещения. В Doom 3 используется много света и теней, которые позволяют графике выглядеть более детально. Gothic 3 и Oblivion вышли на рынок в 2006 году. Но если Gothic 3 продолжила использовать старые эффекты bloom-освещения, Oblivion была основана на новом HDR-рендеринге, который выделяет блестящие участки и подсвечивает цветные поверхности более мягко и аккуратно. Переход на DirectX 10 можно видеть по играм Assassin’s Creed и Mass Effect. Улучшенный HDR-рендеринг (Shader 4) позволяет графике выглядеть более реалистично.
Эффекты глубины и наложение неровностей (Bump Mapping)
Первым играм пришлось довольствоваться глобальным источником освещения, тени и структуры просто добавлялись к сцене. Эволюция 3D-графики дала объектам большее число деталей, а различные источники света дали дополнительные тени. Поскольку многие объекты по-прежнему делались из больших поверхностей, структуры подобные стеклу, листьям или песку просто накладывались в качестве плоской текстуры. Если чему-то, например, рельсам или камню, нужно было выйти за пределы плоскости, то их приходилось задавать на сцене как полноценные 3D-объекты.
Попиксельное освещение позволяло симулировать структуру на поверхности. Песок получил волны, а стена из камня соответствующие неровности. В текстуре информация о высоте записывалась в виде шкалы яркости, которая переносилась на поверхность в виде теней.
Самая простая форма этой техники получила название карты неровностей (bump mapping), где информация о неровностях просто симулировалась. Поверхность оставалась гладкой, геометрия объекта на самом деле не менялась.
Parallax Mapping и карты смещения (Displacement Mapping)
С появлением DirectX 9 технологии эффектов структуры (глубины) продолжали улучшаться. Техника parallax mapping использует модификацию текстурных координат, чтобы симулировать неровные поверхности. Впрочем, объекты и поверхности всё равно остаются гладкими и не изменяются.
В современных играх эффект parallax mapping проработан ещё лучше. Если вы посмотрите на поверхность, то структура будет очень детальной. Однако детализация текстуры зависит от освещения и от угла зрения. Чем меньше угол, тем меньше будет заметен эффект глубины.
Геймеры обычно замечают серьёзные улучшения в 3D-графике каждые два или три года. Такие знаковые игры, как Morrowind, Doom 3 и Far Cry стали известны благодаря знаменитым отражениям в воде, потрясающим эффектам освещения и достоверному миру острова. Сегодня самые продвинутые эффекты обеспечиваются с помощью DirectX 10.1 и Shader 4; DirectX 11 и Shader Model 5 тоже объявлены, они должны дать следующий уровень реализма в играх. В первой статье мы обсудим развитие графики игр и персонажей, а также уделим внимание обзору современных эффектов освещения и эффектов глубины поверхностей. | ||
| ||
История мейнфреймов: от Harvard Mark I до System z10 EC Learn OpenGL. Урок 5.6 – Parallax MappingParallax MappingParallax Mapping относится к семейству техник Displacement Mapping или рельефного текстурирования, которые смещают вершины геометрии на основе значений, хранящихся в специальных текстурных картах. Для примера представьте плоскость, составленную из порядка тысячи вершин. Каждую из них можно сместить согласно величине, считанной из текстуры, представляющую собой высоту плоскости в данной точки. Такая текстура, содержащая значения высоты в каждом текселе, называется картой высот. Примером такой карты, полученной на основе геометрических характеристик поверхности кирпичной кладки, может служить следующее изображение: Осуществляя выборки из данной карты и смещая каждую вершину согласно значению высоты, можно из идеальной плоскости получить выпуклую поверхность, повторяющую геометрические параметры исходной поверхности. Так, взяв плоскость с достаточным количеством вершин и применив карту высот из примера, можно получить следующий результат: Описанный подход прост и легок в реализации, но требует большой плотности вершин в обрабатываемом объекте, иначе результат смещения будет слишком грубым. И если на каждую плоскую поверхность начать отпускать по тысяче с лишком вершин, то очень скоро мы просто не будем успевать рендерить все, что нам требуется. Может быть найдется алгоритм, позволяющий качественно сымитировать качество наивного алгоритма Displacement Mapping, но при этом не требуя таких затрат на геометрию? Если стоите – сядьте, поскольку на изображении выше на самом деле присутствует всего шесть вершин (два треугольника)! Рельеф кирпичной кладки отлично сымитирован благодаря использованию Parallax Mapping, техники рельефного текстурирования, не требующей множества вершин для правдоподобной передачи рельефа поверхности, а, как и Normal Mapping, использующей оригинальный подход для обмана глаз наблюдателя. Основная идея реализации в том, чтобы на основе направления взгляда и данных карты высот исказить текстурные координаты для текущего фрагмента так, чтобы создать иллюзию, будто этот фрагмент принадлежит участку поверхности, лежащей выше или ниже, чем есть на самом деле. Для лучшего понимания принципа посмотрите на схему нашего примера с кирпичами: Здесь грубая красная линия представляет собой значения из карты высот, отражающие геометрические характеристики имитируемой поверхности кирпичной кладки. Вектор представляет собой направление от поверхности на наблюдателя (viewDir). Если бы плоскость действительно была рельефной, то наблюдатель увидел бы точку поверхности . Однако, по факту мы имеем идеальную плоскость и луч по направлению взгляда пересекает плоскость в точке , что очевидно. Задача Parallax Mapping сместить текстурные координаты в точке так, чтобы они стали идентичны координатам, соответствующим точке . Далее для текущего фрагмента (соответствует точке ) мы используем полученные координаты точки для всех необходимы текстурных выборок, что и создает иллюзию, будто наблюдатель видит точку . Основная сложность заключена в том, как вычислить текстурные координаты точки находясь в точке . Parallax Mapping предлагает приближенное решение, применяя простое масштабирование вектора направления от поверхности к наблюдателю на величину высоты для фрагмента . Т.е. просто меняем длину так, чтобы она соответствовала величине выборки из карты высот , соответствующей фрагменту . На схеме ниже показан результат масштабирования – вектор : Далее результирующий вектор раскладывается на компоненты в соответствии с системой координат самой плоскости, которые используются как смещения для исходных текстурных координат. При этом, поскольку вектор вычисляется с использованием величины из карты высот, то чем больше значение высоты соответствует текущему фрагменту, тем сильнее для него будет смещение. Этот простой прием дает неплохие результаты в ряде случаев, но все же является очень грубой оценкой положения точки . Если карта высот содержит участки с резко меняющимися значениями, то результат смещения становится некорректным: скорее всего вектор даже близко не будет попадать в окрестность точки : Исходя из вышеописанного, остается еще один вопрос: каким же образом определить, как корректно спроецировать вектор на произвольно сориентированную поверхность, чтобы получить компоненты для смещения текстурных координат? Было бы неплохо вести расчеты в некой системе координат, где разложение вектора на компоненты x и y всегда бы соответствовало базису текстурной системы координат. Если вы внимательно проработали урок по Normal Mapping, то уже догадались, что речь идет о расчетах в касательном пространстве. Переведя вектор от поверхности к наблюдателю в касательное пространство мы получим измененный вектор , покомпонентное разложение которого всегда будет вестись в соответствии с векторами касательной и бикасательной для данной поверхности. Поскольку касательная и бикасательная всегда сонаправлены с осями текстурной системы координат поверхности, то, независимо от ориентации поверхности, можно спокойно использовать компоненты x и y вектора как смещения для текстурных координат. Однако, довольно теории и, закатав рукава, перейдем к непосредственной реализации. Parallax MappingДля реализации мы будем использовать простую плоскость с рассчитанными для нее касательной и бикасательной – это мы уже умеем делать по уроку о Normal Mapping. Плоскости мы назначим ряд текстурных карт: диффузную, нормалей и смещения, файл каждой из которых доступен по соответствующей ссылке. В уроке мы также применим и Normal Mapping, поскольку Parallax Mapping создает иллюзию рельефа поверхности, которая легко нарушается, если освещение не будет изменяться соответственно рельефу. Поскольку карты нормалей зачастую создаются на основе карт высот, то их совместное применение гарантирует корректную связь освещения с учетом рельефа. Вы, вероятно, уже заметили, что карта смещения, приведенная по ссылке выше, по факту представляет собой инверсию карты, приведенной в начале урока. Реализация Parallax Mapping обычно ведется как раз с использованием таких карт, обратным к картам высот – карт глубин. Так получается, поскольку имитация углублений на плоскости осуществляется несколько легче, чем имитация возвышения. В соответствии с этим изменением меняется и схема работы Parallax Mapping: Снова мы видим знакомые точки и , однако, в этот раз вектор получается вычитанием вектора из текстурных координат в точке . Значения глубины вместо высоты можно получить просто вычитая выборку глубины из единицы или инвертировав цвета текстуры в любом редакторе изображений. Parallax Mapping реализуется в фрагментном шейдере, поскольку данные о рельефе отличаются для каждого фрагмента внутри треугольника. Код фрагментного шейдера потребует расчета вектора от фрагмента к наблюдателю , так что понадобится передать ему положение фрагмента и наблюдателя в касательном пространстве. По результатам урока о Normal Mapping у нас на руках остался вершинный шейдер, который передает все эти вектора уже приведенными к касательному пространству, воспользуемся им: Из важного отмечу только то, что конкретно под нужды Parallax Mapping необходимо передать во фрагментный шейдер aPos и положение наблюдателя viewPos в касательном пространстве. Внутри шейдера реализуем алгоритм Parallax Mapping, что выглядит примерно так: Мы объявили функцию ParallaxMapping, которая принимает текстурные координаты фрагмента и вектор от фрагмента на наблюдателя в касательном пространстве. Результатом функции становятся смещенные текстурные координаты, которые уже и используются для выборок из диффузной текстуры и карты нормалей. В результате диффузный цвет пикселя и его нормаль корректно соответствуют измененной «геометрии» плоскости. Что же скрывается внутри функции ParallaxMapping? Данная относительно простая функция является буквальной реализацией метода, основные моменты которого мы обсудили выше. Берутся исходные текстурные координаты texCoords, с помощью них делается выборка высоты (или глубины) из depthMap для текущего фрагмента. Для расчета вектора берется вектор viewDir в касательном пространстве и пара его компонент x и y делится на компоненту z, а результат масштабируется считанным значением смещения height. Также введен юниформ height_scale для дополнительной возможности управления степенью выраженности эффекта Parallax Mapping, поскольку обычно эффект смещения выходит слишком сильным. Для получения результата мы вычитаем полученный вектор из исходных текстурных координат. Разберемся с моментом деления viewDir.xy на viewDir.z. Поскольку вектор viewDir нормализован, то его компонента z лежит в интервале [0, 1]. Когда вектор практически параллелен поверхности компонента z близка к нулю, а операция деления возвращает вектор гораздо большей длины, чем в случае если viewDir близок к перпендикуляру к поверхности. Другими словами, мы масштабируем вектор так, чтобы он увеличивался при взгляде на поверхность под углом – это позволяет получить более реалистичный результат в таких случаях. Некоторые разработчики предпочитают убирать масштабирование делением на viewDir.z, поскольку, в определенных случаях, такой подход дает некорректные результаты при взгляде под углом. Эта модификация техники называется Parallax Mapping with Offset Limiting. Выбор варианта подхода, по большей части, остается делом личных предпочтений – я, например, более лоялен к результатам работы обычного алгоритма Parallax Mapping. Результирующие измененные текстурные координаты в итоге используются для выборки из диффузной карты и карты нормалей, что дает нам довольно неплохой эффект искажения поверхности (параметр height_scale здесь выбран близким к 0.1): На изображении можно сравнить эффект техник Normal Mapping и Parallax Mapping. Поскольку Parallax Mapping имитирует неровности поверхности, то для этой техники возможны ситуации, когда кирпичики действительно перекрывают друг друга, в зависимости от направления взгляда. Также видны и странные артефакты вдоль границ оттекстурированной плоскости. Появляются они вследствие того, что смещенные алгоритмом Parallax Mapping текстурные координаты могут выпасть за пределы единичного интервала и, в зависимости от режима повторения текстуры (wrapping mode), вызвать появление нежелательных результатов. Простой способ избавления от таких артефактов – просто отбросить все фрагменты, для которых текстурные координаты оказались вне единичного интервала: В итоге все фрагменты, имеющие смещенные текстурные координаты, выпадающие из интервала [0, 1], будут отброшены и визуально результат действия Parallax Mapping станет приемлемым. Очевидно, что этот метод отбраковки не универсален и может быть неприменим для некоторых поверхностей или случаев текстурирования. Но на примере плоскости он работает идеально и помогает усилить эффект изменения рельефа плоскости: Исходники примера находятся здесь. Выглядит неплохо, да и производительность метода отличная – всего то потребовалась одна дополнительная выборка из текстуры! Но простота метода имеет и значительные недостатки: эффект рельефности легко разрушается при взгляде на плоскость под углом (что верно и для Normal Mapping) или при наличии в карте высот участков с резкими перепадами значений: Причина разрушения иллюзии кроется в том, что алгоритм является весьма грубым приближением реального Displacement Mapping. Однако, нас могут выручить несколько дополнительных приемов, которые позволяют получить практически идеальные результаты даже при взгляде под углом или при использовании карт высот с резкими перепадами. Например, мы можем использовать несколько выборок из карты высот, дабы найти точку, ближайшую к точке . Steep Parallax MappingТехника Steep Parallax Mapping является логическим развитием классического Parallax Mapping: используется такой же подход в алгоритме, но вместо единственной выборки берется несколько – для лучшей аппроксимации вектора , использующегося для расчета точки . За счет этих дополнительных выборок результат работы алгоритма визуально гораздо более правдоподобен, даже в условиях взгляда под острыми углами к поверхности. Основа подхода Steep PM заключается в том, чтобы взять некоторый диапазон глубин и разбить его на равные по размеру слои. Далее мы итеративно проходим по слоям одновременно смещая исходные текстурные координаты в направлении вектора и делая выборки из карты глубин, останавливаясь в тот момент, когда глубина из выборки оказывается меньше, чем глубина текущего слоя. Ознакомьтесь со схемой: Как видно, мы движемся по слоям сверху вниз и для каждого слоя сравниваем его глубину со значением из карты глубин. Если глубина слоя оказывается меньше значения из карты глубин, это значит, что вектор , соответствующий этому слою, лежит выше поверхности. Этот процесс повторяется до тех пор, пока глубина слоя не оказывается больше выборки из карты глубин: в этот момент вектор указывает на точку, лежащую под имитируемым рельефом поверхности. Изменения в реализации затронут только функцию ParallaxMapping, поскольку в ней уже собраны все необходимые для работы алгоритма переменные: Сперва осуществим инициализацию: установим количество слоев, рассчитаем глубину каждого из них и, в итоге, найдем размер смещения текстурных координат вдоль направления вектора , на которое необходимо будет смещаться на каждом слое. Далее идет проход по слоям, начиная с верхнего, до тех пор, пока не будет найдена выборка из карты глубин, лежащая «выше» значения глубины текущего слоя: В данном коде мы осуществляем проход по всем слоям глубины и смещаем исходные текстурные координаты, до тех пор, пока выборка из карты глубин не станет меньше глубины текущего слоя. Смещение осуществляется вычитанием из исходных текстурных координат дельты, основанной на векторе . Результатом работы алгоритма становится смещенный вектор текстурных координат, определенный с гораздо большей точностью, нежели классический Parallax Mapping. Используя порядка 10 сэмплов пример с кирпичной кладкой становится гораздо более реалистично выглядящим, даже при взгляде под углом. Но лучше всего достоинства Steep PM видны на поверхностях с картой глубин, имеющей резкие перепады значений. Например, как на этой, уже демонстрировавшейся ранее, деревянной игрушке: Можно улучшить алгоритм еще немного, если немного проанализировать особенности техники Parallax Mapping. Если смотреть на поверхность примерно по нормали, то необходимость сильно смещать текстурные координаты нет, в то время как при взгляде под углом смещение стремится к максимуму (мысленно представьте направление взгляда в обоих случаях). Если параметризировать число выборок в зависимости от направления взгляда, то можно неплохо сэкономить там, где лишние выборки не нужны: Результат скалярного произведения вектора viewDir и положительной полуоси Z используется для определения числа слоев в интервале [minSamples, maxSamples], т.е. направление взгляда определяет необходимое число итераций эффекта (в касательном пространстве положительная полуось Z направлена по нормали к поверхности). Если мы бы мы взглянули параллельно поверхности, то эффект бы использовал все 32 слоя. Модифицированный исходный код находится здесь. Также предлагаю к скачиванию текстуры деревянной игрушки: диффузная, карта нормалей, карта глубин. Не лишен подход и недостатков. Поскольку число сэмплов все равно конечно, то неизбежно появление эффектов алиасинга, что делает переходы между слоями бросающимися в глаза: Можно снизить выраженность артефакта путем наращивания количества используемых сэмплов, но это довольно быстро сожрет всю доступную производительность видеопроцессора. Существует несколько дополнений к методу, которые возвращают как результат не первую же точку, оказавшуюся под мнимым рельефом поверхности, а интерполированное значение двух ближайших слоев, что позволяет еще немного уточнить положение точки . Из этих методов чаще всего используются два: Relief Parallax Mapping и Parallax Occlusion Mapping, причем Relief PM дает наиболее достоверные результаты, но и является немного более требовательным к производительности, по сравнению с Parallax Occlusion Mapping. Поскольку Parallax Occlusion Mapping все же довольно близок по качеству к Relief PM и при этом работает быстрее, то его предпочитают использовать чаще всего. Далее будет рассмотрена реализация именно Parallax Occlusion Mapping. Parallax Occlusion MappingМетод Parallax Occlusion Mapping работает все на тех же базовых принципах, что и Steep PM, но вместо использования текстурных координат первого же слоя, где было обнаружено пересечение с мнимым рельефом, метод использует линейную интерполяцию между двумя слоями: слоем после и до пересечения. Весовой коэффициент для линейной интерполяции основывается на величине отношения текущей глубины рельефа к глубинам обоих рассматриваемых слоев. Посмотрите на схему, чтобы понять получше, как все работает: Как видно, все очень схоже с Steep PM, добавляется лишь один дополнительный шаг интерполяции текстурных координат двух слоев глубины, соседствующих с точкой пересечения. Конечно, и такой метод является всего лишь приближением, но куда более точным, нежели Steep PM. Код, отвечающий за дополнительные действия Parallax Occlusion Mapping является дополнением к коду Steep PM и не слишком сложен: В момент нахождения слоя, лежащего после точки пересечения с мнимым рельефом, мы также определяем текстурные координаты слоя, лежащего до точки пересечения. Далее находим смещения глубины мнимого рельефа относительно глубин двух рассматриваемых слоев и используем их отношение как весовой коэффициент дальнейшей линейной интерполяции текстурных координат, соответствующих двум рассмотренным слоям. Результат интерполяции возвращается функцией для дальнейшего использования. Parallax Occlusion Mapping дает на удивление визуально достоверные результаты, пусть и с небольшими огрехами и артефактами алиасинга. Но для компромиссного по скорости и качеству варианта они незначительны и проявляются лишь при пристальном наблюдении поверхности вблизи от камеры или при очень острых углах взгляда. Код примера находится здесь. Parallax Mapping воистину великолепная техника, позволяющая резко повысить визуальную детализацию вашей сцены, но, безусловно, имеет свои недостатки в виде артефактов, о чем стоит помнить при реализации техники в проекте. По большей части Parallax Mapping используется на плоских поверхностях типа стен или полов – там, где не так то просто определить очертания объекта целиком, а угол взгляда на поверхность часто близок к перпендикуляру. В таком случае огрехи Parallax Mapping практически незаметны, на фоне возросшей детальности поверхности. Бонусы от переводчика:Relief Parallax MappingРаз уж автор упомянул два метода уточнения результата Steep PM, то для полноты картины опишу и второй из подходов. Как и Parallax Occlusion Mapping здесь используется результат выполнения Steep PM, т.е. нам известны глубины двух слоев между которыми лежит реальная точка пересечения вектора с рельефом, а также соответствующие им текстурные координаты и . Уточнение оценки точки пересечения в данном методе идет за счет применения бинарного поиска. Шаги алгоритма уточнения: На изображении видно, что после нахождения точек и мы уполовиниваем размер слоя и размер смещения текстурных координат, что дает нам первую точку итерации бинарного поиска (1). Так как величина выборки в ней оказалась больше, чем текущая глубины слоя, то мы еще раз уполовиниваем параметры и смещаемся вдоль , получая точку (2) с текстурными координатами , что и будет результатом Steep PM для двух итераций бинарного поиска. СамозатенениеТакже небольшое дополнение о добавлении в алгоритм вычисления затенения от выбранного источника света. Решил добавить, поскольку технически метод расчета идентичен рассмотренным выше, а эффект все же интересный и детальности добавляет. По сути применяется тот же Steep PM, но с поиск идет не вглубь имитируемой поверхности вдоль линии взгляда, а из поверхности, вдоль вектора на источник света . Этот вектор также переносится в касательное пространство и используется для определения величины смещения текстурных координат. На выходе метода получится вещественный коэффициент освещенности в интервале [0, 1], который используется для модуляции диффузной и зеркальной компонент в расчетах освещения. Для определения затенения с мягкими краями необходимо проверить несколько точек, лежащих на векторе и находящиеся под поверхностью. Коэффициент затенения при этом берется равным разности глубины текущего слоя и глубины из карты глубин. Также учитывается удаление очередной точки от рассматриваемого фрагмента в виде весового коэффициента равного (1.0 – stepIndex/numberOfSteps). На каждом шаге определяется частичный коэффициент освещенности как: Конечным же результатом является максимальный коэффициент освещенности из всех частичных: Схема работы метода: Ход работы метода для трех итераций в данном примере: Полученный коэффициент используется для модуляции результата работы используемой в примерах модели освещения Блинна-Фонга:
|