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的一些函數。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM