Краткая история и основные принципы работы 3D-ускорителей

Итак, данная статья представляет собой краткий экскурс в историю развития видеокарт, а так же описание общего алгоритма построения трёхмерного изображения Вашим ускорителем трехмерной графики.

Начнём традиционно с истории. Первые видеокарты не были даже не 3D-ускорителями, а не были ускорителями вообще. Они служили лишь как ЦАП (цифро-аналоговый преобразователь) – преобразовывали данные, рассчитанные центральным процессором (представляющий собой цифровой код) в аналоговый сигнал, доступный для отображения на мониторе. Но сложность изображений росла, и дальше так продолжаться не могло. Тенденция усложнения изображений привела к появлению 2D- ускорителя – видеокарты, имеющий свой собственный, пусть и простейший процессор, бравший на себя часть функций, разгружая центральный процессор. Но когда появилась необходимость строить 3D изображения – ситуация осложнилась.

Чтобы построить, скажем, простой фрагмент стены, процессору нужно было выполнить следующие операции: сначала необходимо выделить грани этого объекта, затем наложить текстуры, добавить освещение... а когда таких объектов сотни, их форма сложна, они движутся и перекрываются, отбрасывают тени и т.д. задача становится невероятно сложной. Для помощи процессору в решении этой задачи и были созданы ускорители трёхмерной графики, о работе которых и пойдёт речь в этой статье.

Для начала выясним – а что нам, собственно, нужно, чтобы построить трёхмерное изображение. Необходимо сказать, что видеоакселератор НЕ занимается расчётом того, какую сцену он должен сейчас строить. Определением 3D сцены – объектов, точки наблюдения и т.п. занимается центральный процессор. Как только все необходимые данные собраны – они передаются видеокарте, которая начинает построение сцены.

Построение сцены происходит следующим образом:

  1. Загрузка в чип ускорителя вершин из памяти акселератора, со своими атрибутами.
  2. Далее, каждая из вершин попадает в вершинный процессор. Подробнее мы вернемся к нему чуть позже.
  3. Далее происходит установка треугольников - трансформированные и освещенные (т.е. уже обработанные вершинным шейдером или фиксированным T&L блоком) вершины объединяются по три, и происходит подготовка данных для закраски треугольника. Здесь же происходит отсечение невидимых – перекрытых (overdraw) поверхностей.
  4. Далее, треугольник разбивается на фрагменты, часть которых признаются невидимыми и отбрасывается в ходе Z-теста на уровне фрагментов (то, что мы называем Hidden Surface Removal, HSR). Как правило, конечным результатом этого процесса являются видимые (или частично видимые) фрагменты 2х2 пикселя – так называемые «квады», подлежащие закраске. Именно такие фрагменты наиболее удобны для быстрой закраски пикселей.
  5. Далее квады отправляются на установку фрагментов. Здесь для каждого из них вычисляется (интерполируется) множество необходимых параметров, таких как текстурные координаты, MIP уровень, векторы, установочные параметры анизотропии и т.д.
  6. После установки и интерполяции параметров происходит закраска фрагментов. Существенной частью этого процесса является выборка и фильтрация текстур.
  7. После того как значения цвета были рассчитаны, в пиксельном процессоре происходит смешение (блендинг) - если включен соответствующий режим - или просто запись результирующих значений цвета и глубины в буфер кадра. На этом этапе может происходить несколько дополнительных операций.
  8. Ну а из буфера кадра происходит вывод изображения на экран, иногда после дополнительных проходов для АА.

Отметим, что всё это непрерывная очередь.

Всем желающим подробнее узнать про алгоритм построения сцены современными ускорителями я рекомендую изучить великолепный материал под названием DirectX Current, подготовленный нашими коллегами с iXBT.

Итак, каждая стадия построения изображения очень ресурсоемка, требует множества расчётов. Вполне логичным выглядит шаг вынесения их из CPU и переправка на специализированный процессор видеокарты. Особенно если учитывать, что графические данные имеют потоковый характер, и вычислительную потребность значительно большую, чем логическую.

Каждый новый виток развития ускорителей представляет собой некое поколение, поэтому для начала введём стандартизацию поколений (понимать поколения можно по-разному – я приведу лишь один вариант):

  1. Первое поколение, которое было более-менее распространено – это акселераторы, использующие API Direct3D 5 и Glide. Представителем первых была NVIDIA Riva128, а вторых – 3Dfx Voodoo. Карты этого поколения брали на себя только последнюю часть построения сцены – текстурирование и закраску. Все предыдущие этапы выполнял CPU.
  2. Второе поколение использовало API Direct3D 6, также в это время началось стремительное возрождение API, разработанного SGI – OpenGL. Представителями карт того времени были NVIDIA RivaTNT и ATI Rage. Это было практически эволюционное развитие карт предыдущего поколения.
  3. Третье поколение – Direct3D 7. Именно тогда появились карты, снабженные TCL-блоком, снимавшим с CPU значительную часть нагрузки. Этот блок отвечал за трансформацию, освещение и отсечение. (TCL - Transformaton-Clipping-Lighting) Теперь видеокарта строила сцену самостоятельно – от начала до конца. Представителями этого поколения стали NVIDIA GeForce256 и ATI Radeon.
  4. Четвёртое поколении – очередная революция. Кроме прочих новых возможностей API Direct3D 8 (и 8.1) эти карты принесли с собой самую главную возможность – аппаратные шейдеры. Причину их появления мы опишем чуть позже. Представляют это поколение NVIDIA GeForce 3,4 и ATI Radeon 8500, 9000, 9100, 9200.
  5. Пятое поколение – это, в основном, развитие шейдерных технологий (версия 2.0), и попытка ввести АА и АФ в ряд обязательных к использованию функций. Это поколение, поддерживает API Direct3D версии до 9.0b включительно, представляют ATI RADEON 9500, 9600, 9700, 9800, Х800, а также NVIDIA GeForce FX 5200, 5500, 5600, 5700, 5800, 5900, 5950.
  6. Шестое поколение – это поколение DirectX9.0c. Оно пока включает в себя только одну серию NVIDIA GeForce 6 и платы GeForce 6800Ultra/6800GT/6800 на базе чипа NV40. Эти карты поддерживают шейдеры версии 3.0, и предлагают некоторые другие возможности.

