Новое - статья об ambient occlusion !!!

В ближайшее время - статья про Lua !

OpenGL extensions

Расширение EXT_framebuffer_object

Существует большое количество различных случаев, когда необходимо (или желательно) осуществить так называемый рендеринг в текстуру (render-to-texture, RTT).

До последнего времени единственным удобным способом осуществления этого было использование р-буферов.

Однако использование р-буферов сопряжено с рядом неудобств:

1. Работа с р-буфером осуществляется через зависящий от конкретной платформы интерфейс (к тому же поддерживаемый не всеми платформами).

2. Каждый р-буфер имеет свой собственный контекст рендеринга. Из-за этого переключение к р-буферу и обратно (а также и переключение между различными р-буферами) оказывается весьма дорогостоящей операцией.

3. Каждый р-буфер имеет полный набор буферов (цвета, глубины, трафарета, дополнительные) и их нельзя разделять (т.е. совместно использовать) между несколькими р-буферами.

4. Процедура выбора формата пикселов зависит от платформы и для каждой платформы является достаточно сложной.

Недавно появившееся расширение EXT_framebuffer_object предоставляет очень простую и платформенно-независимую альтернативу использованию р-буферов.

Данное расширение никак не зависит от конкретной платформы и никак не связано с используемой оконной системой. Использование вводимых данным расширением фреймбуферов не требует создания дополнительных контекстов вывода, что делает операцию переключения заметно быстрее.

Также процедура выбора формата пикселов в этом расширении гораздо проще, чем для р-буфера.

Еще одним плюсом является то, что отдельные логические буфера фреймбуфера (например цвета или глубины) могут совместно использоваться сразу несколькими фреймбуферами.

Данное расширение вводит новый тип объекта в OpenGL - фреймбуфер ( framebuffer object, FBO).

Фреймбуфер состоит из набора отдельных логических буферов (цвета, глубины, трафарета) и параметров состояния.

framebuffer object

Рис 1. Структура фреймбуфера.

В качестве логических буферов могут выступать как текстуры подходящих форматов, так и специальные объекты еще одного вводимого типа - рендербуфера ( renderbuffer object, RBO).

Рендербуфер содержит внутри себя простое двухмерное изображение (без использования пирамидального фильтрования) и может использоваться для хранения результатов рендеринга в качестве одного из логических буферов.

Приложение может использовать много фреймбуферов, но все они разделяются на начальный фреймбуфер, созданный оконной системой для приложения (он имеет идентификатор 0), и созданные самим приложением фреймбуфера (они идентифицируются беззнаковыми целыми числами, отличными от нуля).

Приложение может выбрать (bind) один из имеющихся фреймбуферов как текущий. Тогда логические буфера данного фреймбуфера используются (как для чтения, так и для записи) при всех фрагментных операциях.

Каждый фреймбуфер идентифицируется беззнаковым целым числом (GLuint), причем нулевое значение зарезервировано за фреймбуфером, предоставленным оконной системой.

Выбор фреймбуфера как текущего осуществляется при помощи следующей команды:

void glBindFramebufferEXT ( GLenum target, GLuint id );

В качестве параметра target всегда выступает константа GL_FRAMEBUFFER_EXT, а параметр id является идентификатором выбираемого фреймбуфера.

Для выделения блока идентификаторов фреймбуферов и их освобождения служат следующие две команды:

void glGenFramebuffersEXT    ( GLsizei count, GLuint * ids );
void glDeleteFramebuffersEXT ( GLsizei count, GLuint * ids );

Как и для ряда аналогичных функций параметр count задает сколько идентификаторов следует выделить, а параметр ids указывает на массив, куда будут записаны выделенные идентификаторы.

Для проверки того, является ли данное беззнаковое целое число id идентификатором какого-либо фреймбуфера служит следующая функция

GLboolean glIsFramebufferEXT ( GLuint id );

В качестве логических буферов для фреймбуфера может выступать как текстуры, так и рендербуферы.

Каждый рендербуфер идентифицируется беззнаковым целым числом, отличным от нуля. Выделить и освободить блок идентификаторов рендербуферов можно при помощи следующий команд:

