3D графика. Теория


Мы все играли в Quake (если вы не играли в Quake, поиграйте, пожалуйста, при первой же возможности). Многие из нас играли в него на каком-либо ускорителе трехмерной графики. Некоторые, поиграв или понаблюдав за играющими, решили купить этот самый "ускоритель", подвергая себя мучительному интеллектуальному труду, который хорошо знаком современному жителю крупного города: поиску товара по устраивающей его цене, и обладающего при этом оптимальными для него характеристиками. В большинстве случаев именно этот процесс способен неожиданно расширить познания человека в области, рассматриваемой им ранее лишь с потребительской точки зрения. Избежать такие проблемы я сейчас и попытаюсь помочь.

3D-конвейер

Большинство игр, а также множество более серьезных приложений следуют довольно стандартной схеме построения трехмерных изображений (далее мы будем называть этот процесс конвейером). Причем некоторые программы реализуют все стадии этого конвейера, некоторые же перекладывают часть работы на плечи аппаратных устройств, специальных библиотек программ (API), другие программы или даже на пользователя. Итак, конвейер состоит из следующих стадий:

    3D конвейер
  1. Определение состояния объектов (Situation modeling) — эта часть программы не имеет прямого отношения к компьютерной графике, она моделирует тот мир, который будет отображаться в дальнейшем. Например в случае Quake это — правила игры и физические законы перемещения игрока, искусственный интеллект монстров и т.д.
  2. Определение соответствующих текущему состоянию геометрических моделей (Geometry generation) — эта часть конвейера создает геометрическое представление текущего момента нашего маленького "виртуального мира".
  3. Разбиение геометрических моделей на примитивы (Tesselation) — эта первая действительно зависимая от "железа" стадия. На ней создается внешний вид объектов в виде набора определенных примитивов, разумеется, на основе информации из предыдущего шага конвейера. Наиболее распространенным примитивом в наше время является треугольник, и большинство современных программ и ускорителей работают именно с треугольниками. Не вдаваясь в математические подробности скажу, что на треугольники всегда можно разбить любой плоский многоугольник, и именно тремя точками можно однозначно задать плоскость в пространстве. К тому же, все мы знаем, что у Царя было три сына, а у Горыныча — три головы.
  4. Привязка текстур и освещения (Texture and light definition) — на этой стадии определяется, как будут освещены геометрические примитивы (треугольники), а также какие и как на них в дальнейшем будут наложены текстуры (Textures: изображения, передающие внешний вид материала объекта, т.е. негеометрическую визуальную информацию. Хороший пример текстуры — песок на абсолютно ровном пляже). Как правило, на этой стадии информация вычисляется только для вершин примитива.
  5. Видовые геометрические преобразования (Projection) — здесь определяются новые координаты для всех вершин примитивов исходя из положения наблюдателя и направления его взгляда. Сцена как бы проецируется на поверхность монитора, превращаясь в двухмерную, хотя информация о расстоянии от наблюдателя до вершин сохраняется для последующей обработки.
  6. Отбрасывание невидимых примитивов (Culling) — на этой стадии из списка примитивов исключаются полностью невидимые (оставшиеся позади или сбоку от зоны видимости).
  7. Установка примитивов (Setup) — здесь информация о примитивах (координаты вершин, наложение текстур, освещение и т.д.) преобразуется в вид, пригодный для последующей стадии. (Например: координаты точек буфера экрана или текстур — в целые числа фиксированного размера, с которыми работает аппаратура).
  8. Закраска примитивов (Fill) — на этой стадии, собственно, и происходит построение в буфере кадра (памяти, отведенной под результирующее изображение) картинки на основе информации о примитивах, сформированной предыдущей стадией конвейера, и прочих данных. Таких, как текстуры, таблицы тумана и прозрачности и пр. Как правило, на этой стадии для каждой точки закрашиваемого примитива определяется ее видимость, например, с помощью буфера глубин (Z-буфера) и, если она не заслонена более близкой к наблюдателю точкой (другого примитива), вычисляется ее цвет. Цвет определяется на основе информации об освещении и наложении текстур, определенной ранее для вершин этого примитива. Большинство характеристик ускорителя, которые можно почерпнуть из его описания, относятся именно к этой стадии, так как в основном именно эту стадию конвейера ускоряют аппаратно (в случае недорогих и доступных плат).
  9. Финальная обработка (Post processing) — обработка всей результирующей картинки как единого целого какими-либо двумерными эффектами.

