Войти
ПрограммированиеСтатьиГрафика

OpenGL: Использование Register Combiners.

Автор:

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

Один из таких "наворотов" от nVidia - это Register Combiners (не путать с бравыми ребятами, которые в поте лица работают в поле, чтобы мы с вами могли покушать немного хлебушка).

Таким образом, появилось новое расширение для OpenGL, именуемое как GL_NV_register_combiners. Поддержка данного расширения появилась с чипа GeForce (NV10).

Собственно говоря, Register Combiners представляют собой набор из 2-ух или более (до 8-ми) General Combiners, одного Final Combiner и набора всевозможных операций над данными.

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

В заключительной части обработки стоит Final Combiner.

Теория.

Разрешение и запрещение использования Register Combiners производится с помощью вызова функций glEnable и glDisable соответственно. При этом используется константа GL_REGISTER_COMBINERS_NV.

Чтобы указать количество используемых General Combiners, производим вызов функции

glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, num);

где num - есть кол-во General Combiners.

General Combiner состоит из нескольких блоков, через которые последовательно проходят данные:
1. Блок входных RGB и Alpha регистров.
2. Блок предварительной обработки входных данных
3. Блок комбинации входных данных
4. Блок обработки скомбинированных данных

Блоки 1 и 2 имеют 4 потока или переменных (A, B, C, D), которые могут принимать различные значения, в зависимости от того, как вы его (Combiner) запрограммируете.

Программирование General Registers осуществляется с помощью 2-ух функций:
glCombinerInputNV и glCombinerOutputNV.

glCombinerInputNV( GLenum stage, GLenum portion,

GLenum var, GLenum input,
GLenum mapping, GLenum usage)

stage - указывает номер General Combiner, который будет программироваться с помощью этого вызова. Возможные значения - от GL_COMBINER0_NV до GL_COMBINER7_NV;
portion - определяет, какая часть входных данных будет использоваться - цветовые данные или альфа канал (GL_RGB или GL_ALPHA);
var - определяет используемую переменную. Возможные значения - от GL_VARIABLE_A_NV до GL_VARIABLE_D_NV;

input - параметр, определяющий, откуда берутся данные для переменной.
Возможны 10 значений:
GL_ZERO - понятное дело, 0, то есть ничего;
GL_CONSTANT_COLOR0_NV, GL_CONSTANT_COLOR1_NV - данные берутся из регистров постоянного цвета, которые программируются отдельно (см. ниже);
GL_FOG - данные о тумане;
GL_PRIMARY_COLOR_NV - данные берутся из регистра текущего цвета (цвет может быть установлен с помощью glColor, например, или же в процессе программирования General Registers);
GL_SECONDARY_COLOR_NV - еще один регистр цвета (вторичный);
GL_SPARE0, GL_SPARE1 - временные регистры. Устанавливаются в процессе работы General Combiners.
GL_TEXTURE0_ARB, GL_TEXTURE1_ARB - используется текстура с определенного текстурного блока.

mapping - задает работу блока предварительной обработки данных (блок 2).
Возможные значения:
GL_SIGNED_IDENTITY_NV - данные передаются без изменений : f(x)=x;
GL_SIGNED_NEGATE_NV - инвертирование знаковых данных: f(x)=-x;
GL_UNSIGNED_IDENTITY_NV - беззнаковые данные без изменений: f(x)=max(0,x);
GL_UNSIGNED_INVERT_NV - инвертирование беззнаковых данных: f(x)=1-min(max(0,x),1);
GL_EXPAND_NORMAL_NV - "расширение" области значений с [0,1] до [-1,1]: f(x)=2*max(0,x)-1;
GL_EXPAND_NEGATE_NV - то же, что и предыдущее значение, но с отрицанием: f(x)=-2*max(0,x)+1;
GL_HALF_BIAS_NORMAL_NV - то же, что и EXPAND_NORMAL, но "ополовиненное": f(x)=max(0,x)-0.5;
GL_HALG_BIAS_NEGATE_NV - то же, что и EXPAND_NEGATE, но опять же, только половинка: f(x)=-max(0,x)+0.5;
usage - то, что подается на вход блока 3, варианты те же, что и для portion;

Пример:

glCombinerInputNV( GL_COMBINER0_NV, GL_RGB,

GL_VARIABLE_A_NV, GL_TEXTURE0_ARB,
GL_UNSIGNED_IDENTITY, GL_RGB);

На вход Combiner-у 0 подается текстура, из которой берется RGB составляющая и без изменений попадает в переменную A.

Итак, входящие данные мы запрограммировали, теперь надо запрограммировать операции над этими данными и регистры, в которые будет попадать результат этих операций. В блоке комбинации входных данных происходят 3 простейшие операции: переменные A и B перемножаются между собой (аналогично C и D), а так же происходит суммирование этих двух произведений, таким образом, на выходе имеем 3 значения.

glCombinerOutputNV(GLenum stage, GLenum portion,

GLenum abOutput, GLenum cdOutput, GLenum sumOutput,
GLenum scale,GLenum bias,
GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum);

