Philip Taylor "Programmable
Shaders for Direct 8.0" Опубликовано 15 января 2001
Программные шейдеры Direct3D, Часть 1
Добро пожаловать в DirectX. В прошлом месяце я представил вам обзор DirectX Graphics 8.0. Сейчас я хочу показать вам новые возможности программируемого конвейера. Появление программируемого конвейера является основным нововведением DirectX Graphics 8.0.
Программируемость
Имеются всего две программируемые секции в архитектуре Direct3D: вершинные шейдеры и пиксельные шейдеры. Вершинные шейдеры применяются до сборки вершин (vertex assembly) и операциями над ними. Пиксельные же шейдеры применяются после вызова функций DrawPrimitive или DrawIndexedPrimitive и генерируют пиксели, которые выводятся на экран. Программные шейдеры для операций над вершинами и пикселями позволяют достичь качества кино-эффектов в масштабе реального времени. Программируемость конвейера по-настоящему дает разработчику полную свободу в выборе способа реализации своих замыслов. Например, с помощью шейдеров могут быть реализованы следующие возможности:
Программируемый конвейер реализует схему общего синтаксиса для определения новых функций, необходимых разработчику. При фиксированном конвейере, чтобы добавить те же функции в существующий API, необходимо определять новые режимы, флаги и т.д. Следовательно, с повышением мощности аппаратных средств (увеличения разрядности цвета, размеров текстур, количества вершинных потоков и т.д.), постоянное добавление новых функций, становится невероятно сложной задачей как для API, так и для драйвера. С другой стороны, программная модель позволяет легко решит эту задачу. Вам не нужно знать все возможные режимы, достаточно иметь представления о машинной архитектуре конвейера, чтобы реализовать желаемый алгоритм.
Несомненно, программируемый конвейер обеспечивает хорошую масштабируемость и возможность дальнейшего развития API. Аппаратные возможности продолжают быстро развиваться, и новый конвейер позволить быстро приспособить API под них. Это может быть сделано с помощью добавления:
Когда столкнулись с проблемой увеличения и усложнения конвейера, шейдеры явились наилучшим решением масштабирования. Значит, чтобы добавить новые функции в конвейер, достаточно лишь немного изменить внутренний код Direct3D. Наконец, программируемая модель выглядит более предпочтительно, потому что разработчики понимают ее лучше, чем аппаратные средства. API Direct3D, помогая разработчикам, преобразует возможности аппаратных средств в программную парадигму.
Рисунок 1. Архитектура программируемого конвейера Direct3D
Вершинные шейдеры
Вершинные шейдеры заменили в старом фиксированном конвейере Direct3D функции модуля трансформации и освещения (T&L modul). Теперь обработка вершин выполняется вершинными шейдерами, которые обрабатывают одну вершину одновременно. Последовательность операций над вершинами в конвейере такова:
В конце этих операций получается индивидуальная, готовая к растеризации вершина, которая содержит преобразованную позицию (x, y, z, и w), цвет, текстурные координаты, интенсивность тумана и размер точки. Последующая обработка заключается в преобразовании позиции вершины на экран, сборке вершин в примитивы и отсечении полученных примитивов. Вершинный шейдер не выполняет эти операции. Каждый шейдер состоит из декларации, которая определяет интерфейс шейдера, и последовательности команд.
Раздел декларации определяет внешний интерфейс вершинного шейдера. Он связывает входные данные с его внутренними регистрами. Для этого в Direct3D появляется понятие потока. Поток - это унифицированный массив, каждый элемент в котором содержит информацию о вершине: позицию, цвет и т.д. Потоки позволяют графическим чипам параллельно использовать несколько буферов вершин. Следовательно:
Метод IDirect3DDevice8::SetStreamSource связывает буфер вершин с потоком, создавая ассоциацию между вершинными данными и одним из нескольких потоков, для последующей обработки примитивов. Ссылки на данные из потока не происходит до вызова функции рисования примитивов, как например IDirect3DDevice8::DrawPrimitive.
Интерпретация элементов входной вершины программируется с помощью команд шейдера. Функция вершинного шейдера определяется массивом команд, применяемых к каждой вершине. Элементы же выходной вершины также программируются с помощью инструкций шейдера. Функция шейдера оперирует основным типом данных - вектором, потому что они широко используются в 3-х мерной графике и применяются во всех основных ее операциях. Все значения нормализованы и предварительно преобразованы из формата буфера вершин в формат шейдера.
Функция шейдера - это небольшая программа, и очень важно понимать ее значение. Шейдеры используют: входные регистры для доступа к вершинным данным, константные регистры для хранения констант, например матриц или источников света, временные регистры хранят результаты вычислений, адресный регистр, выходные регистры, и массив команд.
Тип
ресурса
|
Макс.
количество
|
Входные регистры (Input registers) |
16
|
Константные регистры (Constant registers) |
96
|
Временные регистры (Temp registers) |
12
|
Адресный регистр (Address register) |
1
|
Выходные регистры (Output registers) |
зависит
от реализации
|
Максимальное количество команд |
128
|
Таблица 1. Ресурсы вершинного шейдера
Имейте ввиду, что шейдер одновременно обрабатывает только одну вершину, и поэтому не может выполнять функций групповой обработки вершин: отсечение, тесселляцию и т.д. Формат команды шейдера такой:
op dest, src0,src1,src2, где
op
- команда
dest - регистр, куда записываются результаты команды
scr[0,1,2] - входные регистры , количество которых
зависит от конкретной команды
Команды вершинных шейдеров бывают двух типов: основные команды и макро-команды. Хочется отметить, что в шейдерах нет команд перехода и циклов. Каждая основная команда выполняется за один такт, а макро-команды, т.к. являются сокращением последовательности нескольких основных команд, выполняются за один такт и более.
Команда
|
Описание
|
Количество
тактов
|
add
|
сумма |
1
|
dp3
|
трех-компонентный dot-product |
1
|
dp4
|
четырех-компонентный dot-product |
1
|
dst
|
вектор расстояния |
1
|
expp
|
экспоненциал 10-битной точности |
1
|
lit
|
световой коэффициент |
1
|
logp
|
логарифм 10-битной точности |
1
|
mad
|
произведение и сложение |
1
|
max
|
максимум |
1
|
min
|
минимум |
1
|
mov
|
копирование |
1
|
mul
|
произведение |
1
|
rcp
|
обратная величина |
>1
|
rsq
|
обратный квадратный корень |
>1
|
sge
|
установить, если больше или равно |
1
|
slt
|
установить, если меньше |
1
|
sub
|
разность |
1
|
Таблица 2. Основные команды вершинного шейдера
Команда
|
Описание
|
Количество
тактов
|
exp
|
экспоненциал двойной точности |
12
|
frc
|
доля |
3
|
log
|
логарифм двойной точности |
12
|
m3x2
|
3x2 произведение вектора и матрицы |
2
|
m3x3
|
3x3 произведение вектора и матрицы |
3
|
m3x4
|
3x4 произведение вектора и матрицы |
4
|
m4x3
|
4x3 произведение вектора и матрицы |
3
|
m4x4
|
4x4 произведение вектора и матрицы |
4
|
Таблица 3. Макро-команды вершинного шейдера
Команда
|
Описание
|
Количество
тактов
|
def
|
определить константу |
|
vs
|
версия и тип шейдера |
|
Таблица 4. Другие команды вершинного шейдера
Примечание, команда версии шейдера должна быть первой, и команды определения констант могут быть после команды версии шейдера и до всех остальных команд.
Рисунок 2. Архитектура вершинного шейдера
Пример вершинного шейдера, выполняющего одно матричное преобразование с константным цветом:
vs.1.0
dp4 r0.x, v0, c[0] ; базовое преобразование
dp4 r0.y, v0, c[1]
dp4 r0.z, v0, c[2]
dp4 r0.w, v0, c[3]
mov oD0, c[4] ; заносим константный цвет в выходной регистр цвета
mov oPos, r0 ; заносим преобразованную позицию в выходной регистр позиции
Этот шейдер содержит шесть основных команд и выполняется за шесть тактов.