Главная -
Статьи -
Проекты -
Ссылки -
Скачать -
Из гельминтов -
Юмор, приколы -
Почитать -
Обо мне -
Мысли -
Гостевая -

Фрагментные программы. Расширение ARB_fragment_program

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

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

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

Желательно иметь возможность явно задавать законы обработки данных на уровне отдельных фрагментов (пикселов). Именно такая функциональность и предоставляется рассматриваемым ниже расширений ARB_fragment_program.

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

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

    const char * vpText =
    "!!ARBfp1.0\
     ATTRIB tex0   = fragment.texcoord [0];\
     ATTRIB color0 = fragment.color;\
     OUTPUT color  = result.color;\
     TEMP temp;\
     TXP temp, tex0, texture [0], 2D;\
     MUL color, temp, color0;\
     END";

    GLuint       progId;

    glGenProgramsARB   ( 1, &progId );
    glBindProgramARB   ( GL_FRAGMENT_PROGRAM_ARB, progId );
    glProgramStringARB ( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
                         strlen ( vpText ), vpText );

    if ( glGetLastError () == GL_INVALID_OPERATION )
    {
        GLint     errorPos;
        GLubyte * errorStr;

        glGetIntegerv ( GL_PROGRAM_ERROR_POSITION_ARB, &errorPos );

        errorString = glGetString ( GL_PROGRAM_ERROR_STRING_ARB );

        fprintf ( stderr, "Error at position %d.\Line: \"%s\"", errorPos, errorStr );
    }

Сразу бросатеся в глаза использование константы GL_FRAGMENT_PROGRAM_ARB вместо GL_VERTEX_PROGRAM_ARB и то, что программа начинается со строки "!!ARBfp1.0".

Перед детальным изучением фрагментных программ рассмотрим место фрагментной программы в графическом конвеере OpenGL.

Стандартный конвеер (pipeline) OpenGL выглядит следующим образом:

Стандартный конвеер в OpenGL

Рис 1. Стандартный конвеер в OpenGL.

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

Фрагментная программа выполняется независимо для каждого фрагмента изображения (в том числе и для точек, линий, растровых изображений).

Также фрагментная программа игнорирует приоритеты текстур (кубической карты над 3D, 3D над 2D, 2D над 1D) и то, какие текстурные цели (кубическая карта, 1-2-3-мерная) разрешены для данного текстурного блока.

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

Фрагментная программа и используемые регистры

Рис 2. Фрагментная программа и используемые регистры.

Как и вершинная программа, фрагментная программа получает в свое распоряжение несколько наборов регистров (4-х мерных вещественных векторов) - атрибуты самого фрагмента (fragment.*), локальные параметры (program.local [n]) и параметры окружения (program.env [n]).

Выходные значения программа записывает в регистры результата (result.*).

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

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

Для переноса фрагментной программы в другой контекст рендеринга, по аналогии с текстурами и вершинными программаи, используется команда wglShareLists.

Работа с параметрами (локальными и окружения) также полностью аналогична работе с ними для вершинных программ.

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

int	maxAluInstructions;
int	maxTexInstructions;
int	maxTexIndirections;
int	maxLocalParams;
int	maxEnvParams;
int	maxTemporaries;
int	maxAttribs;
int	maxInstructions;

glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB,&maxAluInstructions);
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB,&maxTexInstructions);
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB,&maxTexIndirections);
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB,&maxLocalParams    );
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,GL_MAX_PROGRAM_ENV_PARAMETERS_ARB,  &maxEnvParams      );
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,GL_MAX_PROGRAM_TEMPORARIES_ARB,     &maxTemporaries    );
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,GL_MAX_PROGRAM_ATTRIBS_ARB,         &maxAttribs        );
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,GL_MAX_PROGRAM_INSTRUCTIONS_ARB,    &maxInstructions   );

printf ( "\nFragment program limits:\n"                        );
printf ( "\tmax ALU instructions    : %d\n", maxAluInstructions );
printf ( "\tmax tex instructions    : %d\n", maxTexInstructions );
printf ( "\tmax tex indirections    : %d\n", maxTexIndirections );
printf ( "\tmax local parameters    : %d\n", maxLocalParams     );
printf ( "\tmax env. parameters     : %d\n", maxEnvParams       );
printf ( "\tmax program temporaries : %d\n", maxTemporaries     );
printf ( "\tmax attributes          : %d\n", maxAttribs         );
printf ( "\tmax program instructions: %d\n", maxInstructions    );

