osg渲染到纹理技术(一)

render-to-textures(RTT)允许
开发者根据场景的一部分图像创建成一张纹理图,烘焙到场景中的某一物体上,这种技术用于创建更好看的特殊的表现形式,或者被保存用于以后的延迟着色,和多通道渲染,或者更高级的渲染特效.

OSG中动态的实现渲染到纹理.需要以下三个步骤:

1.创建一张纹理用于下一步渲染的结果.

2.渲染到刚刚创建的纹理

3.得到这个渲染的纹理的结果,用于你想使用的地方

步骤详解:我们首先需要创建一张空的纹理图,osg::Texture对象中有setTextureSize()函数可以修改这张2D纹理的大小.以及添加一个深度参数用于3D纹理.其中最主要的是如何得到场景图的一部分,这需要用到osg::Camera类的attach()方法,它需要一个纹理对象作为参数,以及枚举类型作为参数(这个枚举类型用于指定得到帧缓存中的那一部分,作为RTT的数据来源.)

//除了COLOR_BUFFER外还有DEPTH_BUFFER , STENCIL_BUFFER , and COLOR_BUFFER0 to COLOR_BUFFER15
//有多少种渲染目标的输出类型,取决于你自己的显卡
camera->attach( osg::Camera::COLOR_BUFFER, texture.get() );//把frame buffer中的color buffer作为渲染数据

下一步就是设定合适的视图和投影矩阵以及一个合适刚刚设定的texture的大小的viewport在所使用的相机对象中,然后就可以把得到的texture设定为node或者drawables的属性.这样这个texture的结果将会随着相机的渲染结果每一帧变化.随着视图矩阵和投影矩阵的变化而变化.

这里需要注意的是:主摄像机不适合用于绑定一个texutre用于RTT.因为这将没有任何输出到屏幕上,会出现黑屏,但是如果你在进行离屏渲染或者不在乎任何显示.

接下来一个问题是怎么把帧缓存的数据渲染到texture上.OSG提供了下面几种方法.

1.使用glReadPiexels()方法直接读取帧缓存中的数据(放回的是像素数据),再使用glTexImage*()方法(glTexImage2D的数据来自于客户端内存),把像素数据保存为纹理.这种方式最简单易行,但是它需要不断的进行数据的拷贝到texture对象上,所以效率非常慢.

2.使用glCopyTexSubimage*()方法,它定义了一个2D纹理图像,或者立方体映射图像,它的像素数据来源于帧缓存,所以它的效率相对于glTexImage*()会提升很多.然而我们还有可优化的空间.

–---不需要进行数据拷贝,直接把scene的数据渲染到目标(不经过帧缓存)

3.pixel buffer(pbuffer)--像素缓冲区扩展,它可以创建一个带有像素格式的不可见的缓冲区,就像一个窗口一样.它应该在使用后销毁,就像渲染窗口那样做.

4.frame buffer object(FBO)--帧缓冲对象,这种方式在某些情况下比pbuffer要好(数据的存储空间消耗少).

FBO简介:

Opengl默认把framebuffer作为渲染的目的地,它是由窗口系统创建并管理的,FBO是一个二维的数据集合,包括color buffers\ depth buffer以及stencil buffer.

在Opengl扩展中,GL_ARB_framebuffer_object提供了额外的非显示的FBO作为接口,FBO可以理解为应用程序可创建的framebuffer,用于区别默认窗口提供的frambuffer.通过FBO Opengl应用可以重定向渲染输出,让它输出到FBO而不是窗口系统提供的framebuffer.

与窗口系统提供的帧缓冲区类似,它包含了一系列渲染目的地的集合:包括颜色,深度和模板缓冲区.FBO中的这些逻辑缓冲区称为可附着的frame buffer.主要有两种类型的可附着缓冲区(texture和render buffer),如果texutre被附着到FBO,则执行”渲染到纹理”.如果renderbuffer被附着到FBO,则opengl会执行”离屏渲染”.(renderbuffer是在GL_ARB_framebuffer_object扩展中定义的一种新类型的存储对象。它在渲染过程中用作单个2D图像的渲染目的地。)
下图显示了FBO,纹理和renderbuffer之间的连接。多个纹理对象或renderbuffer对象可以通过附着点附加到FBO上。

FBO中,有多个颜色附加点(GL_COLOR_ATTACHMENT0,...,GL_COLOR_ATTACHMENTn),

一个深度附加点(GL_DEPTH_ATTACHMENT)

一个模板附加点(GL_STENCIL_ATTACHMENT)。