Теоретически мы подковались, теперь попробуем разобраться, как все обстоит на самом деле. Во-первых, некоторые стадии этого конвейера могут быть переставлены местами, разбиты на части или совмещены. Во-вторых, они могут отсутствовать вообще (редко) или могут появится новые (часто). И, в-третьих, результат работы каждой из них может быть послан (в обход других стадий) обратно. Например, картинку, полученную на последней стадии, можно использовать как новую текстуру для 8-ой, реализуя таким образом эффект отражающих поверхностей (зеркал). Таких, как мраморный пол в игре Unreal.

3D API

Хорошо, теперь у нас есть последовательность действий. Но для получения результата надо определится с двумя вещами — кто какие стадии будет выполнять и как он это будет делать. У нас есть три основных кандидата на работу — сама программа (как правило, начальные стадии конвейера), библиотека прикладного программирования (интерфейс, API) и сам ускоритель. Впрочем, программы, не использующие ускоритель (Doom, Quake в программном режиме и т.д.), все стадии конвейера выполняют самостоятельно. В понятие библиотеки в данном контексте можно (без особого зазрения совести) поместить драйвера данного ускорителя, т.к., с точки зрения программы, они становятся частью библиотеки. Программ и ускорителей существует великое множество, а вот число общепризнанных библиотек весьма ограничено. Наиболее часто игры используют следующие библиотеки

    3D API
  • OpenGL — созданная первоначально для профессиональных графических станций и программ трехмерного моделирования библиотека. Постепенно она пришла на платформу PC, в основном благодаря стремительному прогрессу в области "железа" и игре Quake, использовавшей урезанный вариант этой библиотеки. Наличие поддержки этой библиотеки у ускорителя крайне желательно из-за большого числа новых игр, ориентированных на нее. Библиотека является в некотором роде высокоуровневой, так как берет на себя все действия, начиная с середины 4-ой ступени нашего конвейера. С одной стороны, это здорово облегчает работу программистам, с другой — способно несколько осложнить ее же, особенно при реализации нестандартных эффектов или необходимости использовать новые возможности ускорителя, выходящие за рамки OpenGL. Хороший пример полезности подобного подхода — возможность выпустить версию OpenGL, значительно ускоряющую (конкретно — геометрические преобразования) работу игр на новых процессорах с SIMD наборами команд — AMD 3Dnow! и Intel Katmai New Instructions (MMX2). Очевидным достоинством также является переносимость программ на другие, не Wintel-платформы. Существенным, но быстро исправляемым недостатком — отсутствие ее полного варианта для некоторых распространенных ускорителей.
  • Direct3D — библиотека, являющаяся частью Microsoft DirectX и поддерживаемая сейчас практически всеми ускорителями. Фактически представляет собой две библиотеки — низкоуровневую (начиная с 7 стадии) и высокоуровневую (с 5-ой). Результат — большая гибкость для программиста в реализации его идей и, как следствие, головная боль для него же из-за множества связанных с конкретной реализацией ускорителя параметров. Сейчас идет бурное развитие этого продукта Microsoft, и, судя по всему, версия 6 Direct3D (которая уже официально вышла) будет вполне достойным конкурентом OpenGL по своим возможностям и скорости.
  • Glide — собственная низкоуровневая библиотека (стадия 7 конвейера и далее) фирмы 3Dfx, добившаяся популярности благодаря большому распространению первых, действительно успешных ускорителей (на базе набора чипов Voodoo). Но, скорее всего, эта библиотека уйдет со сцены в ближайшие несколько лет. Она не поддерживается другими ускорителями и не будет ими поддерживаться (без разрешения 3Dfx это является незаконным), к тому же все новые игры, рассчитанные на нее, работают, как минимум, еще с одной из ранее описанных библиотек. Создатели неприятного исключения из этого правила (а именно, игры Unreal) уже анонсировали дополнение к игре с поддержкой Direct3D и OpenGL.