Ограничения, накладываемые конкретным графическим ускорителем и драйвером состоят из:

  • максимальное количество арифметических операций (maxAluInstructions),
  • максимальное количество текстурных операций (maxTexInstructions),
  • максимальное количество всех операций (maxInstructions),
  • максимальное количество текстурных зависимостей (texture indirections)(maxTexIndirections),
  • максимальное количество локальных параметров (maxLocalParams),
  • максимальное количество параметров окружения (maxEnvParams),
  • максимальное количество временных переменных (maxTemporaries),
  • максимальное количество используемых вершинных атрибутов (maxAttribs).

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

При этом общее количество команд может не превышать допустимых пределов, но программа все равно не будет выполняться.

Вообще под текстурной зависимостью подразумевают зависимость команды обращения к текстуре (TEX, TXP, TXB) от результата предыдущей команды (как текстурной, так и арифметической) для получения своих текстурных координат.

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

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

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

Новый узел в цепочку текстурных зависимостей создается в одном из следующих двух случаев:

  1. Текстурные координаты для текстурной команды берутся из временной переменной, в которую уже была запись в текущем узле (как текстурной, так и арифметической командой).
  2. Результат текстурной команды записывается во временную переменную, в которую уже была запись или чтение арифметической командой в текущем узле.

Обратите внимание, что маски записи и перестановки компонент не играют роли при подсчете количества узлов в цепочке текстурных зависимостей.

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

!!ARBfp1.0
# No texture instructions, but always 1 indirection
MOV result.color, fragment.color;
END

!!ARBfp1.0
# A simple dependent texture instruction, 2 indirections
TEMP myColor;
MUL myColor, fragment.texcoord[0], fragment.texcoord[1];
TEX result.color, myColor, texture[0], 2D;
END

!!ARBfp1.0
# A more complex example with 3 indirections
TEMP myColor1, myColor2;
TEX myColor1, fragment.texcoord[0], texture[0], 2D;
MUL myColor1, myColor1, myColor1;
TEX myColor2, fragment.texcoord[1], texture[1], 2D;
# so far we still only have 1 indirection
TEX myColor2, myColor1, texture[2], 2D; # This is #2
TEX result.color, myColor2, texture[3], 2D; # And #3
END

Обратите внимание на то, что сам факт успешной загрузки фрагментной программы еще не гарантирует, что она будет выполняться непосредственно на GPU, а не эмулироваться.

Проверить это можно при помощи следующего фрагмента кода.

GLboolean ProgramStringIsNative ( GLenum target, GLenum format,
                                  GLsizei len, const GLvoid *string )
{
    GLint errorPos, isNative;

    glProgramStringARB ( target, format, len, string );
    glGetIntegerv      ( GL_PROGRAM_ERROR_POSITION_ARB, &errorPos );
    glGetProgramivARB  ( GL_FRAGMENT_PROGRAM_ARB,
                        GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &isNative );

    if ( (errorPos == -1) && (isNative == 1) )
        return GL_TRUE;

    return GL_FALSE;
}

Ниже приводится таблица со списком фрагментным атрибутов (т.е. индивидуальных атрибутов для каждого фрагмента).

Таблица 1. Фрагментные атрибуты.

НазваниеКомпонентыЗначение
fragment.position(x,y,z,1/w)координаты
fragment.color(r,g,b,a)Первичный цвет
fragment.color.primary(r,g,b,a)Первичный цвет
fragment.color.secondary(r,g,b,a)Вторичный цвет
fragment.texcoord(s,t,r,q)Текстурные координаты для блока 0
fragment.texcoord [n](s,t,r,q)Текстурные координаты для блока n
fragment.fogcoord(f,0,0,1)Координата для тумана (расстояние)

Также из фрагментной программы доступны параметры состояния OpenGL. В следующих таблицах приводятся основные параметры и имена для доступа к ним. Смысл этих параметров такой же как и в вершинных программах.

Таблица 2. Свойства материала.

ИмяКомпонентыКомментарий
state.material.ambient(r,g,b,a)Фоновый цвет материала для лицевой грани
state.material.diffuse(r,g,b,a)Диффузный цвет материала для лицевой грани
state.material.specular(r,g,b,a)Бликовый цвет материала для лицевой грани
state.material.emission(r,g,b,a)Цвет свечения материала для лицевой грани
state.material.shininess(s,0,0,1)Степень Фонга материала для лицевой грани
state.material.front.ambient(r,g,b,a)Фоновый цвет материала для лицевой грани
state.material.front.diffuse(r,g,b,a)Диффузный цвет материала для лицевой грани
state.material.front.specular(r,g,b,a)Бликовый цвет материала для лицевой грани
state.material.front.emission(r,g,b,a)Цвет свечения материала для лицевой грани
state.material.front.shininess(s,0,0,1)Степень Фонга материала для лицевой грани
state.material.back.ambient(r,g,b,a)Фоновый цвет материала для нелицевой грани
state.material.back.diffuse(r,g,b,a)Диффузный цвет материала для нелицевой грани
state.material.back.specular(r,g,b,a)Бликовый цвет материала для нелицевой грани
state.material.back.emission(r,g,b,a)Цвет свечения материала для нелицевой грани
state.material.back.shininess(s,0,0,1)Степень Фонга материала для нелицевой грани