Теперь, определившись с общим устройством конвейера и поколениями видеокарт, мы более подробно рассмотрим вершинный и фрагментный процессоры, а также определимся в отличиях версий соответствующих шейдеров.

Причиной появления шейдеров стало отсутствие какой-либо гибкости у фиксированного TCL блока. Быстро стало понятно, что ждать момента, когда производители внесут очередную порцию функций в TCL блок видеокарт – не лучший выход. Такой подход не устраивал никого. Разработчикам не нравилась мысль, что для того, чтобы внести в, например, игру новый эффект им надо годик подождать выхода нового ускорителя. Производителям тоже не светило ничего хорошего – им бы пришлось постоянно увеличивать как сами чипы, так и драйверы к ним. Это и стало причиной появления шейдеров – программ, способных настраивать ускоритель так, как того требует следующая сцена.

Шейдер – это программа, которая загружается в ускоритель, и конфигурирует его узлы для обработки соответствующих элементов. Теперь нет ограничения заранее заданным набором способов обработки эффектов. Теперь стало возможно составлять из стандартных инструкций любые программы (ограниченные спецификациями используемой версией шейдера), задающие необходимые эффекты.

Шейдеры делятся по своим функциям на вершинные и фрагментные (пиксельные): первые работают с вершинами и треугольниками, заменяя собой функциональность TCL блока (сейчас он практически исчез – в случае необходимости он эмулируется специальным вершинным шейдером). Фрагментные же шейдеры служат для создания программ обработки фрагментов размеров 2х2 пикселя – квадов. Они необходимы для реализации некоторых текстурных эффектов.

Шейдеры также характеризуются номером версии - каждая последующая добавляет к предыдущим всё новые и новые возможности. Наиболее свежей спецификацией фрагментных и вершинных шейдеров на сегодняшний день является версия 3.0, поддерживаемая через API DirectX 9с, - на нее и будут ориентироваться как производители акселераторов, так и разработчики новых игр. На их поддержку аппаратурой стоит обращать внимание и пользователям, желающим приобрести современную игровую видеокарту.

Обратим внимание на главное отличие шейдеров 3.0 от предыдущих версий (кроме 2.0а) – это DFC – Dynamic Flow Control – динамическое управление потоком. С одной стороны – это великолепная возможность, позволяющая заметно повысить скорость построения сцены, с другой – лишние транзисторы, и как вытекающие побочные эффекты, лишнее тепло и ниже максимальные частоты. Давайте более подробно опишем эту возможность.

Представим себе ситуацию, когда для какой-либо вершины (или фрагмента) шейдер нужно выполнить не весь, а только 12% от него. В случае применения DFC мы выполним лишь те необходимые 12%, основываясь на параметрах объекта. Без DFC мы вынуждены выполнить шейдер целиком. Легко заметить, что с DFC мы получим выигрыш без малого в 10 раз, при этом, заплатив пониженной производительностью на вершинах, для которых нужно выполнить все 100% шейдера. Именно по этому в сети до сих пор не утихают споры – хорошо это или нет. Я не буду проводить сравнения – тут каждый сам делает выбор, а лишь отмечу, что я лично сторонник третьей шейдерной модели.

Первые шейдеры состояли всего из нескольких команд, и их нетрудно было написать на низкоуровневом языке ассемблера. Хотя сложность отладки ассемблерного кода поначалу отпугнула от шейдеров многих разработчиков… Но с ростом сложности шейдерных эффектов, насчитывающих иногда десятки и сотни команд, возникла необходимость в более удобном, высокоуровневом языке написания шейдеров. Их появилось сразу два: NVIDIA Cg (C for graphics) и Microsoft HLSL (High Level Shading Language) - последний является частью стандарта DirectX 9. Достоинства и недостатки этих языков, и прочие нюансы будут интересны только программистам, так что подробнее на них мы останавливаться не станем. Отметим только, что Cg не получил широкого распространения, ввиду появления нового, более продвинутого GLSL – аналога HLSL для API OpenGL.

Не будем проводить сравнения этих языков, а лишь отметим, что, как и в любой другой области, применение языков высокого уровня значительно сокращает объём необходимых работ по написанию и отладке кода.

Теперь давайте посмотрим, что необходимо для того, чтобы получить все те возможности, которые дает столь полезная технология, как шейдеры последнего поколения. А нужно следующее:

  • Самая свежая версия DirectX, на данный момент это DirectX 9.0с;
  • Видеокарта с поддержкой DirectX 9с;
  • Самые свежие драйверы видеокарты (в более старых некоторые функции могут отсутствовать);
  • Игра, использующая все эти возможности.

Особенно хочется отметить последнее обстоятельство – обычно на внедрение новых технологий уходит 2 - 3 года, но благодаря высокоуровневым языкам создания шейдеров это время заметно сократилось.

Уже в ближайшее время мы ожидаем появления новых продуктов, использующих шейдеры 3.0 – Stalker, DooM 3, Serious Sam 2 и др.

А не сей веселой ноте, позвольте завершить наше изложение.

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Дата публикации:
Автор публикации: