Последовательный рендеринг вне экрана / захват экрана без оконной системы с использованием OpenSceneGraph

В настоящее время я работаю над внеэкранным рендерером, чтобы я мог выполнять регистрацию взаимной информации для реальных сцен. Я использую OpenSceneGraph, чтобы справляться с большими данными и автоматической загрузкой. У меня проблемы с захватом фреймбуфера в последовательной однопоточной программе.

Ну у меня есть такой класс (заголовок):

#include <osg/ref_ptr>
#include <osg/Array>
#include <osg/ImageUtils>
#include <osgGA/StateSetManipulator>
#include <osgViewer/Viewer>
#include <osg/GraphicsContext>
#include <osg/Texture2D>
#include <osg/FrameBufferObject>
#include <osgDB/WriteFile>
#include <osg/Referenced>
#include <osg/Vec3>
#include <osg/Image>
#include <osg/State>
#include <string>
#include <chrono>
#include <thread>
#include <assert.h>

#include "ImagingPrimitives.h"

class BoundRenderScene {
public:
    BoundRenderScene();
    virtual ~BoundRenderScene();
    void NextFrame(void);
    inline OpenThreads::Mutex* GetMutexObject(void) { return &_mutex; }

    inline osg::Image* GetFrame(void)
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
        return _frame.get();
    }

    inline void GetFrame(osg::Image* img)
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);

        if(_frame.valid() && (img!=NULL) && img->valid())
        {
            glReadBuffer(GL_BACK);
            img->readPixels(0,0,_camera_configuration->GetSX(),_camera_configuration->GetSY(), GL_RGB,GL_UNSIGNED_BYTE);
            uint w = img->s(), h = img->t(), d = img->r(), c = uint(img->getPixelSizeInBits()/8);
            /*
             * bare testing write op
             * osgDB::writeImageFile(const_cast<const osg::Image&>(*img), "/tmp/testimg.png");
             */
        }
    }

    inline void SetCameraConfiguration(CameraConfiguration* configuration) { _camera_configuration = configuration; }
    inline void SetCameraMatrix(osg::Matrixd camera_matrix) { _camera_matrix = camera_matrix; }
    inline void SetScene(osg::Node* scene) { _scene = scene; }

    inline void Initialize(void) {
        if(!_initialized)
            _init();
        else
            _re_init();
    }

protected:
    osgViewer::Viewer _viewer;
    osg::Matrixd _camera_matrix;
    osg::ref_ptr<osg::Texture2D> _tex;
    osg::ref_ptr<osg::FrameBufferObject> _fbo;
    mutable osg::ref_ptr<osg::Image> _frame;
    osg::ref_ptr<osg::Node> _scene;
    osg::ref_ptr<osg::GraphicsContext::Traits> _traits;
    osg::ref_ptr<osg::GraphicsContext> _gc;
    CameraConfiguration* _camera_configuration;
    SnapshotCallback* cb;
    std::string _filepath;

private:
    void _init(void);
    void _re_init(void);
    bool _initialized;
    mutable OpenThreads::Mutex  _mutex;

    osg::Matrixd pre_transform;
    osg::Matrixd transformation;
};

Кроме того, поскольку многие примеры внутриэкранного рендеринга и захвата экрана работают с Post / FinalDrawCallaback, я скопировал структуру обратного вызова из примера «osgdistortion», но добавил мьютекс для синхронизации:

struct SnapshotCallback : public osg::Camera::DrawCallback
{
public:
    inline SnapshotCallback(OpenThreads::Mutex* mtx_obj, std::string filepath, int width, int height) : _filepath(filepath), _output_to_file(false), _mutex(mtx_obj)
    {
        _image = new osg::Image();
        _image->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE);
        if(filepath!="")
            _output_to_file = true;

    }

    inline virtual void operator() (osg::RenderInfo& renderInfo) const
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*_mutex);
        osg::Camera* camera = renderInfo.getCurrentCamera();
        osg::Viewport* viewport = camera ? camera->getViewport() : 0;
        if(viewport && _image.valid())
        {
            glReadBuffer(GL_BACK);
            _image->readPixels(int(viewport->x()),int(viewport->y()),int(viewport->width()),int(viewport->height()), GL_RGB, GL_UNSIGNED_BYTE);
            if(_output_to_file)
            {
                osgDB::writeImageFile(*_image, _filepath);
            }
        }
    }

    inline virtual void operator() (const osg::Camera& camera) const
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*_mutex);
        osg::Viewport* viewport = camera.getViewport();
        if(viewport && _image.valid())
        {
            glReadBuffer(GL_BACK);
            _image->readPixels(int(viewport->x()),int(viewport->y()),int(viewport->width()),int(viewport->height()), GL_RGB, GL_UNSIGNED_BYTE);
            if(_output_to_file)
            {
                osgDB::writeImageFile(*_image, _filepath);
            }
        }
    }

    std::string _filepath;
    bool _output_to_file;

    mutable OpenThreads::Mutex*  _mutex;
    mutable osg::ref_ptr<osg::Image> _image;
};