Таблица 3.. Параметры источников света.

ИмяКомпонентыКомментарий
state.light[n].ambient(r,g,b,a)Фоновый цвет источника света n
state.light[n].diffuse(r,g,b,a)Диффузный цвет источника света n
state.light[n].specular(r,g,b,a)Бликовый цвет источника света n
state.light[n].position(x,y,z,w)Координаты источника света n
state.light[n].attenuation(a,b,c,e)Коэффициента затухания в зависимости от расстояния и показатель для конического источника для источника света n
state.light[n].spot.direction(x,y,z,c)Направление для конического источника света и косинус его угла (для источника света n)
state.light[n].half(x,y,z,1)Нормализованный серединный угол между направлением от глаза на источника света и вектором (0,0,1)
state.lightmodel.ambient(r,g,b,a)Фоновый цвет модели освещения
state.lightmodel.scenecolor(r,g,b,a)Цвет сцены в модели освещения для лицевых граней
state.lightmodel.front.scenecolor(r,g,b,a)Цвет сцены в модели освещения для лицевых граней
state.lightmodel.back.scenecolor/TD>(r,g,b,a)Цвет сцены в модели освещения для нелицевых граней
state.lightprod[n].ambient(r,g,b,a)Произведение фонового цвета источника света и материала
state.lightprod[n].diffuse(r,g,b,a)Произведение диффузного цвета источника света и материала
state.lightprod[n].specular(r,g,b,a)Произведение бликового цвета источника света и материала
state.lightprod[n].front.ambient(r,g,b,a)Произведение фонового цвета источника света и материала для лицевой грани
state.lightprod[n].front.diffuse(r,g,b,a)Произведение диффузного цвета источника света и материала для лицевой грани
state.lightprod[n].front.specular(r,g,b,a)Произведение бликового цвета источника света и материала для лицевой грани
state.lightprod[n].back.ambient(r,g,b,a)Произведение фонового цвета источника света и материала для нелицевой грани
state.lightprod[n].back.diffuse(r,g,b,a)Произведение диффузного цвета источника света и материала для нелицевой грани
state.lightprod[n].back.specular(r,g,b,a)Произведение бликового цвета источника света и материала для нелицевой грани

Таблица 4.. Параметры генерации текстурных координат.

ИмяКомпонентыКомментарий
state.texenv[n].color(r,g,b,a)Цвет окружения для текстурного блока n

Таблица 5.. Параметры вычисления затуманивания.

ИмяКомпонентыКомментарий
state.fog.color(r,g,b,a)RGB-цвет тумана
state.fog.params(d,s,e,r)Плотность тумана (d), линейное начало и конец (s,e) и l/(end-start)

Таблица 6.. Параметры отсечения по глубине.

ИмяКомпонентыКомментарий
state.depth.range(n,f,d,1)Диапазон изменения глубина - near, far и far-near

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

Таблица 7.. Матрицы.

ИмяКомментарий
state.matrix.modelview[n]n-я матрица модельного преобразования. Параметр n необязателен.
state.matrix.projectionМатрица проектирования
state.matrix.mvpПроизведение матрицы модельного преобразования и проектирования: MVP = P * M[0],
state.matrix.texture[n]n-я матрица преобразования текстурных координат. Параметр n необязателен.
state.matrix.palette[n]Палитровая матрица n-го модельного преобразования
state.matrix.program[n]n-я матрица программы

При этом к имени матрицы из таблицы 8 может бать добавлено ".inverse", ".transpose" и ".invtrans" для обозначения того, что следует указанную матрицу обратить/транспонировать/обратить и транспонировать.

Также к имени может быть добавлена ".row [n]" для получения заданного столбца матрицы как 4-х мерного вектора.

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

Таблица 8.. Регистры результата.

ИмяКомпонентыКомментарий
result.color(r,g,b,a)Выходной цвет
result.depth(*,*,d,*)Глубина точки

Если в выходной регистр цвета (result.color) не было произведено записи, то выходное значение цвета становится неопределенным.