void glGenRenderbuffersEXT    ( GLsizei count, GLuint * ids );
void glDeleteRenderbuffersEXT ( GLsizei count, GLuint * ids );

Для проверки того, является ли данное беззнаковое целое число id идентификатором какого-либо рендербуфера служит функция

GLboolean glIsRenderbufferEXT ( GLuint id );

Перед использованием рендербуфера в качестве логического буфера следует при помощи следующей команды задать для него размер и формат пикселов.

void glRenderbufferStorageEXT ( GLenum target, GLenum internalFormat,
                                GLsizei width, GLsizei height );

Параметр target всегда принимает значение GL_RENDERBUFFER_EXT, параметры width и height задают размер рендербуфера в пикселах, а параметр internalFormat задает внутренний формат пикселов буфера.

В качестве этого параметра могут использоваться как обычные форматы текстур (например GL_RGBA8 или GL_DEPTH_COMPONENT24), так и специальные форматы для буфера трафарета, приводимые в следующей таблице.

ЗначениеСмысл
GL_STENCIL_INDEX1_EXT1-битовый буфер трафарета (по одному биту на каждый пиксел)
GL_STENCIL_INDEX4_EXT4-битовый буфер трафарета (по четыре бита на каждый пиксел)
GL_STENCIL_INDEX8_EXT8-битовый буфер трафарета (по восемь бит на каждый пиксел)
GL_STENCIL_INDEX16_EXT16-битовый буфер трафарета (по 16 бит на каждый пиксел)

Для подключения рендербуфера в качестве логического буфера к текущему фреймбуферу служит следующая команда:

void glFramebufferRenderbufferEXT ( GLenum target, GLenum attachment, GLenum rbTarget,
                                    GLuint rbId );

Параметр target принимает значение GL_FRAMEBUFFER_EXT, параметр rbTarget - значение GL_RENDERBUFFER_EXT.

Параметр attachment задает в качестве какого логического буфера следует подключить рендербуфер с идентификатором rbId.

Возможными значениями для параметра attachment являются GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHEMNT1_EXT, ..., GL_DEPTH_ATTACHMENT_EXT и GL_STENCIL_ATTACHMENT_EXT.

Максимальное количество логических буферов цвета, которые можно подключить можно узнать при помощи следующего фрагмента кода:

int maxColorAttachments;

glGetIntegerv ( GL_MAX_COLOR_ATTACHMENTS_EXT, &maxColorAttachments );

Переданное в качестве rbId нулевое значение отсоединяет ранее выбранный рендербуфер от текущего фреймбуфера.

Для подключения к фреймбуферу в качестве логического буфера текстуры служат следующие команды:

void  glFramebufferTexture1DEXT ( GLenum target, GLenum attachment, GLenum texTarget,
                                  GLuint texId, int level );

void  glFramebufferTexture2DEXT ( GLenum target, GLenum attachment, GLenum texTarget,
                                  GLuint texId, int level );

void  glFramebufferTexture3DEXT ( GLenum target, GLenum attachment, GLenum texTarget,
                                  GLuint texId, int level, int zOffset );

В качестве подключаемых текстур могут выступать как одно- и двухмерные текстуры, так и отдельные стороны кубической текстурной карты и слои трехмерной текстуры.

Параметр target всегда принимает значение GL_FRAMEBUFFER_EXT, параметр attachment задает в качестве какого логического буфера следует подсоединить данную текстуру и принимает значения GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHEMNT1_EXT, ..., GL_DEPTH_ATTACHMENT_EXT и GL_STENCIL_ATTACHMENT_EXT.

В качестве параметра texTarget (задающего тип используемой текстуры) могут выступать как GL_TEXTURE_nD, так и GL_TEXTURE_CUBE_MAP_POSITIVE_X,...,GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.

Параметр level задает какой именно уровень (в mipmap-пирамиде) данной текстуры следует использовать в качестве логического буфера.

Для трехмерных текстур параметр zOffset определяет используемый срез трехмерной текстуры.

Обратите внимание, что для успешного использования фреймбуфера, он должен удовлетворять определенным условиям.

В частности необходимо, чтобы все логические буфера имели одинаковый размер, форматы логических буферов соответствовали своим типам подключения (использования).

