|
Главная
Статьи
Ссылки
Скачать
Скриншоты
Юмор
Почитать
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.