Энциклопедия третьего измерения, часть 3
В прошлый раз мы научились превращать несвязный набор цифр в законченные трехмерные сцены. Но при описании процесса рендеринга я намеренно упустил один важный этап — текстурирование. Текстурирование — очень важный процесс, и ему надо посвятить отдельную статью. Ведь именно Ее Величество Текстура делает виртуальный мир ярким, атмосферным, запоминающимся, впечатляющим, настоящим. Не будь текстур, играли бы мы сейчас с вами в экшены с квадратными комнатами, где среди однородных стен сражаются серые чертики, которые могут разве что присниться в страшном сне… Итак, что же такое текстура и зачем она нужна? Выгляньте в окно. Если там не глубокая ночь и не пепелище после атомной бомбардировки, вы увидите много интересного и красивого. Вот, например, простые стволы деревьев. Они испещрены бороздками, их цвет меняется от светло-коричневого до черного, и так далее. У противоположного дома кирпичная кладка тоже не так проста, как кажется на первый взгляд. Каждый кирпич выдается вперед, он не однороден, его
Рис. 1. Самая простая текстура.
украшают щербинки и тени от листьев соседнего тополя. Между кирпичами цемент принимает самые причудливые формы, особенно если дом строили не в совсем трезвом виде (для России привычная картина). И как все это чудо смоделировать? Сколько миллионов полигонов уйдет на реалистичное отображение всех этих красот? На акселераторе какого поколения все это сможет отобразиться с приличными FPS? Представьте, что мы каждый кирпич сделали красным параллелепипедом, а между ними проложили серые плоскости, призванные изобразить цемент. Смотреться все это будет ужасно. И даже такая простая “стена” отъест несколько тысяч полигонов. Так что же делать? Ответ прост — накладывать текстуры! Текстура как двигатель прогресса Текстура — это двумерная битовая карта (или попросту картинка), которая накладывается на полигон и изображает фактуру его поверхности. То, что нельзя смоделировать полигонами, можно просто нарисовать. Причем если на
Рис. 2. Без текстур этот монстр смотрелся бы довольно странно.
поверхности, которую мы моделируем текстурой, нет сильно выдающихся или неоднородных деталей, она будет смотреться реалистично. Текстуры накладывают на стены домов, траву, листья, машины, одежду, лица персонажей и многое-многое другое. __ По аналогии с обычной картинкой, которая состоит из пикселей, текстура состоит из текселей. Есть две основные модели представления цвета и света в трехмерной графике. Первая — знакомая многим RGB или RGBA. Здесь: R — красная составляющая, G — зеленая, B — синяя, и A — коэффициент прозрачности. Составляющая A стала поддерживаться на уровне операционной системы только в WinXP. А в графических библиотеках ( Glide , DirectX и OpenGL ) она поддерживалась с самого начала. Другая распространенная модель — HSB. Ее составляющие: Hue (H) — длина волны отраженного от объекта или поглощенного света (это также угол между вектором чистого красного и текущего цвета, символизирует цвет объекта), Saturation (S) — насыщенность цвета, количество серого по отношению к цвету, и Brightness (B) — яркость. На практике RGB
Рис. 3. Один из самых неприятных глюков с текстурами, происходящий по вине Z-буфера.
выражается целым числом. В играх очень часто можно выбрать разрядность цвета между 16 и 32 битами. Сегодня шестнадцатибитный цвет — анахронизм. Если позволяет ваша видеокарта, всегда выбирайте 32 бита. Иногда в настройках или технической литературе упоминают 24-битный цвет. Что это такое? Обычно под выражением “24-битный цвет” подразумевают две совершенно различных цветовые схемы. Иногда это тот же 16-битный цвет с 8-битной альфа-составляющей (прозрачность). В этом случае реального прироста количества цветов нет. Так что не дайте себя обмануть. Но вот в другом случае 24-битный цвет означает, что берутся два 16-битных цвета и над ними производится какая-нибудь математическая операция. Так вот эмулируется цвет более высокого порядка, почти 32-битный. Текстура накладывается строго по координатам. Координаты разработчики обычно включают в сопроводительную информацию о полигонах. Не думайте, что достаточно нарисовать красивую текстуру, и сразу наступит вселенское счастье. Очень важно грамотно “растянуть” текстуру по поверхности объекта и присвоить ей нужные координаты. Вспомните Mafia. Какие там были замечательные лица персонажей! И надо отдать должное не только художникам (текстуры лиц красивые, но и только), но и моделлерам, которые точно “насадили” лица героев на каркасы их полигональных физиономий. В последнее время разработчики часто потчуют нас обещаниями о фотореалистичных текстурах. Что это значит? Да по большей части только то, что текстуры не нарисованы, а сняты цифровым фотоаппаратом, а потом обработаны и причесаны. К сожалению, это не всегда значит, что объект будет выглядеть действительно фотореалистично. Честное сходство с фотографией (да хоть бы только с фотографией, куда уж там до реальности) еще долго будет оставаться в мечтах геймеров. Может быть, выход Doom III сможет что-то изменить?
Рис. 4. MIP-уровни текстуры можно представить в виде пирамиды. Каждый последующий уровень в 2 или 4 раза меньше предыдущего.
Фильтрация и MIP Mapping При работе с текстурами существует немало проблем, которые приходится решать весьма изощренными способами. У экрана есть свое разрешение и определенное количество пикселей, которые на нем можно отобразить в данной области экрана. Но в соответствующей области виртуального мира количество текселов (текстурных пикселов) может быть и больше, и меньше, чем на экране. Если текстура двумерная и параллельна поверхности экрана, то мудрить ни к чему: каждый тексел перенесется на экран “как есть”. Но что делать, если на какую-то область экрана приходится текселов больше, чем возможных пикселов? Какой цвет назначить пикселу, на который в сцене приходится несколько разноцветных текселов? Еще хуже, если луч от пиксела на экране попадает между текселами на сцене, то есть текселов меньше, чем пикселов. Что тогда делать? __ Задача формулируется так: как определить цвет конкретного пиксела на экране, если в сцене на него приходится меньше или больше текселов. На заре трехмерной графики эту задачу решали методом, который называется Point Sampling. От каждого пиксела на экране опускается луч вглубь сцены. Тексел, который ближе всех оказывается к этому лучу, и накладывается на экран. При этом неизбежно возникают ошибки. Если текселов на каждый пиксел слишком много, часть информации просто теряется. Если же на пиксел приходится меньше одного тексела (когда объект расположен близко к камере), пробелы восполняются несуществующими пикселами. На практике это выглядит так: когда игрок приближается к **
**
Рис. 5. Пример грубой ошибки разработчика: число MIP- уровней явно недостаточно.
объекту, то с удивлением замечает, что виртуальные пикселы его текстуры становятся просто гигантских размеров. Да и само изображение распадается на пикселы, как будто на большом экране поставили маленькое разрешение. Если хотите понаблюдать этот метод в деле, поставьте в любой игре “софтверную эмуляцию эффектов” вместо нормальной 3D-акселерации. Такой низкокачественный метод современного игрока не устраивает, поэтому существуют методы так называемой продвинутой фильтрации. Билинейная фильтрация (Bi-Linear Filtering) — первая ласточка перемен. Когда ее впервые применили в играх, геймеры были в восторге. В чем ее суть? Вместо того чтобы определять цвет пиксела по одному текселу, его цвет получается в результате интерполяции (усреднения) цветов четырех соседних текселов. Если объект расположен далеко от камеры, его текстура почти не искажается. А когда объект недалеко от камеры, и текселов не хватает, интерполяция создает расплывчатое изображение этой области.
Рис. 6. А вот так это должно было выглядеть на самом деле…
Но со временем и эта роскошь перестала устраивать геймерствующий люд. Билинейная фильтрация хорошо работает только для полигонов, которые параллельны или почти параллельны экрану. Дело в том, что четыре соседних тексела, которые берутся для интерполяции, — это почти круг. Если плоскость наклоняется, круг медленно, но неумолимо превращается в эллипс. Но интерполируются текселы по-прежнему по кругу. От этого постоянно накапливаются небольшие ошибки. После определенного угла наклона ошибки становятся уже заметными, текстура фильтруется геометрически неправильно, а игрок наблюдает сильные искажения. Есть и еще одна проблема: если игрок видит текстуру намного дальше или намного ближе, чем предполагал разработчик, текстура очень сильно корежится фильтрацией. До поры до времени это не заметно, но со временем в рисунок текстуры закрадываются сильные искажения, своеобразные “помехи”. Обе проблемы надо было решать, и решение быстро нашлось в симбиозе двух технологий: трилинейной фильтрации (Tri-Linear filtering) и мип-мэппинга (MIP Mapping). Multum in Parvo (MIP) с латыни переводится как “много в одном”. Это изречение стало лозунгом технологии MIP Mapping. Для одной и той же поверхности разработчик создает несколько копий текстуры с разной степенью детализации. Каждая следующая версия текстуры меньше или больше предыдущей в 4 раза. Данная версия текстуры называется мип-уровнем , а все мип-уровни вместе — мип-каскадом. Когда игрок удаляется от текстуры, она сменяется на мип-уровень с меньшим разрешением, а когда приближается — с большим. Плюсов у этой технологии много. Во-первых, независимо от того, на каком расстоянии находится наблюдатель от объекта, текстура отображается без геометрических искажений. Во-вторых, далеко расположенные текстуры не отъедают много вычислительных ресурсов акселератора, ведь полноразмерная текстура 128х128 заменяется на 64х64, а то и на 32х32. Но есть и существенные недостатки. В памяти приходится хранить несколько копий одной и той же текстуры. Переходы между мип-уровнями выполняются резко. Еще год назад вы могли наблюдать в не очень качественных играх характерный артефакт — полосы мип-мэппинга, выражающиеся в резких переходах между разными версиями одной текстуры. По-настоящему востребованной оказалась технология, совмещающая MIP Mapping и билинейную фильтрацию и добавляющая в этот процесс несколько новых этапов. Речь идет о трилинейной фильтрации. Цвет конкретного пиксела на экране определяется в результате интерполяции цветов текселов двух соседних мип-уровней. При этом над каждым мип-уровнем предварительно проводится билинейная фильтрация, а для интерполяции берутся не 4, а 8 соседних текселов. За счет этого переход между мип-уровнями становится плавным и незаметным.
Рис. 7. Сравнение разных способов фильтрации текстур. Как видно, анизотропная трилинейная фильтрация — самая лучшая. Но не каждый компьютер способен ее потянуть.
С самого начала предполагалось, что разработчики сами должны озаботиться созданием текстур разной степени детализации для разных мип-уровней. Однако разработчики очень часто либо ленятся и добавляют в игру слишком мало мип-уровней, либо вообще не создают мип-уровней, оставляя одну базовую текстуру. Но и на таких недобросовестных разработчиков нашлась своя управа — авто-мип-мэппинг. Драйвер по основной текстуре сам рассчитывает необходимое количество мип-уровней. К сожалению, получается это у него порой не очень хорошо. Итог: если в игре вам предложат выбрать между билинейной и трилинейной фильтрацией, выбирайте трилинейную, но не надейтесь на чудо. __ Венцом эволюции стала анизотропная фильтрация (Anisotropic filtering) — самая сложная и ресурсоемкая из всех. После всех предыдущих фильтраций и мип-мэппингов все еще остается одна проблема. Более или менее красиво эти алгоритмы работают для текстур, которые располагаются параллельно экрану. Если текстура сильно наклонена, фильтрация в одном направлении выполняется больше, чем надо, а в другом — меньше. Результат плачевный. Надо спасать знамя фотореалистичной графики! Поэтому и появилась анизотропная фильтрация. Сразу скажу, что под этим словосочетанием подразумевается несколько совершенно разных технологий. Какую из них применяет производитель вашего акселератора — еще вопрос.
Рис. 8. Объемный туман позволяет добиться интересных эффектов.
Во-первых, можно решить проблему “в лоб”. Если восьми текселов на пиксел не хватает, почему бы не интерполировать 16, а то и 32? В результате текстура отображается красиво даже на сильно наклоненных поверхностях. Вот только красоту эту увидит далеко не всякий игрок. Даже сейчас очень мало акселераторов, которые могут справиться с такими объемами информации, ведь требуется гигантская ширина пропускания памяти. Во-вторых, некоторые разработчики пытаются решить проблему умом. Сцена рендерится не целиком, а “плитками”, например, 32х32 пиксела. Для каждой плитки (тайла) применяются очень сложные алгоритмы, которые “делают нам красиво”. Но и на такое далеко не каждый акселератор способен… На практике анизотропная фильтрация пока применяется крайне редко. Итак, как мы выяснили, текстура прибавляет реалистичности изображению. Но все равно не может полностью заменить сложные полигональные поверхности, которые выглядят значительно лучше. Ведь для них просчитываются перспектива и тени. С другой стороны, и ресурсов эти поверхности отъедают много. Разработчики долгое время пытались найти компромисс между качеством и производительностью — и придумали bumpmapping. Он позволяет малой кровью создавать объекты с реалистичной рельефной поверхностью с помощью рельефных карт. Кроме самой текстуры, разработчик готовит карту высот (height map), которая описывает рельеф в виде высот, или же карту смещений нормалей (normal dispmap), описывающую рельеф с помощью нормалей.
Рис. 9. Посмотрите на эту картинку, и вы поймете, что же скрывается за мудреным словом “интерполяция”.
Рельефные карты бывают двух видов: карты освещенности и карты смещений. Карта освещенности содержит светлые и темные пятнышки, которые символизируют неоднородность освещения из-за неоднородности рельефа. Карта смещений задает искажения для environment map (об этом звере мы поговорим в следующий раз). Рельефные карты обоих типов накладываются на основную текстуру альфа-смещением. Игроку кажется, что поверхность действительно рельефна. **Некоторые хитрости __**Для повышения реалистичности изображения разработчики игр используют несколько интересных хитростей. К ним относятся туман и мультитекстурирование. С туманом все крайне интересно. Сперва эта технология применялась для уменьшения нагрузки на акселератор или процессор (когда акселераторов еще не было). На каком-то расстоянии от игрока возникала легкая дымка, которая скрывала объекты от пытливого взора геймера. Если игрок не очень хорошо их видел, можно было уменьшить качество рендера, отключить текстуры и так далее.
Рис. 10. Процедурные текстуры почти не занимают места на диске и отлично подходят для изображения земли, бетона или кирпичной кладки…
Туман постепенно нарастал до тех пор, пока полностью не скрывал все объекты. Начиная с этого расстояния объекты можно было вообще не рендерить — все равно их никто не увидит. Несмотря на надуманность и искусственность этого тумана, игроки относились к нему вполне лояльно. Хотя лет пять назад обычной была ситуация, когда противоположный угол комнаты утопал в тумане. Время шло, акселераторы становились мощнее, и туман из маскировочного элемента превратился в элемент декоративный. Если чуть-чуть подернуть окружающие объекты дымкой, обстановка кажется более красивой. Кроме того, туманом можно замаскировать некоторые неприятные эффекты — например, те же полосы мип-перехода. Туман бывает двух видов — полигонный (per-polygon) и пиксельный (per-pixel). При полигонном тумане значение затенения для каждого тексела интерполируется из значений затененности вершин полигона, а при пиксельном — рассчитывается независимо для каждого тексела. Конечно, пиксельный туман выглядит красивее и естественнее. Есть еще один вид тумана — объемный туман (Volumetric fog). Он не имеет никакого отношения ни к обычному туману, ни к текстурам вообще, потому что формируется из процедурных частиц. Его мы тоже как-нибудь разберем. Мультитекстурирование — очень важный механизм, являющийся основой для многих текстурных преобразований. В общем случае мультитекстурирование — это наложение на один полигон нескольких текстур с использованием альфа-
Рис. 11. Bump mapping позволяет с помощью двумерных текстур реалистично изображать рельефные поверхности.
канала или какого-нибудь логического оператора. В акселераторах специально для этого существует несколько блоков текстурирования , которые работают примерно так же, как конвейеры у процессора. С помощью мультитекстурирования накладываются карты освещенности, отражения, текстуры с детализацией. О последних стоит поговорить поподробнее. В Serious Sam был такой пункт в опциях графики: Detailed Textures. Некоторые игроки включали его — и замечали, что текстуры вроде бы как стали детальнее. Но эти самые “детализованные текстуры” практически не уменьшали FPS. У геймерствующего люда появились сомнения относительно честности этого пункта. Оказалось, что никаких более детальных текстур там нет и в помине. Просто на уже существующую текстуру накладывалась другая полупрозрачная текстура, на которой были нанесены в определенном порядке черные и серые точки и пятнышки. Игрокам казалось, что текстура действительно стала более детализованной или более рельефной. Накладывалась “психологическая” текстура. Причем одна _
_
Рис. 12. Текстура и…
на всю игру. Очень скоро этот метод переняли другие разработчики, и теперь “детализованные” текстуры можно увидеть в каждом втором экшене. Процедурные текстуры Наверняка вы видели так называемые “демки”. Я говорю не о демо-версиях игр, а о своеобразных программах очень маленького размера (до 64 кб), которые способны в течение нескольких минут демонстрировать отличные анимированные трехмерные сцены. “Игромания” неоднократно писала о демо-сценах. Почему я вспомнил о них сейчас? В условиях, когда каждый байт на счету, создатели демо-сцен просто не могли включать в ролик настоящие текстуры — они занимали бы слишком много места. Поэтому они используют процедурные текстуры, то есть текстуры, которые на лету генерируются по хитрым алгоритмам, зачастую фрактальным. Занимают такие текстуры всего ничего — несколько уравнений и параметров, которые умещаются в десятки байт, а выглядят очень красиво.
Рис. 13. …карта высот.
У процедурных текстур есть несколько преимуществ перед “классическими”. Во-первых, они могут неограниченно масштабироваться в реальном времени. Для них не нужны мип-уровни. Во-вторых, они не занимают в оперативной памяти драгоценного места и “разворачиваются” только непосредственно перед рендером. В-третьих, они легко могут быть трехмерными. Немудрено, что и разработчики компьютерных игр обратили на процедурные текстуры свое внимание. Ведь такие текстуры практически не занимают места на диске. Процедурными текстурами можно имитировать дерево, мрамор, камень, кирпичную кладку, мокрый асфальт, стекло — словом, любой однородный материал. Процедурные текстуры такого типа создаются по “шумовым” алгоритмам _ Перлина_. До последнего времени рендер процедурных текстур был довольно ресурсоемкой задачей. Но не так давно появились специальные алгоритмы, заточенные под MMX и 3D Now! , которые позволяют накладывать процедурные текстуры с такой же скоростью, что и обычные. Скорее всего, очень скоро мы увидим процедурные текстуры во многих играх. *** * *** Итак, мы разобрали по косточкам Ее Величество Текстуру. Еще один тайный сундучок 3D-графики вскрыт, свитки изучены, а знания надежно засели в головах. В следующий раз мы пристально взглянем на программные интерфейсы и библиотеки, такие как OpenGL , DirectX ( Direct 3D ), Mesa и некоторые другие. От этих библиотек зависит очень многое, но, к сожалению, именно они — самое слабое звено в цепочке 3D-рендеринга. Так что в следующий раз мы не только подробно изучим их, но и поймем, как их использовать с толком и пользой.