четверг, 8 марта 2012 г.

Основы FBO в OpenGL. Часть первая, немного теории

Часть первая, немного теории
Часть вторая, простой пример
Часть третья, буфер глубины
Часть четвертая, MRT
Часть пятая, чтение
Часть шестая, R2VB
Ссылки по теме    

Не так давно мы с вами познакомились технологией VBO (Vertex Buffer Object), рассказывая о этой технологии я неоднократно упоминал о неком FBO, самое время разобраться что же это такое и как им пользоваться.

Данная статья предполагает что вы уже знакомы с VBO, умеете создать текстуру и знаете что такое шейдеры, потому на этих вопросах я останавливаться не буду.

FBO это технология, предназначенная для внеэкранного рендеринга сцены в текстуру. За поддержку этой технологии отвечает расширение GL_EXT_framebuffer_object. Данная технология применяется во многих аспектах компьютерной графики, таких как пост эффекты, тени, отражение, освещение, а также в технологии известной как R2VB (Render to Vertex Buffer), на которой основаны многие вершинные эффекты, начиняя от построение рельефа поверхности по карте высот и до скелетной анимации на GPU. Отдельно стоит отметить возможность производить параллельные вычисления на GPU, которые так же не возможны без технологии рендеринга в текстуру, даже такой процесс как отладка шейдера и тот может оказаться непосильной задачей без возможности получения результата работы шейдера и за все это у нас отвечает такая простая технология как FBO. В том что эта технология достаточно простая мы скоро убедимся на личном опыте.

Как мы наверное знаем, рендеринг в текстуру можно осуществить несколькими способами, к примеру скопировав часть изображения посредством функций glCopyTexImage2D, данная технология изначально  существовала в OpenGL1.1. Ограничение данной методики состояло в том, что текстура не могла иметь размер больше чем окно вывода. Чуть позже была введена новая технология, решающая предыдущую проблему, так называемые p-buffers. В отличии от предыдущей технологии создаваемый p-buffers был подобен оконному контексту, с тем отличием, что его размеры ограничивались лишь возможностями аппаратуры. К сожалению, обе технологии имели один существенный недостаток – данный буфер и результирующая текстура находились в разных областях памяти. Потому приходилось осуществлять копирование данных из пиксельного буфера в текстурный буфер, что приводило к существенному падению производительности. Чтобы это исправить была введена новая технология – рендеринг в текстуру (расширение ARB_render_texture). Данная технология позволяла привязать пиксельный буфер к текстуре, тем самым устраняя лишнее копирование. Основным неудобством p-buffers была необходимость инициализировать оконный контекст для каждого из буферов, а это требовало большого количества платформенно-зависимого кода. С целью упростить этот процесс и было введено новое расширение GL_EXT_framebuffer_object. В ядро это расширение вошло только в OpenGL 3.x, но большинство видеокарт поддерживают это расширение с 2005 года.

Работа с данным буфером очень похожа на работу с VBO, точно так же нужно сгенерировать идентификатор, забиндить буфер и передать в него данные, но есть и свои особенности. Давайте разберем поэтапно процесс создания данного буфера и собственно процесс рендеринга в текстуру.
Прежде чем что либо делать необходимо создать этот самый буфер кадра, делается это очень просто, по аналогии с VBO:
Листинг 1.
1
2
3
4
5
6
7
Var fboId: GLuint;
Begin
  glGenFramebuffersEXT(1, @fboId);             //Генерируем буфер
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); //Активируем
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);     //Деактивируем
  glDeleteFramebuffersEXT(1, @fboId);         //Удаляем
end;

Как видите, отличий практически нет, за исключением других имен функций. Так функция glGenFramebuffersEXT выдает нам уникальный идентификатор для данного буфера, а функция glBindFramebufferEXT делает этот буфер активным. Функция glDeleteFramebuffersEXT позволяет удалить данный буфер. 
Так же как и в VBO это пока что пустой буфер, и прежде чем мы сможем его использовать его нужно заполнить нашими данными. Для начала разберем какие данные там вообще могут быть – в первую очередь это текстуры (и не удивительно, раз технология для этого предназначена), но кроме текстур (буфера цвета) туда так же могут быть помещены буфер глубины и буфер трафарета. Технология организована таким образом, что можно на лету менять конфигурацию буфера кадра, прикрепляя и удаляя нужные буферы, это значительно быстрее нежели создавать каждый раз новый буфер кадра.
Присоединение буферов производится командами:
1
2
3
4
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
                          GL_TEXTURE_2D, textureId, 0);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                             GL_RENDERBUFFER_EXT, rboId);

Первая строка присоединяет заданный буфер (в примере буфер цвета) как текстуру с идентификатором textureId, в дальнейшем все операции будут производиться уже с соответствующей текстурой. Во втором случае мы присоединяем к буферу кадра специальный буфер рендеринга, имеющий свой внутренний формат и предназначенный для внеэкранного рендеринга. К буферам рендеринга относится буфер глубины (GL_DEPTH_ATTACHMENT_EXT, используется в примере) и буфер трафарета (GL_STENCIL_ATTACHMENT_EXT). Давайте разберем как создается этот буфер:
1
2
3
4
5
6
7
8
Var rboId: GLuint;
Begin 
  glGenRenderbuffersEXT(1, @rboId); //Генерируем буфер
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboId); //Активируем
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
                           WIDTH, HEIGHT); //Задаем формат и размеры
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);//Деактивируем
end;

Строки 3,4 и 7 нам знакомы с VBO, из новых только функция glRenderbufferStorageEXT. Данная функция выделяет память под указанный буфер и задает его формат. В качестве формата буфера глубины может выступать один из следующих:
    GL_DEPTH_COMPONENT16_ARB
    GL_DEPTH_COMPONENT24_ARB
    GL_DEPTH_COMPONENT32_ARB
Которые, как не трудно догадаться, отличаются точностью. В качестве формата буфера трафарета может выступать один из следующих:
    GL_STENCIL_INDEX1_EXT
    GL_STENCIL_INDEX4_EXT
    GL_STENCIL_INDEX8_EXT
    GL_STENCIL_INDEX16_EXT
Наличие этих буферов не обязательно.

Отдельно стоит остановиться на размерах – размеры всех буферов и текстур должны совпадать.

Подытожим, мы выяснили что существует два способа прикрепления буферов – как текстуру, которую можно потом наложить на объект, из которой можно читать посредством glGetTexImage, и как логический буфер трафарета или глубины, не имеющих соответствующих текстурных форматов. Буфер трафарета/глубины может быть прикреплен только один, буферов цвета может быть несколько. Узнать максимальное количество присоединяемых буферов цвета можно воспользовавшись командой:
    glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, @max_col_att);
Обычно видеокарты позволяют прикреплять 4 буфера цвета, но могут быть и исключения.

Вот собственно и все, что необходимо знать для начала работы с FBO. В следующей части статьи мы разберем это более детально на примерах.

Комментариев нет:

Отправить комментарий