在固定功能管线绘制中使用FBO将OpenGL场景渲染为纹理
问题
我在开源游戏torcs(http://torcs.sourceforge.net/)上工作。 游戏的图形管线仍然使用OpenGL 1.3的固定功能管线(FFP)。
我尝试将游戏场景渲染为FBO(Framebuffer Object)中的纹理,以便对渲染的纹理进行一些后期处理。 我使用OpenGL 3.3。 在我的机器上。
目前,我已经在GL_COLOR_ATTACHMENT0&1
(2为了在着色器中有两个连续帧可读)和GL_DEPTH_ATTACHMENT
附加的渲染缓冲区设置了附加纹理的FBO。
在绑定FBO之后,游戏渲染功能被执行。 当我之后解除绑定FBO并进行验证时,通过着色器程序将纹理写回窗口缓冲区,场景不完整。 更具体地说,只有汽车的轮廓被渲染,轮胎的痕迹和一些烟雾。 这表明有些东西是呈现给FBO的纹理,但不是所有东西 。 其中没有纹理(树木,房屋,草地等)被渲染到FBO的纹理。 这表明我的纹理设置不正确,但不幸的是,我对OpenGL的了解有限,这就是我希望得到您的帮助的原因。
值得注意的另一件事是,如果我glActiveTexture(GL_TEXTURE0);
在绘图发生之前,会显示一个纹理(即,将写入FBO并写回到窗口系统帧缓冲区而不是汽车轮廓。
代码
以下代码显示了FBO的初始化 (从https://en.wikibooks.org/wiki/OpenGL_Programming/Post-Processing):
int screen_width = 640;
int screen_height = 480;
/* Texture A*/
glGenTextures(1, &fbo_texture);
glBindTexture(GL_TEXTURE_2D, fbo_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
/* Texture B*/
//glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &fbo_texture_a);
glBindTexture(GL_TEXTURE_2D, fbo_texture_a);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
/* Depth buffer */
glGenRenderbuffers(1, &rbo_depth);
glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, screen_width, screen_height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
/* Framebuffer to link everything together */
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_texture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fbo_texture_a, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo_depth);
GLenum status;
if ((status = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) {
fprintf(stderr, "glCheckFramebufferStatus: error 0x%x", status);
return 0;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
/* Compile and link shaders */
...
以下代码显示绘图发生的位置: 编辑 :如果use_fbo=false
则所有内容都将像以前一样直接渲染到屏幕上。 我做的唯一更改是在括号内。
if (use_fbo)
{
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0,0,grWinw, grWinh);
if (fbo_a) // drawing to fbo_texture_a
{
glDrawBuffer(GL_COLOR_ATTACHMENT1);
glActiveTexture(GL_TEXTURE0+11);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fbo_texture_a);
}
else
{
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glActiveTexture(GL_TEXTURE0+12);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fbo_texture);
}
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
}
glClearColor(0.7f, 0.1f, 0.1f, 1.0f); //clear with red to see what is drawn to the fbo
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
grScreens[0]->update(s, grFps);//THIS IS WHERE THE DRAWING HAPPENS unchanged from original drawing in TORCS
if (use_fbo)
{
glPopAttrib();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glEnable(GL_TEXTURE_2D);
glDrawBuffer(GL_BACK);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glUseProgram(program_postproc);
if (fbo_a) // drawn to fbo_texture_a
{
glUniform1i(uniform_fbo_texture, 11);
glUniform1i(uniform_fbo_texture_a, 12);
fbo_a=!fbo_a;
}
else
{
glUniform1i(uniform_fbo_texture, 12);
glUniform1i(uniform_fbo_texture_a, 11);
fbo_a=!fbo_a;
}
glEnableVertexAttribArray(attribute_v_coord_postproc);
glBindBuffer(GL_ARRAY_BUFFER, vbo_fbo_vertices);
glVertexAttribPointer(
attribute_v_coord_postproc, 2, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(attribute_v_coord_postproc);
glUseProgram(0);
}
我希望我提供了足够的信息来帮助我。 任何建议表示赞赏。
编辑:我再次检查了我的着色器代码和FBO实现(简化为只有一个颜色附件等与简化的绘图),它都工作。 我认为麻烦在于用于绘图的固定功能管道和我的FBO的实施混合...
编辑:这里是use_fbo = true与false发生了什么的两个图像:(注意:红色是FBO绑定后的清晰颜色,用于查看呈现给fbo的内容:除了阴影和滑动标记)
我还尝试将深度缓冲区可视化(将实现更改为深度纹理附件),即使线性化,也没有任何信息。 我想深度也没有正确写入FBO。
当我将您的代码与矿山工作引擎进行比较时,我会看到这些差异,请逐一尝试:
纹理格式
您正在使用:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
所以将你所有的人合并到:
GL_COLOR_ATTACHMENT0: GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE
GL_COLOR_ATTACHMENT1: GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE
我在用:
GL_COLOR_ATTACHMENT0 : GL_RGBA , GL_RGBA8 , GL_UNSIGNED_BYTE
GL_DEPTH_ATTACHMENT : GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16, GL_UNSIGNED_BYTE
GL_STENCIL_ATTACHMENT: GL_STENCIL_INDEX , GL_STENCIL_INDEX8 , GL_UNSIGNED_BYTE
您需要更改纹理的内部像素格式以指定位宽。 如果我的记忆效果很好,当我编写代码时(几年前),它只适用于GL_RGBA,GL_RGBA
。
深度目标
我使用depth
和stencil
纹理与color attachment
相同,我不使用任何RenderBuffer
调用。 这并不意味着你的代码是错误的,但我的测试和工作。
纹理大小
这很可能无效,因为大多数gfx卡支持矩形纹理扩展,但OpenGL纹理应该是2的分辨率。 所以对于初学者来说,尝试使用512x512
而不是640x480
并在代码运行时改回来(只是为了确保...)。
如果它有帮助,那么我的C ++ FBO class
从我的引擎中获取的,所以你可以比较一下(不会单独使用,因为它使用引擎中的纹理和内容):
//------------------------------------------------------------------------------
//--- Open GL FBO object ver 2.31 ----------------------------------------------
//------------------------------------------------------------------------------
#ifndef _OpenGL_FBO_h
#define _OpenGL_FBO_h
//------------------------------------------------------------------------------
class OpenGL_FBO
{
public:
GLuint fbo;
int xs,ys;
struct _dst
{
GLint txr; // texture id
GLenum dst; // GL_DEPTH_COMPONENT, GL_COLOR_ATTACHMENT0, ...
_dst() { txr=-1; dst=GL_COLOR_ATTACHMENT0; }
_dst(_dst& a) { *this=a; }
~_dst() {}
_dst* operator = (const _dst *a) { *this=*a; return this; }
//_dst* operator = (const _dst &a) { ...copy... return this; }
};
List<_dst> dst;
OpenGL_FBO() { fbo=0xFFFFFFFF; xs=1; ys=1; dst.reset(); }
OpenGL_FBO(OpenGL_FBO& a) { fbo=0xFFFFFFFF; dst.reset(); *this=a; }
~OpenGL_FBO() { if (fbo!=0xFFFFFFFF) glDeleteFramebuffers(1,&fbo); }
OpenGL_FBO* operator = (const OpenGL_FBO *a) { *this=*a; return this; }
//OpenGL_FBO* operator = (const OpenGL_FBO &a) { ...copy... return this; }
void resize(OpenGLscreen &scr,int _xs=-1,int _ys=-1)
{
int i;
_dst *d;
if (_xs<=0) _xs=scr.xs;
if (_ys<=0) _ys=scr.ys;
// for (xs=1;xs<_xs;xs<<=1);
// for (ys=1;ys<_ys;ys<<=1);
xs=_xs; ys=_ys; // ****
if (fbo==0xFFFFFFFF) glGenFramebuffers(1,&fbo);
glBindFramebuffer(GL_FRAMEBUFFER,fbo);
for (d=dst.dat,i=0;i<dst.num;i++,d++)
{
scr.txrs.bind(d->txr);
scr.txrs.resize(d->txr,xs,ys,1);
// glFramebufferTexture2D(GL_FRAMEBUFFER,t->dst,GL_TEXTURE_2D,scr.txrs.names[d->txr],0);
glFramebufferTexture(GL_FRAMEBUFFER,d->dst,scr.txrs.names[d->txr],0);
// glCheckFramebufferStatus(GL_FRAMEBUFFER);
}
scr.txrs.unbind();
glBindFramebuffer(GL_FRAMEBUFFER,0);
}
int add(OpenGLscreen &scr,int _dest=GL_COLOR_ATTACHMENT0) // add txr to fbo
{
_dst d;
OpenGL_TXR tmp;
// colro atachments
tmp.pixelformat =GL_RGBA;
tmp.pixeliformat=GL_RGBA8;
tmp.pixeltype=GL_UNSIGNED_BYTE;
tmp.mag=GL_NEAREST;
tmp.min=GL_NEAREST;
if (_dest==GL_DEPTH_ATTACHMENT)
{
tmp.pixelformat =GL_DEPTH_COMPONENT;
tmp.pixeliformat=GL_DEPTH_COMPONENT16;
// tmp.pixeltype=GL_FLOAT;
tmp.pixeltype=GL_UNSIGNED_BYTE;
}
if (_dest==GL_STENCIL_ATTACHMENT)
{
tmp.pixelformat =GL_STENCIL_INDEX;
tmp.pixeliformat=GL_STENCIL_INDEX8;
tmp.pixeltype=GL_UNSIGNED_BYTE;
}
tmp.xs=xs;
tmp.ys=ys;
tmp.zs=1;
tmp._mipmap=0;
tmp.txrtype=GL_TEXTURE_2D;
d.txr=scr.txrs.add(tmp);
d.dst=_dest;
dst.add(d);
return d.txr;
}
void bind(OpenGLscreen &scr) // init fbo >> txr
{
// init and resize
if (fbo==0xFFFFFFFF) glGenFramebuffers(1,&fbo);
glBindFramebuffer(GL_FRAMEBUFFER,fbo);
glViewport(0,0,xs,ys);
scr.cls();
}
void unbind(OpenGLscreen &scr)
{
glBindFramebuffer(GL_FRAMEBUFFER,0);
glViewport(scr.x0,scr.y0,scr.xs,scr.ys);
}
};
//------------------------------------------------------------------------------
#endif
//------------------------------------------------------------------------------
//--- end. ---------------------------------------------------------------------
//------------------------------------------------------------------------------
哪里:
OpenGLscreen scr
是我的渲染引擎
scr.cls()
只是glClear
和init框架的内容
scr.x0,y0,xs,ys
是目标窗口的视口
scr.txrs
是纹理系统类(处理所有纹理),比如add
新纹理加载/保存/从文件, CPU / GPU之间的转换等等。
我也使用我的动态列表模板,所以:
List<double> xxx;
与double xxx[];
相同double xxx[];
xxx.add(5);
将5
添加到列表的末尾
xxx[7]
访问数组元素(安全)
xxx.dat[7]
访问数组元素(不安全但快速直接访问)
xxx.num
是数组实际使用的大小
xxx.reset()
清除数组并设置xxx.num=0
xxx.allocate(100)
预先分配100
项目的空间
典型的用法是:
// [globals and init]
OpenGLScreen scr; // can ignore this
OpenGL_FBO fbo;
scr.init(window_handle); // init OpenGL stuff can ignore this
fbo.add(scr,GL_COLOR_ATTACHMENT0);
fbo.add(scr,GL_DEPTH_ATTACHMENT);
fbo.resize(scr,512,512);
// [render loop]
fbo.bind(scr);
// here render
fbo.unbind(scr);
// here you can use the textures fbo.dst[].txr
请看这里的具体例子:
对于那些坚持使用旧款英特尔高清显卡的用户来说 ,粗糙对于那些由于驱动程序中的错误而不期望这会起作用。 看到这个缓慢的解决方法:
上一篇: Render OpenGL scene to texture using FBO in fixed function pipeline drawing