Часть первая, немного теории
Часть вторая, простой пример
Часть третья, буфер глубины
Часть четвертая, MRT
Часть пятая, чтение
Часть шестая, R2VB
Ссылки по теме В предыдущей части статьи мы научились делать простой рендеринг в текстуру, но для реализации большинства эффектов нам еще может потребоваться буфер глубины. В этой части статьи мы с вами разберем как прикрепить буфер глубины к буферу кадра и сумеем отобразить наш буфер глубины в виде текстуры.
Для реализации нам в первую очередь нам потребуется еще одна глобальная переменная, в которой будет храниться идентификатор буфера рендеринга, назовем ее rboId, тип переменной тот же что и у остальных буферов - GLUInt.
Доработаем процедуру инициализации, добавив туда создание буфера рендеринга и присоединив к нему буфер глубины:
Здесь мы (строки 8-11) создали новый буфер рендеринга и при помощи команды glRenderbufferStorageEXT указали что тип хранимых данных будет соответствовать компонентам глубины. После чего, в строке 20, мы прикрепили его к нашему буферу кадра и указали что в этом буфере рендеринга будет сохраняться глубина сцены.
Полный исходный код примера вы найдете в FBO_DB.
В данном примере буфер глубины хоть и присутствовал, но мы его «не видели» и вам пришлось доверять мне наслово, давайте исправим эту ситуацию. Для того чтобы визуализировать логический буфер глубины его нужно присоединить к буферу FBO в качестве текстуры со специальным внутренним форматом GL_DEPTH_COMPONENT, для этого немного доработаем процедуру инициализации FBOInit:
Во-первых мы полностью удалили создание буфера рендеринга, так как его наличие для данной задачи не обязательно, во-вторых - в 9 строке, вместо записи буфера цвета в текстуру мы сделали запись буфера глубины, просто заменив тип буфера на
GL_DEPTH_ATTACHMENT_EXT. Ну и естественно мы создали новую текстуру с форматом пикселя GL_DEPTH_COMPONENT, в которую можно было бы поместить наш буфер глубины. Для этого, в строке 4, мы воспользовались функцией CreateDepthTexture.
В этом примере мы не используем буфер цвета, потому, чтоб не было ошибок, мы должны запретить чтение/запись в буфер цвета, это мы и делаем в строке 11.
Результат работы этой программы вы можете увидеть на Рис.2.
Здесь белый цвет соответствует пустоте (бесконечности), чем пиксель темнее, тем он ближе к камере.
Исходный код примера вы найдете в FBO_DT.
С этим думаю разобрались, теперь давайте для закрепления напишем демо, где одновременно будут выводиться и буфер глубины и буфер цвета. Для этого нам потребуется создать еще одну текстуру, и присоединить эту текстуру в качестве буфера цвета. Так же немного изменим процедуру рендеринга, чтоб на левую половину экрана была наложена первая текстура а на правую – вторая.
Для этого, в первую очередь объявим еще одну глобальную переменную, в которой будет храниться идентификатор нашего буфера цвета, назовем его ColorTexId, теперь немного изменим InitFBO, добавив туда создание еще одной целочисленной текстуры (строка 4) и присоединение ее в качестве буфера цвета (строка 11):
Так как теперь у нас имеется буфер цвета, то мы удалили строки запрещающие рисование в буфер цвета. Теперь внесем небольшие доработки в процедуру FBORender:
В этом примере я так же включил генерацию мип-уровней для новой текстуры, делается это в строках 14-15. Так же для рисования скринквада (строка 17) мы воспользовались перегружаемой функцией drawScreenQuad, в качестве параметров которой выступает количество строк, количество столбцов и список текстурных юнитов для каждого из квадов. В результате у вас должно получиться примерно то же что и Рис.3:
Полный код примера смотрите в FBO_DC.
Так же стоит сделать небольшое замечание - в случае если вы захотите одновременно использовать буфер глубины и буфер трафарета, то вместо создания двух буферов рендеринга, можно создать один со специальным типом: GL_DEPTH24_STENCIL8_EXT.
Тогда процедура инициализации сведется к такому виду:
Обратите внимание на строки 9-10, мы перенаправляем запись буфера глубины и буфера трафарета в один рендер буфер (rboId). Более подробно этот случай мы разберем в следующих частях.
Часть вторая, простой пример
Часть третья, буфер глубины
Часть четвертая, MRT
Часть пятая, чтение
Часть шестая, R2VB
Ссылки по теме В предыдущей части статьи мы научились делать простой рендеринг в текстуру, но для реализации большинства эффектов нам еще может потребоваться буфер глубины. В этой части статьи мы с вами разберем как прикрепить буфер глубины к буферу кадра и сумеем отобразить наш буфер глубины в виде текстуры.
Для реализации нам в первую очередь нам потребуется еще одна глобальная переменная, в которой будет храниться идентификатор буфера рендеринга, назовем ее rboId, тип переменной тот же что и у остальных буферов - GLUInt.
Доработаем процедуру инициализации, добавив туда создание буфера рендеринга и присоединив к нему буфер глубины:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | procedure InitFBO(Width, Height: integer); var Status: GLUInt; begin //Для начала создадим текстуру куда все это будет рендериться textureId:=CreateTextureRGBA8(Width, Height, true); //Создадим RBO glGenRenderbuffersEXT(1, @rboId); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboId); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, Width, Height); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); //Создадим буфер FBO glGenFramebuffersEXT(1, @fboId); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); //Буферы созданы, осталось лишь присоеденить текстуру и буфер рендеринга к FBO 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); //Проверим статус, чтоб убедиться что нет никаких ошибок Status:=glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); assert(status=GL_FRAMEBUFFER_COMPLETE_EXT, 'FBOError '+inttostr(Status)); //Ну и не забудем деактивировать буфер, иначе весь последующий рендеринг //будет осуществляться в него glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); end; |
Здесь мы (строки 8-11) создали новый буфер рендеринга и при помощи команды glRenderbufferStorageEXT указали что тип хранимых данных будет соответствовать компонентам глубины. После чего, в строке 20, мы прикрепили его к нашему буферу кадра и указали что в этом буфере рендеринга будет сохраняться глубина сцены.
Полный исходный код примера вы найдете в FBO_DB.
В данном примере буфер глубины хоть и присутствовал, но мы его «не видели» и вам пришлось доверять мне наслово, давайте исправим эту ситуацию. Для того чтобы визуализировать логический буфер глубины его нужно присоединить к буферу FBO в качестве текстуры со специальным внутренним форматом GL_DEPTH_COMPONENT, для этого немного доработаем процедуру инициализации FBOInit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | procedure InitFBO(Width, Height: integer); var Status: GLUInt; begin textureId:=CreateDepthTexture(Width, Height); //Создадим буфер FBO glGenFramebuffersEXT(1, @fboId); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); //Буферы созданы, осталось лишь присоеденить текстуру к FBO glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, textureId, 0); //запретить запись/чтение в этот буфер glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); //Проверим статус, чтоб убедиться что нет никаких ошибок Status:=glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); assert(status=GL_FRAMEBUFFER_COMPLETE_EXT, 'FBOError '+inttostr(Status)); //Ну и не забудем деактивировать буфер, иначе весь последующий рендеринг //будет осуществляться в него glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); fboReady:=true; end; |
Во-первых мы полностью удалили создание буфера рендеринга, так как его наличие для данной задачи не обязательно, во-вторых - в 9 строке, вместо записи буфера цвета в текстуру мы сделали запись буфера глубины, просто заменив тип буфера на
GL_DEPTH_ATTACHMENT_EXT. Ну и естественно мы создали новую текстуру с форматом пикселя GL_DEPTH_COMPONENT, в которую можно было бы поместить наш буфер глубины. Для этого, в строке 4, мы воспользовались функцией CreateDepthTexture.
В этом примере мы не используем буфер цвета, потому, чтоб не было ошибок, мы должны запретить чтение/запись в буфер цвета, это мы и делаем в строке 11.
Результат работы этой программы вы можете увидеть на Рис.2.
Рис.2. Запись буфера глубины в текстуру. |
Исходный код примера вы найдете в FBO_DT.
С этим думаю разобрались, теперь давайте для закрепления напишем демо, где одновременно будут выводиться и буфер глубины и буфер цвета. Для этого нам потребуется создать еще одну текстуру, и присоединить эту текстуру в качестве буфера цвета. Так же немного изменим процедуру рендеринга, чтоб на левую половину экрана была наложена первая текстура а на правую – вторая.
Для этого, в первую очередь объявим еще одну глобальную переменную, в которой будет храниться идентификатор нашего буфера цвета, назовем его ColorTexId, теперь немного изменим InitFBO, добавив туда создание еще одной целочисленной текстуры (строка 4) и присоединение ее в качестве буфера цвета (строка 11):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | procedure InitFBO(Width, Height: integer); var Status: GLUInt; begin ColorTexId:=CreateTextureRGBA8(Width, Height, true); textureId:=CreateDepthTexture(Width, Height); // Создадим буфер FBO glGenFramebuffersEXT(1, @fboId); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); //Буферы созданы, осталось лишь присоединить текстуру к FBO glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, textureId, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ColorTexId, 0); //Проверим статус, чтоб убедиться что нет никаких ошибок Status:=glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); assert(status=GL_FRAMEBUFFER_COMPLETE_EXT, 'FBOError '+inttostr(Status)); //Ну и не забудем деактивировать буфер, иначе весь последующий рендеринг //будет осуществляться в него glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); end; |
Так как теперь у нас имеется буфер цвета, то мы удалили строки запрещающие рисование в буфер цвета. Теперь внесем небольшие доработки в процедуру FBORender:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | procedure FBORender; var viewport:array[0..3] of integer; begin glGetIntegerv(GL_VIEWPORT, @viewport); glViewport(0,0,texWidth,texHeight); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); //Активируем FBO glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);//Очищаем буферы цвета и глубины DrawCube; //Рендерим объект/сцену в текстуру glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);//Деактивируем FBO glViewport(viewport[0],viewport[1],viewport[2],viewport[3]); //Сгенерируем для полученных текстур мип-уровни glBindTexture(GL_TEXTURE_2D, textureId); glGenerateMipmapEXT(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, ColorTexId); glGenerateMipmapEXT(GL_TEXTURE_2D); //Нарисуем два квада с разными текстурами drawScreenQuad(1,2,[textureId, ColorTexId]); glBindTexture(GL_TEXTURE_2D, 0);//Деактивируем текстуру end; |
В этом примере я так же включил генерацию мип-уровней для новой текстуры, делается это в строках 14-15. Так же для рисования скринквада (строка 17) мы воспользовались перегружаемой функцией drawScreenQuad, в качестве параметров которой выступает количество строк, количество столбцов и список текстурных юнитов для каждого из квадов. В результате у вас должно получиться примерно то же что и Рис.3:
Рис.3. Одновременный вывод в текстуры буферов глубины и цвета |
Полный код примера смотрите в FBO_DC.
Так же стоит сделать небольшое замечание - в случае если вы захотите одновременно использовать буфер глубины и буфер трафарета, то вместо создания двух буферов рендеринга, можно создать один со специальным типом: GL_DEPTH24_STENCIL8_EXT.
Тогда процедура инициализации сведется к такому виду:
1 2 3 4 5 6 7 8 9 10 11 12 13 | Procedure InitFBO(Width, Height: integer); var Status: GLUInt; Begin ... glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboId); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, Width, Height); glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,GL_DEPTH_ATTACHMENT_EXT,GL_RENDERBUFFER_EXT, rboId); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,GL_STENCIL_ATTACHMENT_EXT,GL_RENDERBUFFER_EXT, rboId); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); ... end; |
Обратите внимание на строки 9-10, мы перенаправляем запись буфера глубины и буфера трафарета в один рендер буфер (rboId). Более подробно этот случай мы разберем в следующих частях.
Здравствуйте, Ваши статьи очень полезны для начинающих, учился основам VBO. Спасибо.
ОтветитьУдалитьВозник вопрос по 3-му примеру, где создается текстура цвета и глубины. В инициализации Вы создаете 2-е текстуры потом создаете RBO объект, который никуда не подключаете, это опечатка или я чегото не понял?
Код:
procedure InitFBO(Width, Height: integer);
var Status: GLUInt;
begin
ColorTexId:=CreateTextureRGBA8(Width, Height, true);
textureId:=CreateDepthTexture(Width, Height);
//Создадим RBO
glGenRenderbuffersEXT(1, @rboId);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboId);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, Width, Height);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
// Создадим буфер FBO
glGenFramebuffersEXT(1, @fboId);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);
//Буферы созданы, осталось лишь присоединить текстуру к FBO
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, textureId, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ColorTexId, 0);
//Проверим статус, чтоб убедиться что нет никаких ошибок
Status:=glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
assert(status=GL_FRAMEBUFFER_COMPLETE_EXT, 'FBOError '+inttostr(Status));
//Ну и не забудем деактивировать буфер, иначе весь последующий рендеринг
//будет осуществляться в него
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
end;
Здравствуйте, это действительно опечатка (копи-паст зло), в этом случае глубина сохраняется в текстуру и все тесты глубины идут через нее, потому наличие рендербуфера глубины не обязательно. Собственно в предыдущем примере я б этом написал:
ОтветитьУдалить"Во-первых мы полностью удалили создание буфера рендеринга, так как его наличие для данной задачи не обязательно".
Но так как примеров много, то из-за банального копирования забыл удалить этот кусок.
Спасибо за ошибку :)