Главная Статьи Ссылки Скачать Скриншоты Юмор Почитать Tools Проекты Обо мне Гостевая |
Под MRT(Multiple Render Targets) понимается возможность одновременного рендеринга сразу в несколько цветовых буферов, а не в один, как обычно происходит.
Именно для поддержки этого в фрагментных программах на GLSL существует возможность записи данных не в gl_FragColor, а в массив gl_FragData [].
При этом каждый элемент массива gl_FragData [] соответствует пикселу в одном из выходных цветовых буферов. Таким образом результатом работы фрагментной программы для фрагмента будет не одно цветовое значение, а сразу несколько, записываемых в разные буфера.
Обратите внимание, что буфера должны быть одного размера и не допускается одновременная работа с переменными gl_FragColor и gl_FragData.
Для того, чтобы из программы, использующей OpenGL, включить запись сразу в несколько буферов можно воспользоваться тремя, практически одинаковыми путями.
Для этого можно воспользоваться функцией glDrawBuffersATI (вводимой расширением ATI_draw_buffers), функцией glDrawBuffersARB (вводимой расширением ARB_draw_buffers) или же воспользоваться функцией glDrawBuffers (вводимой в OpenGL 2.0).
Далее мы будем рассматривать только последний способ (т.к. они фактически отличаются только окончанием имени функции).
Для задания вывода сразу в несколько буферов (в OpenGL 2.0) служит следующая функция:
void glDrawBuffers ( GLsizei n, const GLenum * buffers );
Параметр n задает количество элементов (идентификаторов буферов), передаваемых в параметре buffers.
Каждый элемент массива buffers задает один из используемых буферов вывода, его положение в списке соответствует индексу в массиве gl_FragData. В качестве таких буферов могут выступать как стандартные буфера OpenGL (GL_AUX0, GL_AUX1, ..., GL_NONE, GL_FRONT_LEFT, GL_FRONT_RIGHT и т.д.), так и связанные с FBO текстуры.
Именно последний случай представляет собой наибольший интерес, т.к. при этом фактически происходит рендеринг сразу в несколько текстур.
Чтобы получить количество доступных целей для рендинга, вызовите glGetIntegerv с первым параметром GL_MAX_DRAW_BUFFERS_ARB:
int maxDrawBuffers; glGetIntegerv ( GL_MAX_DRAW_BUFFERS_ARB, &maxDrawBuffers );
Пусть у нас есть FBO, заданный идентификатором fbo и две текстуры, также заданные своими идентификаторами texA, texB.
Тогда для направления вывода в обе этих текстуры можно использовать следующий фрагмент кода:
glBindFramebufferEXT ( GL_FRMABUFFER_EXT, fbo ); glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texA, 0 ); glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, texB, 0 ); GLenum buffers [] = { GL_COLOR_ATTACHMENT0_EXT,GL_COLOR_ATTACHMENT1_EXT }; glDrawBuffers ( 2, buffers );
Для удобства работы модифицируем класс FrameBuffer, чтобы он поддерживал работу сразу с несколькими цветовыми буферами.
Для этого добавим к нему массив colorBuffer, в котором мы будем хранить идентификаторы подключенных к нему текстур. Также нам понадобится изменить метод getColorBuffer, чтобы он принимал в качестве параметра номер требуемой текстуры.
Ниже приводится описание измененного класса FrameBuffer.
class FrameBuffer { int flags; int width; int height; unsigned frameBuffer; // id of framebuffer object unsigned colorBuffer [8]; // texture id or buffer id unsigned depthBuffer; unsigned stencilBuffer; int saveViewport [4]; // saved viewport setting during bind public: FrameBuffer ( int theWidth, int theHeight, int theFlags = 0 ); ~FrameBuffer (); int getWidth () const { return width; } int getHeight () const { return height; } bool hasStencil () const { return stencilBuffer != 0; } bool hasDepth () const { return depthBuffer != 0; } unsigned getColorBuffer ( int no = 0 ) const { return colorBuffer [no]; } unsigned getDepthBuffer () const { return depthBuffer; } bool isOk () const; bool create (); bool bind (); bool unbind (); bool attachColorTexture ( GLenum target, unsigned texId, int no = 0 ); bool attachDepthTexture ( GLenum target, unsigned texId ); bool detachColorTexture ( GLenum target ) { return attachColorTexture ( target, 0 ); } bool detachDepthTexture ( GLenum target ) { return attachDepthTexture ( target, 0 ); } // create texture for attaching unsigned createColorTexture ( GLenum format = GL_RGBA, GLenum internalFormat = GL_RGBA8, GLenum clamp = GL_REPEAT, int filter = filterMipmap ); unsigned createColorRectTexture ( GLenum format = GL_RGBA, GLenum internalFormat = GL_RGBA8 ); // mipmapping support void buildMipmaps ( GLenum target = GL_TEXTURE_2D ) const { glGenerateMipmapEXT ( target ); } enum // flags for depth and stencil buffers { depth16 = 1, // 16-bit depth buffer depth24 = 2, // 24-bit depth buffer depth32 = 4, // 32-bit depth buffer stencil1 = 16, // 1-bit stencil buffer stencil4 = 32, // 4-bit stencil buffer stencil8 = 64, // 8-bit stencil buffer stencil16 = 128 // 16-bit stencil buffer }; enum // filter modes { filterNearest = 0, filterLinear = 1, filterMipmap = 2 }; static bool isSupported (); static int maxColorAttachemnts (); static int maxSize (); };
Также изменения потребуют конструктор класса и метод attachColorTexture, приводимые ниже. Полостью исходные файлы для данного класса можно скачать по ссылке в конце статьи.
FrameBuffer :: FrameBuffer ( int theWidth, int theHeight, int theFlags ) { width = theWidth; height = theHeight; flags = theFlags; frameBuffer = 0; depthBuffer = 0; stencilBuffer = 0; for ( int i = 0; i < 8; i++ ) colorBuffer [i] = 0; } bool FrameBuffer :: attachColorTexture ( GLenum target, unsigned texId, int no ) { if ( frameBuffer == 0 ) return false; if ( target != GL_TEXTURE_2D && target != GL_TEXTURE_RECTANGLE_ARB && (target < GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB) ) return false; glBindTexture ( target, colorBuffer [no] = texId ); glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + no, target, texId, 0 ); return true; }
В качестве примера рассмотрим программу, выводящую в одну текстуру нормальное изображение объектов, освещенных по Блинну, а в другую - представленный в виде цвета единичный вектор направления на источник света, и показывающую одну из полученных текстур.
Ниже приводится только код функции display, весь исходный код можно скачать по ссылке в конце статьи.
void display () { GLenum buffers [] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT }; buffer.bind (); glDrawBuffers ( 2, buffers ); reshape ( buffer.getWidth (), buffer.getHeight () ); glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // draw the light glMatrixMode ( GL_MODELVIEW ); glPushMatrix (); glDisable ( GL_TEXTURE_2D ); glTranslatef ( light.x, light.y, light.z ); glColor4f ( 1, 1, 1, 1 ); glutSolidSphere ( 0.1f, 15, 15 ); glPopMatrix (); glMatrixMode ( GL_MODELVIEW ); glPushMatrix (); glRotatef ( rot.x, 1, 0, 0 ); glRotatef ( rot.y, 0, 1, 0 ); glRotatef ( rot.z, 0, 0, 1 ); glBindTexture ( GL_TEXTURE_2D, diffMap ); blinnProgram.bind (); drawBox ( Vector3D ( -1, -1, -2 ), Vector3D ( 1.7, 1.7, 1.7 ), decalMap ); glTranslatef ( 2.5, 2.5, 1 ); glRotatef ( 15*angle, 0, 1, 0 ); glutSolidTorus ( 0.7, 1.6, 20, 20 ); glRotatef ( 10*angle, 0, 0, 1 ); drawBox ( Vector3D ( -2, -2, 1 ), Vector3D ( 1.5, 1.5, 1.5 ), diffMap ); glEnable ( GL_TEXTURE_2D ); blinnProgram.unbind (); buffer.unbind (); glPopMatrix (); startOrtho (); glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); drawQuad ( buffer.getColorBuffer ( activeMap ) ); endOrtho (); glutSwapBuffers (); }
Также ниже приводится используемый фрагментный шейдер.
varying vec3 l; varying vec3 h; varying vec3 v; varying vec3 n; uniform sampler2D diffMap; vec3 dirToColor ( in vec3 v ) { return 0.5 * ( vec3 ( 1.0 ) + v ); } void main (void) { const vec4 diffColor = vec4 ( 0.5, 0.0, 0.0, 1.0 ); const vec4 specColor = vec4 ( 0.1, 0.1, 0.0, 1.0 ); const float specPower = 20.0; vec3 n2 = normalize ( n ); vec3 l2 = normalize ( l ); vec3 h2 = normalize ( h ); vec4 diff = 0.4 + diffColor * max ( dot ( n2, l2 ), 0.0 ); vec4 spec = 10.0 * specColor * pow ( max ( dot ( n2, h2 ), 0.0 ), specPower ); vec4 clr = diff*texture2D ( diffMap, gl_TexCoord [0].xy ) + spec; gl_FragData [0] = clr; gl_FragData [1] = vec4 ( dirToColor ( l ), 1.0 ); }
Исходный код класса FrameBuffer можно скачать по этой ссылке.
По этой ссылке можно скачать весь исходный код к этой статье. Также доступны для скачивания откомпилированные версии для M$ Windows, Linux и Mac OS X.