OSG 使用整理(6):使用FBO渲染到纹理

发布时间 2023-07-30 17:57:12作者: 王小于的啦

使用FBO渲染到纹理

1.1 FBO 帧缓冲

​ 帧缓冲包括颜色缓冲、深度缓冲和模板缓冲,默认帧缓冲是在我们创建窗口时生成和配置好的。OpenGL中使用过程类似缓存对象分为创建、绑定、读写、解绑、释放。

a . 创建FBO,绑定纹理附件

​ 一个完整的帧缓冲需要附加至少一个缓冲、至少一个颜色附件、所有附件都是完整的、每个缓冲都应该有相同的样本数。

unsigned int framebuffer;

/* brief:创建fbo命令
 para[in]: n  生成的fbo名称个数
 para[in]: framebuffers 缓冲对象名称
*/
glGenFramebuffers(1, &framebuffer);

/* brief:绑定fbo命令
 para[in]: framebuffers 缓冲对象名称,0为默认
*/
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

/* brief:绑定fbo命令
 para[in]: attachment为GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT 或者GL_STENCIL_ATTACHMENT
 para[in]: textarget为0或者GL_TEXTURE_2D
 para[in]: texture为纹理缓冲名称
*/
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

b . 使用FBO渲染到纹理

​ 通常的步骤是将新的帧缓冲绑定为激活的帧缓冲,渲染场景;绑定默认帧缓冲;绘制一个横跨整个屏幕的四边形,将帧缓冲作为它的纹理。

// 第一处理阶段(Pass)
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 我们现在不使用模板缓冲
glEnable(GL_DEPTH_TEST);
DrawScene();    

// 第二处理阶段
glBindFramebuffer(GL_FRAMEBUFFER, 0); // 返回默认
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 
glClear(GL_COLOR_BUFFER_BIT);

screenShader.use();  
glBindVertexArray(quadVAO);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
glDrawArrays(GL_TRIANGLES, 0, 6);  

1.2 osg中渲染到纹理方法

​ 渲染到纹理可以使用glReadPixels()从像素缓冲区拷贝,然后glTexImage()赋值给纹理;也可以使用glCopyTexSubImage();也可以直接渲染到FBO,再使用FBO中的纹理附件。osg::Camera类中方法setRenderTargetImplementation()可以设置渲染目标为FRAME_BUFFER_OBJECT、PIXEL_BUFFER、FRAME_BUFFER中一种对应上述方法。创建RT相机如下所示,首先设置相机绑定的FBO要清空的缓冲区类型和颜色,设置渲染到FBO和渲染次序,然后创建纹理附件,设置相机视口大小,并绑定到FBO,最后设置独立相机的投影视图矩阵。

osg::Camera* createRTTCamera( osg::Camera::BufferComponent
 buffer, osg::Texture* tex, bool isAbsolute )
{
 osg::ref_ptr<osg::Camera> camera = new osg::Camera;
 camera->setClearColor( osg::Vec4() );
 camera->setClearMask(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
 camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT );
 camera->setRenderOrder( osg::Camera::PRE_RENDER );
 
 if ( tex )
 {
 tex->setFilter( osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR );
 tex->setFilter( osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR );
 camera->setViewport( 0, 0, tex->getTextureWidth(),tex->getTextureHeight() );
 camera->attach( buffer, tex );
 }
 if ( isAbsolute )
 {
 camera->setReferenceFrame( osg::Transform::ABSOLUTE_RF );
 camera->setProjectionMatrix( osg::Matrix::ortho2D(
 0.0, 1.0, 0.0, 1.0) );
 camera->setViewMatrix( osg::Matrix::identity() );
 camera->addChild( createScreenQuad(1.0f, 1.0f) );
 }
 return camera.release();
}

1.2.1 读取并显示深度缓冲

​ 使用渲染到纹理技术可以得到很多炫酷效果。示例为显示飞机模型的深度缓冲到屏幕上,过程如下:

a. 渲染原始节点到纹理上

b. 创建屏幕大小四边形节点

c. 创建四边形节点的shader,使用第一步的纹理

d. 渲染四边形到屏幕上

#include <osg/Texture2D>
#include <osg/Group>
#include <osgDB/ReadFile> 
#include <osgViewer/Viewer>


int main( int argc, char** argv )
{
    osg::ArgumentParser arguments( &argc, argv );
    osg::ref_ptr<osg::Node> scene = osgDB::readNodeFiles( arguments );
    if ( !scene ) scene = osgDB::readNodeFile("cessna.osg");
    
    osg::ref_ptr<osg::Texture2D> tex2D = new osg::Texture2D;
    tex2D->setTextureSize( 1024, 1024 );
    tex2D->setInternalFormat( GL_DEPTH_COMPONENT24 );
    tex2D->setSourceFormat( GL_DEPTH_COMPONENT );
    tex2D->setSourceType( GL_FLOAT );
    
    osg::ref_ptr<osg::Camera> rttCamera = createRTTCamera(osg::Camera::DEPTH_BUFFER, tex2D.get());
    rttCamera->addChild( scene.get() );
    
    osg::ref_ptr<osg::Camera> hudCamera = createHUDCamera(0.0, 1.0, 0.0, 1.0);
    hudCamera->addChild(createScreenQuad(0.5f, 1.0f) );
    hudCamera->getOrCreateStateSet()->setTextureAttributeAndModes( 0, tex2D.get() );
    
    osg::ref_ptr<osg::Group> root = new osg::Group;
    root->addChild( rttCamera.get() );
    root->addChild( hudCamera.get() );
    root->addChild( scene.get() );
    
    osgViewer::Viewer viewer;
    viewer.getCamera()->setComputeNearFarMode( osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR );
    viewer.setSceneData( root.get() );
    return viewer.run();
}

1.3 osg中使用slave相机搭建多Pass渲染管线

​ osg中Viewer对象中有个默认的主相机,slave相机用于显示多窗口程序,他们继承了主相机的视图、投影矩阵、渲染上下文。采用多个slave相机,可以让用户场景图不受额外pass影响,独立做到节点渲染、状态设置,多pass的slave相机关系如下:

图片