Hi folks, I would like to post my solution to the problem of rendering to a texture independently on the framerate of the viewer, the code is attached.
My solution is to derived a Viewer from osgViewer::Viewer, added a custom offScreenRender() function mimicking frame() and (offScreen)renderTraversal() function without statistics and swapbuffer. In the offScreenRender() function I exchange the child of the main camera by the off screen rendering node containing only the RTT cameras and switch it back afterwards. This works great except that the shadows do not work correctly in the RTT camera. It seems that the light position is wrongly detected, which is done in the cull traversal as far as I could understand from looking at the code. How do I perform this properly in the offScreenRenderTraversal() function? Cheers, Georg
/* -*-c++-*- Taken from osgprerenderer.cpp * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial applications, * as long as this copyright notice is maintained. * * This application is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. g++ -losgShadow -losgText -losgUtil -losgViewer -losgGA -lOpenThreads -losg -lGL -lGLU -lglut async_test.cpp */ #include <osgDB/ReadFile> #include <osgDB/WriteFile> #include <osgUtil/Optimizer> #include <osg/CoordinateSystemNode> #include <osg/Switch> #include <osgText/Text> #include <osgViewer/Viewer> #include <osgViewer/ViewerEventHandlers> #include <osgViewer/Renderer> #include <osgGA/TrackballManipulator> #include <osgGA/FlightManipulator> #include <osgGA/DriveManipulator> #include <osgGA/KeySwitchMatrixManipulator> #include <osgGA/StateSetManipulator> #include <iostream> #include <sstream> #include <string.h> class AsyncRTTViewer : public osgViewer::Viewer { public: AsyncRTTViewer(){ asyncRTTViewerConstructorInit(); } AsyncRTTViewer(osg::ArgumentParser& arguments) : osgViewer::Viewer(arguments) { asyncRTTViewerConstructorInit(); } AsyncRTTViewer(const osgViewer::Viewer& viewer, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY) : osgViewer::Viewer(viewer,copyop) { asyncRTTViewerConstructorInit(); } virtual ~AsyncRTTViewer() {} /// adds a render to texture camera void addOffScreenRTTNode(osg::Node* node){ offScreenNodes->addChild(node); } /// removes a render to texture camera void removeOffScreenRTTNode(osg::Node* node){ offScreenNodes->removeChild(node); } /** call this function to render the off screen scene. If no off screen nodes (RTT) are supplied than nothing is done */ virtual void renderOffScreen( ) { if (_done || offScreenNodes->getNumChildren() == 0) return; osg::Node* origNode = _camera->getChild(0); _camera->setChild(0,offScreenNodes); // printf("before offscreen\n"); offScreenRenderingTraversals(); //printf("after offscreen\n"); _camera->setChild(0,origNode); } protected: virtual void offScreenRenderingTraversals() { /*** This is copied from ViewerBase::renderingTraversals() and statistics and swapbuffer and so on are removed. */ if (_done) return; offScreenNodes->getBound(); Contexts contexts; getContexts(contexts); Cameras cameras; getCameras(cameras); Contexts::iterator itr; bool doneMakeCurrentInThisThread = false; if (_endDynamicDrawBlock.valid()) { _endDynamicDrawBlock->reset(); } // dispatch the rendering threads if (_startRenderingBarrier.valid()) _startRenderingBarrier->block(); // reset any double buffer graphics objects for(Cameras::iterator camItr = cameras.begin(); camItr != cameras.end(); ++camItr) { osg::Camera* camera = *camItr; osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(camera->getRenderer()); if (renderer) { if (!renderer->getGraphicsThreadDoesCull() && !(camera->getCameraThread())) { renderer->cull(); } } } for(itr = contexts.begin(); itr != contexts.end(); ++itr) { if (_done) return; if (!((*itr)->getGraphicsThread()) && (*itr)->valid()) { doneMakeCurrentInThisThread = true; makeCurrent(*itr); (*itr)->runOperations(); } } // osg::notify(osg::NOTICE)<<"Joing _endRenderingDispatchBarrier block "<<_endRenderingDispatchBarrier.get()<<std::endl; // wait till the rendering dispatch is done. if (_endRenderingDispatchBarrier.valid()) _endRenderingDispatchBarrier->block(); // wait till the dynamic draw is complete. if (_endDynamicDrawBlock.valid()) { // osg::Timer_t startTick = osg::Timer::instance()->tick(); _endDynamicDrawBlock->block(); // osg::notify(osg::NOTICE)<<"Time waiting "<<osg::Timer::instance()->delta_m(startTick, osg::Timer::instance()->tick())<<std::endl;; } if (_releaseContextAtEndOfFrameHint && doneMakeCurrentInThisThread) { //osg::notify(osg::NOTICE)<<"Doing release context"<<std::endl; releaseContext(); } } void asyncRTTViewerConstructorInit(){ offScreenNodes = new osg::Group(); } osg::ref_ptr<osg::Group> offScreenNodes; }; struct MyCameraPostDrawCallback : public osg::Camera::DrawCallback { MyCameraPostDrawCallback(osg::Image* image): _image(image) { } virtual void operator () (const osg::Camera& /*camera*/) const { if (_image && _image->getPixelFormat()==GL_RGBA && _image->getDataType()==GL_UNSIGNED_BYTE) { printf("hello from image processing\n"); // we'll pick out the center 1/2 of the whole image, int column_start = _image->s()/4; int column_end = 3*column_start; int row_start = _image->t()/4; int row_end = 3*row_start; // and then invert these pixels for(int r=row_start; r<row_end; ++r) { unsigned char* data = _image->data(column_start, r); for(int c=column_start; c<column_end; ++c) { (*data) = 255-(*data); ++data; (*data) = 255-(*data); ++data; (*data) = 255-(*data); ++data; (*data) = 255; ++data; } } // dirty the image (increments the modified count) so that any textures // using the image can be informed that they need to update. _image->dirty(); } } osg::Image* _image; }; int main(int argc, char** argv) { bool useImage=true; // use an ArgumentParser object to manage the program arguments. osg::ArgumentParser arguments(&argc,argv); arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName()); arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ..."); // osgViewer::Viewer viewer(arguments); AsyncRTTViewer viewer(arguments); viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded); unsigned int helpType = 0; if ((helpType = arguments.readHelpType())) { arguments.getApplicationUsage()->write(std::cout, helpType); return 1; } // report any errors if they have occurred when parsing the program arguments. if (arguments.errors()) { arguments.writeErrorMessages(std::cout); return 1; } if (arguments.argc()<=1) { arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION); return 1; } // set up the camera manipulators. { osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator; keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() ); keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() ); keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() ); viewer.setCameraManipulator( keyswitchManipulator.get() ); } // add the state manipulator viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); viewer.addEventHandler(new osgViewer::ThreadingHandler); viewer.addEventHandler(new osgViewer::WindowSizeHandler); viewer.addEventHandler(new osgViewer::StatsHandler); viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); viewer.addEventHandler(new osgViewer::RecordCameraPathHandler); viewer.addEventHandler(new osgViewer::LODScaleHandler); viewer.realize(); // load the data osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments); if (!loadedModel) { std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl; return 1; } // we have a root object that contains a scene and some overlayed textures to show // the content if a independently rendered texture osg::Group* root = new osg::Group(); osg::Group* scene = new osg::Group(); root->addChild(scene); scene->addChild(loadedModel.get()); // any option left unread are converted into errors to write out later. arguments.reportRemainingOptionsAsUnrecognized(); // optimize the scene graph, remove redundant nodes and state etc. osgUtil::Optimizer optimizer; optimizer.optimize(loadedModel.get()); viewer.setSceneData(root); // Now we create a normal Render to Texture camera // Create the texture to render to osg::Texture2D* texture = new osg::Texture2D; texture->setTextureSize(256, 256); texture->setInternalFormat(GL_RGBA); texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR); // set up the render to texture camera. osg::Camera* cam = new osg::Camera; cam->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // set up projection. cam->setProjectionMatrixAsPerspective(30, 1,0.1,30); // set view cam->setReferenceFrame(osg::Transform::ABSOLUTE_RF); cam->setViewport(0, 0, 256, 256); // Frame buffer objects are the best option cam->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); // We need to render to the texture BEFORE we render to the screen cam->setRenderOrder(osg::Camera::PRE_RENDER); if(useImage){ osg::Image* image = new osg::Image; image->allocateImage(256, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE); //image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_FLOAT); // attach the image so its copied on each frame. cam->attach(osg::Camera::COLOR_BUFFER, image); cam->setPostDrawCallback(new MyCameraPostDrawCallback(image)); texture->setImage(0, image); }else{ // The camera will render into the texture that we created earlier cam->attach(osg::Camera::COLOR_BUFFER, texture); } // Add world to be drawn to the texture cam->addChild(loadedModel.get()); // now we add the RTT camera to our custom viewer viewer.addOffScreenRTTNode(cam); // set up the place where the content of the texture is seen in the normal scene osg::ref_ptr<osg::Geometry> screenQuad; screenQuad = osg::createTexturedQuadGeometry(osg::Vec3(), osg::Vec3(256, 0.0, 0.0), osg::Vec3(0.0, 256, 0.0)); osg::ref_ptr<osg::Geode> quadGeode = new osg::Geode; quadGeode->addDrawable(screenQuad.get()); osg::StateSet *quadState = quadGeode->getOrCreateStateSet(); quadState->setMode(GL_LIGHTING,osg::StateAttribute::OFF); quadState->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); osg::ref_ptr<osg::Camera> orthoCamera = new osg::Camera; // We don't want to apply perspective, just overlay using orthographic orthoCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 256, 0, 256)); orthoCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); orthoCamera->setViewMatrix(osg::Matrix::identity()); orthoCamera->setViewport(0, 0, 256,256); orthoCamera->setRenderOrder(osg::Camera::POST_RENDER); orthoCamera->addChild(quadGeode.get()); // and add it to the root note root->addChild(orthoCamera.get()); int frame_count=0; const int swap_every=200; // the texture rendering occurs only every 200th frame bool swap=true; while(!viewer.done()) { if (swap && 0 == (frame_count % swap_every)) { osg::Vec3 eye; osg::Vec3 center; osg::Vec3 up; viewer.getCamera()->getViewMatrixAsLookAt(eye,center,up); // simply turn the up vector around cam->setViewMatrixAsLookAt(eye, center, -up); // do the offscreen rendering viewer.renderOffScreen(); }else{ viewer.frame(); } frame_count++; } return 0; }
_______________________________________________ osg-users mailing list osg-users@lists.openscenegraph.org http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org