Я инициализирую и визуализирую сцену следующим образом:

#include "BoundRenderScene.h"

void BoundRenderScene::_init(void)
{
    if(_camera!=NULL)
        _viewer.setDone(true);

    _traits->x = 0;
    _traits->y = 0;
    _traits->width = _camera_configuration->GetSX();
    _traits->height = _camera_configuration->GetSY();
    _traits->red = 8;
    _traits->green = 8;
    _traits->blue = 8;
    _traits->alpha = 0;
    _traits->depth = 24;
    _traits->windowDecoration = false;
    _traits->pbuffer = true;
    _traits->doubleBuffer = true;
    _traits->sharedContext = 0x0;


    if(_gc.get()!=NULL)
    {
        bool release_success = _gc->releaseContext();
        if(!release_success)
            std::cerr << "Error releasing Graphics Context.";
    }
    _gc = osg::GraphicsContext::createGraphicsContext(_traits.get());
    _viewer.getCamera()->setGraphicsContext(_gc.get());

    _viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded);
    _viewer.setUpThreading();
    _viewer.realize();


    _frame->allocateImage(_camera_configuration->GetSX(), _camera_configuration->GetSY(), 1, GL_RGB, GL_UNSIGNED_BYTE);

    _viewer.getCamera()->getOrCreateStateSet();
    _viewer.getCamera()->setRenderTargetImplementation(osg::Camera::PIXEL_BUFFER);

    cb = new SnapshotCallback(&_mutex,_filepath, _camera_configuration->GetSX(), _camera_configuration->GetSY());

    //_viewer.getCamera()->setPostDrawCallback( cb );

    //Clear colour "black" for representing "no information" => background elimination in natural image, pls.
    _viewer.getCamera()->setClearColor(osg::Vec4f(0.25f, 0.25f, 0.25f, 1.0f));
    _viewer.getCamera()->setClearMask(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    _viewer.getCamera()->setDrawBuffer(GL_BACK);
    _viewer.getCamera()->setReadBuffer(GL_BACK);
    _viewer.getCamera()->setViewport(0,0,_camera_configuration->GetSX(),_camera_configuration->GetSY());
    _viewer.getCamera()->setProjectionMatrix(osg::Matrixd::perspective(osg::RadiansToDegrees(_camera_configuration->GetFoV()), _camera_configuration->GetAspectRatio(), 0.1, 150.0));
    //looking in geo-coord system
    _viewer.getCamera()->setViewMatrix(osg::Matrixd::lookAt(osg::Vec3d(0.0, 0.0, -1.0), osg::Vec3d(0.0, 0.0, 1.0), osg::Vec3d(0.0, 1.0, 0.0)));
    _viewer.getCamera()->attach(osg::Camera::COLOR_BUFFER, _frame.get());

    _viewer.getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
    _tex->setTextureSize(_camera_configuration->GetSX(), _camera_configuration->GetSY());
    _tex->setInternalFormat(GL_RGB);
    _tex->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
    _tex->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
    _tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
    _tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
    _tex->setResizeNonPowerOfTwoHint(false);
    _tex->setImage(0,_frame.get());

    _fbo->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(_tex.get()));
    _viewer.setDone(false);
    _viewer.setSceneData(_scene.get());
    _viewer.setCameraManipulator(0x0);
}

void BoundRenderScene::NextFrame(void)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
    if(_frame.valid() && !_viewer.done())
    {
        osg::Matrixd inverse_cam = osg::Matrixd::inverse(_camera_matrix);
        transformation = inverse_cam * pre_transform;

        _viewer.getCamera()->setViewMatrix(transformation);
        _viewer.updateTraversal();
        _viewer.frame();
    }
    else
        std::cout << "Viewer or Camera invalid." << std::endl;
}

Основной рабочий процесс выглядит так (упрощенно):

BoundRenderScene renderer;
std::vector<osg::Matrixd> poses;
/*
 * setting initial parameters
 * fill poses with camera positions to render, for regsitration
 */