颜色附着点的数量取决于实现,但每个FBO必须至少具有一个颜色附加点。您可以使用GL_MAX_COLOR_ATTACHMENTS查询最大数量的颜色附加点,这些数据由显卡支持。

FBO具有多个颜色附加点的原因是允许在同一时间将颜色缓冲区渲染到多个目的地。这个“多个渲染目标”(MRT)可以由GL_ARB_draw_buffers扩展完成。请注意,FBO本身不存放数据,它只有多个附着点。这有点像数据结构中的指针,它只存放指针,而不存放数据。

FBO提供了一种高效的切换机制;从FBO中分离先前的帧缓冲区,并将一个新的可附着的帧缓冲图像附加到FBO中。切换可附着的帧缓冲图像比在FBO之间切换要快得多。 FBO提供glFramebufferTexture2D()来切换2D纹理对象,并将glFramebufferRenderbuffer()切换到renderbuffer对象。

OSG中使用FBO的方式是:

camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER );

渲染到纹理实例:

作者:kjwang
链接:https://zhuanlan.zhihu.com/p/38841667
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

#include <osg/Camera>
#include <osg/Texture2D>
#include <osgDB/ReadFile>
#include <osgGA/TrackballManipulator>
#include <osgViewer/Viewer>

class FindTextureVisitor : public osg::NodeVisitor
{
public:
    FindTextureVisitor( osg::Texture* tex ) : _texture(tex)
    {
        setTraversalMode( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN );
    }
    
    virtual void apply( osg::Node& node )
    {
        replaceTexture( node.getStateSet() );
        traverse( node );
    }

    virtual void apply( osg::Geode& geode )
    {
        replaceTexture( geode.getStateSet() );
        for ( unsigned int i=0; i<geode.getNumDrawables(); ++i )
        {
            replaceTexture( geode.getDrawable(i)->getStateSet() );
        }
        traverse( geode );
    }

    void replaceTexture( osg::StateSet* stateset )
    {
        if ( stateset )
        {
            osg::Texture* oldTexture = dynamic_cast<osg::Texture*>(
                stateset->getTextureAttribute(0, osg::StateAttribute::TEXTURE) );
            if ( oldTexture ) stateset->setTextureAttribute( 0, _texture.get() );
        }
    }
    
protected:
    osg::ref_ptr<osg::Texture> _texture;
};

int main( int argc, char** argv )
{
    // setMinimumNumAlphaBits(1);
    
    osg::ref_ptr<osg::Node> model = osgDB::readNodeFile( "/home/aqrose23/Downloads/aq_depends/OpenSceneGraph/data/OpenSceneGraph-Data-master/lz.osg" );
    osg::ref_ptr<osg::Node> sub_model = osgDB::readNodeFile( "/home/aqrose23/Downloads/aq_depends/OpenSceneGraph/data/OpenSceneGraph-Data-master/glider.osg" );
    
    int tex_width = 1024, tex_height = 1024;
    
    osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
    texture->setTextureSize( tex_width, tex_height );
    texture->setInternalFormat( GL_RGBA );
    texture->setFilter( osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR );
    texture->setFilter( osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR );
    
    FindTextureVisitor ftv( texture.get() );
    if ( model.valid() ) model->accept( ftv );
    
    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setViewport( 0, 0, tex_width, tex_height );
    camera->setClearColor( osg::Vec4(1.0f, 1.0f, 1.0f, 0.0f) );
    camera->setClearMask( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
    
    camera->setRenderOrder( osg::Camera::PRE_RENDER );
    camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT );
    camera->attach( osg::Camera::COLOR_BUFFER, texture.get() );
    
    camera->setReferenceFrame( osg::Camera::ABSOLUTE_RF );
    camera->addChild( sub_model.get() );
    
    osg::ref_ptr<osg::Group> root = new osg::Group;
    root->addChild( model.get() );
    root->addChild( camera.get() );
    
    osgViewer::Viewer viewer;
    viewer.setSceneData( root.get() );
    viewer.setCameraManipulator( new osgGA::TrackballManipulator );
    
    float delta = 0.1f, bias = 0.0f;
    osg::Vec3 eye(0.0f,-5.0f, 5.0f);
    while ( !viewer.done() )
    {
        if ( bias<-1.0f ) delta = 0.1f;
        else if ( bias>1.0f ) delta = -0.1f;
        bias += delta;
        camera->setViewMatrixAsLookAt( eye, osg::Vec3(), osg::Vec3(bias, 1.0f, 1.0f) );
        
        viewer.frame();
    }
    return 0;
}

本文转自:https://www.zhihu.com/column/c_187589347

猜你喜欢

转载自blog.csdn.net/danshiming/article/details/113802377
今日推荐