Но программа не обязана производить запись в выходной регистр глубины (result.depth). В случае, когда в этот регистр не было ни одной операции записи, то в качестве выходного значения глубины будет использовано значение, полученное при растеризации примитива.

Обратите внимание на то, что используется нормированное значение глубины (т.е. приведенное из отрезка [zNear,zFar] в отрезок [0,1].

Структура фрагментной программы

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

!!ARBfp1.0
ATTRIB  h         = fragment.texcoord [1];
PARAM   amb       = { 0, 0, 0.5 };
PARAM   one       = 1;
PARAM   two       = 2;
PARAM   shininess = 10;
PARAM   specColor = { 1, 1, 0 };

TEMP	n, hn, color, h2, n2;
                                                # get normal
TEX     n, fragment.texcoord [0], texture [0], 2D;
MAD     n, n, two, -one;
                                                # normalize n
DP3     n2.w, n, n;
RSQ     n2.w, n2.w;
MUL     n2.xyz, n, n2.w;

                                                # normalize h
DP3     h2.w, h, h;
RSQ     h2.w, h2.w;
MUL     h2.xyz, h, h2.w;

                                                # compute (n,h) ^ shininess
DP3_SAT hn.a, n2, h2;                           # compute max ( (n,h), 0 )
LG2     hn.a, hn.a;
MUL     hn.a, hn.a, shininess;
EX2     hn.a, hn.a;                             # compute max ((h,n), 0) ^ shininess

                                                # return color
MUL     color, specColor, hn.a;                 # return amb + specColor*max((h,n),0)^shininess
ADD_SAT result.color, color, amb;

END

Первая строка программы должна быть "!!ARBfp1.0", сообщающая, что далее идет фрагментная программа, соответствующая версии 1.0.

Каждая команда (кроме команды END) должна завершаться точкой с запятой ";".

Завершается фрагментная программа командой END.

Весь текст, идущий после символа "#" и до конца строки является комментарием и игнорируется.

Обратите внимание на то, что все имена команд, ключевые слова, используемые при объявлении переменных, а также слова fragment, state, program и result, являются зарезервированными.

В начале фрагментной программы можно задать набор опций при помощи команды OPTION.

Фрагментные программы подерживают две группы опций - опции точности (precision) и опции тумана (fog).

При помощи опций точности можно явно задать свои пожелания к точности - высокая точность (ARB_precision_hint_nicest) или высокая скорость (ARB_precision_hint_fastest). Однако это является только пожеланием, необязательным к выполнению.

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

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

Пример.

OPTION ARB_precision_hint_fastest;

Идентификаторы

В качестве идентификаторов в вершинной программе служат произвольные последовательности латинских букв, цифр, символа подчеркивания ("_") и знака доллара ("$"), начинающаяся не с цифры (например, Mu$st_Die).

Обратите внимание, что идентификаторы в вершинных программых чувствительны к регистрам букв, т.е. идентификаторы а1 и А1 различаются.

Каждый идентификатор переменной до его использования должен быть объявлен, само объявление может находится в любом месте программы (но до первого использования данного идентификатора).

Временные переменные

Для хранения промежуточных значений вершинные программы могут использовать временные переменнные - 4-мерные вещественные вектора, которые перед использованием должны быть объявлены в команде TEMP.

Все временные переменные доступны фрагментной программе как для чтения, так и для записи.

Пример.

TEMP flag;
TEMP a,b,c;

Начальные значения временных переменных (как и выходных перемнных) не определены и зависят от реализации райвера.

Параметры

В качестве параметров могут выступать как 4-мерных вектора, так и массивы таких векторов.

Параметры могут задаваться как явно (через команду PARAM), так и неявно (путем непосредственной подстановки в текст).

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

Пример.

PARAM a = { 1, 2, 3, 4 };              # vector (1, 2, 3, 4)
PARAM b = { 3 };                       # vector (3, 0, 0, 1)
PARAM c = { 3, 4 };                    # vector (3, 4, 0, 1)
PARAM e = 3;                           # vector (3, 3, 3, 3)
                                       # array of two vectors
PARAM arr [2] = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 } };

ADD a, b, { 1, 2, 3, 4 };
ADD a, c, 3;

Матрица задается как массив из 4 векторов-столбцов.

Если исходная матрица задавалась следующим фрагментом кода.

// When loaded, the first row is "1, 2, 3, 4", because of column-major
// (OpenGL spec) vs. row-major (C) differences.
GLfloat matrix [16] =
{
   1, 5, 9,  13,
   2, 6, 10, 14,
   3, 7, 11, 15,
   4, 8, 12, 16
};