Еще существует около 10 библиотек низкого уровня, созданных разработчиками различных ускорителей; таких как: R-Redline фирмы Rendition, S3D Toolkit фирмы S3 и т.д. Как и следовало ожидать, программы, написанные специально для них, постепенно исчезают.

Хотя большинство современных ускорителей берут на себя лишь две-три последние стадии конвейера, существует важное и быстро прогрессирующие исключение из этого правила: появились первые доступные чипы с поддержкой геометрических преобразований, способные значительно увеличить скорость построения изображения на компьютерах с недостаточной вычислительной мощностью (а она, мощность, недостаточна по определению — иначе не было бы потребности в ускорителях вообще).

3D акселерация

3D ускоритель Итак, самый общий ускоритель состоит из геометрического процессора (Geometry Processor, пока практически всегда отсутствует), механизма установки (Setup engine, стадия 7 конвейера) и механизма отрисовки примитивов — закраски (Fill engine, стадии 8 и 9), который при детальном рассмотрении оказывается комбинацией двух блоков — обработки текстур (Texel engine) и обработки буфера кадра (Pixel engine). Производительность ускорителя зависит от процессора, производительности памяти, шины и самих обрабатывающих блоков. Как правило, приводятся два числа — максимальная пропускная способность (треугольников в секунду, triangle throughput) и максимальная производительность закраски (точек в секунду, fill rate). Такой подход возможен, но не очень корректен. Да и все мы знаем, что лучшим тестом является скорость игры, в которую мы любим играть (fps, кадров в секунду на каком-то стандартном наборе действий) и качество изображения (в цифрах не измерить). Именно эти параметры и необходимо узнать в первую очередь в Internet или, может быть, у соседа в подъезде, но не стоит при этом забывать о сильной зависимости числа кадров от объема памяти и мощности процессора компьютера этого самого соседа.

Если не вдаваться в глубокие технические подробности, закраска происходит следующим образом: блок обработки буфера кадра определяет, видна ли закрашиваемая точка, например, с помощью буфера глубин (Z Buffer). Если она видна, блок обработки текстур вычисляет цвет текстуры, соответствующий этой точке примитива. Здесь есть несколько важных моментов — интерполяция (filtering, сглаживание) и выбор уровня текстуры (mip-mapping). Первый обеспечивает изображение без резких прямоугольных пикселей, даже когда вы находитесь вблизи предмета и разрешение текстуры явно недостаточное. Второй устраняет искажения (странные узоры), возникающие при чрезмерном удалении от текстуры, выбирая аналог текстуры с меньшим разрешением. Фактически, существует несколько режимов комбинирования этих двух методов. Сейчас наиболее распространены билинейная фильтрация (bilinear filtering — вначале определяется необходимая для этого расстояния текстура, а затем значение цвета линейно интерполируется между четырьмя соседними точками текстуры, по каждой из координат на поверхности выбранной текстуры) и трилинейная (trilinear — две билинейных для двух текстур, с меньшим и большим разрешениями, затем результат интерполируется между ними). Последняя выглядит более приятно, но и требует больших затрат, так как не реализуется за один такт на современных ускорителях с одним блоком обработки текстур и, в результате, вдвое понижает скорость закраски. Сейчас появились первые ускорители с анизотропной (anisotropic — внутри куба, из двух наборов по 4 соседних точки текстур, итого по 8 точкам) фильтрацией, которая выглядит совсем хорошо, но и занимает еще вдвое больше времени. Затем вычисленный с помощью одного из вышеописанных методов цвет текстуры помещается в буфер кадра, заменяя находившееся там ранее значение, либо комбинируется с ним по какому-либо правилу (combination, blending, alpha-blending). Как минимум, ускоритель должен поддерживать режимы Источник*Приемник (Src*Dest) и Источник+Приемник (Src+Dest). Подобные возможности позволяют реализовать цветное освещение, эффекты типа металла или отражения, реализовывать трилинейную фильтрацию там, где она не поддерживается аппаратно и т.д. (так называемое многопроходное построение изображения, multipass rendering).

