netlib.narod.ru | < Назад | Оглавление | Далее > |
В паре следующих игр из этой книги вы будете использовать шейдер наложения нормалей, который значительно повышает реализм большинства трехмерных объектов, особенно если у них есть характерная структура поверхности и много мелких деталей, шипов, трубок, кабелей и т.д., которые потребовали бы многих миллионов полигонов для корректного отображения всех этих деталей (рис. 7.1). Вы можете посмотреть примеры наложения нормалей на Интернет-сайтах, где сравниваются позорно выглядящие объекты и плохие текстуры с замечательными объектами высокого разрешения с наложением нормалей. Я буду здесь использовать реальный объект из реальной игры (он применяется в гоночной игре, которую вы увидите в этой книге далее) и вы увидите, что версия без наложения нормалей тоже выглядит неплохо, поскольку текстура рассеяния уже немного затенена. Таким образом, вы можете визуализировать без наложения нормалей и объекты будут выглядеть приемлемо. С включенным наложением нормалей объекты выглядят гораздо лучше и, несмотря на то, что в них только 1000 полигонов, они выглядят очень гладко (справа на рис. 7.1).
Рис. 7.1 |
Лучших результатов можно достигнуть с наложением параллакса (Parallax Mapping — имитация высоты), наложением смещения (Offset Mapping — несколько слоев для лучшей имитации высоты) или наложением сдвига (Displacement Mapping — реальное перемещение вершин, возможное в шейдерной модели 4.0, но с очень ресурсоемкими и медленными вычислениями). Эти техники более сложны и требуют наличия карты высот в дополнение к текстуре рассеивания и текстуре карты нормалей. Но простое использование наложения нормалей тоже выглядит замечательно.
Хорошая сторона наложения нормалей — не слишком сложная реализация, если у вас есть видеокарта с поддержкой шейдеров. Оно работает даже на очень старых GeForce 3 (пиксельные шейдеры версии 1.1), но может быть усовершенствовано для поддержки пиксельных шейдеров 2.0, что позволит реализовать лучшие эффекты отражаемого освещения на современном оборудовании. Знания, полученные в предыдущей главе, уже дали вам фундамент для создания шейдеров, вы знаете, как преобразуются вершины и как пиксели в итоге отображаются на экране.
Наложение нормалей использует дополнительную текстуру. Текстура рассеивания используется для отображения основной структуры материала, цветов и т.д. Затем карта нормалей добавляет дополнительные трехмерные детали, предоставляя дополнительные векторы нормалей для каждого отдельного пикселя, который вы будете визуализировать в пиксельном шейдере (рис. 7.2).
Рис. 7.2 |
Пожалуйста, обратите внимание, что если вы откроете текстуры KaktusSeg.dds и KaktusSegNormal.dds из трехмерной модели кактуса, растровое изображение карты нормалей будет выглядеть иначе. Карта нормалей сжата, а альфа-канал и красный канал в ней поменяли местами. Этот трюк применяется, чтобы позволить сжатие текстуры нормалей с коэффициентом 1:4 без значительной потери деталей, чтобы объект по-прежнему выглядел хорошо. Более подробно о технике мы поговорим чуть позже — просто помните, что внутренне текстура карты нормалей выглядит подобно показанной на рис. 7.2; сжатая версия просто по-другому хранит данные.
Чтобы вычислить освещение для каждого пикселя в пиксельном шейдере вашего эффекта наложения нормалей используются данные текстуры карты нормалей. Каждый пиксель в текстуре карты нормалей представляет вектор нормали для поверхности полигона. Эти данные хранятся в касательном пространстве, которое немного обсуждается на рис. 7.4. Чтобы получить вектор нормали из цветовых данных в текстуре используется формула (значение r, g или b - 0.5)/0.5 (рис. 7.3). RGB-данные в карте нормалей видны как числа с плавающей точкой, позволяя вам использовать эту формулу. Для преобразования из RGB в число с плавающей точкой просто разделите имеющееся значение на 255. Это делается автоматически в вашем пиксельном шейдере. Всегда проще работать со значениями с плавающей точкой, чем с байтовыми данными, хранящимися в файле растрового изображения.
Рис. 7.3 |
Рис. 7.4 |
Большинство пикселей в карте нормалей светло-синие (RGB 128, 128, 255), и это значит, что большинство результирующих векторов направлены вверх, подобно Vector3 (0, 0, 1). Если вся текстура светло-синяя, это значит, что каждый вектор равен (0, 0, 1) и карта нормалей не оказывает влияния, поскольку это поведение вы и так получите при обычном расчете рассеиваемого освещения.
Пиксели карты нормалей обычно сохраняются в касательном пространстве, а это значит, что каждая нормаль сохраняется относительно поверхности полигона. Касательным пространство называется потому что касательную матрицу можно сконструировать с помощью касательных и векторов бинормалей каждой вершины. Если у вас нет бинормалей, они могут быть легко созданы с помощью векторного произведения нормали и касательной (рис. 7.4). В предыдущей главе у вас для каждой точки были только координаты местоположения, нормаль и координаты текстуры. Этих данных недостаточно для наложения нормалей, вам необходим, как минимум, еще вектор касательной для каждой вершины, чтобы построить очень важную касательную матрицу. Без этого наложение нормалей не будет выглядеть правильно, и вам даже не следует пытаться реализовать его, если у вас нет этих касательных.
Вы можете думать о касательной и бинормали, как о векторах указывающих на следующую вершину в направлении X и Y, но они остаются ортогональными (расположенными под углом 90 градусов) к вектору нормали. Это значит, что даже, если в вашей трехмерной геометрии нет никаких данных касательных, вы можете сконструировать их самостоятельно, перебрав все вершины (это непросто, но возможно).
Проблема заключается в том, что часто вершины используются для нескольких полигонов и иногда слияние текстур выполняется неправильно. Для простого наложения рассеивания это не имеет значения, поскольку при вычислениях освещения используется только вектор нормали, но если вы полагаетесь на касательное пространство, чтобы преобразовывать ваши нормали для каждого пикселя, данные должны соответствовать. Это означает, что гораздо лучше генерировать данные касательных в программе трехмерного моделирования, которая использовалась для создания трехмерной геометрии. Она знает, какие касательные используются и экспортируемые нормали гораздо лучше подходят для шейдера наложения нормалей. Внутри вершинного шейдера у вас есть доступ только к обрабатываемой в данный момент вершине; вы не можете получить следующую вершину или построить новый вектор, указывающий на нее. Это значит, что очень важно передавать правильные данные касательных в вершинный шейдер. Если вы просто определите входную структуру данных шейдера с поддержкой данных касательных, это не значит, что приложение сможет передавать правильные данные. По умолчанию в конвейере содержимого используются файлы .x и .fbx, в которых нет никаких данных касательных (рис. 7.5).
Рис. 7.5 |
Это означает, что у вас есть только правильные нормали, но отсутствуют касательные, что не позволяет конструировать касательное пространство для наложения нормалей. Для простых объектов, таких как яблоко, это не имеет большого значения, но чем больше кривых и координат текстур в вашей модели не совпадают, тем худшие проблемы появляются. На рис. 7.6 показаны ошибки касательных в модели яблока, которые вы можете увидеть, запустив демонстрационное приложение из предыдущей главы.
Рис. 7.6 |
Узнав обо всех этих проблемах, вы можете перейти к трехмерным моделям и шейдерам.
Сложно создать хорошо выглядящий трехмерный объект, подобный показанному на рис. 7.1; создатель моделей обычно тратит много времени на разработку высокополигональной версии, в которой будет несколько миллионов полигонов. Вы, вероятно, потратите много времени на тонкую настройку шейдеров и их оптимизацию, чтобы позволить движку отображать одновременно множество объектов, сохраняя приемлемую частоту кадров. Чтобы сохранить простоту вы будете использовать несколько шикарно выглядящих объектов астероидов с несколькими миллионами полигонов в высокополигональных версиях, но не так трудно создать и низкополигональную версию в которой всего около 1000 полигонов. Эти модели будут использованы в следующей главе для игры Rocket Commander XNA.
Чтобы еще больше оптимизировать производительность, для тех же астероидов были созданы модели с 500, 200 и даже 50 полигонами, что не очень сложно, поскольку используется тот же самый высокополигональный объект и его карта нормалей хорошо смотрится на низкополигональных версиях. В тестах производительности версия с 200 полигонами была наиболее эффективной. Она визуализировалась почти так же быстро, как и версия с 50 полигонами, но выглядела намного лучше. Версия с 500 полигонами не дала заметного улучшения внешнего вида, но визуализировалась дольше, особенно на слабых компьютерах. Таким образом, в финальной версии игры я остановился на версиях с 1000 полигонов и с 200 полигонами для всех астероидов (рис. 7.7).
Рис. 7.7 |
На рис. 7.8 показаны три используемые для астероидов текстуры. Поскольку все модели астероидов в Rocket Commander используют шейдер наложения параллакса, вам для каждого объекта необходима карта рассеивания, карта нормалей и карта высот. Как упоминалось ранее, карты нормалей сжаты и поэтому выглядят красноватыми, вместо синего оттенка по умолчанию в исходных картах высот. Краснота сжатой карты нормалей объясняется тем, что канал красного цвета максимально светлый, а альфа-канал, который хранит оригинальные значения красного канала, невидим. За дополнительными деталями обращайтесь к шейдеру ParallaxMapping.fx в следующей главе и доступным текстурам карт нормалей.
Рис. 7.8 |
В 2005 году я написал небольшую утилиту, названную NormalMapCompressor (рис. 7.9), которая доступна в моем дневнике на http://abi.exdream.com. Она может быть использована для преобразования карт нормалей в сжатые версии, экономящие до 75% дискового пространства и памяти текстур без значительной потери качества. Это действительно здорово, потому что если вы на секунду задумаетесь о размерах текстур, то увидите много проблем с видеопамятью только при использовании наложения нормалей или наложения параллакса. В прошлом обычными были текстуры рассеивания с небольшим разрешением — 128 × 128, 256 × 256 или иногда 512 × 512.
Рис. 7.9 |
Текстура, размером 128 × 128, хранимая как несжатое растровое изображение с 32-разрядным цветом (RGBA) занимает 128 * 128 * 4 байта = 64 Кбайт. Сжатая как DXT1 файл .dds она занимает только 8 Кбайт. Файл JPG займет около 5 – 10 Кбайт. Для текстуры размером 256 × 256 эти размеры увеличиваются в четыре раза (256 Кбайт несжатая, 32 Кбайт для DXT1, около 20 Кбайт для JPEG), и для 512 × 512 все еще раз четырехкратно увеличивается. Даже в последнем случае вам потребуется только 1 Мбайт, если вы вообще не будете использовать сжатие.
Но сегодня обычны текстуры размером 1024 × 1024, 2048 × 2048 и даже 4096 × 4096. Даже если вы используете только текстуры 1024 × 1024, вам уже потребуется 4 Мбайт памяти текстур для карты рассеивания, если вы не используете сжатие. Поскольку для крутых шейдерных эффектов вам также нужна карта нормалей и карта высот, требования внезапно повышаются до 12 Мбайт только для одной отдельной текстуры. Если ваша игра использует несколько сотен различных текстур, это полностью непрактично. Вы должны использовать сжатие и думать о загрузке только тех данных, которые действительно необходимы. Вот почему игры для консолей часто перезагружают данные уровня и обычно не используют текстуры с очень высоким разрешением; на это требуется слишком много памяти, особенно на Xbox 360 и старых консолях.
Текстуры рассеивания могут быть легко сжаты с DXT1, чтобы занимать в восемь раз меньше места, чем RGBA или в шесть раз меньше места для RGB. Если вам нужны альфа-данные для прозрачных объектов, можно использовать DXT5, который занимает в четыре раза меньше места, чем несжатые данные RGBA. Вы можете сказать «Почему бы не использовать файлы .jpg или .png?». Да, вы можете сохранить пространство на диске, используя файлы .jpg, но вам придется распаковывать эти файлы, когда будете сохранять их в видеопамяти. В файлах .dds замечательно то, что формат DXT совместим с современным графическим оборудованием, и его использование экономит также и видеопамять.
У карт нормалей главная проблема в том, что алгоритм сжатия DXT был разработан для цветовых данных; зеленый канал поедает большую часть сжатия, потому что зеленый цвет более визуально важен. Синий канал в картах нормалей не так важен, потому что он обычно очень яркий (смотри рис. 7.3), а красный канал после сжатия DXT1 будет похож на дерьмо. В пиксельных шейдерах 1.1 вы не можете заново нормализовать нормали из-за чего во многих случаях длина вектора будет неправильной и освещение с картой нормалей будет выглядеть неправильным и неубедительным (рис. 7.10).
Рис. 7.10 |
Это действительно трудно исследовать без переключения между включенным и выключенным сжатием. На экране визуальные отличия явно видимы. NormalMapCompressor решает эту проблему используя формат сжатия DXT5 и сохраняя красный канал в альфа-канале, который в DXT5 сжимается отдельно. Это обеспечивает немного лучший коэффициент сжатия для зеленой и синей составляющих, поскольку красный канал теперь полностью засвечен. В шейдере вам надо будет только переключить красный и альфа каналы и все будет хорошо. Если вы пристальнее взглянете на NormalMapCompressor (рис. 7.9), то увидите, что у красного канала самое высокое варьирование (информационная панель слева), а это значит, что в красном канале наибольшее количество различных цветовых значений и вариаций. Синий канал обычно весьма однообразен. Проверьте несколько карт нормалей, чтобы увидеть различные значения варьирования.
Для карт рассеивания и высот вы можете использовать DXT1. Если вы используете только наложение нормалей, карта высот вам не нужна, и вы можете сэкономить еще немного видеопамяти. Теперь текстура размером 1024 × 1024 использует только 512 Кбайт для карты рассеивания, 1 Мбайт для карты нормалей (DXT5 занимает в два раза больше места) и еще 512 Кбайт для необязательной карты высот, если она нужна. Если вы используете уровни детализации текстуры, добавьте для этого еще 25%. С 2 – 2.5 Мбайт можно работать дальше и все будет выглядеть почти так же хорошо, как и при 12 Мбайт несжатых текстур 1024 × 1024. Я также должен упомянуть, что благодаря качественным эффектам наложения нормалей и наложения параллакса текстуры часто выглядят гораздо четче, и вы можете обойтись текстурами 512 × 512 вместо 1024 × 1024, как сделал я в Rocket Commander. Поразительно, что полная игра Rocket Commander помещается в 10 Мбайт, включая две звуковых дорожки, множество звуковых эффектов, пять типов астероидов, пять моделей предметов, модель ракеты, много других моделей, текстур и эффектов и четыре замечательно выглядящих уровня. На Xbox 360 она занимает немного больше, поскольку там невозможно воспроизведение .mp3, но поразительно показать трехмерную игру, занимающую менее 15 Мбайт, работающую при разрешении экрана 1920 × 1080 пикселей (HDTV) и выглядящую четче, чем большинство коммерческих игр для Xbox 360.
В последующих главах книги мы также обсудим варианты использования наложения нормалей для ваших собственных трехмерных данных, таких как треки и другие буферы вершин со сгенерированными трехмерными данными, которые замечательно выглядят с картами нормалей и не требуют много пространства для текстур, благодаря обсуждавшейся здесь технике сжатия.
netlib.narod.ru | < Назад | Оглавление | Далее > |