Сайт Алексея Озерицкого

Искусственный интеллект, суперкомпьютерные системы, параллельные вычисления, численные методы, алгоритмы

Поддержка текстур формата ARGB в OpenGL

Как известно, в 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);