stage, portion - то же, что и у glCombineInputNV;
abOutput, cdOuput, 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 - масштаб результата, может принимать значения GL_NONE, GL_SCALE_BY_TWO_NV, GL_SCALE_BY_FOUR_NV, GL_SCALE_BY_ONE_HALF_NV. Умножение производится на 0, 2, 4, 0.5 соответственно.
bias - сдвиг результата: GL_NONE или GL_BY_NEGATIVE_ONE_HALF_NV. Во втором случае, из результата вычитается 0.5
abDotProduct, cdDotProduct - параметры, определяющие, каким образом будет происходить перемножение переменных. Если какой-то из них = GL_TRUE, то в соответствующем регистре появится результат операции Dot3:
res(x,y,z) = (a.x*b.x+a.y*b.y+a.z*b.z, a.x*b.x+a.y*b.y+a.z*b.z, a.x*b.x+a.y*b.y+a.z*b.z) - аналогично для C*D
иначе, в случае GL_FALSE будет умножение:
res(x,y,z) = (a.x*b.x, a.y*b.y, a.z*.b.z).
muxSum - в случае GL_FALSE получаем A*B + C*D, а в случае GL_TRUE - A*B or C*D.

Пример:

glCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,

GL_SPARE0, GL_SPARE1, GL_DISCARD, GL_RGB);

На вход Combiner-у 0 подается текстура, из которой берется RGB составляющая и без изменений попадает в переменную A.

Вот почти и все - осталось только настроить Final Combiner и дело в шляпе :)

glFinalCombinerInputNV(GLenum var, GLenum input,

GLenum mapping, GLenum usage);

input - значение, подаваемое на вход переменной var. Принимает те же самые значения, что и для General Combiner, но с добавлением следующего: GL_E_TIMES_F_NV (умножение переменной E на F - см. ниже) и GL_SPARE0_PLUS_SECONDARY_COLOR_NV - думаю, ясно из названия
var - то же самое, что и для General Combiner, но переменных немного больше - от A до G. Причем, после поступления на вход Final Combiner этих данных, результат рассчитывается по следующей формуле:
Res = A*B + (1-A) * C + D
mapping поддерживает только 2 параметра (см. вверху) - GL_UNSIGNED_IDENTITY_NV и GL_UNSIGNED_INVERT_NV;

Пример:

glFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_SPARE0_NV,

GL_UNSIGNED_IDENTITY_NV, GL_GL_RGB);

На вход A Final Combiner-а подается значение временного регистра SPARE0, из которого берется RGB составляющая.

Пример реализации Bump-mapping, используя Register Combiners.

Приведенный здесь пример реализует простейший вариант Bump-mapping. Он состоит в следующем: имеется основная текстура (Base) и bump-карта (ее можно сформировать с помощью plug-in для Photoshop, который можно достать опять же на nVidia.

Определяется вектор от источника света и производится его Dot3 умножение на bump-карту, в результате чего мы получаем карту освещенности поверхности, а уже после этого накладываем основную карту умножением.

Разрешаем использование Register Combiners:

glEnable(GL_REGISTER_COMBINERS_NV);

Устанавливаем количество используемых General Combiners - 1:

glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);

Устанавливаем цвет вершин (это будет PRIMARY_COLOR), который будет являться вектором (нормализованным) от источника света:

glColor3f(light.x, light.y, light.z);

Программируем первый General Combiner - нам надо умножить вектор света на первую текстуру путем Dot3, значит, в переменную A пойдет Bump-текстура, причем, мы расширяем диапазон ее значений до [-1,1]:

glCombinerInputNV(

  GL_COMBINER0_NV,    // num combiner
  GL_RGB,            // portion
  GL_VARIABLE_A_NV,  // variable
  GL_TEXTURE0_ARB,    // input
  GL_EXPAND_NORMAL_NV,// mapping
  GL_RGB              // component usage
);

Переменную B настроим на вектор света:

glCombinerInputNV(

  GL_COMBINER0_NV,
  GL_RGB,
  GL_VARIABLE_B_NV,
  GL_PRIMARY_COLOR_NV,
  GL_EXPAND_NORMAL_NV,
  GL_RGB
);

Производим настройку операции Dot3 - результат попадает в SPARE0

glCombinerOutputNV(

  GL_COMBINER0_NV,
  GL_RGB,
  GL_SPARE0_NV,      // AB output
  GL_DISCARD_NV,      // CD output
  GL_DISCARD_NV,      // sum output
  GL_NONE,            // scale
  GL_NONE,            // bias
  GL_TRUE,            // ab dot product
  GL_FALSE,          // cd dot product
  GL_FALSE            // muxsum
);

Настраиваем Final Combiner - надо умножить A на B, а потом умножить на Base текстуру. A*B у нас лежит в SPARE0 значит, для Final Combiner настраиваем переменную A на SPARE0, а переменную B на Base текстуру, в результате получаем (см. формулу выше) res = A*B + (1-A)*C + D = A*B;

    glFinalCombinerInputNV(GL_VARIABLE_A_NV,
        GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);

    glFinalCombinerInputNV(GL_VARIABLE_B_NV,
        GL_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    
    glFinalCombinerInputNV(GL_VARIABLE_C_NV,
        GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    
    glFinalCombinerInputNV(GL_VARIABLE_D_NV,
        GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    
    glFinalCombinerInputNV(GL_VARIABLE_E_NV,
        GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    
    glFinalCombinerInputNV(GL_VARIABLE_F_NV,
        GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);

Потом идет рендер полигона и все !!! :) Только не забудьте выключить Register Combiners, если они Вам больше не нужны:

glDisable(GL_REGISTER_COMBINERS_NV);

Базовая текстура

Bump текстура

Результат

Изображение

Изображение

Изображение

#OpenGL, #шейдеры

15 февраля 2002 (Обновление: 17 июня 2009)

Комментарии [2]