AFSparse

[ATI_fragment_shader program parser]

 

Copyright © Chain Studios, 2006

 

Введение. 1

Подключение библиотеки AFSparse. 1

Структура AFS-программы.. 2

Команды.. 3

Список текстурных команд AFS. 3

Список общих команд AFS. 4

Модификаторы общих команд. 5

Выходные регистры и модификаторы.. 5

Входные регистры и модификаторы.. 6

Ошибки в программах и отладка. 6

Список ошибок парсера AFS. 7

Примеры шейдеров на AFS. 7

Искажение отражения в воде с помощью шумовой текстуры.. 7

Имитация преломлений с помощью карты нормалей. 8

Совмещение искаженного отражения и преломления (имитация полупрозрачной воды) 8

Обратная связь. 9

 

Введение

 

Написание фрагментных шейдеров с использованием расширения ATI_fragment_shader представляет собой довольно трудоемкий процесс, требуется знать довольно большое число символических констант и следить за аппаратными лимитами. Кроме того, команды задаются путем вызовов функций расширения, в результате чего для внесения изменений в шейдер требуется перекомпиляция проекта. Для решения этих проблем была разработана статическая библиотека AFSparse.

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

Вы можете свободно использовать библиотеку AFSparse и программы, написанные на языке AFS, в своих коммерческих и некоммерческих проектах, при условии указания имени разработчика библиотеки (Chain Studios)  в информации о вашем проекте.

 

Подключение библиотеки AFSparse

 

Для использования AFSparse вам необходимы следующие файлы:

Для успешной сборки проекта вы должны подключить к нему opengl32.lib (как правило, он уже подключен к проекту, использующему функции OpenGL), а также объявить несколько функций, соответствующих расширению ATI_fragment_shader:

 

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-программы.

 

void afsparse(const char * const text);

 

Пример:

 

glBindFragmentShaderATI( uiShaderIdentifier );

afsparse( szProgramBuffer );

glBindFragmentShaderATI(0);

 

Далее вы можете использовать этот шейдер при рисовании:

 

glBindFragmentShaderATI(uiShaderIdentifier );

glEnable(GL_FRAGMENT_SHADER_ATI);

// Код рисования

glDisable(GL_FRAGMENT_SHADER_ATI);

 

Если во время сборки шейдера произошли ошибки, вы можете узнать об этом и получить их список (см. раздел Ошибки в программах и отладка)

 

Структура AFS-программы

 

Рассмотрим базовую структуру AFS-программ.

 

!!AFS1.0

// Объявления констант

{

// Первый проход шейдера

 

//Текстурные инструкции

//Общие инструкции

}

 

{

// Второй проход шейдера (необязателен)

 

//Текстурные инструкции

//Общие инструкции

 

}

 

Каждая AFS-программа должна начинаться со строки «!!AFS1.0».

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

CONSTi  value1, value2, value3, value4;

Где i варьирует от 0 до GL_NUM_FRAGMENT_CONSTANTS_ATI-1.

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

Вы можете оставлять комментарии в любой строке, кроме первой. Символы // и \\ говорят о том, что вся строка – комментарий. Вы также можете использовать комментарии в стиле С (/* … */).

Открывающая фигурная скобка «{» объявляет начало нового прохода. Всего может быть один (простой шейдер) или два прохода (сложный шейдер с доступом к текстуре по вычисленным текстурным координатам). Каждый проход должен завершаться закрывающей фигурной скобкой «}».

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

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

 

Команды

 

Команды программы напоминают ассемблерные:

cmdName dest src0 [, src1 [, sr2]];

Первым операндом служит выходной регистр. Второй и последующие операнды – исходные регистры. У команд может быть 2, 3 или 4 операнда.

В вашем распоряжении 4 текстурных и 12 общих команд.

Общие команды формируют пары – первая команда в паре относится к RGB-части, а вторая – к альфа-части. Они выполняются параллельно, и результаты одной из них не могут влиять на результаты другой.

В каждом проходе можно задать не более 8 таких пар (всего не более 16 команд).

Если вам нужно использовать подряд 2 команды, работающие с RGB-частью, используйте в качестве второй команды пары команду NOP (аналогично – для двух подряд альфа-команд, тогда потребуется еще и NOP перед первой альфа-командой).

К общим командам могут применяться модификаторы (см. табл.)

Список текстурных команд 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)

 

Список общих команд 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], иначе результаты работы команды не определены.

Для получения значений в альфа-частях регистров нужно поместить такую же команду в альфа-часть пары.

 

Модификаторы общих команд

Модификаторы отделяются от названия команды символом подчеркивания и могут идти в любом порядке и количестве.

Модиф.

Пример

Действие

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

 

 

 

Выходные регистры и модификаторы

В качестве выходных регистров можно использовать любые доступные для записи регистры (reg0..regi, где i < GL_NUM_FRAGMENT_REGISTERS_ATI).