glMatrixMode  ( GL_MATRIX0_ARB );
glLoadMatrixf ( matrix         );

Также пусть в вершинной программе присутствуют следующие объявления.

PARAM mat1[4] = { state.matrix.program[0] };
PARAM mat2[4] = { state.matrix.program[0].transpose };

Тогда mat1[0] принимает значение (1,2,3,4), mat1[3] принимает значение (13,14,15,16), mat2[0] принимат значение (1,5,9,13), и mat2[3] принимает значеие (4,8,12,16).

Вот еще несколько примеров параметров, основанных на матрицах.

PARAM m0   = state.matrix.modelview[1].row[0];
PARAM m1   = state.matrix.projection.transpose.row[3];
PARAM m2[] = { state.matrix.program[0].row[1..2] };
PARAM m3[] = { state.matrix.program[0].transpose };
Можно связать параметр с каким-либо локальным параметром или параметром окружения:

PARAM c       = program.local [0];
PARAM mat [4] = program.env [0..3];
PARAM ambient = state.material.ambient;

Выходные значения

Выходные параметры доступны только для записи и задаются командой OUTPUT:

OUTPUT oCol = result.color.primary;

Фактически команда OUTPUT просто создает ссылку на соответствующий выходной регистр.

Также можно создать второе имя для уже существующего параметра (alias):

ALIAS b = a;

Система команд

Всего поддерживается 33 различных инструкций, работающих с 4-мерными вещественными векторами. Каждая такая команда имеет следующий вид:

opCode dest, [-]src0 [,[-]src1 [,[-]src2]]

Здесь opCode - это символьный код инструкции, dest - это регистр, в который будет помещен результат выполнения команды, а величины src0, src1 и src2 - это регистры с исходными данными. Квадратными скобками обозначены необязатаельные величины.

Все команды делятся на три категории -

  • скалярные операции (COS, EX2, LG2, RCP, RSQ, SIN, SCS, POW),
  • операции с текстурами (TEX, TXP, TXB, KIL),
  • векторные операции.

Векторные операции делятся на арифметико-логические операции (ABS, FLR, FRC, SUB, XPD, CMP, LPR, MAD, MOV, ADD, DP3, DP4) и специальные операции (LIT, DPH, DST).

Пример.

MOV R1, R2;
MAD R1, R2, R3, -R4;

Необязательный знак минус ("-") позволяет в качестве источника использовать как сам регистр, так и вектор, получающийся из него умножением на -1.

Для входных регистров (src0, src1 и src2) существует возможность использовать вместо самого регистра вектор, получающийся из исходного путем перестановки его компонент, например:

MOV R1, R2.yzwx;
MOV R2, -R3.yzwx;

Для выходного регистра можно задать маску, защищающую отдельные компоненты регистра от изменения

MOV R2.xw, -R3;

Наряду с именами компонент xyzw можно также использовать имена rgba, но их смешение в одной маске или перестановке компонент регистра не допускается, т.е. конструкция R1.xya не допустима.

Рассмотрим систему команд.

Таблица 9.. Скалярные ивекторные команды.

