Основные понятия
3D-ускоритель работает с примитивами: треугольниками и линиями - в
своей основе эти примитивы являются непрерывными объектами, и лишь при
растеризации превращаются в пикселы. Этот процесс называется сэмплингом,
и ему присущ артефакт под названием алиасинг. При сэмплинге цвет
пиксела определяется цветом полигона, которому принадлежит центр пиксела.
Примеры алиасинга:
- Лестничный эффект (Stairstep or "jaggies")
- Муар (Moire Patterns), разрушение текстур (Texture disintegration)
- Временной алиасинг: мерцание объектов, вращение колеса в другую
сторону, анимация лестничного эффекта
Для решения проблемы алиасинга предназначен антиалиасинг. Антиалиасинг
делится на две категории - пространственный (spatial) и временной
(temporal).
Пространственный антиалиасинг бывает двух видов:
- Суперсэмплинг (supersampling), или полный антиалиасинг
(full-scene antialiasing)
- Полигональная фильтрация (polygon filtering), или краевой
антиалиасинг (edge antialiasing)
Полный антиалиасинг
Суперсэмплинг - наиболее популярный метод, применяемый в 3D-ускорителях
(прежде всего из-за своей простоты). Каждый пиксел состоит из субпикселов,
для каждого вычисляется цвет, а затем усреднением получается цвет пиксела.
Конкретное представление пиксела называется субпиксельной маской
(к примеру 2x2, 4x4). Чем больше субпикселов отводится на каждый пиксел,
тем качественнее получается антиалиасинг. Маска 4x4 считается оптимальной
с точки зрения качества, но при этом скорость заполнения снижается в
16 раз. С другой стороны, более простая маска обеспечивает более высокую
скорость, но и менее качественное изображение. В любом случае суперсэмплинг
снижает алиасинг, но кардинально не решает проблему.
Теоретически при суперсэмплинге субпиксельная маска может быть любой
(к примеру при стохастическом суперсэмплинге субпикселы расположены
в псевдо-случайном порядке). В 3D-ускорителях применяется регулярная
маска размером от 2x1 до 4x4. Окончательный цвет пиксела вычисляется
усреднением либо на основе весов, либо без них. К примеру для маски
3x3 центральный субпиксел должен иметь больший вес, чем угловые субпикселы,
поэтому усреднение на основе весов имеет лучший результат.
Существует несколько реализаций суперсэмплинга:
- Выполняется рендеринг при разрешении в большем разрешении, затем
выполняется масштабирование до реального разрешения. Например при
разрешении 800x600 и субпиксельной маске 2x2 рендеринг выполняется
при виртуальном разрешении 1600x1200. Это требует вчетверо большего
объема памяти. Эта методика стала стандартной в игровых 3D-ускорителях.
- Фреймбуфер делится на фрагменты (tiles).
Каждый фрагмент рендерится в большем разрешении, соответствующему субпиксельной маске, а затем масштабируется до меньшего разрешения.
Эта методика применяется в PowerVR 250.
- Рендеринг выполняется в несколько проходов (для маски 2x2 - в 4 прохода).
Каждый раз все координаты смещаются на некоторую дельту.
Промежуточный результат хранится в буфере-акумуляторе, и результат каждого прохода суммируется с ним.
Преимущество метода - нетребовательность к объему памяти (даже для маски 4x4 требуется объем памяти, как при обычном рендеринге без антиалиасинга).
Метод применялся в некоторых профессиональных OpenGL-ускорителях.
Краевой антиалиаиснг
Полигональная фильтрация - метод для сглаживания краев полигонов.
Пикселы, на которые приходятся края полигонов, обрабатываются особым образом.
Пикселы, которые находятся внутри полигона, обрабатываются как обычно.
Фильтрация полностью убирает лестничный эффект.
В качестве фильтра чаще всего используют технику усреднения по площади (area averaging).
Цвет пиксела определяется на основании того, насколько каждый полигон перекрывает данный пиксел.
Например как показано на рисунке ниже, пиксел перекрывают два полигона: A и B.
Метод усреднения по площади определяет области перекрытия пиксела полигонами.
Пусть полигон A занимает 40% площади пиксела, а полигон B - 60%.
Результирующий цвет в этом случае определяется цветами A и B c весовыми коэффициентами 40% и 60% соответственно.
К сожалению, многим реализациям краевого антиалиасинга присущи ограничения, например невозможность сглаживания текстурированных полигонов.
Кроме того, пересечения полигонов также невозможно обработать, так как невозможно узнать заранее в каком месте пересекутся полигоны.
Для линий и точек метод тот же самый, в данном случае считается, что линии и точки имеют ненулевую площадь (в этом случае говорят об антиалиасинге линий и точек).
Помимо этого, существует методика апроксимации антиалиасинга - размывание краев (edge blur) после растеризации.
В отличие от истинного антиалиасинга этот метод только ухудшает качество изображения.
Временной антиалиасинг
Всем известно, что в кино колесо часто крутится в другую сторону.
Природа этого явления та же - непрерывный во времени процесс при сэмплинге превращается в дискретные фазы.
Если колесо успевает сделать за один кадр 3/4 оборота, то наблюдателю будет казаться, что колесо сделало 1/4 оборота в другую сторону.
Для борьбы с этим применяется временной антиалиасинг - размытие в движении (motion blur).
Здесь также существует метод апроксимации - комбинирование нескольких фаз в одном кадре.
В примере с колесом этот метод вызовет "фантомные" картинки, а колесо все равно будет крутиться "не в ту сторону".
Текстуры с детализацией
Основные понятия
Текстуры с детализацией (detail textures; composite textures) - эффективная технология моделирования высокодетализированных текстур.
Текстуры с детализацией - замена текстур высокого разрешения.
Основная причина того, что разработчики не используют текстуры высокого разрешения - в том, что такие текстуры занимают очень много места.
При локальном текстурировании большие текстуры быстро заполнят видеопамять, при DiME - полосу пропускания шины AGP.
При мипмэппинге добавление мип-уровня более высокого разрешения увеличивает размер мип-каскада в 2,5 раза.
К примеру 16-битная текстура размером 256x256 занимает 170 KB.
Добавление мип-уровня 512x512 добавляет к размеру каскада 256 KB, 1024x1024 - еще 2 MB.
Если использовать такие текстуры в качестве покрытий для всех стен, пола и потолка, то ресурсы будут быстро исчерпаны.
Представьте кирпичную стену. На большом расстоянии наблюдатель видит отдельные кирпичи, но каждый кирпич разглядеть он не в состоянии.
Однако приблизившись, он замечает мелкие неровности каждого кирпича.
Можно было нарисовать большую текстуру кирпичной стены, где на маленьких мип-уровнях будут кирпичи без неровностей, а на больших - кирпичи с неровностями.
Однако так ли уж необходимо рисовать уникальные трещинки на каждом кирпиче?
Не лучше ли сделать все кирпичи одинаковыми, сэкономив кучу текстурной памяти?
Glide-версия Unreal именно так и делает - использует вместо одной большой текстуры две маленькие - на основную текстуру накладывается текстура с детализацией.
Без текстуры с детализацией при сильном приближении наблюдателя к объекту текстура поверхности становится размытым.
Рассмотрим на примере.
Без использования текстуры с детализацией при сильном приближении текстура становится размытой.
Текстура с детализацией несет информацию о мелких деталях, поэтому текстура не размывается при сильном приближении.
Принцип действия
Текстура с детализацией - обычно маленькая текстура, которая накладывается на основную с помощью альфа-смешения или мультитекстурирования.
Степень видимости текстуры с детализацией зависит от расстояния до наблюдателя.
На большом расстоянии текстура с детализацией не видна вообще.
По мере приближения она постепенно начинает "проявляться".
Glide напрямую поддерживает текстуры с детализацией - на Voodoo 2 основная текстура и текстура с детализацией может накладываться за один проход с помощью двух TMU.
Степень "видимости" текстуры с детализацией зависит от мип-уровня. Разработчик делает мип-каскад, в котором показаны фазы видимости.
Когда поверхность далеко, Voodoo 2 делает выборку мип-уровня, в котором "пусто" - наблюдатель видит только основную текстуру.
По мере приближения Voodoo 2 выбирает следующие мип-уровни в которых текстура с детализацией видна лучше.
При самом близком приближении выбирается мип-уровень, где текстура с детализацией видна лучше всего.
Очевидно, что в рассмотренном случае текстура с детализацией "проявляется" не плавно, а скачками, но в действительности скачков практически не видно, так как текстура с детализацией - сама по себе полупрозрачный объект.
Для того, чтобы текстура с детализацией становилась видимой там, где надо, используется механизм LOD-биасинг (LOD biasing), который указывает расстояния до наблюдателя, где происходит переход мип-уровней.
Вернемся к нашему примеру. Пусть основная текстура имеет размер 210x160, текстура с детализацией - 80x80.
Представьте, что мы накладываем основную текстуру на прямоугольник 640x480, при этом она растягивается и теряет четкость.
Наша текстура с детализацией - черный квадрат со светлыми вкраплениями. Именно эти белые вкрапления и "перенесутся" на основную текстуру.
Альфа-смешением или мультитекстурированием мы складываем текстуру с детализацией с основной текстурой, как показано на рисунке.
Создание текстуры с детализацией
Создание текстуры с детализацией - своего рода компрессия текстуры.
Берется одна большая текстура, на выходе получается две маленькие текстуры.
Взяв за основу 8-битную текстуру размером 640x480, которая занимала 194 KB, я получил 8-битную основную текстуру 210x160 (17 KB) и 4-битную текстуру с детализацией 80x80 (2 KB), практически не потеряв в качестве.
Степень сжатия - 10:1. Однако не с каждой текстуру можно так сжать.
Суть этого сжатия - разделение оригинала на высокие и низкие частоты.
Низкие частоты - оригинал, потерявший все мелкие детали.
Высокие частоты - это выделенные мелкие детали.
Низкие частоты станут основной текстурой после обработки, в высокие - текстурой с детализацией.
Разделить на низкие и высокие частоты оригинал несложно.
Для получения низких частот достаточно использовать фильтр "gaussian blur".
Для получения высоких частот - вычесть из оригинала низкие частоты (то есть то, что получилось после фильтра).
Схематично это можно представить так:
В итоге обе картинки имеют размер оригинала (640x480). Низкие частоты можно просто геометрически сжать (я сжал в 9 раз).
С высокими частотами сложнее - структура должна быть либо периодической, либо хаотической.
Надо выбрать небольшой фрагмент, причем такой, чтобы его можно было зациклить и получить нечто похожее на полную картину высоких частот.
При рендеринге происходит обратный процесс.
В качестве исходных данных выступают две текстуры, которые несут информацию о низких и высоких частотах.
Освещение и затенение
Основные понятия
Освещенность - количество отраженной энергии в данной точке, в качестве параметров участвуют интенсивность и направление света и параметры поверхности.
Рассчет освещенности (lighting) производится на основании выбранной модели освещенности (lighting model; reflectance model) и метода затенения (shading).
Затенение - техника интерполяции для снижения количества вычислений при рассчете освещенности.
Параметры, которые интерполируются при затенении, обычно называются интерполянтами.
Интерполянтами могут быть как скаляры (например R,G,B компоненты цвета), так и векторы (например нормали).
В реальной жизни объекты взаимодействуют со светом весьма нетривиально.
Модель иллюминации (illumination model; global lighting model) описывает распределение света в среде на основании формы и расположения всех объектов и источников света.
В компьютерной графике часто используются аппроксимации вместо реальной физики света.
Самая общая модель освещенности при аппаратном рендеринге - Модель Ламберта (Lambertian reflection mode).
Затенение Гуро
При затенении Гуро (Gouraud shading) освещенность рассчитывается в вершинах, и цвета интерполируются по всему полигону.
Затенение Гуро является наиболее общим методом для аппартной реализации.
Затенение Гуро не учитывает перспективу, поэтому для большей реалистичности требуется перспективная коррекция.
Синоним затенения Гуро - вершинное освещение (vertex lighting), так как освещенность рассчитывается только для вершин.
1) В каждой вершине рассчитывается освещенность
2) Цвета интерполируются по краям полигона
3) Цвета интерполируются по сканлайнам
4) Полигон, затененный по методу Гуро
Затенение Гуро имеет существенный недостаток - ему свойственен артефакт под названием "stars" (звезды).
Взгляните на изображение шара, затененного по Гуро. Белый блик, который в идеале должен иметь форму эллипса, имеет форму звезды.
Затенение Фонга
При затенении Фонга (Phong shading) интерполируются не цвета, а нормали, которые задаются для вершин полигона.
Синоним затенения Фонга - попиксельное освещение (per-pixel lighting).
Для каждого пиксела по нормали рассчитывается вектор наблюдения и вектор света, по которым рассчитывается цвет пиксела посредством какой-либо модели освещенности.
Чаще всего используется модель освещенности Фонга, но это необязательно.
Заметьте: модель освещенности Фонга (Phong lighting) и затенение Фонга (Phong shading) - разные понятия.
Вектора наблюдения и света также могут интерполироваться или аппроксимироваться.
1) В каждой вершине задана нормаль
2) Нормали интерполируются по краям полигона
3) Нормали интерполируются по сканлайнам
4) Для каждого пиксела рассчитывается освещенность
Интерполяция нормали обычно выполняется из рассчета одно векторное сложение и одна ренормализация для каждого пиксела.
Для определения цвета требуется одна операция скалярного умножения (для моделей Фонга и Ламберта).
Существуют различные методики быстрой аппроксимации, некоторые из них приближаются к скорости затенения Гуро.
Затенение Фонга лучше подходит для моделирования бликов (specular highlights) на глянцевых поверхностях.
Для этого должна использоваться модель освещенности Фонга, в которой для каждого пиксела вычисляется вектор отражения.
Источники света
Бесконечно удаленный источник света (infinite light source).
Математически самый простой источник света (если не считать ambient света).
Направление и интенсивность света одинаковы для всех точек.
Точечный источник света (point light source). Интенсивность света обратно пропорциональна расстоянию до источника.
Прожектор (spot light source). Область света - конус, интенсивность уменьшается с увеличением угла.
Аппаратная поддержка
В настоящее время в 3D-ускорителях используется только затенение Гуро, причем в некоторых случаях с перспективной коррекцией (например на TNT).
Затенение Фонга совместно с моделью Фонга дает гораздо более качественный результат, нежели затенение Гуро.
Но это требует во-первых аппаратной реализации геометрической стадии конвейера рендеринга, так как для каждого пиксела производятся операции с векторами.
То есть затенение Фонга возможно только в 3D-ускорителях с геометрическим сопроцессором.
Во-вторых реализация логики в железе требует большой элементной базы, что сильно увеличит стоимость чипов.
А это не имеет смысла, так как сегодняшние приложения не будут использовать эту функцию.
Геометрический сопроцессор может брать на себя рассчет освещенности для ограниченного числа источников света (обычно точечных или бесконечно удаленных).
Это разгружает центральный процессор. Сегодня только 3D-ускорители класса High-end имеют геометрический сопроцессор.
Практически все 3D-ускорители поддерживают зеркальную составляющую цвета, и могут делать блики, используя затенение Гуро.
Важно понимать, что термин "освещение" в контексте 3D-ускорителей применим для вычислений в реальном времени.
Карты освещенности (lightmaps) или карты среды (environment maps) не имеют отношения к освещению.
MPEG2
Основные понятия
Видеопоток MPEG2 состоит из кадров двух типов: эталонные (reference) и предсказанные (predicted).
Эталонный кадр (I-кадр) кодируется целиком из одного кадра исходного несжатого материала, такие кадры вставляются в видеопоток через определенный интервал.
Видеопоток MPEG2 может состоять целиком из I-кадров, при этом коэффициент сжатия будет невысоким, но лишь такой поток можно редактировать.
В большинстве случаев картинка на соседних кадрах меняется несильно.
Сжатие MPEG2 призвано устранить подобную избыточность.
Для этого используется механизм предсказания кадров на основании эталонных кадров, называемый "компенсация движения".
Если объект на экране движется, то можно предположить, что все его точки двигаются одинаковым образом.
Значит для описания движения достаточно "сфотографировать" объект и указать вектор движения - этих данных будет достаточно для декодера.
Исходное изображение делится на блоки размером 16x16, и каждому блоку присваивается вектор движения.
Предсказанные кадры бывают двух видов: B и P. В P-кадре (predicted) кодируется ошибка предсказания - отличия между исходым изображением и изображением, получаемым в результате предсказания.
Ошибка предсказания кодируется примерно так же, как и I-кадры.
B-кадр (bidirectional interpolated) получается путем интерпооляции ближайших I или P кадров с учетом вектора движения.
Структура MPEG2-потока, то есть в каком порядке идут друг за другом I, P и B-кадры, определяется MPEG2-кодера.
I-кадры кодируются с помощью алгоритма преобразования Фурье, который снижает избыточности внутри одного кадра.
Обратная операция - обратное преобразование Фурье (iDCT) - служит для декомпрессии. DCT работает с блоками размером 8x8.
Аппаратная поддержка
Аппаратная реализация компенсации движения и iDCT сильно снижает загрузку центрального процессора при воспроизведении DVD.
Компенсацию движения поддерживают:
- ATI Rage Pro, LT Pro, Rage Mobility, Rage 128, Rage 128 Pro
- Intel i810
- NeoMagic MagicMedia256AV
- NVidia GeForce 256
- S3 Savage 3D, S3 Savage 4, S3 Savage 2000
- SiS 6326DVD, SiS 300
- Silicon Motion Lynx3D
- Trident Blade3D
iDCT поддерживают:
- ATI Rage 128, Rage 128 Pro, Rage Mobility
- SiS 6326DVD
Также как большинство видов видеокомпрессии, MPEG-2 работает с цветовым пространством YCrCb, и видеопоследовательноть предварительно сжимается YUV-компрессией.
Поэтому функция преобразования цветового пространства YCrCb в RGB (как и маштабировании изображения по двум осям) также является критической частью графического ускорителя.
Геометрическая обработка
Трансформация и рассчет освещенности
В связи со скорой интеграцией геометрических процессоров (geometry processor) в игровые 3D-ускорители, остро назрел вопрос о том, что же такое геометрическая обработка вообще, и в каком виде она может быть аппаратно реализована.
Геометрическая обработка в английском варианте называется TNL - Transformation and Lighting, трансформация и рассчет освещенности.
В названии собственно указаны две основные функции, которые должен выполнять геометрический процессор.
Трансформация (transformation) - операция над вектором для перевода его в другую систему координат.
Программа обычно рассчитывает всю сцену в мировой системе координат - это удобно и естественно.
Затем все вершины нужно перевести в систему координат камеры, а затем спроецировать их в двухмерную систему координат экрана.
Растеризатор любого 3D-ускорителя работает с псевдотрехмерными объектами - вершинами в двухмерной системе координат.
Хотя z-буфер хранит данные о глубине (z-координате), растеризатор не знает что такое нормаль, что накладывает ограничения на трехмерные операции определенного рода (об этом речь ниже).
Трансформация - это просто умножение вектора на матрицу, которая описывает взаимное расположение систем координат.
В простейшем случае вершины можно трансформировать из мировой системы координат в двухмерную систему координат экрана.
Рассчет освещенности (lighting) - более сложная операция.
Его цель на первый взгляд тривильна - определить цвет вершины.
Однако на сцене может быть несколько источников света - и каждый может принимать участие в рассчете освещенности вершины.
Например, 8 источников света потребуют в 8 раз большего объема вычислений.
Второй момент: у света есть две составляющае - диффузная (diffuse) и зеркальная (specular).
Диффузная составляющая света после соприкосновения с объектом распространяется во все стороны равномерно.
Зеркальная - наоборот, неравномерно (пример - блик на металическом шаре).
Из-за этой неравномерности освещенность зависит от точки наблюдения.
Чтобы учитывать точку наблюдения, необходимо разбить трансформацию на два этапа: сначала все вершины переводятся в систему координат камеры, выполняется рассчет освещенности, затем все переводится в двухмерную систему координат экрана, то есть объем вычислений также увеличивается.
Пиксельные и вершинные операции
Все операции делятся на вершинные (per-vertex) и пиксельные (per-pixel). (Субпиксельные (subpixel) операции являются частным случаем пиксельных.)
Сначала центральный процессор или геометрический процессор выполняет рассчет освещенности для вершин.
Затем трансформированные и освещенные вершины передаются 3D-ускорителю.
Блок подготовки треугольников (triangle setup) выполняет настройку всех нужных параметров вершин и интерполирует их по всему полигону.
После этого растеризатор выполняет пиксельные операции.
Обратите внимание, что я не уточнаю какие именно операции выполняются конвейером.
Многие операции могут быть как вершинными, так и пиксельными.
К примеру туман - его можно рассчитать в вершинах, а затем интерполировать по всему треугольнику (вершинный туман, vertex fog), а можно рассчитать в каждом пикселе (пиксельный туман, per-pixel fog), используя z-буфер.
Естественно, что пиксельные операции гораздо лучше вершинных. Но затенение (shading) сегодня выполняется только на вершинном уровне.
Затенение Гуро и Фонга
Рассмотрим работу конвейера на следующем примере: затененный треугольник с текстурой.
Затенение - это та операция, ради которой выполняется рассчет освещенности.
Освещение рассчитывается для вершин (центральным процессором или геометрическим процессором).
Освещенность интерполируется по поверхности треугольника. Это - затенение Гуро (Gouraud shading), вершинное затенение.
Если освещенность не интерполировать, а рассчитывать в каждом пикселе, - это будет затенение Фонга (Phong shading), пиксельное затенение.
Почему же так трудно реализовать затенение Фонга? Дело в том, что на стадии растеризации требуется геометрическая обработка.
Сегодняшние геометрические процессоры помочь не могут, так как они выполняют только вершинные операции.
А затенение Фонга требует пиксельных векторных операций .
Как уже было сказано, растеризатор работает с пикселами - псевдотрехмерными объектами.
Так как у пиксела нет нормали, его нельзя трактовать как полностью трехмерный объект, и поэтому на стадиии растеризации невозможно привлечь геометрический процессор для каких-то рассчетов.
Для этого надо сохранять нормаль на стадии растеризации, а сохранять негде.
Некоторые ускорители, такие как Permedia 3, используют трюк с сохранением нормали в полях, для этого не предназначенных, например в RGB, но это нельзя назвать капитальным решением.
Еще один очевидный момент - сегодняшние геометрические процессоры могут обрабатывать до 3 млн векторов в секунду.
Для операций с вершинами эта цифра внушительна на фоне 1-1.5 млн векторов/сек для самых мощных центральных процессоров.
Для пиксельных операций вектора превращаются в пикселы, 3-4 млн пикселов/сек - это просто смешно.
Так что в ближайшие 2-3 года на не светит затенение Фонга и другие эффекты, основанные на пиксельной геометрической обработке, например наложение рельефа методом Блинна (метод Dot Product является его аппроксимацией).
Это потребует переработки конвейера и адаптации трехмерных API.
Аппаратная поддержка
Сегодня аппаратный TNL имеют только профессиональные OpenGL-ускорители, причем число источников света сильно ограничено.
Игровые 3D-ускорители нового поколения будут иметь аппаратный TNL, среди них уже наверняка будут чипы от NVidia, 3dfx и S3.
Аппаратный TNL поддерживается OpenGL и DirectX 7. Однако есть и подводные камни. Игра должна использовать TNL-функции этих API.
До сих пор практически все Direct3D-игры использовали собственные TNL-движки.
Такие игры не получат абсолютно никаких преимуществ от геометрического процессора и DirectX 7.
|