前半部分引用的是这位大侠的博客,目的说明渲染到纹理的过程。后面例子是我自己写的。
原文地址:http://www.cppblog.com/init/archive/2012/02/16/165778.aspx
帧缓冲区对象呢又称为FBO,它允许我们把渲染从窗口的帧缓冲区转移到我们所创建的一个或者多个离屏帧缓冲区。被推荐用于数据渲染到纹理对象,相对于其他同类技术,如数据拷贝或者交换缓冲区等等,使用FBO技术会更高效且易于实现。此buffer包含了color buffer,depth buffer,stencil buffer.渲染到纹理这个技术在游戏中经常用来模拟电视机或者监视器等等的效果。 这里一个重要信息,你可能也注意到了,我们只是在绘制的时候绑定或解除FBO,但是我们没有重新绑定纹理或渲染缓冲区,这里因为在FBO中会一直保存了这种绑定关系,除非你要把它们分开或FBO对像被销毁了。
来到这里,我们已经把屏幕的数据渲染到了一个图像纹理上。现在我们来看一看如何来使用这张已经渲染好了的图像纹理。这个操作的本身其实是很简单的,我们只要把这张图像纹理当作普通纹理一样,绑定为当前纹理就可以了。
glBindTexture(GL_TEXTURE_2D, img);
以上这一函数调用完成之后,这张图像纹理就成了一个在绘图的时候用于被读取的普通纹理。
根据你在初始化时所指定的不同纹理滤波方式,你也许会希望为该纹理生成多重映像(mipmap)信息。如果要建立多重映像信息,多数的人都是在上传纹理数据的时候,通过调用函数gluBuild2DMipmaps()来实现,当然有些朋友可能会知道如何使用自动生成多重映像的扩展,但是在FBO扩展中,我们增加了第三种生成映像的方法,也就是使用GenerateMipmapEXT()函数。
这个函数的作用就是让OpenGL帮你自动创建多重映像信息。中间实现的过程,根据不同的显卡会有所不同,我们只关心它们最终的结果是一样就行了。值得注意的是:对于这种通过FBO渲染出来的纹理,要实现多重映像的话,只有这一种方法是正确的,这里你不可以使用自动生成函数来生成多重映像,这其中的原因有很多,如果你想深入了解的话,可以查看一下技术文档。
使用这一函数使方便,你所要做的就是先把该纹理对像绑定为当前纹理,然后调用一次该函数就可以了。
glGenerateMipmapEXT(GL_TEXTURE_2D);
OpenGL将会自动为我们生成所需要的全部信息,到现在我们的纹理便可以正常使用了。
一个重点要注意的地方:如果你打算使用多重映像(如 GL_LINEAR_MIPMAP_LINEAR),该函数glGenerateMipmapEXT()必须要在执行渲染到纹理之前调用。
在创建纹理的时候,我们可以按以下代码来做。
glGenTextures(1, &img);
glBindTexture(GL_TEXTURE_2D, img);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmapEXT(GL_TEXTURE_2D);
到现在,这张纹理和普通纹理没什么区别,我们就按处理普通纹理的方法来使用就可以了。
同样的,你如果分配了渲染缓冲对像,也别忘了要把它清理掉。本实例中我们分配的是深度缓存渲染对像,我们用以下函数来清除它:
glDeleteRenderbuffersEXT(1, &depthbuffer);
FBO的完整性
在向FBO输出渲染结果之前,需要测试FBO的完整性。如果FBO不完整,任何渲染操作都会失败。我们可以使用glCheckFramebufferStatusEXT()函数来测试FBO的完整性(此函数不能在glBegin()和glEnd()函数之间调用)。FBO完整性的判别法则如下:
-----------------------------------------
#include
using namespace std;
#include "GL/glew.h"
#include "GL/gl.h"
#include "GL/glu.h"
#include "GL/glut.h"
/****************************************************************************************************
* 全局变量定义
*****************************************************************************************************/
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int TEXTURE_WIDTH = 512;
const int TEXTURE_HEIGHT = 512;
const double NEAR_PLANE = 1.0f;
const double FAR_PLANE = 1000.0f;
GLuint fbo = 0; // FBO对象的句柄
GLuint depthbuffer = 0;
GLuint rendertarget = 0; // 纹理对象的句柄
/****************************************************************************************************
* 全局函数定义
*****************************************************************************************************/
void SetupWindow(void)
{
int argc = 0; char* argv[] = {0};
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH|GLUT_SINGLE|GLUT_RGBA);
//glutInitWindowPosition(100, 100);
glutInitWindowSize(800, 600);
glutCreateWindow("Framebuffer Sample");
GLenum err = glewInit(); // GLEW的初始化必须在OpenGL上下文被创建之后调用
}
// 初始化摄像机
void SetupCamera(void)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, (double)SCREEN_WIDTH/(double)SCREEN_HEIGHT, NEAR_PLANE, FAR_PLANE);
gluLookAt(5, 5, 5, 0, 0, 0, 0, 1, 0);
// 各种变换应该在GL_MODELVIEW模式下进行
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Z-buffer
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
// 启用2D贴图
glEnable(GL_TEXTURE_2D);
}
// 初始化几何形体
void SetupResource(void)
{
// 创建纹理
glGenTextures(1, &rendertarget);
glBindTexture(GL_TEXTURE_2D, rendertarget);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
/*
// 创建深度缓冲区
glGenRenderbuffersEXT(1, &depthbuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
*/
// 创建FBO对象
glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, rendertarget, 0);
//glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if(status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
}
}
// 渲染到窗体
void Render(void)
{
// 绑定默认FBO(窗体帧缓冲区的ID是0)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glBindTexture(GL_TEXTURE_2D, rendertarget);
glViewport(0,0,SCREEN_WIDTH, SCREEN_HEIGHT);
// 渲染
glClearColor( 0, 0, 1, 0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glBegin(GL_POLYGON);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor3f(1, 1, 1);
glTexCoord2f(1, 1);
glVertex3d( 1, 1, 0);
glTexCoord2f(0, 1);
glVertex3d(-1, 1, 0);
glTexCoord2f(0, 0);
glVertex3d(-1, -1, 0);
glTexCoord2f(1, 0);
glVertex3d( 1, -1, 0);
glEnd();
glutSwapBuffers();
}
// 渲染到纹理
void RenderToTarget(void)
{
glBindTexture(GL_TEXTURE_2D, 0); // 取消绑定,因为如果不取消,渲染到纹理的时候会使用纹理本身
// 绑定渲染目标
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glViewport(0,0,TEXTURE_WIDTH, TEXTURE_HEIGHT);
// 渲染
glClearColor( 1, 1, 0, 1 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glBegin(GL_POLYGON);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor4f(1, 0, 0, 1);
glVertex3d( 0, 1, 0);
glVertex3d(-1, -1, 0);
glVertex3d( 1, -1, 0);
glEnd();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
void Clear(void)
{
}
void renderScene()
{
RenderToTarget();
Render();
//Render1();
}
/****************************************************************************************************
* 主程序入口
*****************************************************************************************************/
int main(int argc, char* argv[])
{
SetupWindow();
SetupCamera();
SetupResource();
glutDisplayFunc(renderScene);
glutMainLoop();
return 0;
}
-----------