НазваниеСинтаксисКомментарий
ABS
Вычисление модуля
ABS dest, src;Данная команда осуществляет покомпонентное вычисление модуля
dest=fabs(src)
ADD
Сложение
ADD dest, src0, src1;Данная команда покомпонентно складывает два параметра и записывает сумму в результат
dest=src0+src1
CMP
Сравнение
CMP dest, src0,src1, src2;Данная команда покомпонентно, в зависимости от знака соответствующей компоненты src0 выбирает очередную компоненты либо из src1, либо из src2
dest.x = src0.x < 0 ? src1.x : src2.x
dest.y = src0.y < 0 ? src1.y : src2.y
dest.z = src0.z < 0 ? src1.z : src2.z
dest.w = src0.w < 0 ? src1.w : src2.w
COS
Вычисление косинуса угла
COS dest, src.C;Данная команда осуществляет покомпонентное вычисление скалярного аргумента и записывает его значение во все разрешенные компоненты результата, угол выражается в радианах и не обязан лежать в [-PI,PI]
DP3
Вычисление скалярного произведения по первым трем компонентам
DP3 dest, src0, src1;Данная команда вычисляет скалярное произведение источников как трехмерных векторов
dest=src0.x*src1.x + src0.y*src1.y + src0.z*src1.z
DP4
Вычисление скалярного произведения
DP4 dest, src0, src1;Данная команда вычисляет скалярное произведение четырехмерных векторов и записывает результат во все компоненты dest
dest=src0.x*src1.x+src0.y*src1.y+src0.z*src1.z+src0.w*src1.w
DPH
Вычисление однородного скалярного произведения
DPH dest, src0, src1;Данная команда служит для вычисления однородного скалярного произведения
dest=src0.x*src1.x + src0.y*src1.y + src0.z*src1.z + src1.w
DST
Вычисление расстояния
DST dest, src0, src1;Данная команда эффективно вычисляет вектор dest=(1,d,d^2,1/d) по двум значениям
src0=(*, d^2, d^2, *)
src1=(*,1/d,*,1/d)
Через * обозначено, что соответствующее значение не важно
EX2
Приближенное вычисление 2 в степени
EX2 dest, src.C;Для заданного скалярного операнда src.C вычисляет приближеное значение 2^src.C и записывает во все компоненты регистра dest
FLR
Вычисление floor
FLR dest, src;Осуществляет покомпонентное вычисление целой части (floor)(наибольшего целого, не превосходящего аргумента) от компонент источника
FRC
Вычисление дробной части
FRC dest, src;Осуществляет покомпонентное вычисление дробной части.
Дробная часть frac(x)=x-floor(x)
LG2
Приближенное вычисление логарифма по основанию 2
LG2 dest, src.C;Вычисляет приближенное значение логарифма по основанию 2 от скалярного аргумента и записывает во все компоненты источника
LIT
Вычисление коэффициентов освещения
LIT dest, src;Служит для ускорения вычисления диффузной и бликовой освещенности в вершинах
src.x=(n,l)
src.y=(n,h>
src.w=p
- степень, приводится к отрезку [-128,128]
На выходе
dest.x=1
dest.y=max(0,(n,l))
dest.z=(n.l)>0?roughAppr2ToX(max(0,(n,h))^p):0
dest.w=1
LRP
Линейная интерполяция
LRP dest, src0, src1, src2;Осуществляет покомпонентную линейную интерполяцию между значениями src1 и src2 на основе src0
dest.x = src0.x*src1.x+(1-src0.x)*src2.x
dest.y = src0.y*src1.y+(1-src0.y)*src2.y
dest.z = src0.z*src1.z+(1-src0.z)*src2.z
dest.w = src0.w*src1.w+(1-src0.w)*src2.w
MAD
Умножение и сложение
MAD dest, src0, src1, src2;Вычисляет
dest=src0*src1+src2
MAX
Вычисление максимума
MAX dest, src0, src1;Покомпонентное вычисление максимума
dest=max(src0,src1)
MIN
Вычисление минимума
MIN dest, src0, src1;Покомпонентное вычисление минимума
dest=min(src0,src1)
MOV
Копирование
MOV dest, src;dest=src
MUL
Покомпонентное умножение
MUL dest, src0, src1;Вычисляет покомпонентное произведение
dest=src0*src1
POW
Возведение в степень
POW dest, src0.C1, src1.C2;Вычисление приближенного значения первого скалярного операнда, возведенного в степень второго скалярного операнда и записывает результат во все компоненты регистра dest
apprPow(a,b)=apprExp2(b*apprLog2*a))
Обратите внимание, что 0^0=1
RCP
Вычисление обратного
RCP dest, src.C;Вычисляет приближенное значение обратного к скалярному операнду и записывает во все компоненты dest
dest=1/src.C
RSQRSQ dest, src.C;dest=1/sqrt(fabs(src.C))
SCSSCS dest, src.C;dest.x=cos (src.C)
dest.y=sin(src.C)

Значение скалярного операнда должно лежать в [-PI,PI], компоненты z и w необпределены
SGE
Set on Greater or Equal
SGE dest, src0, src1;dest.x=src0.x>=src1.x?1:0
dest.y=src0.y>=src1.y?1:0
dest.z=src0.z>=src1.z?1:0
dest.w=src0.w>=src1.w?1:0
SINSIN dest, src.C;dest=sin(src.C)
Вычисление синуса от скалярного операнда, выражающего угол в радианах, угол не обязательно должен лежать в [-PI,PI]
SLT
Set on Less Than
SLT dest, src0, src1;dest.x=src0.x<src1.x?1:0
dest.y=src0.y<src1.y?1:0
dest.z=src0.z<src1.z?1:0
dest.w=src0.w<src1.w?1:0
SUB
Вычисление разности
SUB dest, src0, src1;Покомпонентное вычисление разности операндов
dest=src0-src1
SWZ
"перемешивание" компонент
SWZ dest, src, <extSwizzle>
где
<extSwizzle> ::= <comp>, <comp>, <comp>, <comp>
<comp> ::= [-] (0 | 1 | x | y | z | w | r | g | b | a)
Данная команда позволяет произвольным образом перемешать компоненты (или их часть), в качстве источника для компоненты может выступать любая компонента исходного регистра (со знаком или без) или же значения 0 и 1
XPD
Вычисление векторного произведения
XPD dest, src0, src1;Данная команда осуществляет вычисление векторного произвдения первых трех компонент первого операнда на первые три компоненты второго операнда и записывает в первые три компоненты dest. Значение dest.w не определено

