• OpenGL中FrameBuffer使用


    这边先引用别人写的比较好的文章,以便快速的了解关于framebuffer的一些函数。

    http://longzxr.i.sohu.com/blog/view/168909774.htm

    《-------------------------------------------------------------------一下内容为引用-----------------------------------------------------------------------》

     

    Frame Buffer Object(FBO)扩展,被推荐用于把数据渲染到纹理对像。相对于其它同类技术,如数据拷贝或交换缓冲区等,使用FBO技术会更高效并且更容易实现。
    在这篇文章中,我将会快速地讲解一下如何来使用这一扩展,同时会介绍一些在使用过程中我们要注意的地方。学会该技术后,你便可以把一些渲染到纹理(render to texture)的功能加入到你的程序中,实现更快速的运行。

    建立

    和OpenGL中的其它对像一样,如纹理对像(texture object), 像素缓冲对像(pixel buffer objects) , 顶点缓冲对像(vertex buffer object)等,在使用一个FBO对像之前,你必须先要生成该对像,并取得一个有效的对像标识。

    GLuint fbo;glGenFramebuffersEXT(1, &fbo);

    要对一个FBO进行任何的操作,你必须先要对它进行绑定。这一步骤与我们平时使用VBO或者纹理的过程很像。绑定对像后,我们便可以对FBO进行各种操作了,以下代码演示如何进行绑定。

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);

    第一个参数是“目标(target)”,指的是你要把FBO与哪个帧缓冲区进行绑定,目前来说,我个参数就只有一些预定义的选择(GL_FRAMEBUFFER_EXT),但将来扩展的发展,可能会来现其它的选择,让你把FBO与其它的目标进行绑定。整型变量fbo,是用来保存FBO对像标识的,这个标识我们已在前面生成了。要实现任何与FBO有关的操作,我们必须有一个FBO被绑定,否则调用就会出错

    加入一个深度缓存(Depth Buffer)

    一个FBO它本身其实没有多大用处,要想让它能被更有效的利用,我们需要把它与一些可被渲染的缓冲区绑定在一起,这样的缓冲区可以是纹理,也可以是下面我们将要介绍的渲染缓冲区(renderbuffers)。

    一个渲染缓冲区,其实就是一个用来支持离屏渲染的缓冲区。通常是帧缓冲区的一部份,一般不具有纹理格式。常见的模版缓冲和深度缓冲就是这样一类对像。

    在这里,我们要为我们的FBO指定一个渲染缓冲区。这样,当我们渲染的时候,我们便把这个渲染缓冲区作为FBO的一个深度缓存来使用。

    和FBO的生成一样,我们首先也要为渲染缓冲区指定一个有效的标识。

    GLuint depthbuffer;glGenRenderbuffersEXT(1, &depthbuffer);

    成功完成上面一步之后,我们就要对该缓冲区进行绑定,让它成为当前渲染缓冲,下面是实现代码。

    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);

    和FBO的绑定函数一样,第一个参数是“目标(target)”,指的是你要与哪个目标进行绑定,目前来说,只能是一些预定义好的目标。变量dephtbuffer用来保存对像标识。

    这里有一个关键的地方,也就是我们生成的渲染缓冲对像,它本身并不会自动分配内存空间。因此我们要调用OpenGL的函数来给它分配指定大小的内存空间,在这里,我们分配一个固定大小的深度缓显空间。

    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height);

    上面这一函数成功运行之后,OpenGL将会为我们分配好一个大小为width x height的深度缓冲区。注意的是,这里用了GL_DEPTH_COMPONENT,就是指我们的空间是用来保存深度值的,但除了这个之外,渲染缓冲区 还可以用来保存普通的RGB/RGBA格式的数据或者是模板缓冲的信息。

    准被好了深度缓存的显存空间后,接下来要做的工作就是把它与前面我们准备好了的FBO对像绑定在一起。

    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer);

    这个函数看起来有点复杂,但其实它很好理解的。它要做的全部工作就是把把前面我们生成的深度缓存对像与当前的FBO对像进行绑定,当然我们要注意一个FBO有多个不同绑定点,这里是要绑定在FBO的深度缓冲绑定点上。

    加入用于渲染的纹理

    到现在为止,我们还没有办法往FBO中写入颜色信息。这也是我们接下来正要讨论的,我们有以下两种方法来实现它:

    1. 把一个颜色渲染缓冲与FBO绑定。
    2. 把一个纹理与FBO绑定。

    前者在某些地方会用到,后面的章节我们会深入讨论。现在我们先来说说第二种方法。

    在你想要把纹理与一个FBO进行绑定之前,我们得先要生成这个纹理。这个生成纹理的过程种我们平时见到的纹理生成没什么区别。

    GLuint img;glGenTextures(1, &img);glBindTexture(GL_TEXTURE_2D, img);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

    这个实例中,我们生成一个普通的RGBA图像,大小是width x height,与前面我们生成的渲染缓冲区的大小是一样的,这一点很重要,也就是FBO中所有的绑定对像,都必须要有相同的宽度和高度。还有要注意的就是:这里我们没有上传任何的数据,只是让OpenGL保留分配好的空间,稍后我们将会用到。

    生成好纹理之后,接下来的工作就是把这个纹理与FBO绑定在一起,以便我们可以把数据渲染到纹理空间中去。

    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, img, 0);

    这里再次看到这个看起来非常可怕的函数,当然它也并没有我们想像中那么难理解。参数GL_COLOR_ATTACHMENT0_EXT是告诉OpenGL把纹理对像绑定到FBO的0号绑定点(一个FBO在同一个时间内可以绑定多个颜色缓冲区,每个对应FBO的一个绑定点),参数GL_TEXTURE_2D是指定纹理的格式,img保存的是纹理标识,指向一个之前就准备好了的纹理对像。纹理可以是多重映射的图像,最后一个参数指定级级为0,指的是使用原图像。

    最后还有一步要做的工作,就是检查一下FBO的准备工作是否全部完成,是否以经能被正确使用了。

    这个测试工作由下面一个函数来完成,它会返回一个当前绑定的FBO是否正确的状态信息。

    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

    如果所有工作都已经做好,那么返回的状态值是GL_FRAMEBUFFER_COMPLETE_EXT,也就是说你的FBO已经准备好,并可以用来作为渲染对像了。否则就会返回其它一个错误码,通过查找定义文档,可以找到相关的错误信息,从而了角错误大概是在哪一步骤中产生的。

    渲染到纹理

    所有困难的工作就是前面建立FBO环境的部份,剩下来的工作就相当简单了,相关的事情就只是调用一下以下这个函数:glBindFramebufferEXT().

    当我们要把数据渲染并输出到FBO的时候,我们只需要用这个函数来把一个FBO对像进行绑定。当我们要停止输出到FBO,我们只要把参数设为0,再重新调用一次该函数就可以了。当然,停止向FBO输出,这也是很重要的,当我们完成了FBO的工作,就得停止FBO,让图像可以在屏幕上正确输出。

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);glPushAttrib(GL_VIEWPORT_BIT);glViewport(0,0,width, height);// Render as normal here// output goes to the FBO and it's attached buffersglPopAttrib();glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

    上面另外三行代码glPushAttrib/glPopAttrib 及 glViewport,是用来确保在你跳出FBO渲染的时候可以返回原正常的渲染路径。glViewport在这里的调用是十分必要的,我们不要常试把数据渲染到一个大于或小于FBO大小的区域。 函数glPushAtrrib 和 glPopAttrib 是用来快速保存视口信息。这一步也是必要的,因为FBO会共享主上下文的所有信息。任何的变动,都会同时影响到FBO及主上下文,当然也就会直接影响到你的正常屏幕渲染。

    这里一个重要信息,你可能也注意到了,我们只是在绘制的时候绑定或解除FBO,但是我们没有重新绑定纹理或渲染缓冲区,这里因为在FBO中会一直保存了这种绑定关系,除非你要把它们分开或FBO对像被销毁了。

     

    《------------------------------------------------------------------------以上内容为引用-----------------------------------------------------------------------》

    看完上面的内容,你应该对FrameBuffer有一个比较完整的了解,其实我要做的是整理framebuffer程序为接口,以便被使用。

    CFramebuffer.h

    #pragma once
    
    #include <gl/glew.h>
    #include <glut.h>
    #include <cv.h>
    #include <highgui.h>
    
    class CFrameBuffer
    {
    public:
    	CFrameBuffer();
    public:
    	~CFrameBuffer();
    private:
    	unsigned int m_FboID;
    	unsigned int m_RboID;
    	unsigned int m_tex;
    	bool     m_bIsBegined;
    	int      m_curbuff;
    
    public:
    	int     m_width;
    	int		m_height;
    
    public:
    	void init(int width, int height);
    	bool begin();
    	bool end();
    	void saveFrameBuff(const char* fileName);
    
    	unsigned int getTex(){return m_tex;}
    };


    CFramebuffer.cpp

    #include "stdafx.h"
    
    #include <iostream>
    #include "CFrameBuffer.h"
    CFrameBuffer::CFrameBuffer()
    {
    	m_FboID = 0;
    	m_RboID = 0;
    	m_tex   = 0;
    	m_bIsBegined = false;
    	m_width = 0;
    	m_height = 0;
    	m_curbuff = 0;
    }
    
    CFrameBuffer::~CFrameBuffer()
    {
    	if(m_bIsBegined)
    	{
    		end();
    		m_bIsBegined = false;
    	}
    	glDeleteTextures(1,&m_tex);
    	glDeleteRenderbuffersEXT(1,&m_RboID);
    	glDeleteFramebuffersEXT(1,&m_FboID);
    }
    
    void CFrameBuffer::init(int width, int height)
    {
    	glewInit();
    	m_width = width;
    	m_height = height;
    
    	glEnable(GL_TEXTURE_2D);
    	glGenTextures(1,&m_tex);
    	glBindTexture(GL_TEXTURE_2D,m_tex);
    
    	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
    	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
    	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    	glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,m_width,m_height,0,GL_BGR,GL_UNSIGNED_BYTE,NULL);
    	glBindTexture(GL_TEXTURE_2D,0);
    	glDisable(GL_TEXTURE_2D);
    
    	glEnable(GL_RENDERBUFFER_EXT);
    	glGenRenderbuffersEXT(1,&m_RboID);
    	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,m_RboID);
    	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,GL_DEPTH_COMPONENT,m_width,m_height);
    	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,0);
    	glDisable(GL_RENDERBUFFER_EXT);
    
    	glEnable(GL_FRAMEBUFFER_EXT);
    	glGenFramebuffersEXT(1,&m_FboID);
    	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,m_FboID);
    	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE_2D,m_tex,0);
    	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,GL_DEPTH_ATTACHMENT_EXT,GL_RENDERBUFFER_EXT,m_RboID);
    	
    
    	GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    
    	if(status != GL_FRAMEBUFFER_COMPLETE_EXT)
    	{
    		switch(status)
    		{
    		case GL_FRAMEBUFFER_COMPLETE_EXT:
    			std::cout << "Framebuffer complete." << std::endl;
    			break;
    
    		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
    			std::cout << "[ERROR] Framebuffer incomplete: Attachment is NOT complete." << std::endl;
    			break;
    
    		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
    			std::cout << "[ERROR] Framebuffer incomplete: No image is attached to FBO." << std::endl;
    			break;
    
    		case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
    			std::cout << "[ERROR] Framebuffer incomplete: Attached images have different dimensions." << std::endl;
    			break;
    
    		case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
    			std::cout << "[ERROR] Framebuffer incomplete: Color attached images have different internal formats." << std::endl;
    			break;
    
    		case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
    			std::cout << "[ERROR] Framebuffer incomplete: Draw buffer." << std::endl;
    			break;
    
    		case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
    			std::cout << "[ERROR] Framebuffer incomplete: Read buffer." << std::endl;
    			break;
    
    		case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
    			std::cout << "[ERROR] Unsupported by FBO implementation." << std::endl;
    			break;
    
    		default:
    			std::cout << "[ERROR] Unknow error." << std::endl;
    			break;
    		}
    	}
    	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0);
    	glDisable(GL_FRAMEBUFFER_EXT);
    	
    	m_curbuff = 0;
    }
    
    bool CFrameBuffer::begin()
    {
    	if(m_bIsBegined)
    	{
    		return false;
    	}
    	else
    	{
    		//glPushAttrib(GL_ALL_ATTRIB_BITS);
    		//glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT,&m_curbuff);
    		//glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,m_FboID);
    
    		glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT,&m_curbuff);
    		glPushAttrib(GL_VIEWPORT_BIT);
    		glMatrixMode(GL_PROJECTION);
    		glPushMatrix();
    		glMatrixMode(GL_MODELVIEW);
    		glPushMatrix();
    
    		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,m_FboID);
    		//cout<<"begin after : GL_FRAMEBUFFER_BINDING_EXT = "<<FboId<<endl;
    		glViewport(0,0,m_width,m_height);
    		glMatrixMode(GL_PROJECTION);
    		glLoadIdentity();
    		glOrtho(0,m_width,0,m_height,-1000,1000);
    		glMatrixMode(GL_MODELVIEW);
    		glLoadIdentity();
    
    		m_bIsBegined = true;
    		return true;
    	}
    }
    
    bool CFrameBuffer::end()
    {
    	if(m_bIsBegined)
    	{
    		//glPopAttrib();
    		//glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,m_curbuff);
    		glMatrixMode(GL_MODELVIEW);
    		glPopMatrix();
    		glMatrixMode(GL_PROJECTION);
    		glPopMatrix();
    		glPopAttrib();
    
    		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,m_curbuff);
    		m_bIsBegined = false;
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    void CFrameBuffer::saveFrameBuff(const char* fileName)
    {
    	IplImage* pImage = cvCreateImage(cvSize(m_width,m_height),8,3);
    	glEnable(GL_TEXTURE_2D);
    	glBindTexture(GL_TEXTURE_2D,m_tex);
    	glGetTexImage(GL_TEXTURE_2D,0,GL_BGR,GL_UNSIGNED_BYTE,pImage->imageData);
    	glDisable(GL_TEXTURE_2D);
    
    	cvFlip(pImage,NULL,0);
    	cvSaveImage(fileName,pImage);
    	cvReleaseImage(&pImage);
    }


    上面已经很详细写出的framebuffer的内容,你只要在draw函数之前调用begin()和draw函数之后用end()就可以完成将纹理绘制到framebuffer中了,这里还使用一个函数来保存framebuffer的纹理到图片,接触到OpenCV的一些函数。

     

     

     

  • 相关阅读:
    【前端大神面考面试官系列】入门Vue全家桶
    【综合篇】浏览器的工作原理:浏览器幕后揭秘
    【星云测试】开发者测试(2)-采用精准测试工具对J2EE Guns开发框架进行测试
    【星云测试】开发者测试(3)-采用精准测试工具对springcloud微服务应用进行穿透测试
    【星云测试】开发者测试(4)-采用精准测试工具对dubbo微服务应用进行测试
    【星云测试】精准测试的软件产品质量效率变化分析
    巧用location.hash保存页面状态
    全面解析ASP.NET MVC模块化架构方案
    在多线程编程中lock(string){...}隐藏的机关
    注释是恶魔,请不要再写一行注释
  • 原文地址:https://www.cnblogs.com/leven20061001/p/2724692.html
Copyright © 2020-2023  润新知