Art Tevs wrote:
Hi J.P., Paul
yes, exactly like Paul said, I know the number of iterations at cull time.
Chaning the number of iterations at rendering time would be either to write a
special RenderStage, as Paul propose, or to write a draw callback which will
iterate the whole execution. However, this seems to be as also complex as
writing custom RenderStage ;)
Hi Art -- I have a relatively simple custom RenderStage example, which
I've attached here, as I thought you might find it useful for osgPPU.
In a nutshell, my idea was to derive a class from RenderStage and
override drawInner(). Right now it does nothing other than call the base
class, but this could be extended to do multiple RenderBin::draw() calls
quite easily.
To get the RenderStage into the render graph, I made my own custom node
and I override traverse(). During cull, the traverse() method basically
does the same kind of RenderStage setup as is currently done for the
Camera node in CullVisitor::apply(Camera&). Except I create and hook in
my own custom RenderStage rather than osgUtil::RenderStage.
For depth peeling, the idea is that the app will make one of these
custom nodes and add it as a child of their Camera, and use some methods
to specify the depth peeling parameters, then attach their translucent
subgraph as a child of this node. At draw time, multiple passes will be
done automatically and in a thread-safe way for multi-context rendering.
Note that this is a work in progress and might have some bugs. I don't
have a lock for the RenderStage cache yet, for example, so this might
crash with multiple cull threads. But I'll have this fixed eventually.
When all this is working for depth peeling, I'll make it available in
some public form.
Paul Martz
Skew Matrix Software LLC
_http://www.skew-matrix.com_ <http://www.skew-matrix.com/>
+1 303 859 9466
// Copyright (c) 2008 Skew Matrix Software LLC. All rights reserved.
#include <osg/Node>
#include <osg/Group>
#include <osg/Camera>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Texture2D>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgUtil/RenderStage>
#include <osgUtil/CullVisitor>
#include <string>
const int winW( 800 ), winH( 600 );
class DepthPeelRenderStage : public osgUtil::RenderStage
{
public:
DepthPeelRenderStage();
DepthPeelRenderStage( const osgUtil::RenderStage& rhs, const osg::CopyOp&
copyop=osg::CopyOp::SHALLOW_COPY );
DepthPeelRenderStage( const DepthPeelRenderStage& rhs, const osg::CopyOp&
copyop=osg::CopyOp::SHALLOW_COPY );
virtual osg::Object* cloneType() const { return new DepthPeelRenderStage();
}
virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new
DepthPeelRenderStage( *this, copyop ); } // note only implements a clone of
type.
virtual bool isSameKindAs(const osg::Object* obj) const { return
dynamic_cast<const DepthPeelRenderStage*>(obj)!=0L; }
virtual const char* className() const { return "DepthPeelRenderStage"; }
virtual void drawInner( osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*&
previous, bool& doCopyTexture );
protected:
~DepthPeelRenderStage();
};
DepthPeelRenderStage::DepthPeelRenderStage()
: osgUtil::RenderStage()
{
}
DepthPeelRenderStage::~DepthPeelRenderStage()
{
}
DepthPeelRenderStage::DepthPeelRenderStage( const osgUtil::RenderStage& rhs,
const osg::CopyOp& copyop )
: osgUtil::RenderStage( rhs )
{
}
DepthPeelRenderStage::DepthPeelRenderStage( const DepthPeelRenderStage& rhs,
const osg::CopyOp& copyop )
: osgUtil::RenderStage( rhs )
{
}
void
DepthPeelRenderStage::drawInner( osg::RenderInfo& renderInfo,
osgUtil::RenderLeaf*& previous, bool& doCopyTexture )
{
// TBD For now, just call base class drawInner.
// In future, do this ourselves, calling RenderBin::draw() directly,
// bindinh FBOs and texture objects as needed before each pass.
osg::notify( osg::ALWAYS ) << "DepthPeelRenderStage::drawInner" <<
std::endl;
osgUtil::RenderStage::drawInner( renderInfo, previous, doCopyTexture );
}
// Lifted directly from CullVisitor
class RenderStageCache : public osg::Object
{
public:
RenderStageCache() {}
RenderStageCache(const RenderStageCache&, const osg::CopyOp&) {}
META_Object(myLib,RenderStageCache);
void setRenderStage( osgUtil::CullVisitor* cv, DepthPeelRenderStage* rs)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
_renderStageMap[cv] = rs;
}
DepthPeelRenderStage* getRenderStage( osgUtil::CullVisitor* cv )
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
return _renderStageMap[cv].get();
}
typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr< DepthPeelRenderStage
> > RenderStageMap;
OpenThreads::Mutex _mutex;
RenderStageMap _renderStageMap;
};
class DepthPeelGroup : public osg::Group
{
public:
DepthPeelGroup();
DepthPeelGroup( const DepthPeelGroup& rhs, const osg::CopyOp&
copyOp=osg::CopyOp::SHALLOW_COPY );
META_Node(myLib,DepthPeelGroup);
virtual void traverse( osg::NodeVisitor& nv );
void setRenderingCache( osg::Object* rc ) { _renderingCache = rc; }
osg::Object* getRenderingCache() { return _renderingCache.get(); }
const osg::Object* getRenderingCache() const { return
_renderingCache.get(); }
void resizeGLObjectBuffers( unsigned int maxSize );
void releaseGLObjects( osg::State* state ) const;
protected:
~DepthPeelGroup();
osg::ref_ptr< osg::Object > _renderingCache;
};
DepthPeelGroup::DepthPeelGroup()
{
}
DepthPeelGroup::DepthPeelGroup( const DepthPeelGroup& rhs, const osg::CopyOp&
copyOp )
{
}
DepthPeelGroup::~DepthPeelGroup()
{
}
void
DepthPeelGroup::traverse( osg::NodeVisitor& nv )
{
if( nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR )
{
// Not the cull visitor. Traverse as a Group and return.
osg::Group::traverse( nv );
return;
}
// Dealing with cull traversal from this point onwards.
osgUtil::CullVisitor* cv = static_cast< osgUtil::CullVisitor* >( &nv );
//
// Basic idea of what follows was derived from CullVisitor::apply( Camera&
).
//
osgUtil::RenderStage* previousStage = cv->getCurrentRenderBin()->getStage();
osg::Camera* camera = previousStage->getCamera();
osg::ref_ptr< RenderStageCache > rsCache = dynamic_cast< RenderStageCache*
>( getRenderingCache() );
if( !rsCache )
{
rsCache = new RenderStageCache;
setRenderingCache( rsCache.get() );
}
osg::ref_ptr< DepthPeelRenderStage > dprs = rsCache->getRenderStage( cv );
if( !dprs )
{
// TBD
// Need to lock the DepthPeelGroup before writing the cache.
//OpenThreads::ScopedLock< OpenThreads::Mutex > lock(
*(camera.getDataChangeMutex()) );
dprs = new DepthPeelRenderStage( *previousStage );
rsCache->setRenderStage( cv, dprs.get() );
}
else
{
// reusing render to texture stage, so need to reset it to empty it
from previous frames contents.
dprs->reset();
}
osgUtil::RenderBin* previousRenderBin = cv->getCurrentRenderBin();
cv->setCurrentRenderBin( dprs.get() );
{
// Traverse
osg::Group::traverse( nv );
}
cv->setCurrentRenderBin( previousRenderBin );
// TBD Might need to support RenderOrder a la Camera node.
cv->getCurrentRenderBin()->getStage()->addPreRenderStage( dprs.get(),
camera->getRenderOrderNum() );
}
void
DepthPeelGroup::resizeGLObjectBuffers( unsigned int maxSize )
{
if( _renderingCache.valid() )
const_cast< DepthPeelGroup* >( this
)->_renderingCache->resizeGLObjectBuffers( maxSize );
osg::Group::resizeGLObjectBuffers(maxSize);
}
void
DepthPeelGroup::releaseGLObjects( osg::State* state ) const
{
if( _renderingCache.valid() )
const_cast< DepthPeelGroup* >( this
)->_renderingCache->releaseGLObjects( state );
osg::Group::releaseGLObjects(state);
}
osg::Node*
postRender( osgViewer::Viewer& viewer )
{
osg::Camera* rootCamera( viewer.getCamera() );
// Create the texture; we'll use this as our color buffer.
// Note it has no image data; not required.
osg::Texture2D* tex = new osg::Texture2D;
tex->setTextureWidth( winW );
tex->setTextureHeight( winH );
tex->setInternalFormat( GL_RGBA );
tex->setBorderWidth( 0 );
tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::NEAREST );
tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::NEAREST );
// Attach the texture to the camera. Tell it to use multisampling.
// Internally, OSG allocates a multisampled renderbuffer, renders to it,
// and at the end of the frame performs a BlitFramebuffer into our texture.
rootCamera->attach( osg::Camera::COLOR_BUFFER0, tex, 0, 0, false, 8, 8 );
rootCamera->setRenderTargetImplementation(
osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::FRAME_BUFFER );
// Configure postRenderCamera to draw fullscreen textured quad
osg::ref_ptr< osg::Camera > postRenderCamera( new osg::Camera );
postRenderCamera->setClearColor( osg::Vec4( 0., 1., 0., 1. ) ); // should
never see this.
postRenderCamera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER,
osg::Camera::FRAME_BUFFER );
postRenderCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF );
postRenderCamera->setRenderOrder( osg::Camera::POST_RENDER );
postRenderCamera->setViewMatrix( osg::Matrixd::identity() );
postRenderCamera->setProjectionMatrix( osg::Matrixd::identity() );
osg::Geode* geode( new osg::Geode );
geode->addDrawable( osg::createTexturedQuadGeometry(
osg::Vec3( -1,-1,0 ), osg::Vec3( 2,0,0 ), osg::Vec3( 0,2,0 ) ) );
geode->getOrCreateStateSet()->setTextureAttributeAndModes(
0, tex, osg::StateAttribute::ON );
geode->getOrCreateStateSet()->setMode( GL_LIGHTING,
osg::StateAttribute::OFF );
postRenderCamera->addChild( geode );
return( postRenderCamera.release() );
}
int
main( int argc, char** argv )
{
osg::ref_ptr< DepthPeelGroup > root( new DepthPeelGroup );
root->addChild( osgDB::readNodeFile( "cow.osg" ) );
if( root->getNumChildren() == 0 )
return( 1 );
osgViewer::Viewer viewer;
viewer.setThreadingModel( osgViewer::ViewerBase::SingleThreaded );
viewer.setUpViewInWindow( 10, 30, winW, winH );
viewer.setSceneData( root.get() );
viewer.realize();
root->addChild( postRender( viewer ) );
// Clear to white to make AA extremely obvious.
viewer.getCamera()->setClearColor( osg::Vec4( 1., 1., 1., 1. ) );
return( viewer.run() );
}
_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org