Фрагментные программы. Расширение 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 выглядит следующим образом:
Рис 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)
от результата предыдущей команды (как текстурной, так и арифметической) для получения своих текстурных
координат.
Для данной программы можно построить цепочку таких текстурных зависимостей, состоящую из отдельных
узлов. Каждый узел такой цепочки содержит набор текстурных команд, котрые могут выполняться
параллельно, за которыми следуют арифметические команды.
Команда обращения к текстуре является зависимой, если она использует временную переменную в качестве
текстурных координат (вместо атрибута или параметра).
Программа без текстурных зависимостей будет представляться цепочкой, состоящей всего из одного узла
(т.е. количество текстурных зависимостей будет в ней равно единице).
Новый узел в цепочку текстурных зависимостей создается в одном из следующих двух случаев:
-
Текстурные координаты для текстурной команды берутся из временной переменной, в которую уже была
запись в текущем узле (как текстурной, так и арифметической командой).
-
Результат текстурной команды записывается во временную переменную, в которую уже была запись или чтение
арифметической командой в текущем узле.
Обратите внимание, что маски записи и перестановки компонент не играют роли при подсчете количества
узлов в цепочке текстурных зависимостей.
Ниже приводятся примеры программ с одной, двумя и тремя текстурными зависимостями.
!!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 |
RSQ | RSQ dest, src.C; | dest=1/sqrt(fabs(src.C)) |
SCS | SCS 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 |
SIN | SIN 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.. Текстурные команды.
Название | Синтаксис | Комментарий |
TEX | TEX dest, src0, texture [n], type; | Данная команда осуществляет выборку из текстуры типа type c n-го текстурного блока, используя в качестве текстурных координат первые компоненты параметра src0, и записывает результат в dest. Параметр type принимает одно из следующих значений - 1D, 2D, 3D, cube и rectangle |
TXP | TXP dest, src0, texture [n], type; | Данная команда осуществляет выборку из текстуры типа type c n-го текстурного блока, используя в качестве текстурных координат первые компоненты параметра src0 деленные на src0.w , и записывает результат в dest. Параметр type принимает одно из следующих значений - 1D, 2D, 3D, cube и rectangle |
TXB | TXB 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. Еще один вариант попиксельного освещения.
Рис 5. Освещение общего вида - карты светимости, диффузного освещения, бликового и bumpmap.
Рис 6. Chrome-шейдер.
Весь исходный код к этой статье, включая реализацию класса FragmentProgram и все приведенные примеры можно
скачать здесь.
Более полное описание вершинных и фрагментных шейдеров, включая использование шейдеров на OpenGL
Shading Language и многочисленные примеры, можно найти в недавно вышедшей книге
"Расширения OpenGL".
|