Как известно, в OpenGL «из коробки» поддерживаются текстуры в форматах RGB, RGBA и BRGA. Что же делать, если из внешней библиотеки пришло изображение в ином формате, например в ARGB? Это случается довольно часто, если OpenGL используется совместно с библиотекой Qt.
Один из вариантов решения такой проблемы — использовать встроенную в Qt функцию конвертации. Однако, конвертация выполняется на процессоре и требует дополнительного копирования памяти. Другой, более быстрый способ, — использовать текстуру в том виде, в котором отдает ее Qt, сделав преобразование на видеокарте.
Допустим, мы хотим в OpenGL использовать текстуру как RGBA. Тогда шейдер преобразования будет выглядеть так (язык GLSL):
uniform sampler2D RGBtex;
void main(void) {
float nx,ny,r,g,b,a;
nx=gl_TexCoord[0].x;
ny=gl_TexCoord[0].y;
r=texture2D(RGBtex,vec2(nx,ny)).b;
g=texture2D(RGBtex,vec2(nx,ny)).g;
b=texture2D(RGBtex,vec2(nx,ny)).r;
a=texture2D(RGBtex,vec2(nx,ny)).a;
r *= a;
g *= a;
b *= a;
gl_FragColor=vec4(r,g,b,a);
}
Не все видеокарты поддерживают OpenGL 2.0 и GLSL, поэтому, для совместимости, с помощью следующей команды преобразуем шейдер в ассемблер для использования совместно с расширением ARB_fragment_program:
cgc -oglsl -profile arbfp1 argb2rgba.glsl
Утилита cgc входит в состав nVidia SDK, однако, полученный код будет работать и на любой другой видеокарте:
!!ARBfp1.0 TEMP R0; TEX R0, fragment.texcoord[0], texture[0], 2D; MUL result.color.xyz, R0.zyxw, R0.w; MOV result.color.w, R0; END
Остается заключительный этап — загрузка и использование шейдера:
static const char *program=
"!!ARBfp1.0\n"
"TEMP R0;\n"
"TEX R0, fragment.texcoord[0], texture[0], 2D;\n"
"MUL result.color.xyz, R0.zyxw, R0.w;\n"
"MOV result.color.w, R0;\n"
"END";
unsigned int shader;
glEnable(GL_FRAGMENT_PROGRAM_ARB);
glGenProgramsARB(1, &shader);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, shader);
glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
program, strlen(program));
if ( glGetError () == GL_INVALID_OPERATION ) {
// ошибка загрузки шейдера
}
// ...
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, shader);
QImage image = get_image();
// используем текстуру как обычно
// glBindTexture(GL_TEXTURE_2D, texture_id)
// glTexImage2D/glTexSubImage2D(GL_TEXTURE_2D, ..., GL_RGBA,
// ..., gimage.bits());
//
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);