 |
Написание фрагментных шейдеров с использованием расширения ATI_fragment_shader представляет собой довольно трудоемкий процесс, требуется знать довольно большое число символических констант и следить за аппаратными лимитами. Кроме того, команды задаются путем вызовов функций расширения, в результате чего для внесения изменений в шейдер требуется перекомпиляция проекта. Для решения этих проблем была разработана статическая библиотека AFSparse.
Содержание статьи:
- Введение
- Подключение библиотеки AFSparse
- Структура AFS-программы
- Команды
- Выходные регистры и модификаторы
- Входные регистры и модификаторы
- Ошибки в программах и отладка
- Примеры шейдеров на AFS
1. Введение
Библиотека содержит основную функцию ассемблирования фрагментного шейдера из текста AFS-программы, а также несколько вспомогательных функций контроля за ошибками.
Вы можете свободно использовать библиотеку AFSparse и программы, написанные на языке AFS, в своих коммерческих и некоммерческих проектах, при условии указания имени разработчика библиотеки (Chain Studios) в информации о вашем проекте.
2. Подключение библиотеки AFSparse
Для использования AFSparse вам необходимы следующие файлы:
- afsparse.h – содержит прототипы функций библиотеки.
- afsparse.lib – собственно библиотека, вам нужно подключить ее к проекту.
Для успешной сборки проекта вы должны подключить к нему opengl32.lib (как правило, он уже подключен к проекту, использующему функции OpenGL), а также объявить несколько функций, соответствующих расширению ATI_fragment_shader:
C++ Source Code:PFNGLBINDFRAGMENTSHADERATIPROC glBindFragmentShaderATI; PFNGLBEGINFRAGMENTSHADERATIPROC glBeginFragmentShaderATI; PFNGLENDFRAGMENTSHADERATIPROC glEndFragmentShaderATI; PFNGLPASSTEXCOORDATIPROC glPassTexCoordATI; PFNGLSAMPLEMAPATIPROC glSampleMapATI; PFNGLCOLORFRAGMENTOP1ATIPROC glColorFragmentOp1ATI; PFNGLCOLORFRAGMENTOP2ATIPROC glColorFragmentOp2ATI; PFNGLCOLORFRAGMENTOP3ATIPROC glColorFragmentOp3ATI; PFNGLALPHAFRAGMENTOP1ATIPROC glAlphaFragmentOp1ATI; PFNGLALPHAFRAGMENTOP2ATIPROC glAlphaFragmentOp2ATI; PFNGLALPHAFRAGMENTOP3ATIPROC glAlphaFragmentOp3ATI; PFNGLSETFRAGMENTSHADERCONSTANTATIPROC glSetFragmentShaderConstantATI;
Учтите, что вы должны сами проверить поддержку расширения ATI_fragment_shader и получить указатели на эти функции до вызова функций AFSparse.
Для сборки шейдера вам нужно получить свободный идентификатор, привязать его к текущему шейдеру и вызвать функцию afsparse, в качестве аргумента которой выступает текст AFS-программы.
C++ Source Code:void afsparse(const char * const text);
Пример:
C++ Source Code:glBindFragmentShaderATI( uiShaderIdentifier ); afsparse( szProgramBuffer ); glBindFragmentShaderATI(0);
Далее вы можете использовать этот шейдер при рисовании:
C++ Source Code:glBindFragmentShaderATI(uiShaderIdentifier ); glEnable(GL_FRAGMENT_SHADER_ATI); glDisable(GL_FRAGMENT_SHADER_ATI);
Сразу после сборки шейдера вы можете узнать, сколько он содержит инструкций (в обоих проходах), вызвав функцию afsparse_get_instruction_count:
C++ Source Code:int numCmds = afsparse_get_instruction_count();
Если во время сборки шейдера произошли ошибки, вы можете узнать об этом и получить их список (см. раздел Ошибки в программах и отладка).
3. Структура AFS-программы
Рассмотрим базовую структуру AFS-программ.
Code:!!AFS1.0 { } { }
Каждая AFS-программа должна начинаться со строки «!!AFS1.0».
Далее могут идти объявления констант, используемых в шейдере. Для этого используется следующий синтаксис:
Code:CONSTi value1, value2, value3, value4;
Где i варьирует от 0 до GL_NUM_FRAGMENT_CONSTANTS_ATI-1.
Разделение параметров с помощью запятой и постановка точки запятой в конце необязательно, но рекомендуется для повышения читабельности программы.
Вы можете оставлять комментарии в любой строке, кроме первой. Символы // и \\ говорят о том, что вся строка – комментарий. Вы также можете использовать комментарии в стиле С (/* … */).
Открывающая фигурная скобка «{» объявляет начало нового прохода. Всего может быть один (простой шейдер) или два прохода (сложный шейдер с доступом к текстуре по вычисленным текстурным координатам). Каждый проход должен завершаться закрывающей фигурной скобкой «}».
Инструкции прохода делятся на текстурные и общие (вычислительные). Текстурные инструкции должны предшествовать общим, т.к. они инициализируют используемые в дальнейшем регистры.
Количество доступных регистров, констант и текстурных блоков ограничено. Библиотека проверяет их максимально допустимое количество и генерирует ошибку, если в программе фигурирует неподдерживаемый параметр.
4. Команды
Команды программы напоминают ассемблерные:
Code:cmdName dest src0 [, src1 [, src2]];
Первым операндом служит выходной регистр. Второй и последующие операнды – исходные регистры. У команд может быть 2, 3 или 4 операнда.
В вашем распоряжении 4 текстурных и 12 общих команд.
Общие команды формируют пары – первая команда в паре относится к RGB-части, а вторая – к альфа-части. Они выполняются параллельно, и результаты одной из них не могут влиять на результаты другой.
В каждом проходе можно задать не более 8 таких пар (всего не более 16 команд).
Если вам нужно использовать подряд 2 команды, работающие с RGB-частью, используйте в качестве второй команды пары команду NOP (аналогично – для двух подряд альфа-команд, тогда потребуется еще и NOP перед первой альфа-командой).
К общим командам могут применяться модификаторы (см. табл.)
4.1. Список текстурных команд AFS
Команда | Синтаксис | Действие | TEX | TEX map, coords, coords_mod; | Получение данных из текстуры по указанным координатам. В качестве текстуры берется карта с индексом регистра map (например, для reg1 берется текущая текстура первого блока). Координаты в первом проходе должны быть texX, где X – номер блока. Во втором проходе допускается чтение из произвольного регистра. Coords_mod задает, какие координаты использовать для сэмплирования и принимает два значения:
STR (s,t,r)
STQ (s,t,q) | TXP | TXP map, coords, coords_mod; | То же, что и TEX, но осуществляется проективное преобразование координат:
STR (s/r,t/r,1/r)
STQ (s/q,t/q,1/q) | PTC | PTC reg, coords, coords_mod; | Сохранение в регистр текстурных координат (в первом проходе) или значения, вычисленного на первом проходе (во втором проходе). Coords_mod аналогичен значениям в команде TEX:
STR (s,t,r)
STQ (s,t,q) | PTP | PTP reg, coords, coords_mod; | Проективное преобразование координат. В остальном аналогичен PTC.
STR (s/r,t/r,1/r)
STQ (s/q,t/q,1/q) |
4.2. Список общих команд AFS
Команда | Синтаксис | Действие | NOP | NOP | Пропуск соответствующей команде в паре | MOV | MOV dst, src; | R(dst) = R(src0)
G(dst) = G(src0)
B(dst) = B(src0)
A(dst) = A(src0) | ADD | ADD dst, src0, src1; | R(dst) = R(src0) + R(src1)
G(dst) = G(src0) + G(src1)
B(dst) = B(src0) + B(src1)
A(dst) = A(src0) + A(src1) | SUB | SUB dst, src0, src1; | R(dst) = R(src0) - R(src1)
G(dst) = G(src0) - G(src1)
B(dst) = B(src0) - B(src1)
A(dst) = A(src0) - A(src1) | MUL | MUL dst, src0, src1; | R(dst) = R(src0) * R(src1)
G(dst) = G(src0) * G(src1)
B(dst) = B(src0) * B(src1)
A(dst) = A(src0) * A(src1) | MAD | MAD dst, src0, src1, src2; | R(dst) = R(src0) * R(src1) + R(src2)
G(dst) = G(src0) * G(src1) + G(src2)
B(dst) = B(src0) * B(src1) + B(src2)
A(dst) = A(src0) * A(src1) + A(src2) | DP2 | DP2 dst, src0, src1, src2; | R(dst) = G(dst) = B(dst) = A(dst) =
R(src0) * R(src1) + G(src0) * G(src1) + B(src2) | DP3 | DP3 dst, src0, src1; | R(dst) = G(dst) = B(dst) = A(dst) =
R(src0) * R(src1) + G(src0) * G(src1) + B(src0) * B(src1) | DP4 | DP4 dst, src0, src1; | R(dst) = G(dst) = B(dst) = A(dst) =
R(src0) * R(src1) + G(src0) * G(src1) + B(src0) * B(src1) + A(src0) * A(src1)
Альфа-часть пары должна также содержать инструкцию DP4 и те же операнды, что и RGB-часть. | CMP | CMP dst, src0, src1, src2; | R(dst) = (R(src2) > 0.5) ? R(src0) : R(src1)
G(dst) = (G(src2) > 0.5) ? G(src0) : G(src1)
B(dst) = (B(src2) > 0.5) ? B(src0) : B(src1)
A(dst) = (A(src2) > 0.5) ? A(src0) : A(src1) | CMZ | CMZ dst, src0, src1, src2; | R(dst) = (R(src2) >= 0) ? R(src0) : R(src1)
G(dst) = (G(src2) >= 0) ? G(src0) : G(src1)
B(dst) = (B(src2) >= 0) ? B(src0) : B(src1)
A(dst) = (A(src2) >= 0) ? A(src0) : A(src1) | LRP | LRP dst, src0, src1, src2; | R(dst) = R(src0) * R(src1) + (1 - R(src0)) * R(src2)
G(dst) = G(src0) * G(src1) + (1 - G(src0)) * G(src2)
B(dst) = B(src0) * B(src1) + (1 - B(src0)) * B(src2)
A(dst) = A(src0) * A(src1) + (1 - A(src0)) * A(src2)
Src0 должен лежать в диапазоне [0,1], иначе результаты работы команды не определены. | Для получения значений в альфа-частях регистров нужно поместить такую же команду в альфа-часть пары.
4.3. Модификаторы общих команд
Модификаторы отделяются от названия команды символом подчеркивания и могут идти в любом порядке и количестве.
Модификатор | Пример | Действие | SAT | DP3_SAT | Результат скалярного произведения отсекается по отрезку [0,1] | 2X | ADD_2X | Результат сложения умножается на 2 | 4X | MUL_4X | Результат умножения умножается на 4 | 8X | MAD_8X | Результат операции умножается на 8 | HALF | DP3_HALF | Результат скалярного произведения умножается на 0.5 | QUAT | ADD_QUAT | Результат сложения умножается на 0.25 | EIGH | MUL_EUGH | Результат умножения умножается на 0.125 |
5. Выходные регистры и модификаторы
В качестве выходных регистров можно использовать любые доступные для записи регистры (reg0..regi, где i < GL_NUM_FRAGMENT_REGISTERS_ATI).
К выходному значению можно применить модификатор и маску записи.
Маска записи опциональна и позволяет защитить части регистра от записи. Маска состоит из комбинации букв r,g,b. Например, выходной регистр reg0.rg означает, что результат операции будет записан в красную и зеленую компоненту регистра 0.
Модификаторы выходного значения указываются в названии команды (см. Модификаторы общих команд).
Результат вычислений шейдера должен быть записан в регистр reg0.
6. Входные регистры и модификаторы
В качестве входных регистров можно использовать любые доступные для чтения регистры (reg0..regi, где i < GL_NUM_FRAGMENT_REGISTERS_ATI, константы con0..coni, где i < GL_NUM_FRAGMENT_CONSTANTS_ATI, текущий цвет col0, вторичный цвет col1, а также значения 1 и 0).
Обращение к текущему цвету можно осуществлять только во втором проходе двухпроходного шейдера.
Вы можете взять только одну компоненту регистра для вычислений, для этого укажите r, g, b, или a. Например, если аргумент равен reg0.r, то в команде будет использована только красная компонента регистра.
К аргументам можно применить дополнительные простые операции. Их синтаксис жестко задан, пробелы не разрешены:
Модификатор | Действие | 1-reg0 | reg0 = 1 - reg0 | -reg0 | reg0 = - reg0 | 2*reg0 | reg0 = 2 * reg0 | -2*reg0 | reg0 = -2 * reg0 | reg0-0.5 | reg0 = reg0 – 0.5 | -reg0-0.5 | reg0 = - reg0 – 0.5 | 2*reg0-0.5 | reg0 = 2 * reg0 – 0.5 | -2*reg0-0.5 | reg0 = -2 * reg0 – 0.5 | 7. Ошибки в программах и отладка
Библиотека предоставляет несколько фукций для получения информации об ошибках.
C++ Source Code:const char *afsparse_get_errors( void );
Возвращает строку, содержащую все ошибки, возникшие при сборке программы, или NULL, если ошибок не было. Строки разделены символом переноса строки («\n»)
C++ Source Code:int afsparse_get_error_count( void );
Возвращает количество ошибок при сборке программы.
C++ Source Code:void afsparse_set_error_filter( int filter );
Позволяет задать фильтр для ошибок. По умолчанию проверяется как синтаксис программы, так и статус OpenGL при компиляции каждой инструкции. Вы можете опционально отключать эти проверки, указывая маску-фильтр:
- AFS_NONE – отключить обработку ошибок.
- AFS_PARSER – выводить информацию об ошибках в программе
- AFS_DRIVER – выводить информацию об ошибках OpenGL
- AFS_PARSER | AFS_DRIVER – выводить все ошибки
У каждой ошибки указывается номер строки, где произошел сбой.
7.1. Список ошибок парсера AFS
Shader is empty – длина переданного буфера нулевая.
!!AFS1.0 expected – буфер не является AFS-программой.
Shader stage incomplete – нет закрывающей фигурной скобки.
Too many passes – более чем 2 прохода (открывающих фигурных скобок)
Unexpected symbol – неизвестный символ вне прохода
Bad register index – индекс регистра не указан или превышает лимит
Bad texture index – индекс текстуры не указан или превышает лимит
Bad constant index – индекс константы не указан или превышает лимит
Bad color index – индекс цвета должен быть 0 или 1
Unrecognized source register – неправильное/неподдерживаемое имя входного регистра
Unrecognized destination register – неправильное/неподдерживаемое имя выходного регистра
Unrecognized swizzle (must be STR or STQ) – неправильный модификатор координат
Unrecognized instruction – неизвестная команда
Unrecognized argument replication – неправильная компонента
Bad destination – ошибка обработки выходного параметра функции
Bad source – ошибка обработки входного параметра функции
Not allowed after general instructions – текстурная команда обнаружена после общей
Too many instructions in pass – более 16 инструкций в проходе
error in pass – при чтении прохода произошла ошибка
8. Примеры шейдеров на AFS
8.1. Искажение отражения в воде с помощью шумовой текстуры
В качестве текстуры нулевого блока выступает текстура отражения с настроенной проективной матрицей. На первом блоке разрешено 3D-текстурирование и привязана шумовая 3D-текстура.
Code:!!AFS1.0 CONST0 2.0, 2.0, 0.0, 0.0; { PTC reg0, tex0, STR; TEX reg1, tex1, STR; MAD reg2, reg1, con0, reg0; } { TXP reg0, reg2, STR; NOP MOV reg0, col0.a; }
8.2. Имитация преломлений с помощью карты нормалей
В качестве текстуры нулевого блока выступает текстура сцены с настроенной проективной матрицей. На первом блоке разрешено 2D-текстурирование и привязана карта нормалей. Нормаль к поверхности передается как координаты текстуры второго блока.
Code:!!AFS1.0 { PTC reg0, tex0, STR; TEX reg1, tex1, STQ; PTC reg2, tex2, STR; DP3 reg2.rg, 2*reg1-0.5, reg2; NOP MUL reg0.rg, reg0, reg2; NOP } { TXP reg0, reg0, STR; NOP MOV reg0, 1; }
8.3. Совмещение искаженного отражения и преломления (имитация полупрозрачной воды)
В качестве текстуры нулевого блока выступает текстура отражения с настроенной проективной матрицей. В качестве текстуры первого блока выступает текстура сцены с настроенной проективной матрицей. На втором блоке разрешено 2D-текстурирование и привязана карта нормалей. На третьем блоке разрешено 3D-текстурирование и привязана шумовая 3D-текстура. Нормаль к поверхности передается как координаты текстуры четвертого блока. И наконец, прозрачность воды передается в альфа-компоненте текущего цвета.
В основе имитации прозрачности лежат следующие сообращения. Полностью прозрачная вода – это текстура сцены. Полностью непрозрачная – текстура отражения. Следовательно, нужно интерполировать эти значения на основе альфа-компонента цвета, для этого есть соответствующая команда – LRP.
Кроме того, шейдер применяет и описанные выше искажения.
Code:!!AFS1.0 CONST0 2.0, 2.0, 0.0, 0.0; { PTC reg0, tex0, STQ; PTC reg1, tex1, STQ; TEX reg2, tex2, STQ; TEX reg3, tex3, STR; PTC reg4, tex4, STR; DP3 reg2.rg, 2*reg2-0.5, reg4; NOP MUL reg1.rg, reg1, reg2; NOP MAD reg0, reg3, con0, reg0; } { TXP reg0, reg0, STR; TXP reg1, reg1, STR; LRP reg0, col0.a, reg0, reg1; MOV reg0, 1; }
Скачать библиотеку: Прикрепленный файл
Copyright © Chain Studios, 2006
|
 |