OpenGL Framebuffer
OpenGL中Framebuffer的特性:
1.Framebuffer由什么组成:color buffer,depth buffer,stencil buffer
2.The default framebuffer is created and configured when you create your window (GLFW does this for us).
3.For a framebuffer to be complete the following requirements have to be satisfied:
- We have to attach at least one buffer (color, depth or stencil buffer).
- There should be at least one color attachment.
- All attachments should be complete as well (reserved memory).
- Each buffer should have the same number of samples.
4.两种attachment:texture attachment(可读写,一般用作color buffer)和renderbuffer attachment(可写,一般用作depth stencil buffer)
5.一般使用方法:用texture作为color attachment ,depth attachment 和stencil attachment 共同使用一个buffer(GL_DEPTH24_STENCIL8)作为一个DepthStencil attachment(GL_DEPTH_STENCIL_ATTACHMENT),并且使用renderbuffer object作为attachment。
OpenGL使用Framebuffer的流程:
1.Gen
GLuint fbo;
glGenFramebuffers(1, &fbo);
2.Bind
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3.Attachment
(1)Texture Color Attachment
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB,GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,0);
(2)Texture DepthStencil Attachment
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8,800, 600, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture,0);
(3)Renderbuffer Color Attachments
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 800, 600);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
(4)Renderbuffer DepthStencil Attachments
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
4.check complete
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
// log info output
5.Delete
glDeleteFramebuffers(1, &fbo); Coco2d-x Framebuffer
1.使用framebuffer需要的所有参数:
1)Framebuffer size:width,height(Pixels Unit)
2)Attachment:color buffer(texture or renderbuffer),depth stencil buffer(renderbuffer)
3)Clear value for glClear*(type value):vec3 clearcolor, GLfloat cleardepth, int8 clearstencil
2.删减版framebuffer.h
class CC_DLL FrameBuffer : public Ref
{
public:
static FrameBuffer* create(uint8_t fid, unsigned int width, unsigned int height);
bool init(uint8_t fid, unsigned int width, unsigned int height);
public:
//call glclear to clear frame buffer object : color, depth, stencil
void clearFBO();
void applyFBO();
void restoreFBO();
void setClearColor(const Color4F& color) { _clearColor = color;}
void setClearDepth(float depth) { _clearDepth = depth; }
void setClearStencil(int8_t stencil) { _clearStencil = stencil; }
// set _rt
void attachRenderTarget(RenderTargetBase* rt);
// set _rtDepthStencil
void attachDepthStencilTarget(RenderTargetDepthStencil* rt);
bool isDefaultFBO() const { return _isDefault; }
unsigned int getWidth() const { return _width; }
unsigned int getHeight() const { return _height; }
CC_CONSTRUCTOR_ACCESS:
FrameBuffer();
virtual ~FrameBuffer();
// init _defaultFBO with GLView
bool initWithGLView(GLView* view);
private:
//openGL content for FrameBuffer
GLuint _fbo;
GLuint _previousFBO;
//dirty flag for fbo binding, if true need apply new attachment
bool _fboBindingDirty;
//
uint8_t _fid;
//
Color4F _clearColor;
float _clearDepth;
int8_t _clearStencil;
int _width;
int _height;
RenderTargetBase* _rt;
RenderTargetDepthStencil* _rtDepthStencil;
bool _isDefault;
public:
static FrameBuffer* getOrCreateDefaultFBO(GLView* glView);
static void applyDefaultFBO();
static void clearAllFBOs();
private:
//static GLuint _defaultFBO;
static FrameBuffer* _defaultFBO;
static std::set<FrameBuffer*> _frameBuffers;
};
3.FBO相关操作
1)Create FrameBuffer
FrameBuffer* FrameBuffer::create(uint8_t fid, unsigned int width, unsigned int height)
{
auto result = new (std::nothrow) FrameBuffer();
if(result && result->init(fid, width, height))
{
result->autorelease();
return result;
}
else
{
CC_SAFE_DELETE(result);
return nullptr;
}
}
FrameBuffer::FrameBuffer()
: _clearColor(Color4F(0, 0, 0, 1))
, _clearDepth(1.0)
, _clearStencil(0)
, _fbo(0)
, _previousFBO(0)
, _rt(nullptr)
, _rtDepthStencil(nullptr)
, _fboBindingDirty(true)
, _isDefault(false)
{
_frameBuffers.insert(this);
}
bool FrameBuffer::init(uint8_t fid, unsigned int width, unsigned int height)
{
_fid = fid;
_width = width;
_height = height;
GLint oldfbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfbo);
glGenFramebuffers(1, &_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
glBindFramebuffer(GL_FRAMEBUFFER, oldfbo);
return true;
}new FrameBuffer 和init()都没有create RenderTarget,仅仅gen了一个FBO id,所以需要根据使用需求自己生成RenderTarget,并attach到FBO
2)Create RenderTarget
RenderTarget包括:RenderTargetBase(Render Target基类),RenderTarget(colorbuffer attachment为Texture2D的RenderTarget),RenderTargetRenderBuffer(colorbuffer attachment为RenderBuffer的RenderTarget),RenderTargetDepthStencil(DepthStencil attachment为RenderBuffer的RenderTarget),FrameBuffer。
//--------------------------------------------------------------------
//
// RenderTarget
//
//--------------------------------------------------------------------
RenderTarget* RenderTarget::create(unsigned int width, unsigned int height, Texture2D::PixelFormat format/* = Texture2D::PixelFormat::RGBA8888*/)
{
auto result = new (std::nothrow) RenderTarget();
if(result && result->init(width, height,format))
{
result->autorelease();
return result;
}
else
{
CC_SAFE_DELETE(result);
return nullptr;
}
}
bool RenderTarget::init(unsigned int width, unsigned int height, Texture2D::PixelFormat format)
{
if(!RenderTargetBase::init(width, height))
{
return false;
}
_texture = new (std::nothrow) Texture2D();
if(nullptr == _texture) return false;
//TODO: FIX me, get the correct bit depth for pixelformat
auto dataLen = width * height * 4;
auto data = malloc(dataLen);
if( nullptr == data) return false;
memset(data, 0, dataLen);
if(_texture->initWithData(data, dataLen, format, width, height, Size(width, height)))
{
_texture->autorelease();
CC_SAFE_RETAIN(_texture);
free(data);
}
else
{
CC_SAFE_DELETE(_texture);
free(data);
return false;
}
return true;
}
//--------------------------------------------------------------------
//
// RenderTarget
//
//--------------------------------------------------------------------
RenderTargetDepthStencil* RenderTargetDepthStencil::create(unsigned int width, unsigned int height)
{
auto result = new (std::nothrow) RenderTargetDepthStencil();
if(result && result->init(width, height))
{
result->autorelease();
return result;
}
else
{
CC_SAFE_DELETE(result);
return nullptr;
}
}
bool RenderTargetDepthStencil::init(unsigned int width, unsigned int height)
{
if(!RenderTargetBase::init(width, height)) return false;
GLint oldRenderBuffer(0);
glGetIntegerv(GL_RENDERBUFFER_BINDING, &oldRenderBuffer);
//generate depthStencil
glGenRenderbuffers(1, &_depthStencilBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthStencilBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, oldRenderBuffer);
return true;
}
//--------------------------------------------------------------------
//
// RenderTargetRenderBuffer
//
//--------------------------------------------------------------------
bool RenderTargetRenderBuffer::init(unsigned int width, unsigned int height)
{
if(!RenderTargetBase::init(width, height)) return false;
GLint oldRenderBuffer(0);
glGetIntegerv(GL_RENDERBUFFER_BINDING, &oldRenderBuffer);
//generate depthStencil
glGenRenderbuffers(1, &_colorBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorBuffer);
//todo: this could have a param
glRenderbufferStorage(GL_RENDERBUFFER, _format, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, oldRenderBuffer);
return true;
}
RenderTargetRenderBuffer* RenderTargetRenderBuffer::create(unsigned int width, unsigned int height)
{
auto result = new (std::nothrow) RenderTargetRenderBuffer();
if(result && result->init(width, height))
{
result->autorelease();
return result;
}
else
{
CC_SAFE_DELETE(result);
return nullptr;
}
}3)attach RenderTarget
Color Attachment
void FrameBuffer::attachRenderTarget(RenderTargetBase* rt)
{
if(isDefaultFBO())
{
CCLOG("Can not apply render target to default FBO");
return;
}
CC_ASSERT(rt);
if(rt->getWidth() != _width || rt->getHeight() != _height)
{
CCLOG("Error, attach a render target with different size, Skip.");
return;
}
CC_SAFE_RETAIN(rt);
CC_SAFE_RELEASE(_rt);
_rt = rt;
_fboBindingDirty = true;//因为更新_rt,下次applyFBO()需要重新attach color attachment
}
DepthStencil Attachment
void FrameBuffer::attachDepthStencilTarget(RenderTargetDepthStencil* rt)
{
if(isDefaultFBO())
{
CCLOG("Can not apply depth stencil target to default FBO");
return;
}
if(nullptr != rt && (rt->getWidth() != _width || rt->getHeight() != _height))
{
CCLOG("Error, attach a render target Depth stencil with different size, Skip.");
return;
}
CC_SAFE_RETAIN(rt);
CC_SAFE_RELEASE(_rtDepthStencil);
_rtDepthStencil = rt;
_fboBindingDirty = true;//因为更新了_rtDepthStencil,下次applyFBO()需要重新attach depth stencil attachment
}4) applyFBO
void FrameBuffer::applyFBO()
{
CHECK_GL_ERROR_DEBUG();
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_previousFBO);
glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
// CCASSERT(_fbo==0 || _fbo != _previousFBO, "calling applyFBO without restoring the previous one");
CHECK_GL_ERROR_DEBUG();
if(_fboBindingDirty && !isDefaultFBO())
{
CHECK_GL_ERROR_DEBUG();
if(RenderTargetBase::Type::Texture2D == _rt->getType())
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _rt->getTexture()->getName(), 0);
else
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _rt->getBuffer());
CHECK_GL_ERROR_DEBUG();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, nullptr == _rtDepthStencil ? 0 : _rtDepthStencil->getBuffer());
CHECK_GL_ERROR_DEBUG();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, nullptr == _rtDepthStencil ? 0 : _rtDepthStencil->getBuffer());
CHECK_GL_ERROR_DEBUG();
CCLOG("FBO is %d _fbo %d color, %d ds", _fbo, RenderTargetBase::Type::Texture2D == _rt->getType() ? _rt->getTexture()->getName() : _rt->getBuffer(), nullptr == _rtDepthStencil ? 0 : _rtDepthStencil->getBuffer());
_fboBindingDirty = false;
}
if(GL_FRAMEBUFFER_COMPLETE != glCheckFramebufferStatus(GL_FRAMEBUFFER))
{
CCLOG("FrameBuffer Status Error %d", (int)glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
CHECK_GL_ERROR_DEBUG();
}在attach时没有进行_rt断言,所以需要注意记得attach
5)clearAllFBO
void FrameBuffer::clearAllFBOs()
{
for (auto fbo : _frameBuffers)
{
fbo->clearFBO();
}
}
void FrameBuffer::clearFBO()
{
applyFBO();
glClearColor(_clearColor.r, _clearColor.g, _clearColor.b, _clearColor.a);
glClearDepth(_clearDepth);
glClearStencil(_clearStencil);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
restoreFBO();
}
void FrameBuffer::restoreFBO()
{
glBindFramebuffer(GL_FRAMEBUFFER, _previousFBO);
}4.Director中FBO使用流程
1)mainloop
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)//end()函数会把_purgeDirectorInNextLoop设置为true
{
_purgeDirectorInNextLoop = false;//把_purgeDirectorInNextLoop恢复为默认值
purgeDirector();//清除Director
}
else if (_restartDirectorInNextLoop)
{
_restartDirectorInNextLoop = false;
restartDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}2)drawScene
void Director::drawScene()
{
// calculate "global" dt
calculateDeltaTime();
if (_openGLView)
{
_openGLView->pollEvents();
}
//tick before glClear: issue #533
if (! _paused)
{
_eventDispatcher->dispatchEvent(_eventBeforeUpdate);
_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);
}
_renderer->clear();
experimental::FrameBuffer::clearAllFBOs();
/* to avoid flickr, nextScene MUST be here: after tick and before draw.
* FIXME: Which bug is this one. It seems that it can't be reproduced with v0.9
*/
if (_nextScene)
{
setNextScene();
}
pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
if (_runningScene)
{
#if (CC_USE_PHYSICS || (CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION) || CC_USE_NAVMESH)
_runningScene->stepPhysicsAndNavigation(_deltaTime);
#endif
//clear draw stats
_renderer->clearDrawStats();
//render the scene
_openGLView->renderScene(_runningScene, _renderer);
_eventDispatcher->dispatchEvent(_eventAfterVisit);
}
// draw the notifications node
if (_notificationNode)
{
_notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
}
if (_displayStats)
{
showStats();
}
_renderer->render();
_eventDispatcher->dispatchEvent(_eventAfterDraw);
popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_totalFrames++;
// swap buffers
if (_openGLView)
{
_openGLView->swapBuffers();
}
if (_displayStats)
{
calculateMPF();
}
}3)clearAllFBO
void FrameBuffer::clearAllFBOs()
{
for (auto fbo : _frameBuffers)
{
fbo->clearFBO();
}
}
void FrameBuffer::clearFBO()
{
applyFBO();
glClearColor(_clearColor.r, _clearColor.g, _clearColor.b, _clearColor.a);
glClearDepth(_clearDepth);
glClearStencil(_clearStencil);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
restoreFBO();
}
void FrameBuffer::restoreFBO()
{
glBindFramebuffer(GL_FRAMEBUFFER, _previousFBO);
}4)renderScene
void GLView::renderScene(Scene* scene, Renderer* renderer)
{
CCASSERT(scene, "Invalid Scene");
CCASSERT(renderer, "Invalid Renderer");
if (_vrImpl)
{
_vrImpl->render(scene, renderer);
}
else
{
scene->render(renderer, Mat4::IDENTITY, nullptr);
}
}
5)scene->render
void Scene::render(Renderer* renderer, const Mat4& eyeTransform, const Mat4* eyeProjection)
{
auto director = Director::getInstance();
Camera* defaultCamera = nullptr;
const auto& transform = getNodeToParentTransform();
for (const auto& camera : getCameras())
{
if (!camera->isVisible())
continue;
Camera::_visitingCamera = camera;
if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT)
{
defaultCamera = Camera::_visitingCamera;
}
// There are two ways to modify the "default camera" with the eye Transform:
// a) modify the "nodeToParentTransform" matrix
// b) modify the "additional transform" matrix
// both alternatives are correct, if the user manually modifies the camera with a camera->setPosition()
// then the "nodeToParent transform" will be lost.
// And it is important that the change is "permanent", because the matrix might be used for calculate
// culling and other stuff.
if (eyeProjection)
camera->setAdditionalProjection(*eyeProjection * camera->getProjectionMatrix().getInversed());
camera->setAdditionalTransform(eyeTransform.getInversed());
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, Camera::_visitingCamera->getViewProjectionMatrix());
// apply viewport
camera->apply();
//clear background with max depth
camera->clearBackground();
//visit the scene
visit(renderer, transform, 0);
#if CC_USE_NAVMESH
if (_navMesh && _navMeshDebugCamera == camera)
{
_navMesh->debugDraw(renderer);
}
#endif
renderer->render();
camera->restore();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
// we shouldn't restore the transform matrix since it could be used
// from "update" or other parts of the game to calculate culling or something else.
// camera->setNodeToParentTransform(eyeCopy);
}
#if CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION
if (_physics3DWorld && _physics3DWorld->isDebugDrawEnabled())
{
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, _physics3dDebugCamera != nullptr ? _physics3dDebugCamera->getViewProjectionMatrix() : defaultCamera->getViewProjectionMatrix());
_physics3DWorld->debugDraw(renderer);
renderer->render();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
}
#endif
Camera::_visitingCamera = nullptr;
// experimental::FrameBuffer::applyDefaultFBO();
}6)camera->apply
void Camera::apply()
{
applyFrameBufferObject();
applyViewport();
}
void Camera::applyFrameBufferObject()
{
if(nullptr == _fbo)//如果没有设置_fbo说明使用的是默认FBO,不需要bindFBO
{
// inherit from context if it doesn't have a FBO
// don't call apply the default one
// experimental::FrameBuffer::applyDefaultFBO();
}
else
{
_fbo->applyFBO();
}
}
void Camera::applyViewport()
{
glGetIntegerv(GL_VIEWPORT, _oldViewport);
if(nullptr == _fbo)
{
glViewport(getDefaultViewport()._left, getDefaultViewport()._bottom, getDefaultViewport()._width, getDefaultViewport()._height);
}
else
{
glViewport(_viewport._left * _fbo->getWidth(), _viewport._bottom * _fbo->getHeight(),
_viewport._width * _fbo->getWidth(), _viewport._height * _fbo->getHeight());
}
}
本文深入探讨OpenGL中的Framebuffer对象(FBO),介绍其基本概念、组成部分、创建及使用流程,并结合Coco2d-x引擎实例,展示如何实现自定义帧缓冲。

1346

被折叠的 条评论
为什么被折叠?



