FBO:渲染到纹理,绘制渲染纹理时纹理映射错误
Posted
技术标签:
【中文标题】FBO:渲染到纹理,绘制渲染纹理时纹理映射错误【英文标题】:FBO: render to texture, wrong texture mapping when drawing rendered texture 【发布时间】:2012-02-06 12:23:07 【问题描述】:我在 Mac OS X 应用程序上使用 OpenGL 在 NSOpenGLView
上绘制纹理。
该应用程序是一个电影播放器。它将电影帧解码为CVOpenGLTextureRef
(这是OpenGL纹理),我使用GL_QUAD
将它们直接绘制到视图中。一切正常。
下面是代码的相关部分。
// "image" is the CVOpenGLTextureRef containing the movie frame as texture
GLenum textureTarget = CVOpenGLTextureGetTarget(image);
GLuint textureName = CVOpenGLTextureGetName(image);
glEnable(textureTarget);
glBindTexture(textureTarget, textureName);
glBegin(GL_QUADS);
// Draw the quads
//Note: textureTagret is NOT GL_TEXTURE_2D, therefore texture coordinates
//are NOT scaled to [0, 1]
glTexCoord2f(0.0f, imageRect.size.height);
glVertex2f (0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex2f (0.0f, windowRect.size.height);
glTexCoord2f(imageRect.size.width, 0.0f);
glVertex2f (windowRect.size.width, windowRect.size.height);
glTexCoord2f(imageRect.size.width, imageRect.size.height);
glVertex2f (windowRect.size.width,0.0f);
glEnd();
glDisable(textureTarget);
glFlush();
它工作得很好,我可以调整窗口大小,并且纹理正确映射到较小的窗口。
查看此处了解从全屏到 500x280 像素的不同窗口大小:
我现在想使用 FBO 渲染到纹理,我开始制作一个非常简单的实现,其中包括将电影帧渲染到屏幕外 FBO(纹理),然后将该纹理绑定到屏幕上绘制。
代码如下:
// "image" is the CVOpenGLTextureRef containing the movie frame as texture
GLenum textureTarget = CVOpenGLTextureGetTarget(image);
GLuint textureName = CVOpenGLTextureGetName(image);
////////////////////////////////////////////////////////////////////
// the creation on the FBO is done only once on program start:
GLuint fboId;
GLuint textureId;
float targetWidth = 2048;
float targetHeight = 2048;
// create a texture object
glGenTextures(1, &textureId);
glBindTexture(textureTarget, textureId);
glTexParameterf(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(textureTarget, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
glTexImage2D(textureTarget, 0, GL_RGBA8, targetWidth, targetHeight, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(textureTarget, 0);
// create a framebuffer object
glGenFramebuffersEXT(1, &fboId);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);
// attach the texture to FBO color attachment point
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
textureTarget, textureId, 0);
// check FBO status
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if(status != GL_FRAMEBUFFER_COMPLETE_EXT)
return FALSE;
// switch back to window-system-provided framebuffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
///////////////////////////////////////////////////////////////////////////////
// Render to texture
glEnable(textureTarget);
glBindTexture(textureTarget, textureName);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS);
// Draw the quads
glTexCoord2f(0.0f, imageRect.size.height);
glVertex2f (0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex2f (0.0f,imageRect.size.height);
glTexCoord2f(imageRect.size.width, 0.0f);
glVertex2f (imageRect.size.width, imageRect.size.height);
glTexCoord2f(imageRect.size.width, imageRect.size.height);
glVertex2f (imageRect.size.width,0.0f);
glEnd();
glFlush();
// Bind newly rendered texture
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(textureTarget, textureId);
glGenerateMipmapEXT(textureTarget);
// draw on-screen
glBegin(GL_QUADS);
// Draw the quads
//Note: textureTagret is NOT GL_TEXTURE_2D, therefore texture coordinates
//are NOT scaled to [0, 1]
glTexCoord2f(0.0f, imageRect.size.height);
glVertex2f (0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex2f (0.0f, windowRect.size.height);
glTexCoord2f(imageRect.size.width, 0.0f);
glVertex2f (windowRect.size.width, windowRect.size.height);
glTexCoord2f(imageRect.size.width, imageRect.size.height);
glVertex2f (windowRect.size.width,0.0f);
glEnd();
glDisable(textureTarget);
glFlush();
代码不能正常工作,因为图像/纹理不仅是颠倒的,而且纹理映射也是错误的。只有当窗口全屏时它才能正常工作,否则它的行为真的很奇怪。
见下图:
如您所见,纹理在窗口内正确缩放,但会根据全屏窗口大小和实际窗口大小之间的差异按比例“裁剪”。
我什么都试过了。
由于这是我第一次使用 OpenGL,我是否遗漏了什么?我快疯了..
【问题讨论】:
【参考方案1】:// Render to texture glEnable(textureTarget); glBindTexture(textureTarget, textureName); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); glClear(GL_COLOR_BUFFER_BIT);
我没有在此处设置帧缓冲区对象的视口和投影。
glBegin(GL_QUADS); // Draw the quads glTexCoord2f(0.0f, imageRect.size.height); g lVertex2f (0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex2f (0.0f,imageRect.size.height); glTexCoord2f(imageRect.size.width, 0.0f); glVertex2f (imageRect.size.width, imageRect.size.height); glTexCoord2f(imageRect.size.width, imageRect.size.height); glVertex2f (imageRect.size.width,0.0f); glEnd(); glFlush();
【讨论】:
哦..我的错..非常感谢您的回答。我只在调整窗口大小时设置视口和投影。我没有意识到我必须为 FBO 和后台缓冲区指定两个不同的视口和投影。这解决了纹理映射问题,但(显然)纹理仍然颠倒/反射。我知道我可以更改glTexCoord2f
和 glVertex2f
之间的关联来修复它,但我为什么需要它?如果我直接绘制到后台缓冲区,纹理的方向是正确的..为什么我需要在绘制到 FBO 时进行更改?
@Andrea3000:嗯,它被翻转了,因为你把它颠倒了。图像原点是左下角,而不是您似乎假设的右上角。至于视口和投影的设置:这大约是我告诉 OpenGL 新手的 80%——在绘图函数中设置它们,而不是在窗口调整大小处理程序中。窗口调整大小处理程序是一个不好的地方,因为它在使用 FBO 或绘制 HUD 或类似时会导致麻烦。所有的OpenGL操作(静态数据上传除外)都属于绘图函数。
感谢您对 OpenGL 的建议,我会记住它们的 ;-) 关于翻转图像:FBO 和后缓冲有不同的坐标系吗?我问你是因为我不明白图像在绘制到后台缓冲区时如何正确,以及在绘制到 FBO 然后再到后台缓冲区时如何翻转。 glTexCoord2f
和 glVertex2f
对于这两种情况都是一样的。
@Andrea3000:最有可能发生的情况是,假设原点位于左上角,电影帧已被存储。所以你必须翻转这个。某些图像格式(例如 TGA)具有固定的来源。其他像 PNG 或 BMP 允许两者。你必须考虑到这一点。 OpenGL 总是假定图像数据从左下角开始。因此,要么适当地翻转纹理坐标,要么在提供给 glTexImage2D 之前重新排序图像数据。
好吧,也许现在我明白了,谢谢你的解释;-) 如果我明白你的意思,事情就是这样:我的电影帧的原点位于左上角。当我将它渲染到 FBO 时,我正确地渲染它(未翻转),但现在原点位于左下角。所以从现在开始我必须改变纹理坐标,因为即使我将它渲染到多个 FBO,原点也总是在左下角。这样对吗?所以渲染到纹理是正确的,我必须将第二张图更改为后缓冲区。以上是关于FBO:渲染到纹理,绘制渲染纹理时纹理映射错误的主要内容,如果未能解决你的问题,请参考以下文章
WebGL入门(四十一)-使用帧缓冲区对象(FBO)实现将渲染结果作为纹理绘制到另一个物体上