Расширение NV_register_combiners в OpenGL
Начиная с видеокарт серии GeForce компания NVIDIA предложила новую схему обработки данных ( pipeline),
кроме стандартной схемы, используемой в OpenGL.
Новая схема, основанная на так называемых register combiner'ах, поддерживает гораздо более
сложную модель обработки данных и хорошо подходит для целого ряда попиксельных операция включая расчет
попиксельного диффузного и бликового (specular) освещения.
В новой модели все входные данные помещаются в набор регистров, далее значения в регистрах проходят
через один или несколько блоков обработки, называемых general combiner'ами, после чего они
попадают в блок, называемый final combiner, который по ним регистрам определяет выходное
значение цвета и альфа канала.
Рис 1. Общая схема работы register combiners
Используемые регистры представлены в следующей таблице.
Таблица 1. Регистры
Название | Значение | Константа в OpenGL |
primary color | Цвет фрагмента | GL_PRIMARY_COLOR_NV |
secondary color | Дополнительный (вторичный) цвет | GL_SECONDARY_COLOR_NV |
texture0, texture1 ... | Значение с соответствующего текстурного блока | GL_TEXTURE0_ARB GL_TEXTURE1_ARB ... |
spare0 spare1 | Временные регистры, их начальные значения не определены | GL_SPARE0 GL_SPARE1 |
fog | Степень затуманивания | GL_FOG |
constant color 0 constant color 1 | Задаваемые пользователем дополнительные цвета | GL_CONSTANT_COLOR0_NV GL_CONSTANT_COLOR1_NV |
zero | Нулевой цвет (0,0,0,0) Доступен только для чтения | GL_ZERO |
Каждый из этих регистров состоит из цветовой (RGB) части и альфа-части, которые могут обрабатываться
совершенно независимо.
Каждый блок General Combiner работает следующим образом - на основе входных регистров из
таблицы 1 с использованием простых преобразований строятся четыре внутренних переменных - A, B, C и D.
При этом для каждой из этих переменных (отдельно для RGB- и альфа-частей) задается из какого входного
регистра следует взять соответствующее значение и какому преобразованию следует подвергнуть это
значение перед записью в соответствующую переменную (см рис. 2).
Рис 2. Работа general combiner'а для RGB части
На основе переменных A, B, C и D вычисляется три RGBA значения, которые (после преобразований
масштабирования и сдвига) отсекаются по отрезку [-1,1] и записываются в выходные регистры.
При этом соответствия между переменными, значения которых будут записываться, и выходными регистрами,
в которые будет происходить запись, может задаваться отдельно как для цветовой (RGB) части, так
и для альфа-части.
На следующих двух рисунках приводятся схемы, показывающие как по входным переменным A, B, C и D
вычисляются три выходных значения (RGB и альфа).
Рис 3. Вычисление выходных значений для RGB части
Рис 4. Вычисление выходных значений для альфа части
Операция mux вводится следующи образом: a mux b возвращает значение
a, если spare0.a > 0.5. В противном случае возвращается b.
Обратите внимание, что по умолчанию альфа-часть регистра spare0 инициализируется
альфа-значением из нулевого текстурного блока texture0.
Для получения значений внутренних переменных A, B, C и D по значениям соответствующих регистров
могут использоваться следующие операции (mappings):
Таблица 2. Допустимые операции
Название | Формула | Константа в OpenGL |
unsigned indenity | max ( 0, x ) | GL_UNSIGNED_IDENTITY_NV |
unsigned invert | 1-min(max(0,x)) | GL_UNSIGNED_INVERT_NV |
expand normal | 2*max(0,x)-1 | GL_EXPAND_NORMAL_NV |
expand negate | 1-2*max(0,x) | GL_EXPAND_NEGATE_NV |
half-bias normal | max(0,x)-0.5 | GL_HALF_BIAS_NORMAL_NV |
half-bias negate | 0.5 - max ( 0,x ) | GL_HALF_BIAS_NEGATE_NV |
signed identity | x | GL_SIGNED_IDENTITY_NV |
signed negate | -x | GL_SIGNED_NEGATE_NV |
Рис 5. Допустимые преобразования для general combiner'ов
Выходные значения (перед записью в регистры) подвергаются масштабированию(scale) и
сдвигу(bias).
Таблица 3. Допустимые значения для масштабирования.
Константа в OpenGL | Множитель |
GL_NONE | 1 |
GL_SCALE_BY_TWO_NV | 2 |
GL_SCALE_BY_FOUR_NV | 4 |
GL_SCALE_BY_ONE_HALF_NV | 0.5 |
Таблица 4. Допустимые значения для сдвига.
Константа в OpenGL | Величина сдвига |
GL_NONE | 0 |
GL_BY_NEGATIVE_ONE_HALF_NV | -0.5 |
В результате на выходе получаются три RGB значения и три альфа значения. RGB значения могут быть
записаны в RGB части регистров (доступных для записи), альфа значения могут быть записаны как в
альфа части регистров, так и в RGB часть (в этом случае получается цвет, все три компоненты
кторого равны между собой). Записанные в регистры значения являеются входными для
следующего combiner'а.
Любое из трех выходных значений может быть просто отброшено (т.е. никуда не записано).
Внутри каждого из general combiner'ов все операции выполняются в диапазоне [-1,1].
Конфигурация и обработка RGB и альфа частей происходят полностью независимо.
Обычно general combiner'ы образуют цепочку - выход одного является входом для следующего,
хотя это не обязательно.
Заключительным шагом обработки является final combiner.
Рис 6. Работа final combiner'а
Final combiner вычисляет RGBA значение, которое и будет выходным цветом. Он вычисляет
беззнаковые выходные значения по беззнаковым входным значениям. Все отритцательные значения на
входе в final combiner преобразуются в 0.
Для вычисления выходного RGB значения final combiner содержит шесть внутренних
переменных - A, B, C, D, E и F. Каждая из них является RGB- величиной и
может быть построена на основе любого из входных регистров, причем как из RGB-части, так и из
альфа-части. Значение любой из входных частей может быть инвертировано.
Рис 7. Допустимые преобразования для final combiner'а
Также имеются два псевдорегистра - EF и spare0+secondary color.
Псевдорегистр EF равен произведению переменных E и F, он может быть использован
в качестве исходного значения для любой из переменных A, B, C и D.
Псевдорегистр spare0+secondary color может выступать в качестве входного значения для
переменных B, C и D.
Для получения RGB части выхода final combiner вычисляет значение
AB+(1-A)C+D.
Значение седьмой переменной G определяет альфа-канал выходного RGBA значения. Эта переменная состоит
только из альфа части и может получить свое значение из альфа-части любого из регистров за
исключением двух псевдорегистров. Также значение G может быть инвертировано.
При этом величина spare0+secondary color может либо отсекаться по отрезку [0,1], либо
принимать значения из отрезка [0,2].
Рассмотрим теперь как осуществляется программирование register combiner'ов.
Первым шагом является их включение:
glEnable ( GL_REGISTER_COMBINERS_NV );
Далее слежует указать количество general comber'ов, которое будет использоваться:
glCombinerParamteriNV ( GL_NUM_GENERAL_COMBINERS_NV, num );
Узнать максимальное количество general combiner'ов, поддерживаемое графической картой и
драйвером можно узнать при помощи следующего фрагмента кода:
int maxCombiners;
glGetIntegerv ( GL_MAX_GENERAL_COMBINERS_NV, &maxCombiners );
Далее, для каждого из используемых general combiner'ов следует задать входные и выходные
величины.
Задание входных величин осуществляется при помощи функции:
glCombinerInputNV ( GLenum stage, GLenum portion, GLenum var,
GLenum input, GLenum mapping, GLenum usage );
Параметр stage задает номер используемого combiner'а и принимает одно из следующих
значений от GL_COMBINER0_NV до GL_COMBINER7_NV.
Параметр portion определяет какая часть переменной (RGB или альфа) будет задаваться
и принимает одно из двух значений: GLRGB или GL_ALPHA.
Параметр var определяет используемую внутреннюю переменную и принимает одно из значений:
GL_VARIABLE_A_NV, GL_VARIABLE_B_NV, GL_VARIABLE_C_NV и GL_VARIABLE_D_NV.
Параметр input определяет из какого входного регистра следует взять значение для
соответствующей переменной. Допустимые значения для этого параметра приведены в таблице 1.
Параметр mapping задает отображение, применяемое к значению регистра перед записью
в переменную. Допустимые значения приведены в таблице 2.
Параметр usage определяет какя часть (RGB или альфа) регистра будет записана
(после преобразования) в соответствующую переменную.
Значение для RGB части переменной может буть получено или из RGB части любого из регистров или
из альфа части любого из регистров.
Значение для альфа части переменной может быть получено либо из альфа части регистра, либо из синей
(blue) цветовой компоненты регистра.
Задание выходных величин осуществляется при помощи функции:
glCombinerOutputNV ( GLenum stage, GLenum portion,
GLenum abOutput, GLenum cdOutput, GLenum sumOutput,
GLenum scale, GLenum bias,
GLboolean abDotProduct, GLboolean cdDotProduct,
GLboolean muxSum );
Параметры stage и portion имеют тот же смысл, что и для функции glCombinerInputNV.
Параметры abOutput, cdOutput и sumOutput определяют в какой регистр пойдет каждое
из трех выходных значений. Допустимыми значениями являются GL_DISCARD_NV (отбросить соответствующую
величину), GL_PRIMARY_COLOR_NV, GL_SECONDARY_COLOR_NV, GL_SPARE0_NV, GL_SPARE1_NV, GL_TEXTURE0_ARB
и GL_TEXTURE1_ARB.
Параметр scale задает масштабирующий множитель, на который будет умножено значение переменной,
допустимые значения указаны в таблице 3.
Параметр bias позволяет задать значение, которое будет вычтено из переменной после операции
масштабирования. Допустимые значения указаны в таблице 4.
Параметры abDotProduct и cdDotProduct определяют каким образом происходит вычисление
двух выходных значений (AB и CD). Если параметр принимает значение GL_TRUE, то на соответствующем выходе будет
величина, все компоненты которой равны соответствующему скалярному произведению ((A,B) или (C,D)).
В противном случае на выходе будет покомпонентное произведение входных величин.
Если хотя бы один из параметров abDotProduct или cdDotProduct не равен нулю, то
параметр sumOutput должен принимать значение GL_DISCARD_NV.
Если параметр muxSum равен GL_FALSE, то на выходе ABCD мы получим AB+CD. В противном случае
мы получим AB mux CD.
Заключительным шагом является настройка final combiner'а.
Она осуществляется следующей командой:
glFinalCombinerInputNV ( GLenum var, GLenum input,
GLenum mapping, GLenum usage );
Параметр var задает имя переменной, которая будет задаваться, и принимает одно из
значений от GL_VARIABLE_A_NV до GL_VARIABLE_G_NV.
Параметр input может принимать как значения из таблицы 1, так и дополнительные значения,
соответствующие псевдорегистрам - GL_E_TIMES_F_NV и GL_SPARE0_PLUS_SECONDARY_COLOR_NV.
Параметр mapping задает преобразование входных значений и может принимать только одно из
двух значений - GL_UNSIGNED_IDENTITY_NV и GL_UNSIGNED_INVERT_NV (см. таблицу 2).
Параметр usage определяет какая из частей (RGB или альфа) входного регистра будет
использоваться для задания соответствующей переменной и принимает одно из значений - GL_RGB
и GL_ALPHA.
Однако, если задается переменная G, то usage должно равняться GL_ALPHA.
Если переменная принимает входное значение либо из псевдорегистра EF, либо из псевдорегистра
spare0+secondary color, то usage должно принимать значение GL_RGB.
Значения псевдорегистра spare0+secondary color могут принимать значения из отрезка [0,2],
либо обрезаться по отрезку [0,1].
Для установки отсечения по отрезку [0,1] используется следующая команда:
glCombinerParameteriNV ( GL_COLOR_SUM_CLAMP_NV, GL_TRUE );
Для установки значений регистров constant color можно использовать
следующий фрагмент кода.
glCombinerParamterfvNV ( GL_CONSTANT_COLOR0_NV, c0 );
glCombinerParamterfvNV ( GL_CONSTANT_COLOR1_NV, c1 );
По умолчанию оба регистра constant color являются нулевыми и режим отсечения по отрезку
[0,1] включен.
Исходный код для примера, проверяющего поддержку register combiners и
печатающего максимально доступное количество general combiner'ов можно
скачать здесь.
При подготовке статьи использовались презентации компании NVIDIA, доступные на ее
сайте.
|