renderer._init();
for(uint i = 0; i < poses.size(); i++)
{
    renderer.SetCameraMatrix(poses.at(i));
    renderer.NextImage();
    sleep(0.04); // to get the 25fps frame limit
    osg::Image* reg_image = renderer.GetImage();
    /*
     * Do further processing
     */
}

Теперь приходит суть: пример OpenSceneGraph "osgprenderer" (включенный в OSG) выполняет рендеринг вне экрана, используя osg :: Camera :: DrawCallback в качестве моего SnapshotCallback. К сожалению, в моем случае функция operator () никогда не вызывается в моем графе сцены, поэтому такой способ захвата экрана не работает для меня. Это также довольно неудобно, так как остальная часть процедуры взаимной информации представляет собой довольно последовательный конвейер.

Другие оболочки (https://github.com/xarray/osgRecipes/blob/master/integrations/osgberkelium/osgberkelium.cpp) используют методы, аналогичные моему методу void GetFrame (osg :: Image * img), где изображение активно читается с помощью readPixels. Это очень удобно для моего рабочего процесса, но метод всегда возвращает пустое изображение. Он не падает, но и не выполняет свою работу.

Работает метод osg: and: Image * GetFrame (void), который возвращает привязанное / прикрепленное изображение FBO. Это похоже на пример "osgdistortion". Он действительно работает для рендеринга одного или двух изображений, но через некоторое время рендеринг и обработка перестают синхронизироваться, и приложение вылетает следующим образом:

[---FIRST FRAME---]
GraphicsCostEstimator::calibrate(..)
cull_draw() 0x1998ca0
ShaderComposer::~ShaderComposer() 0x35a4d40
Renderer::compile()
OpenGL extension 'GL_ARB_vertex_buffer_object' is supported.
OpenGL extension 'GL_EXT_secondary_color' is supported.
OpenGL extension 'GL_EXT_fog_coord' is supported.
OpenGL extension '' is not supported.
OpenGL extension 'GL_EXT_packed_depth_stencil' is supported.
Setting up osg::Camera::FRAME_BUFFER_OBJECT
end cull_draw() 0x1998ca0
[processing]
[   SECOND FRAME   ]
cull_draw() 0x1998ca0
OpenGL extension 'GL_ARB_fragment_program' is supported.
OpenGL extension 'GL_ARB_vertex_program' is supported.
OpenGL extension 'GL_ARB_shader_objects' is supported.
OpenGL extension 'GL_ARB_vertex_shader' is supported.
OpenGL extension 'GL_ARB_fragment_shader' is supported.
OpenGL extension 'GL_ARB_shading_language_100' is supported.
OpenGL extension 'GL_EXT_geometry_shader4' is supported.
OpenGL extension 'GL_EXT_gpu_shader4' is supported.
OpenGL extension 'GL_ARB_tessellation_shader' is supported.
OpenGL extension 'GL_ARB_uniform_buffer_object' is supported.
OpenGL extension 'GL_ARB_get_program_binary' is supported.
OpenGL extension 'GL_ARB_gpu_shader_fp64' is supported.
OpenGL extension 'GL_ARB_shader_atomic_counters' is supported.
glVersion=4.5, isGlslSupported=YES, glslLanguageVersion=4.5
Warning: detected OpenGL error 'invalid operation' at end of SceneView::draw()
end cull_draw() 0x1998ca0

[-FROM 3rd FRAME ONWARDS-]
[workload, matrix setup]
[_viewer.frame()]
cull_draw() 0x1998ca0
Warning: detected OpenGL error 'invalid operation' at start of State::apply()
end cull_draw() 0x1998ca0
[next frame]

[BREAKING]
cull_draw() 0x1998ca0
Warning: detected OpenGL error 'invalid operation' at start of State::apply()
end cull_draw() 0x1998ca0
[more work]
Segmentation fault (core dumped)

Итак, вопрос:

  • Я просмотрел исходные файлы из osg для классов, связанных с Viewer, но не смог определить, где ошибка

    Предупреждение: обнаружена ошибка OpenGL 'недопустимая операция' при запуске State :: apply ()

    происходит от. Есть идеи, где его искать?

  • Какой метод лучше всего использовать в OSG для последовательного рендеринга и захвата экрана?

  • Как я могу получить мьютекс обычного osg :: Viewer, чтобы синхронизировать рендерер с остальной частью конвейера py? (Рендерер однопоточный)
  • Есть ли другие предложения из опыта работы с внеэкранными модулями рендеринга OpenSceneGraph и снимками экрана?

person CKBergen    schedule 26.07.2015    source источник
comment
Вы должны попытаться сделать его меньше и более организованным, при этом ваш вопрос будет понятен всем, кто хочет помочь.   -  person Gabriel    schedule 26.07.2015


Ответы (1)


Как показало более глубокое исследование, освобождение графического контекста в деструкторе класса освободило конвейер OpenGL, НО: оно также привело к дислокации связанных с набором состояний текстур загруженной сцены / модели, хотя сама модель не была приостановлена ​​(как указано в вопросе: она повторно используется в следующих проходах). Итак, в дальнейших проходах рендеринга конвейер рендеринга хотел получить доступ к активам OSG, которые были выпущены путем освобождения контекста GL.

в коде он изменился с:

BoundRenderScene::~BoundRenderScene() {
    // TODO Auto-generated destructor stub
    _viewer.setDone(true);
    _viewer.setReleaseContextAtEndOfFrameHint(true);
    _gc->releaseContext();

#ifdef DEBUG
    std::cout << "BoundRenderScene deleted." << std::endl;
#endif
}

to:

BoundRenderScene::~BoundRenderScene() {
    // TODO Auto-generated destructor stub
    _viewer.setDone(true);
    _viewer.setReleaseContextAtEndOfFrameHint(true);

#ifdef DEBUG
    std::cout << "BoundRenderScene deleted." << std::endl;
#endif
}

Это разрешило внутренние сообщения об ошибках OpenSceneGraph. Теперь, чтобы решить саму проблему захвата кадра, я реализовал обратный вызов от osgprenderer:

struct SnapshotCallback : public osg::Camera::DrawCallback
{
public:
    inline SnapshotCallback(std::string filepath) : _filepath(filepath), _output_to_file(false), _image(NULL)
    {
        if(filepath!="")
            _output_to_file = true;
        _image = new osg::Image();
    }

    inline virtual void operator() (osg::RenderInfo& renderInfo) const
    {
        osg::Camera* camera = renderInfo.getCurrentCamera();
        osg::Viewport* viewport = camera ? camera->getViewport() : 0;
        if(viewport)
        {
            glReadBuffer(camera->getDrawBuffer());
            _image->allocateImage(int(viewport->width()), int(viewport->height()), 1, GL_RGB, GL_UNSIGNED_BYTE);
            _image->readPixels(int(viewport->x()),int(viewport->y()),int(viewport->width()),int(viewport->height()), GL_RGB, GL_UNSIGNED_BYTE);
            if(_output_to_file)
            {
                osgDB::writeImageFile(*reinterpret_cast<osg::Image*>(_image->clone(osg::CopyOp::DEEP_COPY_ALL)), _filepath);
            }
        }
    }

    inline virtual void operator() (const osg::Camera& camera) const
    {
        osg::Viewport* viewport = camera.getViewport();
        if(viewport)
        {
            glReadBuffer(camera.getDrawBuffer());
            _image->allocateImage(int(viewport->width()), int(viewport->height()), 1, GL_RGB, GL_UNSIGNED_BYTE);
            _image->readPixels(int(viewport->x()),int(viewport->y()),int(viewport->width()),int(viewport->height()), GL_RGB, GL_UNSIGNED_BYTE);
            if(_output_to_file)
            {
                osgDB::writeImageFile(*reinterpret_cast<osg::Image*>(_image->clone(osg::CopyOp::DEEP_COPY_ALL)), _filepath);
            }
        }
    }

    inline osg::Image* GetImage(void)
    {
        return reinterpret_cast<osg::Image*>(_image->clone(osg::CopyOp::DEEP_COPY_ALL));
    }

protected:
    std::string _filepath;
    bool _output_to_file;
    mutable osg::ref_ptr<osg::Image> _image;
};

Теперь, с клонированным буфером вместо фактического буфера изображения (идея, взятая из примера osgscreencapture), я получаю реальное изображение без ошибок памяти.

Для рендеринга с двойной буферизацией мне нужно каким-то образом дважды визуализировать сцену, чтобы правый буфер содержал изображения объектов, но это для моего варианта использования, который в настоящее время не представляет проблемы (рендеринг с привязкой к вводу-выводу, а не с привязкой к операции ).

Итак, основная функция выглядит следующим образом:

BoundRenderScene renderer;
std::vector<osg::Matrixd> poses;
/*
 * setting initial parameters
 * fill poses with camera positions to render, for registration
 */
renderer._init();
for(uint i = 0; i < poses.size(); i++)
{
    renderer.SetCameraMatrix(poses.at(i));
    renderer.NextImage();
    renderer.NextImage();
    osg::Image* reg_image = renderer.GetImage();
    /*
     * Do further processing
     */
}
person CKBergen    schedule 13.08.2015