К выходному значению можно применить модификатор и маску записи.

Маска записи опциональна и позволяет защитить части регистра от записи. Маска состоит из комбинации букв r,g,b. Например, выходной регистр reg0.rg означает, что результат операции будет записан в красную и зеленую компоненту регистра 0.

Модификаторы выходного значения указываются в названии команды (см. Модификаторы общих команд).

Результат вычислений шейдера должен быть записан в регистр reg0.

 

Входные регистры и модификаторы

В качестве входных регистров можно использовать любые доступные для чтения регистры (reg0..regi, где i < GL_NUM_FRAGMENT_REGISTERS_ATI, константы con0..coni, где i < GL_NUM_FRAGMENT_CONSTANTS_ATI, текущий цвет col0, а также значения 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

 

 

Ошибки в программах и отладка

Библиотека предоставляет несколько фукций для получения информации об ошибках.

 

const char *afsparse_get_errors( void );

Возвращает строку, содержащую все ошибки, возникшие при сборке программы, или NULL, если ошибок не было. Строки разделены символом переноса строки («\n»)

 

int afsparse_get_error_count( void );

Возвращает количество ошибок при сборке программы.

 

void afsparse_set_error_filter( int filter );

Позволяет задать фильтр для ошибок. По умолчанию проверяется как синтаксис программы, так и статус OpenGL при компиляции каждой инструкции. Вы можете опционально отключать эти проверки, указывая маску-фильтр:

AFS_NONE – отключить обработку ошибок.

AFS_PARSER – выводить информацию об ошибках в программе

AFS_DRIVER – выводить информацию об ошибках OpenGL

AFS_PARSER | AFS_DRIVER – выводить все ошибки

 

У каждой ошибки указывается номер строки, где произошел сбой.

Список ошибок парсера 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 – индекс константы не указан или превышает лимит

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 – при чтении прохода произошла ошибка

 

Примеры шейдеров на AFS

Искажение отражения в воде с помощью шумовой текстуры

В качестве текстуры нулевого блока выступает текстура отражения с настроенной проективной матрицей. На первом блоке разрешено 3D-текстурирование и привязана шумовая 3D-текстура.

!!AFS1.0

CONST0 2.0, 2.0, 0.0, 0.0;

{

//Pass reflection coords in reg0

PTC reg0, tex0, STQ;

 

//Get 3D noise into reg1

TEX  reg1, tex1, STR;

 

//Offset reflection coords by noise

MAD reg2, reg1, con0, reg0;

}

{

//Do dependent projective texture read

TXP  reg0, reg2, STR;

 

//RGB channel ignored

NOP

//Output primary color alpha

MOV reg0, col0.a;

}

 

Имитация преломлений с помощью карты нормалей

В качестве текстуры нулевого блока выступает текстура сцены с настроенной проективной матрицей. На первом блоке разрешено 2D-текстурирование и привязана карта нормалей. Нормаль к поверхности передается как координаты текстуры второго блока.

!!AFS1.0

{

//Pass screentexture coords

PTC reg0, tex0, STQ;

 

//Get normal from normalmap

TEX  reg1, tex1, STQ;

 

//Pass surface normal

PTC reg2, tex2, STR;

 

//Compute expand(reg1) . reg2

DP3 reg2.rg, 2*reg1-0.5, reg2;

NOP

 

//reg0 = reg0 * (N.N')

MUL  reg0.rg, reg0, reg2;

NOP

}

{

//Do dependent projective texture read

TXP  reg0, reg0, STR;

 

//RGB channel ignored

NOP

//Output primary color alpha

MOV reg0, 1;

}

 

Совмещение искаженного отражения и преломления (имитация полупрозрачной воды)

В качестве текстуры нулевого блока выступает текстура отражения с настроенной проективной матрицей. В качестве текстуры первого блока выступает текстура сцены с настроенной проективной матрицей. На втором блоке разрешено 2D-текстурирование и привязана карта нормалей. На третьем блоке разрешено 3D-текстурирование и привязана шумовая 3D-текстура. Нормаль к поверхности передается как координаты текстуры четвертого блока. И наконец, прозрачность воды передается в альфа-компоненте текущего цвета.

В основе имитации прозрачности лежат следующие сообращения. Полностью прозрачная вода – это текстура сцены. Полностью непрозрачная – текстура отражения. Следовательно, нужно интерполировать эти значения на основе альфа-компонента цвета, для этого есть соответствующая команда – LRP.

Кроме того, шейдер применяет и описанные выше искажения.

 

!!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;

}

 

 

Обратная связь

О найденных ошибках в библиотеке или неточностях в документации сообщайте на нашем форуме:

www.xash.ru/forum

или по почтовому адресу:

chainstudios@narod.ru

 

Разработчик: Chain Studios

2006 год