Apr 13, 2011

Qt + OpenSceneGraph + OffScreen Rendering


Hoy vamos a ver como integrar OpenSceneGraph (a partir de ahora, OSG) con nuestra aplicación en Qt, pero no vamos a renderizar los frames directamente en pantalla, sino que utilizaremos una técnica llamada OffScreen Rendering. Esto es, renderizar la imagen directamente en un buffer o textura y luego transferir esta imagen resultante a otro lugar o mostrarla en pantalla.

Primero haremos un repaso de las herramientas a utilizar y porque:
  • Qt: Para este ejemplo en particular he decidido utilizar el lenguaje C++, el lenguaje en el que está escrito el framework original, en primer lugar porque la gran mayoría de los motores gráficos están escritos en este lenguaje, y segundo por una cuestión de rendimiento.
    Para renderizar la escena utilisaremos el widget QGLWidget, el cual nos permite renderizar los frames sin necesidad de mostrar el widget (aunque también es posible mostrar el widget y renderizar directamente en pantalla, con casi nula modificación), los frames los obtendremos mediante el método QGLWidget::renderPixmap().
  • OpenSceneGraph: OSG es un motor de gráficos 3D multiplataforma, utilizado para desarrollar videojuegos, programas de realidad virtual y aumentada, visualización científica y modelado 3D, entre otros usos que se le pueden dar. OSG está escrito en C++ estándar y usa OpenGL para renderizar la escena 3D.
    Las razones para elegirlo es por estar en una etapa de desarrollo considerada estable y en producción, por tener un gran numero de usuarios, y porque esta especialmente diseñado para usarse como motor de renderizado sin necesidad de depender de una interfaz gráfica en particular, se puede incluso renderizar una escena 3D directamente desde consola sin necesidad de lanzar ninguna ventana.
  • OffScreen Rendering: Es la técnica por la cual es posible renderizar una escena 3D directamente en un buffer o textura sin pasar por pantalla.
    Algunos usos de esta técnica incluyen la generación de imágenes intermedias, renderizado por lotes de animaciones no interactivas, generación de imágenes de Alta resolución, etc, que cada uno saque sus propias conclusiones sobre el uso que podría darle al OffScreen Rendering.
Este tutorial es una pequeña modificación de algunos tutoriales que pueden encontrarse sobre OSG que pueden encontrarse navegando por la red.
Mi filosofía, como siempre lo ha sido, es mostrar solo el mínimo código necesario para levantar el entorno deseado. Aquí sólo mostraré como cargar un modelo 3D y como realizar una animación muy simple, sin interactuar con el usuario.
Comenzamos declarando la clase QOSGWidget la cual es derivada de QGLWidget. Esta sera la encargada de crear e inicializar el contexto OpenGL.
#include <QGLWidget>
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osgGA/TrackballManipulator>
#include <osg/MatrixTransform>

class QOSGWidget: public QGLWidget
{
    Q_OBJECT

    public:
        explicit QOSGWidget(QWidget *parent = 0);

    private:
        void initializeGL();
        void resizeGL(int width, int height);
        void paintGL();

    signals:

    public slots:

    private:
        float cow_rot;

        osg::ref_ptr<osgViewer::Viewer> viewer;
        osg::observer_ptr<osgViewer::GraphicsWindowEmbedded> window;
        osg::ref_ptr<osg::Node> loadedModel;
        osg::ref_ptr<osg::MatrixTransform> transformation;
};
Luego definimos sus metodos.
QOSGWidget::QOSGWidget(QWidget *parent): QGLWidget(parent)
{
    // Creamos un nuevo visor para mostrar la escena mediante OSG.
    viewer = new osgViewer::Viewer;

    // Creamos una matriz de transformación que luego usaremos para rotar el modelo 3D.
    // Esta, actuará como nodo raíz.
    transformation = new osg::MatrixTransform;

    // Cargamos el modelo 3D.
    // Este, actuará como Geodo (Nodo Geométrico o Nodo Hoja).
    loadedModel = osgDB::readNodeFile("data/cow.osg");

    // Establecemos al modelo 3D como descendiente de la transformación.
    transformation->addChild(loadedModel.get());

    // Angulo de rotación de 0°.
    cow_rot = 0;
}