При подключении сразу нескольких буферов цвета необходимо, чтобы у всех них был одинаковый внутренний формат.

Для проверки состояния фреймбуфера (т.е. можно ли осуществлять в него рендеринг) служит следующая функция:

GLenum glCheckFramebufferStatusEXT ( GLenum target );

В качестве параметра target следует использовать константу GL_FRAMEBUFFER_EXT. Возможными возвращаемыми значениями являются: GL_FRAMEBUFFER_COMPLETE_EXT, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT, GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT, GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT, GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT, GL_FRAMEBUFFER_UNSUPPORTED_EXT и GL_FRAMEBUFFER_STATUS_ERROR_EXT.

В случае готовности фреймбуфера возвращается значение GL_FRAMEBUFFER_COMPLETE_EXT. Возвращенное значение GL_FRAMEBUFFER_UNSUPPORTED_EXT означает, что следует попробовать другую комбинацию форматов буферов.

Также этим расширением вводится функция, позволяющая построить все уровни в пирамидальном фильтровании для текстуры.

void glGenerateMipmapEXT ( GLenum target );

Параметр target принимает одно из следующих значений: GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP и GL_TEXTURE_3D.

Удобно сразу завернуть фреймбуфер в класс, инкапсулировав внутри него все операции с ним. Ниже приводится заголовочный файл подобной реализации.

class  FrameBuffer
{
    int       flags;
    int       width;
    int       height;
    unsigned  frameBuffer;
    unsigned  depthBuffer;
    unsigned  stencilBuffer;

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;
    }

    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 );
    }

    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
    };

    static  int maxColorAttachemnts ();
};

Конструктор класса FrameBuffer в качестве входных параметров принимает размеры буфера в пикселах (theWidth и heHeight) и набор битовых флагов, задающих форматы пикселов для буфера глубины и трафарета (theFlags).

При этом считается, что в качестве буфера цвета будет подключена текстура. Также, если в качестве буфера глубины также планируется использовать текстуру, то не следует задавать флаг для формата буфера глубины.

Обратите внимание, что в конструкторе класса происходит только присваивание значений, никаких объектов OpenGL в нем не создается, для их создания служит метод create.

Метод create данного класса создает фреймбуфер и необходимые рендербуфера и возвращает значение false в случае ошибки. Если буфер создан успешно, то возвращается значение true.

Метод bind выбирает данный фреймбуфер как текущий.

Метод unbind выбирает в качестве текущего фреймбуфера фреймбуфер, предоставленный оконной системой (т.е. имеющий идентификатор 0).

Метод attachColorTexture подключает текстуру с заданным идентификатором в качестве цветового буфера с номером no. Параметр target принимает значение GL_TEXTURE_2D или значение, задающее определенную сторону кубической текстурной карты (GL_TEXTURE_CUBE_MAP_POSITIVE_X,...,GL_TEXTURE_CUBE_MAP_NEGATIVE_Z).

Метод attachDepthTexture служит для подключения текстуры, заданной своим идентификатором, в качестве буфера глубины.

Методы detachColorTexture и detachDepthTexture служат для отсоединения текстур от соответствующих буферов.

Метод isOk проверяет готовность фреймбуфера к использованию.

Реализация данного класса приводится ниже.

#include  "libExt.h"
#include  "FrameBuffer.h"

FrameBuffer :: FrameBuffer  ( int theWidth, int theHeight, int theFlags )
{
    width         = theWidth;
    height        = theHeight;
    flags         = theFlags;
    frameBuffer   = 0;
    depthBuffer   = 0;
    stencilBuffer = 0;
}

FrameBuffer :: ~FrameBuffer ()
{
    if ( depthBuffer != 0 )
        glDeleteRenderbuffersEXT ( 1, &depthBuffer );

    if ( stencilBuffer != 0 )
        glDeleteRenderbuffersEXT ( 1, &stencilBuffer );

    if ( frameBuffer != 0 )
        glDeleteFramebuffersEXT ( 1, &frameBuffer );
}

