Главная Статьи Ссылки Скачать Скриншоты Юмор Почитать Tools Проекты Обо мне Гостевая |
С самого начала программируемые GPU работали исключительно с вещественными числами (32-bit float) - для этого достаточно взглянуть в спецификации расширений ARB_vertex_program и ARB_fragment_program.
Хотя в языке GLSL и есть поддержка типов bool и int, но фактически эти типы реализовывались через тип float, что в частности выражалось в отсутствии поддержки побитовых операций (для которых нет floating point аналогов).
Однако начиная с GPU Nvidia GeForce 8xxx появилась наконец полноценная поддержка целочисленных типов данных. При этом эта поддержка осуществляется как на уровне шейдеров, так и на уровне текстур - добавлены новые форматы текстур с целочисленными компонентами.
Эта поддержка опирается на два расширения OpenGL - EXT_texture_integer и EXT_gpu_shader4.
Первое расширение (EXT_texture_integer) вводит понятие "ненормализованных" целых. Дело в том, что OpenGL ранее использовал целые числа для ряда целей (например, представления цветов), но при этом эти числа считались представлением вещественных чисел из отрезка [0,1] и приводились к нему - т.е. эти числа при требовали нормализации и в исходном виде были недоступны (так обычно все компоненты текстуры представлены в виде 8-битовых беззнаковых значений, однако как для OpenGL, так и для шейдеров эти компоненты передаются уже как нормализованные вещественные числа).
Понятие ненормализованных чисел означает, что никакого преобразования их не происходит и они передаются как были заданы (т.е. при чтении из соответствующей текстуры шейдер получит именно целочисленное значение, записанное в текстуру).
Также расширение EXT_texture_integer вводит целый ряд форматов текстур с ненормализованными целочисленными знаковыми и беззнаковыми компонентами и различным количеством бит на компоненту (8/16/32).
Таблица 1. Целочисленные форматы текстур, вводимые расширением EXT_texture_integer.
Формат | Число компонент | Число бит на компоненту | Знаковая/беззнаковая |
---|---|---|---|
GL_ALPHA8I_EXT | 1 | 8 | знаковая |
GL_ALPHA8UI_EXT | 1 | 8 | беззнаковая |
GL_ALPHA16I_EXT | 1 | 16 | знаковая |
GL_ALPHA16UI_EXT | 1 | 16 | беззнаковая |
GL_ALPHA32I_EXT | 1 | 32 | знаковая |
GL_ALPHA32UI_EXT | 1 | 32 | беззнаковая |
GL_LUMINANCE_ALPHA8I_EXT | 2 | 8 | знаковая |
GL_LUMINANCE_ALPHA8UI_EXT | 2 | 8 | беззнаковая |
GL_LUMINANCE_ALPHA16I_EXT | 2 | 16 | знаковая |
GL_LUMINANCE_ALPHA16UI_EXT | 2 | 16 | беззнаковая |
GL_LUMINANCE_ALPHA32I_EXT | 2 | 32 | знаковая |
GL_LUMINANCE_ALPHA32UI_EXT | 2 | 32 | беззнаковая |
GL_RGB8I_EXT | 3 | 8 | знаковая |
GL_RGB8UI_EXT | 3 | 8 | беззнаковая |
GL_RGB16I_EXT | 3 | 16 | знаковая |
GL_RGB16UI_EXT | 3 | 16 | беззнаковая |
GL_RGB32I_EXT | 3 | 32 | знаковая |
GL_RGB32UI_EXT | 3 | 32 | беззнаковая |
GL_RGBA8I_EXT | 4 | 8 | знаковая |
GL_RGBA8UI_EXT | 4 | 8 | беззнаковая |
GL_RGBA16I_EXT | 4 | 16 | знаковая |
GL_RGBA16UI_EXT | 4 | 16 | беззнаковая |
GL_RGBA32I_EXT | 4 | 32 | знаковая |
GL_RGBA32UI_EXT | 4 | 32 | беззнаковая |
Кроме того, данное расширение вводит несколько новых функций, наиболее важными из которых являются следующие две:
void glClearColorIiEXT ( GLint r, GLint g, GLint b, GLint a ); void glClearColorIuiEXT ( GLuint r, GLuint g, GLuint b, GLuint a );
Данные функции служат для задание целочисленного (знакового и беззнакового) RGBA-значения, используемого для очистки фреймбуфера.
Расширение EXT_gpu_shader4 вводит необходимую поддержку целых чисел в шейдеры на GLSL.
Помимо уже имеющихся типов данных добавлены новые типы для беззнаковых целых - unsigned int, uvec2, uvec3 и uvec4.
Для всех целочисленных форматов введена полная поддержка всех побитовых операций, включая битовые сдвиги.
Для доступа к текстурам с целочисленными форматами ведены новые типы сэмплеров - isampler1D, isampler2D, isampler3D, isamplerCube, isampler2DRect, isampler1DArray, isampler2DArray, isamplerBuffer, usampler1D, usampler2D, usampler3D, usamplerCube, usampler2DRect, usampler1DArray, usampler2DArray и usamplerBuffer.
Для чтения из таких текстур используются стандартные функции доступа к текстурам (такие как texture2D, texture2DRect и т.п.), только с соответствующим типом сэмплера. Обратите внимание, что результаты чтения из целочисленных текстур также являются целочисленными (знакового или беззнакового типа в зависимости от типа сэмплера), т.е. тип возвращаемого значения зависит от типа сэмплера.
Также расширение вводит целочисленные вершинные атрибуты и целочисленные uniform и varying переменные. Для доступа к ним введены следующие новые функции:
void glVertexAttribI1iEXT ( GLuint index, GLint x ); void glVertexAttribI1uiEXT ( GLuint index, GLuint x ); void glVertexAttribI2iEXT ( GLuint index, GLint x, GLint y ); void glVertexAttribI2uiEXT ( GLuint index, GLuint x, GLuint y ); void glVertexAttribI3iEXT ( GLuint index, GLint x, GLint y, GLint z ); void glVertexAttribI3uiEXT ( GLuint index, GLuint x, GLuint y, GLuint z ); void glVertexAttribI4iEXT ( GLuint index, GLint x, GLint y, GLint z, GLint w ); void glVertexAttribI4uiEXT ( GLuint index, GLuint x, GLuint y, GLuint z, GLuint w ); void glVertexI4ivEXT ( GLuint inrex, const GLint * ptr ); void glVertexI4uivEXT ( GLuint inrex, const GLuint * ptr ); void glVertexAttribIPointerEXT ( GLenum index, int size, GLenum type, GLsizei stride, const void * ptr ); void glUniform1uiEXT ( GLint loc, GLuint x ); void glUniform2uiEXT ( GLint loc, GLuint x, GLuint y ); void glUniform3uiEXT ( GLint loc, GLuint x, GLuint y, GLuint z ); void glUniform4uiEXT ( GLint loc, GLuint x, GLuint y, GLuint z,GLuint z );
Еще одно важное изменение - у фрагментного шейдера появился новый способ возвращения значения - для этой цели можно использовать переменные, объявленные как varying out. Именно этот способ следует использовать при рендеринге в текстуру с целочисленными компонентами ( использовать gl_Color и gl_FragData нельзя , так как они floating point).
Расширение EXT_gpu_shader4 вводит некоторые ограничения - нельзя читать из текстур с целочисленными компонентами во floating point переменные, точно так же нельзя использовать для записи в целочисленный фреймбуфер floating point переменные. Поэтому основной способ для записи в такой фреймбуфер - это использовать ivec4 varying out переменные.
Ниже приводится пример простейшего фрагментного шейдера, осуществляющего чтение из целочисленной текстуры и запись в целочисленный фреймбуфер.
#extension GL_EXT_gpu_shader4 : enable // turn off warning uniform isampler2DRect srcMap; varying out ivec4 result; void main () { ivec4 tex = texture2DRect ( srcMap, gl_TexCoord [0].xy ); tex += ivec4 ( 1 ); result = ivec4 ( tex.x ^ tex.y, tex.x & tex.y, tex.x | tex.y, 777 ); }
Данный шейдер читает знаковое целочисленное значение из входной текстуры и возвращает результат применение нескольких побитовых операций над прочтенными значениями.
Обратите внимание на директиву #extension в начале шейдер - она сообщает компилятору о требуемом расширение и выключает выдачу предупредительных сообщений в лог компиляции.
Для связывания имени выходной переменной во фрагментном шейдере с определенным буфером используется двухшаговый процесс - сначала имя связывается с номером цветового канала при помощи функции glBindFragDataLocationEXT, а потом этому номеру сопоставляется соответствующий буфер при помощи команды glDrawBuffers. Ниже приводится описание команды glBindFragDataLocationEXT.
void glBindFragDataLocationEXT ( GLuint program, GLuint colorNumber, const char * name );
Далее приводится исходный текст на С++ простой программы, создающей одну целочисленную текстуру, заполняющую ее данными, а потом использующей шейдер для вывода сразу в два целочисленных фреймбуфера.
#include "libExt.h" #include <glut.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> #include "libTexture.h" #include "Vector3D.h" #include "Vector4D.h" #include "Framebuffer.h" #include "GlslProgram.h" #include "utils.h" unsigned srcMap; GLenum format = GL_RGBA32UI_EXT; FrameBuffer buffer ( 512, 512, 0 ); GlslProgram program; void display () { glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glutSwapBuffers (); } void reshape ( int w, int h ) { glViewport ( 0, 0, (GLsizei)w, (GLsizei)h ); startOrtho ( w, h ); } void key ( unsigned char key, int x, int y ) { if ( key == 27 || key == 'q' || key == 'Q' ) // quit requested exit ( 0 ); } int main ( int argc, char * argv [] ) { // initialize glut glutInit ( &argc, argv ); glutInitDisplayMode ( GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL ); glutInitWindowSize ( 512, 512 ); // create window glutCreateWindow ( "OpenGL integer textures demo" ); // register handlers glutDisplayFunc ( display ); glutReshapeFunc ( reshape ); init (); initExtensions (); assertExtensionsSupported ( "GL_EXT_texture_integer GL_EXT_framebuffer_object GL_EXT_gpu_shader4" ); unsigned screenMap1 = buffer.createColorRectTexture ( GL_RGBA_INTEGER_EXT, format ); unsigned screenMap2 = buffer.createColorRectTexture ( GL_RGBA_INTEGER_EXT, format ); buffer.create (); buffer.bind (); if ( !buffer.attachColorTexture ( GL_TEXTURE_RECTANGLE_ARB, screenMap1, 0 ) ) printf ( "buffer error with color attachment\n"); if ( !buffer.attachColorTexture ( GL_TEXTURE_RECTANGLE_ARB, screenMap2, 1 ) ) printf ( "buffer error with color attachment\n"); if ( !buffer.isOk () ) printf ( "Error with framebuffer\n" ); buffer.unbind (); if ( !program.loadShaders ( "vertex.vsh", "fragment-2.fsh" ) ) { printf ( "Error loading shaders:\n%s\n", program.getLog ().c_str () ); return 3; } int * buf = new int [4*256*256]; int * ptr = buf; for ( int i = 0; i < 256; i++ ) for ( int j = 0; j < 256; j++ ) { *ptr++ = i; *ptr++ = j; *ptr++ = 0; *ptr++ = 0; } glEnable ( GL_TEXTURE_RECTANGLE_ARB ); glGenTextures ( 1, &srcMap ); glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ); // set 1-byte alignment glBindTexture ( GL_TEXTURE_RECTANGLE_ARB, srcMap ); glTexImage2D ( GL_TEXTURE_RECTANGLE_ARB, 0, format, 256, 256, 0, GL_RGBA_INTEGER_EXT, GL_UNSIGNED_INT, buf ); glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameteri ( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP ); GLenum buffers [] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT }; program.bindFragOut ( "result1", 0 ); program.bindFragOut ( "result2", 1 ); program.bind (); program.setTexture ( "srcMap", 0 ); program.unbind (); reshape ( buffer.getWidth (), buffer.getHeight () ); buffer.bind (); glDrawBuffers ( 2, buffers ); program.bind (); drawQuad ( 256, 256 ); program.unbind (); buffer.unbind (); memset ( buf, '\0', 4*256*245*sizeof (int) ); buffer.bind (); glReadBuffer ( GL_COLOR_ATTACHMENT0_EXT ); glReadPixels ( 0, 0, 255, 256, GL_RGBA_INTEGER_EXT, GL_UNSIGNED_INT, buf ); glReadBuffer ( GL_COLOR_ATTACHMENT1_EXT ); glReadPixels ( 0, 0, 255, 256, GL_RGBA_INTEGER_EXT, GL_UNSIGNED_INT, buf ); buffer.unbind (); return 0; }
Соответствующие вершинный и фрагментный шейдеры приводится ниже.
void main(void) { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord [0] = gl_MultiTexCoord0; }
#extension GL_EXT_gpu_shader4 : enable uniform isampler2DRect srcMap; varying out ivec4 result1; varying out ivec4 result2; void main () { ivec4 tex = texture2DRect ( srcMap, gl_TexCoord [0].xy ); tex += ivec4 ( 1 ); result1 = ivec4 ( tex.x ^ tex.y, tex.x & tex.y, tex.x | tex.y, 777 ); result2 = ivec4 ( 1, 2, 3, 4 ); }
По этой ссылке можно скачать весь исходный код к этой статье,а также откомпилированные примеры для M$ Windows и Linux.
Обратите внимание, что для компиляции примера вам понадобится новые версии библиотек libExt и классов FrameBuffer и GlslProgram.