OSG学习记录二
前言
上一节主要讲了,在编译OSG过程中遇到的一些问题,希望能对大家在编译OSG有所帮助。还顺便简单提了一下如何在Qt中嵌入OSG。
嵌入OSG
先上代码吧!
头文件
// OSGViewWidget.h
#ifndef __OSG_VIEW_WIDGET_H__
#define __OSG_VIEW_WIDGET_H__
#include <osgQt/GraphicsWindowQt>
#include <osgViewer/Viewer>
#include "TravelManipulator.h"
class OSGViewWidget : public osgQt::GLWidget, public osgViewer::Viewer
{
Q_OBJECT
public:
OSGViewWidget(QWidget* parent = 0);
~OSGViewWidget(void);
public:
osg::ref_ptr<TM::TravelManipulator> GetCurrentManipulator() {return m_pTravelManipulator;}
protected:
virtual void resizeEvent( QResizeEvent* event );
virtual void paintEvent(QPaintEvent* event);
private:
osg::ref_ptr<TM::TravelManipulator> m_pTravelManipulator;
};
#endif // __OSG_VIEW_WIDGET_H__
Cpp文件
// OSGViewWidget.cpp
#include "OSGViewWidget.h"
#include <osgGA/TrackballManipulator>
OSGViewWidget::OSGViewWidget(QWidget* parent /*= 0*/)
:osgQt::GLWidget(parent),
m_pTravelManipulator(NULL)
{
this->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
this->_gw = new osgQt::GraphicsWindowQt(this);
// osg::ref_ptr<osg::Camera> camera = new osg::Camera;
// camera->setGraphicsContext(this->_gw);
// camera->setClearColor( osg::Vec4(0.2, 0.2, 0.6, 1.0) );
// camera->setClearDepth(1.0);
// camera->setViewport( new osg::Viewport(0, 0, _gw->getTraits()->width, _gw->getTraits()->height) );
// GLenum buffer = _gw->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT;
// camera->setDrawBuffer(buffer);
// camera->setReadBuffer(buffer);
// camera->setProjectionMatrixAsPerspective(30.f,
// static_cast<double>(_gw->getTraits()->width)/static_cast<double>(_gw->getTraits()->height),
// 10.0f, 10000.0f );
// // 对比两种设置Camera方式,调用setGlobalDefaults()的图像区别比较大
// camera->getOrCreateStateSet()->setGlobalDefaults();
// this->setCamera(camera.get());
this->getCamera()->setGraphicsContext(_gw);
this->getCamera()->setViewport( new osg::Viewport(0, 0, _gw->getTraits()->width, _gw->getTraits()->height));
this->getCamera()->setClearColor(osg::Vec4(0.2,0.2,0.6, 1.0f));
GLenum buffer = _gw->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT;
getCamera()->setDrawBuffer(buffer);
getCamera()->setReadBuffer(buffer);
getCamera()->setClearDepth(1.0);
osgGA::TrackballManipulator* trackBallMpl = new osgGA::TrackballManipulator();
setCameraManipulator(trackBallMpl);
// m_pTravelManipulator = TM::TravelManipulator::CreateManipulator(this);
//
// setCameraManipulator(m_pTravelManipulator.get());
}
OSGViewWidget::~OSGViewWidget(void)
{
}
void OSGViewWidget::resizeEvent(QResizeEvent* event)
{
GLWidget::resizeEvent(event);
if (_gw->getTraits()->height > 0)
{
getCamera()->setViewport(0 ,0, _gw->getTraits()->width, _gw->getTraits()->height);
// 分析OSG源码后,相机的坐标系统影响home初始化
getCamera()->setProjectionMatrixAsPerspective(
60.f, static_cast<double>(_gw->getTraits()->width)/static_cast<double>(_gw->getTraits()->height), 1.0f, 10000.0f );
}
frame();
}
void OSGViewWidget::paintEvent(QPaintEvent* event)
{
frame();
update();
}
首先声明一个OSGViewWidget继承osgQt::GLWidget和osgViewer::Viewer,在Qt中嵌入OSG主要就是使用该类进行显示OSG的三维图像。在OSGViewWidget中配置osgViewer::Viewer的相机参数以及漫游方式,配置osgViewer::Viewer的相机有两种方式
1. 自己new一个Camera后,然后配置Camera的基本配置,最后通过setCamera将相机设置到osgViewer::Viewer中;
2. 通过调用getCamera()函数来创建默认的Camera,然后配置Camera参数;
通过阅读osgViewerQt例子中的代码可知,在Qt5.0以后的版本中必须使用单线程模式,所以在osgViewer::Viewer中必须设置this->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
我们要将OSG显示在Qt中除了需要构建一个Widget之外,还必须构建osg::GraphicsContext,这样才能将osg显示在窗口中,GraphicsContext顾名思义则是图像上下文。
在Camera类的成员函数中,setGraphicContext()函数的工作是设置相机对应的图形设备对象,换句话说,下面要介绍的GraphicsContext类就是图形设备对象的载体。用一句话来描述的话,GraphicsContext是任意图形子系统的抽象层接口,它提供了统一的图形设备操作函数,用来实现渲染结果和底层设备的交互;同时它还具有平台无关性,因而将OSG的渲染过程与操作系统平台剥离开来,使两者相互独立。用户即可以将渲染的内容传递给Windows或者X11的窗口与像素缓存对象,也可以自定义一个支持OpenGL的图形设备,并将结果反映在其上。
图形设备对象的主要工作是提供场景渲染结果的载体,这个载体可以显示缓存,进而绘制到一个图像窗口中,也可以是其他特殊的缓存对象,从而实现复杂的渲染和图像多次曝光等功能,创建一个图像设备不能简单地使用new运算符,因为GraphicContext类是一个不能被实例化的抽象类(这个体现在valid()等一大批纯虚函数上);通常应当使用createContext()静态函数,自动根据当前的用户环境和特性参数traits,构建一个平台相关的图形设备对象。
所以我们在代码中必须要this->_gw = new osgQt::GraphicsWindowQt(this),然后将_gw设置到Camera中。
Qt的Widget在显示时,必须调用paint来将控件和图像绘制到屏幕上,所以在widget中显示OSG图像时也必须不停的绘制OSG,所以我们必须在虚函数paintevent中不停的调用osgViewer的frame()函数,来实时的绘制osg图像。
当widget改变窗口大小时,我们也必须实时的修改Camera的视口范围,否则改变Widget大小后osg的图像无法跟着一起改变,所以我们必须在resizeEvent中重新更新OSGViewer::Viewer的Camera视口大小以及Camera的投影坐标。
到这一步,我们已经完成在Qt中嵌入OSG的核心代码,后面调用OSG时只需add这个Widget即可,如下所示:
ui.setupUi(this);
m_pOSGWidget = new OSGViewWidget(this);
ui.gridLayout->addWidget(m_pOSGWidget);
后面即可在Qt中添加相应的模型内容。
代码
所有代码见(https://github.com/Timo-China/OSGQtDemo)[https://github.com/Timo-China/OSGQtDemo]