<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div>Hello,</div><div>In my project, I need to render an OSG frame without any window appearing on the screen. So called "off screen".</div><div>In order to do that, I found the following code taken osgprerenderer.cpp that I intend to inspire from if I can make it work correctly:<br></div><div><br></div><div>AsyncRTTViewer class:</div><div><br></div><div>/* -*-c++-*- Taken from osgprerenderer.cpp<br> *<br> * This application is open source and may be redistributed and/or modified   <br> * freely and without restriction, both in commericial and non commericial applications,<br> * as long as this copyright notice is maintained.<br> * <br> * This application is distributed in the hope that it will be useful,<br> * but WITHOUT ANY WARRANTY; without even the implied warranty of<br> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.<br><br> g++ -losgShadow -losgText -losgUtil -losgViewer -losgGA -lOpenThreads -losg -lGL -lGLU -lglut async_test.cpp<br><br>*/<br>#include <osgDB/ReadFile><br>#include <osgDB/WriteFile><br><br>#include <osgUtil/Optimizer><br>#include <osg/CoordinateSystemNode><br><br>#include <osg/Switch><br>#include <osgText/Text><br><br>#include <osgViewer/Viewer><br>#include <osgViewer/ViewerEventHandlers><br>#include <osgViewer/Renderer><br><br><br>#include <osgGA/TrackballManipulator><br>#include <osgGA/FlightManipulator><br>#include <osgGA/DriveManipulator><br>#include <osgGA/KeySwitchMatrixManipulator><br>#include <osgGA/StateSetManipulator><br><br>#include <iostream><br>#include <sstream><br>#include <string.h><br><br><br>class AsyncRTTViewer : public osgViewer::Viewer {<br>public:<br>    <br>  AsyncRTTViewer(){<br>    asyncRTTViewerConstructorInit();<br>  }<br><br>  AsyncRTTViewer(osg::ArgumentParser& arguments)<br>    : osgViewer::Viewer(arguments) {<br>    asyncRTTViewerConstructorInit();<br>  }<br><br>  AsyncRTTViewer(const osgViewer::Viewer& viewer, <br>                 const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)  <br>    : osgViewer::Viewer(viewer,copyop) {<br>    asyncRTTViewerConstructorInit();<br>  }<br>    <br>  virtual ~AsyncRTTViewer() {}<br><br>  /// adds a render to texture camera <br>  void addOffScreenRTTNode(osg::Node* node){<br>    offScreenNodes->addChild(node);<br>  }<br><br>  /// removes a render to texture camera <br>  void removeOffScreenRTTNode(osg::Node* node){<br>    offScreenNodes->removeChild(node);  <br>  }<br>    <br>  /** call this function to render the off screen scene.<br>      If no off screen nodes (RTT) are supplied than nothing is done      <br>  */<br>  virtual void renderOffScreen( ) {<br>    if (_done || offScreenNodes->getNumChildren() == 0) return;<br>      <br>    osg::Node* origNode = _camera->getChild(0);<br>    _camera->setChild(0,offScreenNodes);<br>    //    printf("before offscreen\n");    <br>    offScreenRenderingTraversals();<br>    //printf("after offscreen\n");<br>    _camera->setChild(0,origNode);<br>  }<br><br>protected:<br>    <br><br>  virtual void offScreenRenderingTraversals() {        <br>      <br>    /*** This is copied from ViewerBase::renderingTraversals() and <br>         statistics and swapbuffer and so on are removed.       <br>    */<br>      <br>    if (_done) return;<br>      <br>    offScreenNodes->getBound();<br>      <br>    Contexts contexts;<br>    getContexts(contexts);<br>      <br>    Cameras cameras;<br>    getCameras(cameras);<br>      <br>    Contexts::iterator itr;<br>      <br>    bool doneMakeCurrentInThisThread = false;<br>      <br>    if (_endDynamicDrawBlock.valid())<br>      {<br>        _endDynamicDrawBlock->reset();<br>      }<br>      <br>    // dispatch the rendering threads<br>    if (_startRenderingBarrier.valid()) _startRenderingBarrier->block();<br>      <br>    // reset any double buffer graphics objects<br>    for(Cameras::iterator camItr = cameras.begin();<br>        camItr != cameras.end();<br>        ++camItr)<br>      {<br>        osg::Camera* camera = *camItr;<br>        osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(camera->getRenderer());<br>        if (renderer)<br>          {<br>            if (!renderer->getGraphicsThreadDoesCull() && !(camera->getCameraThread()))<br>              {<br>                renderer->cull();<br>              }<br>          }<br>          <br>      }<br>      <br>    for(itr = contexts.begin();<br>        itr != contexts.end();<br>        ++itr)<br>      {<br>        if (_done) return;<br>        if (!((*itr)->getGraphicsThread()) && (*itr)->valid())<br>          {<br>            doneMakeCurrentInThisThread = true; <br>            makeCurrent(*itr);<br>            (*itr)->runOperations();<br>          }<br>      }<br><br>    /*for (itr = contexts.begin();<br>        itr != contexts.end() && !_done;<br>        ++itr)<br>    {<br>        if (!((*itr)->getGraphicsThread()) && (*itr)->valid())<br>        {<br>            doneMakeCurrentInThisThread = true;<br>            makeCurrent(*itr);<br>            (*itr)->swapBuffers();<br>        }<br>    }*/<br><br>    // osg::notify(osg::NOTICE)<<"Joing _endRenderingDispatchBarrier block "<<_endRenderingDispatchBarrier.get()<<std::endl;<br><br>    // wait till the rendering dispatch is done.<br>    if (_endRenderingDispatchBarrier.valid()) _endRenderingDispatchBarrier->block();<br><br>    // wait till the dynamic draw is complete.<br>    if (_endDynamicDrawBlock.valid()) <br>      {<br>        // osg::Timer_t startTick = osg::Timer::instance()->tick();<br>        _endDynamicDrawBlock->block();<br>        // osg::notify(osg::NOTICE)<<"Time waiting "<<osg::Timer::instance()->delta_m(startTick, osg::Timer::instance()->tick())<<std::endl;;<br>      }<br>    <br>    if (_releaseContextAtEndOfFrameHint && doneMakeCurrentInThisThread)<br>      {<br>        //osg::notify(osg::NOTICE)<<"Doing release context"<<std::endl;<br>        releaseContext();<br>      }<br><br>  }<br><br>  void asyncRTTViewerConstructorInit(){<br>    offScreenNodes = new osg::Group();<br>  }<br>        <br>  osg::ref_ptr<osg::Group> offScreenNodes;<br>};<br><br><br><br><br>struct MyCameraPostDrawCallback : public osg::Camera::DrawCallback<br>{<br>  MyCameraPostDrawCallback(osg::Image* image):<br>    _image(image)<br>  {<br>  }<br><br>  virtual void operator () (osg::RenderInfo& renderInfo) const<br>  {<br>    if (_image && _image->getPixelFormat()==GL_RGBA && _image->getDataType()==GL_UNSIGNED_BYTE)<br>      {<br>        printf("hello from image processing\n");<br>        // we'll pick out the center 1/2 of the whole image,<br>        int column_start = _image->s()/4;<br>        int column_end = 3*column_start;<br>            <br>        int row_start = _image->t()/4;<br>        int row_end = 3*row_start;<br>            <br>        // and then invert these pixels<br>        for(int r=row_start; r<row_end; ++r)<br>          {<br>            unsigned char* data = _image->data(column_start, r);<br>            for(int c=column_start; c<column_end; ++c)<br>              {<br>                (*data) = 255-(*data); ++data;<br>                (*data) = 255-(*data); ++data;<br>                (*data) = 255-(*data); ++data;<br>                (*data) = 255; ++data;<br>              }<br>          }<br><br>        // dirty the image (increments the modified count) so that any textures<br>        // using the image can be informed that they need to update.<br>        _image->dirty();<br><br>        if (osgDB::writeImageFile(*_image, std::string("C:\\image.jpg")))<br>        {<br>            std::cout << R"(Saved screen image to `C:\image.jpg`)" << std::endl;<br>        }<br><br>      }<br>       <br>  }   <br><br>  virtual void operator () (const osg::Camera& /*camera*/) const<br>  {<br>      if (_image && _image->getPixelFormat() == GL_RGBA && _image->getDataType() == GL_UNSIGNED_BYTE)<br>      {<br>          printf("hello from image processing\n");<br>          // we'll pick out the center 1/2 of the whole image,<br>          int column_start = _image->s() / 4;<br>          int column_end = 3 * column_start;<br><br>          int row_start = _image->t() / 4;<br>          int row_end = 3 * row_start;<br><br>          // and then invert these pixels<br>          for (int r = row_start; r < row_end; ++r)<br>          {<br>              unsigned char* data = _image->data(column_start, r);<br>              for (int c = column_start; c < column_end; ++c)<br>              {<br>                  (*data) = 255 - (*data); ++data;<br>                  (*data) = 255 - (*data); ++data;<br>                  (*data) = 255 - (*data); ++data;<br>                  (*data) = 255; ++data;<br>              }<br>          }<br><br>          // dirty the image (increments the modified count) so that any textures<br>          // using the image can be informed that they need to update.<br>          _image->dirty();<br><br>          if (osgDB::writeImageFile(*_image, std::string("C:\\image.jpg")))<br>          {<br>              std::cout << R"(Saved screen image to `C:\image.jpg`)" << std::endl;<br>          }<br>      }<br><br>  }<br><br>  osg::Image* _image;<br>};<br><br><br>int main222()<br>{<br><br>  bool useImage=true;<br><br>    //    osgViewer::Viewer viewer(arguments);<br>  AsyncRTTViewer viewer;<br>  viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded);<br><br>  // set up the camera manipulators.<br>  {<br>    osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;<br><br>    keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );<br>    keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );<br>    keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );<br>    viewer.setCameraManipulator( keyswitchManipulator.get() );<br>  }<br><br>  // add the state manipulator<br>  viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );    <br>  viewer.addEventHandler(new osgViewer::ThreadingHandler);<br>  viewer.addEventHandler(new osgViewer::WindowSizeHandler);<br>  viewer.addEventHandler(new osgViewer::StatsHandler);<br>  //viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));<br>  viewer.addEventHandler(new osgViewer::RecordCameraPathHandler);<br>  viewer.addEventHandler(new osgViewer::LODScaleHandler);    <br>  viewer.realize();    <br><br><br>  // load the data<br>  osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFile(std::string("C:\\cow.osg"));<br>  if (!loadedModel) <br>    {<br>      //std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;<br>      return 1;<br>    }<br><br>  // we have a root object that contains a scene and some overlayed textures to show<br>  // the content if a independently rendered texture<br>  osg::Group* root = new osg::Group();<br>  osg::Group* scene = new osg::Group();<br>  root->addChild(scene);<br>  scene->addChild(loadedModel.get());<br>  // any option left unread are converted into errors to write out later.<br>  //arguments.reportRemainingOptionsAsUnrecognized();<br><br>  // optimize the scene graph, remove redundant nodes and state etc.<br>  osgUtil::Optimizer optimizer;<br>  optimizer.optimize(loadedModel.get());<br><br>  viewer.setSceneData(root);    <br>    <br>  // Now we create a normal Render to Texture camera <br>  // Create the texture to render to<br>  osg::Texture2D* texture = new osg::Texture2D;<br>  texture->setTextureSize(256, 256);<br>  texture->setInternalFormat(GL_RGBA);<br>  texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);<br>  texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);<br><br>  // set up the render to texture camera.<br>  osg::Camera* cam = new osg::Camera;<br>  cam->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);<br><br>  // set up projection.<br>  cam->setProjectionMatrixAsPerspective(30, 1,0.1,30);    <br>  // set view<br>  cam->setReferenceFrame(osg::Transform::ABSOLUTE_RF);<br>  cam->setViewport(0, 0, 256, 256);<br>  // Frame buffer objects are the best option<br>  cam->setRenderTargetImplementation(osg::Camera::PIXEL_BUFFER_RTT);<br>  // We need to render to the texture BEFORE we render to the screen<br>  cam->setRenderOrder(osg::Camera::PRE_RENDER);    <br><br>  osg::Image* image = new osg::Image;<br>  if(useImage){<br>    image->allocateImage(256, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE);<br>    //image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_FLOAT);<br>    // attach the image so its copied on each frame.<br>    cam->attach(osg::Camera::COLOR_BUFFER, image);<br>    cam->setPostDrawCallback(new MyCameraPostDrawCallback(image));<br>    texture->setImage(0, image);<br>  }else{<br>    // The camera will render into the texture that we created earlier<br>    cam->attach(osg::Camera::COLOR_BUFFER, texture);<br>  }<br><br>  // Add world to be drawn to the texture<br>  cam->addChild(loadedModel.get());<br><br>  // now we add the RTT camera to our custom viewer<br>  viewer.addOffScreenRTTNode(cam);<br><br>  // set up the place where the content of the texture is seen in the normal scene<br>  osg::ref_ptr<osg::Geometry> screenQuad;<br>  screenQuad = osg::createTexturedQuadGeometry(osg::Vec3(),<br>                                               osg::Vec3(256, 0.0, 0.0),<br>                                               osg::Vec3(0.0, 256, 0.0));<br>  osg::ref_ptr<osg::Geode> quadGeode = new osg::Geode;<br>  quadGeode->addDrawable(screenQuad.get());<br>  osg::StateSet *quadState = quadGeode->getOrCreateStateSet();<br>  quadState->setMode(GL_LIGHTING,osg::StateAttribute::OFF);<br>  quadState->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);    <br>  osg::ref_ptr<osg::Camera> orthoCamera = new osg::Camera;<br>  // We don't want to apply perspective, just overlay using orthographic<br>  orthoCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 256, 0, 256));    <br>  orthoCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);<br>  orthoCamera->setViewMatrix(osg::Matrix::identity());<br>        <br>  orthoCamera->setViewport(0, 0, 256,256);      <br>  orthoCamera->setRenderOrder(osg::Camera::POST_RENDER);<br><br>  //osg::Image* image = new osg::Image;<br>  //image->allocateImage(256, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE);<br>  //image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_FLOAT);<br>  // attach the image so its copied on each frame.<br>  //orthoCamera->attach(osg::Camera::COLOR_BUFFER, image);<br>  //orthoCamera->setPostDrawCallback(new MyCameraPostDrawCallback(image));<br><br>  orthoCamera->addChild(quadGeode.get());<br>  // and add it to the root note<br>  root->addChild(orthoCamera.get());    <br><br> <br>  int frame_count=0;  <br>  const int swap_every=200; // the texture rendering occurs only every 200th frame<br>  bool swap=true;<br>  while(!viewer.done())<br>    {        <br>      if (swap && 0 == (frame_count % swap_every))<br>        {<br>        osg::Vec3 eye; osg::Vec3 center; osg::Vec3 up; <br>        viewer.getCamera()->getViewMatrixAsLookAt(eye,center,up);<br>        // simply turn the up vector around<br>        cam->setViewMatrixAsLookAt(eye, center, -up);           <br>        // do the offscreen rendering<br>        viewer.renderOffScreen();<br><br>        //viewer.frame();<br><br>        image->readPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE);<br><br>        int column_start = image->s() / 4;<br>        int column_end = 3 * column_start;<br><br>        int row_start = image->t() / 4;<br>        int row_end = 3 * row_start;<br><br>        // and then invert these pixels<br>        for (int r = row_start; r < row_end; ++r)<br>        {<br>            unsigned char* data = image->data(column_start, r);<br>            for (int c = column_start; c < column_end; ++c)<br>            {<br>                (*data) = 255 - (*data); ++data;<br>                (*data) = 255 - (*data); ++data;<br>                (*data) = 255 - (*data); ++data;<br>                (*data) = 255; ++data;<br>            }<br>        }<br><br>        //// dirty the image (increments the modified count) so that any textures<br>        //// using the image can be informed that they need to update.<br>        image->dirty();<br><br>        //if (osgDB::writeImageFile(*image, std::string("C:\\image.jpg")))<br>        //{<br>        //    std::cout << R"(Saved screen image to `C:\image.jpg`)" << std::endl;<br>        //}<br><br>      }<br>    else<br>    {<br>        viewer.frame();<br>    }<br>      <br>      frame_count++;      <br>    }<br><br>  return 0;<br>}<br></div><div><br></div><div><b>The way I am using this class is:</b></div><div><br></div><div>  bool useImage=true;<br><br>    //    osgViewer::Viewer viewer(arguments);<br>  AsyncRTTViewer viewer;<br>  viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded);<br><br>  // set up the camera manipulators.<br>  {<br>    osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;<br><br>    keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );<br>    keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );<br>    keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );<br>    viewer.setCameraManipulator( keyswitchManipulator.get() );<br>  }<br><br>  // add the state manipulator<br>  viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );    <br>  viewer.addEventHandler(new osgViewer::ThreadingHandler);<br>  viewer.addEventHandler(new osgViewer::WindowSizeHandler);<br>  viewer.addEventHandler(new osgViewer::StatsHandler);<br>  //viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));<br>  viewer.addEventHandler(new osgViewer::RecordCameraPathHandler);<br>  viewer.addEventHandler(new osgViewer::LODScaleHandler);    <br>  viewer.realize();    <br><br><br>  // load the data<br>  osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFile(std::string("C:\\cow.osg"));<br>  if (!loadedModel) <br>    {<br>      //std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;<br>      return 1;<br>    }<br><br>  // we have a root object that contains a scene and some overlayed textures to show<br>  // the content if a independently rendered texture<br>  osg::Group* root = new osg::Group();<br>  osg::Group* scene = new osg::Group();<br>  root->addChild(scene);<br>  scene->addChild(loadedModel.get());<br>  // any option left unread are converted into errors to write out later.<br>  //arguments.reportRemainingOptionsAsUnrecognized();<br><br>  // optimize the scene graph, remove redundant nodes and state etc.<br>  osgUtil::Optimizer optimizer;<br>  optimizer.optimize(loadedModel.get());<br><br>  viewer.setSceneData(root);    <br>    <br>  // Now we create a normal Render to Texture camera <br>  // Create the texture to render to<br>  osg::Texture2D* texture = new osg::Texture2D;<br>  texture->setTextureSize(256, 256);<br>  texture->setInternalFormat(GL_RGBA);<br>  texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);<br>  texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);<br><br>  // set up the render to texture camera.<br>  osg::Camera* cam = new osg::Camera;<br>  cam->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);<br><br>  // set up projection.<br>  cam->setProjectionMatrixAsPerspective(30, 1,0.1,30);    <br>  // set view<br>  cam->setReferenceFrame(osg::Transform::ABSOLUTE_RF);<br>  cam->setViewport(0, 0, 256, 256);<br>  // Frame buffer objects are the best option<br>  cam->setRenderTargetImplementation(osg::Camera::PIXEL_BUFFER_RTT);<br>  // We need to render to the texture BEFORE we render to the screen<br>  cam->setRenderOrder(osg::Camera::PRE_RENDER);    <br><br>  osg::Image* image = new osg::Image;<br>  if(useImage){<br>    image->allocateImage(256, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE);<br>    //image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_FLOAT);<br>    // attach the image so its copied on each frame.<br>    cam->attach(osg::Camera::COLOR_BUFFER, image);<br>    cam->setPostDrawCallback(new MyCameraPostDrawCallback(image));<br>    texture->setImage(0, image);<br>  }else{<br>    // The camera will render into the texture that we created earlier<br>    cam->attach(osg::Camera::COLOR_BUFFER, texture);<br>  }<br><br>  // Add world to be drawn to the texture<br>  cam->addChild(loadedModel.get());<br><br>  // now we add the RTT camera to our custom viewer<br>  viewer.addOffScreenRTTNode(cam);<br><br>  // set up the place where the content of the texture is seen in the normal scene<br>  osg::ref_ptr<osg::Geometry> screenQuad;<br>  screenQuad = osg::createTexturedQuadGeometry(osg::Vec3(),<br>                                               osg::Vec3(256, 0.0, 0.0),<br>                                               osg::Vec3(0.0, 256, 0.0));<br>  osg::ref_ptr<osg::Geode> quadGeode = new osg::Geode;<br>  quadGeode->addDrawable(screenQuad.get());<br>  osg::StateSet *quadState = quadGeode->getOrCreateStateSet();<br>  quadState->setMode(GL_LIGHTING,osg::StateAttribute::OFF);<br>  quadState->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);    <br>  osg::ref_ptr<osg::Camera> orthoCamera = new osg::Camera;<br>  // We don't want to apply perspective, just overlay using orthographic<br>  orthoCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 256, 0, 256));    <br>  orthoCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);<br>  orthoCamera->setViewMatrix(osg::Matrix::identity());<br>        <br>  orthoCamera->setViewport(0, 0, 256,256);      <br>  orthoCamera->setRenderOrder(osg::Camera::POST_RENDER);<br><br>  //osg::Image* image = new osg::Image;<br>  //image->allocateImage(256, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE);<br>  //image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_FLOAT);<br>  // attach the image so its copied on each frame.<br>  //orthoCamera->attach(osg::Camera::COLOR_BUFFER, image);<br>  //orthoCamera->setPostDrawCallback(new MyCameraPostDrawCallback(image));<br><br>  orthoCamera->addChild(quadGeode.get());<br>  // and add it to the root note<br>  root->addChild(orthoCamera.get());    <br><br> <br>  int frame_count=0;  <br>  const int swap_every=200; // the texture rendering occurs only every 200th frame<br>  bool swap=true;<br>  while(!viewer.done())<br>    {        <br>      if (swap && 0 == (frame_count % swap_every))<br>        {<br>        osg::Vec3 eye; osg::Vec3 center; osg::Vec3 up; <br>        viewer.getCamera()->getViewMatrixAsLookAt(eye,center,up);<br>        // simply turn the up vector around<br>        cam->setViewMatrixAsLookAt(eye, center, -up);           <br>        // do the offscreen rendering<br>        viewer.renderOffScreen();<br><br>        //// dirty the image (increments the modified count) so that any textures<br>        //// using the image can be informed that they need to update.<br>        image->dirty();<br><br>        //if (osgDB::writeImageFile(*image, std::string("C:\\image.jpg")))<br>        //{<br>        //    std::cout << R"(Saved screen image to `C:\image.jpg`)" << std::endl;<br>        //}<br><br>      }<br>    else<br>    {<br>        viewer.frame();<br>    }<br>      <br>      frame_count++;      <br>    }</div><div><br></div><div>This code in no longer present in OSG 3.4.0, so it must be older and may have worked with earlier versions of OSG? I am not sure....<br></div><div><br></div><div>The issue is that the image I need, that should be done in PRE_RENDER, so before the first frame, is not produced. And none of the sybsequent 200'th frame are not done either.</div><div>The call back installed here:</div><div><br></div><div>cam->setPostDrawCallback(new MyCameraPostDrawCallback(image));</div><div><br></div><div>is not invoked in MyCameraPostDrawCallback::operator () (osg::RenderInfo& renderInfo) const</div><div><br></div><div>Any idea why this callback is not called? I can't move on because of this issue.</div><div><br></div><div>If I install it on the other camera on osg::Camera::POST_RENDER phase (orthoCamera) than it gets called, but this is not what I need as it creates a window...I must avoid that...<br></div><div><br></div><div>Any help will be aprreciated.</div><div><br></div><div>Thank you,</div><div>Sorin.<br></div></div></div></div></div></div></div></div></div></div></div>