Таблица 10.. Текстурные команды.

НазваниеСинтаксисКомментарий
TEXTEX dest, src0, texture [n], type;Данная команда осуществляет выборку из текстуры типа type c n-го текстурного блока, используя в качестве текстурных координат первые компоненты параметра src0, и записывает результат в dest.
Параметр type принимает одно из следующих значений - 1D, 2D, 3D, cube и rectangle
TXPTXP dest, src0, texture [n], type;Данная команда осуществляет выборку из текстуры типа type c n-го текстурного блока, используя в качестве текстурных координат первые компоненты параметра src0 деленные на src0.w , и записывает результат в dest.
Параметр type принимает одно из следующих значений - 1D, 2D, 3D, cube и rectangle
TXBTXB dest, src0, texture [n], type;Данная команда осуществляет выборку из текстуры типа type c n-го текстурного блока, используя в качестве текстурных координат первые компоненты параметра src0, и записывает результат в dest.
Параметр type принимает одно из следующих значений - 1D, 2D, 3D, cube и rectangle.
Значение src0.w используется для смещения уровня в пирамидальном фильтровании с которого будет взято значение
KILL
Условное прерыывание выполнения вершинной программы
KIL src;Данная команда в случае, когда хотя бы одна из компонент операнда меньше нуля, прерывает выполнение фрагментной программы для данного фрагмента. При этом дальнейшие шаги конвеера для данного фрагмента будут пропущены

К командам может быть добавлен префикс _SAT, обозначающий, что выходные компоненты результата перед записью в регистр-приемник должны быть отсечены по отрезку [0,1].

Хотя при обращении к текстуре явно задается тип текстуры (texture target), но использовать разные типы для одного и того же текстурного блока в пределах одной программмы нельзя.

Примеры

Пример. Нормирование трехмерного вектора.

DP3 result.w, vector, vector;      # result.w = nx^2+ny^2+nz^2
RSQ result.w, result.w;            # result.w = 1/sqrt(nx^2+ny^2+nz^2)
MUL result.xyz, result.w, vector;  # normalize by multiplying by 1/length

Пример. Приведение угла к отрезку [-PI, PI].

PARAM myParams = { 0.5, -3.14159, 6.28319, 0.15915 };
MAD myOperand.x, myOperand.x, myParams.w, myParams.x; # a = (a/(2*PI))+0.5
FRC myOperand.x, myOperand.x; # a = frac(a)
MAD myOperand.x, myOperand.x, myParams.z, myParams.y # a = (a*2*PI)-PI

Пример. Простая программа, осуществляющая модулирование текстуры.

!!ARBfp1.0
                                       # Simple program to show how to code up the default
                                       # texture environment
ATTRIB tex = fragment.texcoord;        # first set of texture coordinates
ATTRIB col = fragment.color.primary;   # diffuse interpolated color
OUTPUT outColor = result.color;
TEMP tmp;
TXP tmp, tex, texture, 2D; #sample the texture
MUL outColor, tmp, col; #perform the modulation
END

Пример.Различные способы вычисления затуманивания

# Exponential fog
# f = exp(-d*z)
#
PARAM p = {DENSITY/LN(2), NOT USED, NOT USED, NOT USED};
PARAM fogColor = state.fog.color;
TEMP fogFactor;
ATTRIB fogCoord = fragment.fogcoord.x;
MUL fogFactor.x, p.x, fogCoord.x;
EX2_SAT fogFactor.x, -fogFactor.x;
LRP result.color.rgb, fogFactor.x, finalColor, fogColor;

#
# 2nd-order Exponential fog
# f = exp(-(d*z)^2)
#
PARAM p = {DENSITY/SQRT(LN(2)), NOT USED, NOT USED, NOT USED};
PARAM fogColor = state.fog.color;
TEMP fogFactor;
ATTRIB fogCoord = fragment.fogcoord.x;
MUL fogFactor.x, p.x, fogCoord.x;
MUL fogFactor.x, fogFactor.x, fogFactor.x;
EX2_SAT fogFactor.x, -fogFactor.x;
LRP result.color.rgb, fogFactor.x, finalColor, fogColor;