bool  FrameBuffer :: create ()
{
    if ( width <= 0 || height <= 0 || width >= GL_MAX_RENDERBUFFER_SIZE_EXT ||
         height >= GL_MAX_RENDERBUFFER_SIZE_EXT )
             return false;

    glGenFramebuffersEXT ( 1, &frameBuffer );
    glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, frameBuffer );

    int  depthFormat   = 0;
    int  stencilFormat = 0;

    if ( flags & depth16 )
        depthFormat = GL_DEPTH_COMPONENT16_ARB;
    else
	if ( flags & depth24 )
        depthFormat = GL_DEPTH_COMPONENT24_ARB;
    else
    if ( flags & depth32 )
        depthFormat = GL_DEPTH_COMPONENT32_ARB;

    if ( flags & stencil1 )
        stencilFormat = GL_STENCIL_INDEX1_EXT;
    else
    if ( flags & stencil4 )
        stencilFormat = GL_STENCIL_INDEX4_EXT;
    else
    if ( flags & stencil8 )
        stencilFormat = GL_STENCIL_INDEX8_EXT;
    else
    if ( flags & stencil16 )
        stencilFormat = GL_STENCIL_INDEX16_EXT;

    if ( depthFormat != 0 )
    {
        glGenRenderbuffersEXT        ( 1, &depthBuffer );
        glBindRenderbufferEXT        ( GL_RENDERBUFFER_EXT, depthBuffer );
        glRenderbufferStorageEXT     ( GL_RENDERBUFFER_EXT, depthFormat, width, height );
        glFramebufferRenderbufferEXT ( GL_FRAMEBUFFER_EXT,  GL_DEPTH_ATTACHMENT_EXT,
                                       GL_RENDERBUFFER_EXT, depthBuffer );
    }

    if ( stencilFormat != 0 )
    {
        glGenRenderbuffersEXT        ( 1, &stencilBuffer );
        glBindRenderbufferEXT        ( GL_RENDERBUFFER_EXT, stencilBuffer );
        glRenderbufferStorageEXT     ( GL_RENDERBUFFER_EXT, stencilFormat, width, height );
        glFramebufferRenderbufferEXT ( GL_FRAMEBUFFER_EXT,  GL_STENCIL_ATTACHMENT_EXT,
                                       GL_RENDERBUFFER_EXT, stencilBuffer );
    }

    bool complete = glCheckFramebufferStatusEXT ( GL_FRAMEBUFFER_EXT ) ==
                                                  GL_FRAMEBUFFER_COMPLETE_EXT;

    glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, 0 );

    return complete;
}

bool  FrameBuffer :: isOk () const
{
    unsigned  currentFb;

    glGetIntegerv ( GL_FRAMEBUFFER_BINDING_EXT, (int *)&currentFb );

    if ( currentFb != frameBuffer )
        glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, frameBuffer );

    bool complete = glCheckFramebufferStatusEXT ( GL_FRAMEBUFFER_EXT ) ==
                                                  GL_FRAMEBUFFER_COMPLETE_EXT;

    if ( currentFb != frameBuffer )
       glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, currentFb );

    return complete;
}

bool  FrameBuffer :: bind ()
{
    if ( frameBuffer == 0 )
        return false;

    glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, frameBuffer );

    return true;
}

bool  FrameBuffer :: unbind ()
{
    if ( frameBuffer == 0 )
        return false;

    glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, 0 );

    return true;
}

bool  FrameBuffer :: attachColorTexture ( GLenum target, unsigned texId, int no )
{
    if ( frameBuffer == 0 )
        return false;

    if ( target != GL_TEXTURE_2D && (target < GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB ||
                                     target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB) )
        return false;

    glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + no,
                                target, texId, 0 );

    return true;
}

bool  FrameBuffer :: attachDepthTexture ( GLenum target, unsigned texId )
{
    if ( frameBuffer == 0 )
        return false;

    glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                                target, texId, 0 );

    return true;
}

int FrameBuffer :: maxColorAttachemnts ()
{
    int n;

    glGetIntegerv ( GL_MAX_COLOR_ATTACHMENTS_EXT, &n );

    return n;
}

Исходный код (файлы FrameBuffer.h и FrameBuffer.cpp) к данной статье можно скачать здесь.

Valid HTML 4.01 Transitional

Напиши мне
Используются технологии uCoz