Как правило, для каждой точки текстуры, кроме RGB цвета, можно задать степень ее прозрачности (alpha, RGBA формат текстуры), которая будет использоваться ускорителем для регулирования воздействия источника на приемник. Подобным образом закрашиваются, например, полупрозрачные по краям взрывы и ореолы вокруг источников света в Unreal.

Не является жизненно необходимой, но очень ускоряет работу программ, использующих многопроходное построение изображения (например, Quake2 или Unreal), возможность мультитекстурирования (multitexturing). Фактически, это наличие двух (или даже более) блоков обработки текстур, способных одновременно вычислять два цвета по двум текстурам для одной точки примитива, а затем комбинировать их между собою. Подобной возможностью обладает Vоodoo2, RivaTNT и еще несколько пока "недоделанных" чипов.

Важны также точность представления цветов (16 бит — Hi-Color или 32 бита — True-Color, последний гораздо лучше) и точность буфера глубин (Z-Buffer, 16 бит хуже, 24 и 32 лучше), используемого для определения видимости отдельных точек примитива. Ошибки в определении глубины способны приводить к странным эффектам, например, к проглядывающим сквозь постамент ногам статуи в игре Unreal.

И последнее действие блока обработки буфера кадра — наложениие глобальных эффектов на готовую картинку. Например, туман, дымка или темнота (с точки зрения ускорителя это одно и то же), т.е. эффект, известный как fogging. Или используемый в RivaZX метод полноэкранного сглаживания (full screen antialiasing), когда изображение рассчитывается с большим разрешением, чем показывается пользователю, причем соседние точки изображения суммируются, и именно суммарное значение демонстрируется пользователю как одна точка. Подобным образом устраняются резкие границы между полигонами, изображению придается приятный "монолитный" вид. Существует также краевое сглаживание примитивов, приводящее к практически идентичному (чуть хуже) результату (edge antialiasing), но требующее гораздо меньше затрат на построение изображения.

Резюме

Займемся подведением итогов. Хороший ускоритель на ближайшее будущее это:

  • Поддержка OpenGL и Direct3D 5.X и 6.0
  • Достаточно большой fill rate (около 100 миллионов точек в секунду)
  • Достаточно большой triangle throughput (около 1 миллиона треугольников в секунду)
  • Аппаратная трилинейная фильтрация (tri-linear filtering)
  • Режимы alpha-blending, Src*Dest, Src+Dest
  • Хорошая глубина цвета — True-Color 32 бита
  • Хорошая точность Z-Buffer — 32 бита
  • Желателен multitexturing
  • Желательно сглаживание — antialiasing, full screen или edge
  • Достаточное количество памяти — не менее 8 Мб для карты на шине PCI или не менее 4 Мб на шине AGP

И самое главное: приятное для вас изображение и скорость в распространенных играх.




Дополнительно

iXBT BRAND 2016

«iXBT Brand 2016» — Выбор читателей в номинации «Процессоры (CPU)»:
Подробнее с условиями участия в розыгрыше можно ознакомиться здесь. Текущие результаты опроса доступны тут.

Нашли ошибку на сайте? Выделите текст и нажмите Shift+Enter

Код для блога бета

Выделите HTML-код в поле, скопируйте его в буфер и вставьте в свой блог.