#
# Linear fog
# f = (end-z)/(end-start)
#
PARAM p = {-1/(END-START), END/(END-START), NOT USED, NOT USED};
PARAM fogColor = state.fog.color;
TEMP fogFactor;
ATTRIB fogCoord = fragment.fogcoord.x;
MAD_SAT fogFactor.x, p.x, fogCoord.x, p.y;
LRP result.color.rgb, fogFactor.x, finalColor, fogColor;

Примеры использования фрагментных программ

Вычисление необходимых параметров для попиксельного бликового освещения

Рассмотрим каким образом можно использовать фрагментную программу для вычисления попиксельного бликового (specular) освещения.

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

Тогда вектор нормали n можно брать из карты нормалей, но при этом, поскольку из текстуры мы получаем вектор в RGB-представлении, его необходимо отобразить обратно в координаты.

Также желательно провести его нормирование и нормирование вектора h, причем можно обойтись без нормирующих кубических карт, а сделать это непосредственно командами DP3, RSQ и MUL.

В результате мы получаем вектор нормали и вектор h сразу в касательном пространстве и нам остатся найти их скалярное произведение и возвести в степень.

Соответствующая фрагментная программа приводится ниже.

!!ARBfp1.0
#
# simple specular shader
# on entry:
#     fragment.texcoord [0] - texture coordinates
#     fragment.texcoord [1] - h
#
#      texture [0] - bump map
#

ATTRIB  h         = fragment.texcoord [1];
PARAM   amb       = { 0, 0, 0.5 };
PARAM   one       = 1;
PARAM   two       = 2;
PARAM   shininess = 10;
PARAM   specColor = { 1, 1, 0 };

TEMP    n, ln, hn, color, h2, l2, n2;

                                               # get normal perturbation
TEX     n, fragment.texcoord [0], texture [0], 2D;
MAD	    n, n, two, -one;
                                               # normalize n
DP3     n2.w, n, n;
RSQ     n2.w, n2.w;
MUL     n2.xyz, n, n2.w;

                                                # normalize h
DP3     h2.w, h, h;
RSQ     h2.w, h2.w;
MUL     h2.xyz, h, h2.w;

                                                # compute (n,h) ^ shininess
DP3_SAT hn.a, n2, h2;                           # compute max ( (n,h), 0 )
LG2     hn.a, hn.a;
MUL     hn.a, hn.a, shininess;
EX2     hn.a, hn.a;                             # compute max ((h,n), 0) ^ shininess

                                                # return color
MUL     color, specColor, hn.a;                 # return amb + specColor*max((h,n),0)^shininess
ADD_SAT result.color, color, amb;
END

Заворачиваем фрагментную программу в класс

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

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

class	FragmentProgram
{
protected:
    int                 errorCode;
    string              errorString;
    unsigned            id;                 // program id

public:
    ParamArray          local;              // local parameters

    static ParamArray   env;                // environment params
    static int          activeProgram;
    static int          maxAluInstructions ();
    static int          maxTexInstructions ();
    static int          maxTexIndirections ();
    static int          maxInstructions    ();
    static int          maxLocalParams     ();
    static int          maxEnvParams       ();
    static int          maxTemporaries     ();
    static int          maxProgramAttribs  ();


    FragmentProgram  ();
    FragmentProgram  ( const char * fileName );
    ~FragmentProgram ();

    unsigned	getId () const
    {
        return id;
    }

    string	getErrorString () const
    {
        return errorString;
    }

    void	bind   ();
    void	unbind ();

    void	enable  ();
    void	disable ();

    bool	load ( Data * data );
    bool	load ( const char * fileName );
};

Ниже приводятся скриншоты из программ к этой статье.

Простейший пример попиксельного бликового освещения.

Рис. 3. Простейший пример попиксельного бликового освещения.

Еще один вариант попиксельного освещения

Рис 4. Еще один вариант попиксельного освещения.

Освещение общего вида - карты светимости, диффузного освещения, бликового и bumpmap.

Рис 5. Освещение общего вида - карты светимости, диффузного освещения, бликового и bumpmap.

Chrome-шейдер.

Рис 6. Chrome-шейдер.

Весь исходный код к этой статье, включая реализацию класса FragmentProgram и все приведенные примеры можно скачать здесь.

Более полное описание вершинных и фрагментных шейдеров, включая использование шейдеров на OpenGL Shading Language и многочисленные примеры, можно найти в недавно вышедшей книге "Расширения OpenGL".


Copyright © 2004 Алексей В. Боресков

Используются технологии uCoz