void QOSGWidget::initializeGL()
{
    // Asociamos el contexto OpenGL creado por el QGLWidget a OSG
    // y configuramos el viewport.
    window = viewer->setUpViewerAsEmbeddedInWindow(0, 0, width(), height());

    // Asociamos algún método de manipulación de cámara para OSG.
    viewer->setCameraManipulator(new osgGA::TrackballManipulator);
}

void QOSGWidget::resizeGL(int width, int height)
{
    if (window.valid())
    {
        // Ajustamos las dimensiones de la escena OSG si el widget cambia de tamaño.
        window->resized(window->getTraits()->x, window->getTraits()->y, width, height);
        window->getEventQueue()->windowResize(window->getTraits()->x, window->getTraits()->y, width, height);
    }
}

void QOSGWidget::paintGL()
{
    // Establecemos una matriz de rotación para girar el modelo 3D
    // al rededor del eje vertical (eje z).
    transformation->setMatrix(osg::Matrix::rotate(cow_rot, 0, 0, 1));

    // Colocamos el nodo raíz adentro de la escena.
    viewer->setSceneData(transformation.get());

    // Aumentamos el angulo de rotación.
    cow_rot += 0.1;

    // Renderizamos el frame.
    if (viewer.valid())
        viewer->frame();
}
El código es tan simple y escueto que resulta realmente fácil de entender.
Aquí tienen el CF completo para ver la implementación.

8 comments:

  1. Por favor, podrias postear el codigo con el Trackball funcionando en lugar de variar la rotacion y arbitrariamente?

    Estoy comenzando con OpenSceneGraph, y me vendria bien esa ayuda.

    Gracias.

    ReplyDelete
  2. Hi,

    Using ur application for OSG viewer. i used up ur QT code and build with QTCreator. Having the following issues now:

    C:\Users\admin\Desktop\QtOpenSceneGraph-build-desktop\..\OpenSceneGraph-3.0.0\include\osg\Referenced:179: error: undefined reference to `_imp___ZN11OpenThreads6AtomicmmEv'

    C:\Users\admin\Desktop\QtOpenSceneGraph-build-desktop\..\OpenSceneGraph-3.0.0\include\osg\Referenced:198: error: undefined reference to `_imp___ZNK3osg10Referenced24signalObserversAndDeleteEbb'

    There is seeming to be undefined reference errors. Can you suggest me to overcome the issues. Will be great if you can reply fast with the solution.

    P.S: have installed OpenSG and have included the paths in pro file.

    ReplyDelete
  3. Hi Vignesh, maybe the problem is that the example was compiled with OpenSceneGraph 2.8.5, and you are using the 3.0.0 version, recently declared as stable, but I have not tested with this version yet. This is still not available in the Archlinux repositories, but when available I will update the example.
    For now you can try the example with the previous version:

    OpenSceneGraph-2.8.5.zip

    If you still have problems running the example, let me know and maybe I can give a more suitable solution.

    ReplyDelete
  4. Daniel, cuando tenga un poco más de tiempo voy a incluir un ejemplo con TrackBall, por el momento estoy muy ocupado avanzando en el área de QtScript.
    De todas maneras, tengo planeado crear unos tutoriales más completos de OSG después de presentar un nuevo proyecto, cuando termine con la parte de QtScript. :)

    ReplyDelete
  5. Hi,

    I tried using opensg 2.8.5 provided by you. Same errors throws up. WIll be great if you can provide me with some solutions

    ReplyDelete
  6. 1) Try adding the paths to the precompiled OSG libraries with following line in your .pro file:

    LIBS += -L/path/to/OSG/DLLS -losg -losgAnimation ...

    2) Have you compiled the OSG code for mingw? try to compile OSG as shown in this tutorial.
    In that case, you must change the line to:

    LIBS += -L/path/to/OSG/A -losg -losgAnimation ...

    ReplyDelete
  7. Sorry, were says:

    LIBS += -L/path/to/OSG/DLLS -losg -losgAnimation ...

    is:

    LIBS += -L/path/to/OSG/LIBS -losg -losgAnimation ...

    were the precompiled (Visual Studio) .lib files are.
    If using MingW the precompiled libraries are .a files.

    ReplyDelete
  8. Me parece un ejemplo grandioso, pero me gustaría que incidieras en la interactividad (ratón, teclado, trackball...) . Enhorabuena.

    ReplyDelete