OSG 使用Qt的QOpenGLWidget

OSG 使用Qt的QOpenGLWidget

有osg 封装的qt默认使用QGLWidget 组件,现在想使用QT的QOpenGLWidget,之前尝试封装使用RTTCamera是一直有问题,最近看到解决办法,就记录一下。

环境:OSG 3.4 版本以上,QT5.9

解决办法

就是需要设置一个默认fbo id,帧缓存。
参考bbs

if (mIsFirstRenderRun)
	{
		// set set the default FBO-id. 
		// this id will be used when the rendering-backend is finished with RTT FBOs 
		GLuint defaultFboId = this->defaultFramebufferObject();
		//mpGraphicsContext->setDefaultFboId(defaultFboId);
		graphicsWindow_->setDefaultFboId(defaultFboId);
		mIsFirstRenderRun = false;
	}
	viewer_->frame();

完整代码

  1. OSGWidget.h文件
#pragma once
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
namespace osgWidget
{
	//! The subclass of osgViewer::CompositeViewer we use
	/*!
	* This subclassing allows us to remove the annoying automatic
	* setting of the CPU affinity to core 0 by osgViewer::ViewerBase,
	* osgViewer::CompositeViewer's base class.
	*/
	class Viewer : public osgViewer::CompositeViewer
	{
	public:
		virtual void setupThreading();
	};
}
class OSGWidget;
class Renderer : public QObject, protected QOpenGLFunctions
{
	Q_OBJECT

public:
	Renderer(OSGWidget *w);
	void lockRenderer() { m_renderMutex.lock(); }
	void unlockRenderer() { m_renderMutex.unlock(); }
	QMutex *grabMutex() { return &m_grabMutex; }
	QWaitCondition *grabCond() { return &m_grabCond; }
	void prepareExit() { m_exiting = true; m_grabCond.wakeAll(); }

signals:
	void contextWanted();
public :
	void render();

private:

	bool m_inited;
	qreal m_fAngle;
	qreal m_fScale;
	QVector<QVector3D> vertices;
	QVector<QVector3D> normals;
	int vertexAttr;
	int normalAttr;
	int matrixUniform;
	OSGWidget *m_glwidget;
	QMutex m_renderMutex;
	QElapsedTimer m_elapsed;
	QMutex m_grabMutex;
	QWaitCondition m_grabCond;
	bool m_exiting;
};
// openg 组件信息过程
class OSGWidget : public QOpenGLWidget
{
	Q_OBJECT

public:
	OSGWidget(osgViewer::GraphicsWindowEmbedded* graphic,osgViewer::View* view,QWidget* parent = 0,
		Qt::WindowFlags f = 0);

	virtual ~OSGWidget();

protected:

	virtual void paintEvent(QPaintEvent* paintEvent);
	virtual void paintGL();
	virtual void resizeGL(int width, int height);

	virtual void keyPressEvent(QKeyEvent* event);
	virtual void keyReleaseEvent(QKeyEvent* event);

	virtual void mouseMoveEvent(QMouseEvent* event);
	virtual void mousePressEvent(QMouseEvent* event);
	virtual void mouseReleaseEvent(QMouseEvent* event);
	virtual void mouseDoubleClickEvent(QMouseEvent * event);
	virtual void wheelEvent(QWheelEvent* event);

	virtual bool event(QEvent* event);

public:

	virtual void onHome();
	virtual void onResize(int width, int height);

	osgGA::EventQueue* getEventQueue() const;

	osg::ref_ptr<osgViewer::GraphicsWindowEmbedded> graphicsWindow_;
	osg::ref_ptr<osgWidget::Viewer> viewer_;


	void processSelection();
	//
	//
signals:
	void renderRequested();

	public slots:
	void grabContext();

	private slots:
	void onAboutToCompose();
	void onFrameSwapped();
	void onAboutToResize();
	void onResized();
private:
	QThread *m_thread;
	Renderer *m_renderer;
	bool mIsFirstRenderRun;
	int64_t m_lastTime;
};

  1. OSGWidget.cpp 文件
#include "OSGWidget.h"
//#include "PickHandler.h"
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 

#include 

#include 
#include 

#include 
#include 
#include 
#include 

namespace
{



}

namespace osgWidget
{
	void Viewer::setupThreading()
	{
		if (_threadingModel == SingleThreaded)
		{
			if (_threadsRunning)
				stopThreading();
		}
		else
		{
			if (!_threadsRunning)
				startThreading();
		}
	}
}

OSGWidget::OSGWidget(osgViewer::GraphicsWindowEmbedded* graphic, osgViewer::View* view, QWidget* parent,
	Qt::WindowFlags f)
	: QOpenGLWidget(parent,
		f)
	, graphicsWindow_(graphic)
	, viewer_(new osgWidget::Viewer)
	,mIsFirstRenderRun(true),
	m_lastTime(0)
{
	QSurfaceFormat format;
	format.setRenderableType(QSurfaceFormat::OpenGL);
	format.setProfile(QSurfaceFormat::CoreProfile);
	//format.setVersion(4, 1);
	format.setSamples(16);
	setFormat(format);
	viewer_->addView(view);
	viewer_->setThreadingModel(osgViewer::CompositeViewer::SingleThreaded);
	viewer_->realize();

	// This ensures that the widget will receive keyboard events. This focus
	// policy is not set by default. The default, Qt::NoFocus, will result in
	// keyboard events that are ignored.
	this->setFocusPolicy(Qt::StrongFocus);
	this->setMinimumSize(100, 100);

	// Ensures that the widget receives mouse move events even though no
	// mouse button has been pressed. We require this in order to let the
	// graphics window switch viewports properly.
	this->setMouseTracking(true);
	//后台线程模式
	//connect(this, &QOpenGLWidget::aboutToCompose, this, &OSGWidget::onAboutToCompose);
	//connect(this, &QOpenGLWidget::frameSwapped, this, &OSGWidget::onFrameSwapped);
	//connect(this, &QOpenGLWidget::aboutToResize, this, &OSGWidget::onAboutToResize);
	//connect(this, &QOpenGLWidget::resized, this, &OSGWidget::onResized);

	//m_thread = new QThread;
	//m_renderer = new Renderer(this);
	//m_renderer->moveToThread(m_thread);
	//connect(m_thread, &QThread::finished, m_renderer, &QObject::deleteLater);

	//connect(this, &OSGWidget::renderRequested, m_renderer, &Renderer::render);
	//connect(m_renderer, &Renderer::contextWanted, this, &OSGWidget::grabContext);

	//m_thread->start();
}

OSGWidget::~OSGWidget()
{
	//m_renderer->prepareExit();
	//m_thread->quit();
	//m_thread->wait();
	//delete m_thread;
}

void OSGWidget::paintEvent(QPaintEvent* /* paintEvent */)
{
	this->makeCurrent();

	QPainter painter(this);

	this->paintGL();

#ifdef WITH_SELECTION_PROCESSING
	if (selectionActive_ && !selectionFinished_)
	{
		painter.setPen(Qt::black);
		painter.setBrush(Qt::transparent);
		painter.drawRect(makeRectangle(selectionStart_, selectionEnd_));
	}
#endif

	painter.end();

	this->doneCurrent();
}

void OSGWidget::paintGL()
{
	int64_t curTime = QDateTime::currentMSecsSinceEpoch();
	if(curTime-m_lastTime<5)
		QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
	m_lastTime = curTime;
	if (mIsFirstRenderRun)
	{
		// set set the default FBO-id. 
		// this id will be used when the rendering-backend is finished with RTT FBOs 
		GLuint defaultFboId = this->defaultFramebufferObject();
		//mpGraphicsContext->setDefaultFboId(defaultFboId);
		graphicsWindow_->setDefaultFboId(defaultFboId);
		mIsFirstRenderRun = false;
	}
	viewer_->frame();
	QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
}

void OSGWidget::resizeGL(int width, int height)
{
	this->getEventQueue()->windowResize(this->x(), this->y(), width, height);
	graphicsWindow_->resized(this->x(), this->y(), width, height);

	this->onResize(width, height);
}

void OSGWidget::keyPressEvent(QKeyEvent* event)
{
	QString keyString = event->text();
	const char* keyData = keyString.toLocal8Bit().data();

	if (event->key() == Qt::Key_S)
	{
#ifdef WITH_SELECTION_PROCESSING
		selectionActive_ = !selectionActive_;
#endif

		// Further processing is required for the statistics handler here, so we do
		// not return right away.
	}
	else if (event->key() == Qt::Key_D)
	{
		return;
	}
	else if (event->key() == Qt::Key_H)
	{
		this->onHome();
		return;
	}

	this->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KeySymbol(*keyData));
}

void OSGWidget::keyReleaseEvent(QKeyEvent* event)
{
	QString keyString = event->text();
	const char* keyData = keyString.toLocal8Bit().data();

	this->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KeySymbol(*keyData));
}

void OSGWidget::mouseMoveEvent(QMouseEvent* event)
{
	// Note that we have to check the buttons mask in order to see whether the
	// left button has been pressed. A call to `button()` will only result in
	// `Qt::NoButton` for mouse move events.
	{
		auto pixelRatio = this->devicePixelRatio();

		this->getEventQueue()->mouseMotion(static_cast<float>(event->x() * pixelRatio),
			static_cast<float>(event->y() * pixelRatio));
	}
}

void OSGWidget::mousePressEvent(QMouseEvent* event)
{

	{
		// 1 = left mouse button
		// 2 = middle mouse button
		// 3 = right mouse button

		unsigned int button = 0;

		switch (event->button())
		{
		case Qt::LeftButton:
			button = 1;
			break;

		case Qt::MiddleButton:
			button = 2;
			break;

		case Qt::RightButton:
			button = 3;
			break;

		default:
			break;
		}

		auto pixelRatio = this->devicePixelRatio();

		this->getEventQueue()->mouseButtonPress(static_cast<float>(event->x() * pixelRatio),
			static_cast<float>(event->y() * pixelRatio),
			button);
	}
}

void OSGWidget::mouseReleaseEvent(QMouseEvent* event)
{

	{
		// 1 = left mouse button
		// 2 = middle mouse button
		// 3 = right mouse button

		unsigned int button = 0;

		switch (event->button())
		{
		case Qt::LeftButton:
			button = 1;
			break;

		case Qt::MiddleButton:
			button = 2;
			break;

		case Qt::RightButton:
			button = 3;
			break;

		default:
			break;
		}

		auto pixelRatio = this->devicePixelRatio();

		this->getEventQueue()->mouseButtonRelease(static_cast<float>(pixelRatio * event->x()),
			static_cast<float>(pixelRatio * event->y()),
			button);
	}
}
void OSGWidget::mouseDoubleClickEvent(QMouseEvent * event)
{
	int button = 0;
	switch (event->button())
	{
	case Qt::LeftButton: button = 1; break;
	case Qt::MidButton: button = 2; break;
	case Qt::RightButton: button = 3; break;
	case Qt::NoButton: button = 0; break;
	default: button = 0; break;
	}
	int pixelRatio = this->devicePixelRatio();
	this->getEventQueue()->mouseDoubleButtonPress(event->x()*pixelRatio, event->y()*pixelRatio, button);
}
void OSGWidget::wheelEvent(QWheelEvent* event)
{
	// Ignore wheel events as long as the selection is active.

	event->accept();
	int delta = event->delta();

	osgGA::GUIEventAdapter::ScrollingMotion motion = delta > 0 ? osgGA::GUIEventAdapter::SCROLL_UP
		: osgGA::GUIEventAdapter::SCROLL_DOWN;

	this->getEventQueue()->mouseScroll(motion);
}

bool OSGWidget::event(QEvent* event)
{
	bool handled = QOpenGLWidget::event(event);

	// This ensures that the OSG widget is always going to be repainted after the
	// user performed some interaction. Doing this in the event handler ensures
	// that we don't forget about some event and prevents duplicate code.
	switch (event->type())
	{
	case QEvent::KeyPress:
	case QEvent::KeyRelease:
	case QEvent::MouseButtonDblClick:
	case QEvent::MouseButtonPress:
	case QEvent::MouseButtonRelease:
	case QEvent::MouseMove:
	case QEvent::Wheel:
		this->update();
		break;

	default:
		break;
	}

	return handled;
}

void OSGWidget::onHome()
{
	osgViewer::ViewerBase::Views views;
	viewer_->getViews(views);

	for (std::size_t i = 0; i < views.size(); i++)
	{
		osgViewer::View* view = views.at(i);
		view->home();
	}
}

void OSGWidget::onResize(int width, int height)
{
	std::vector<osg::Camera*> cameras;
	viewer_->getCameras(cameras);

	assert(cameras.size() >= 1);

	auto pixelRatio = this->devicePixelRatio();

	cameras[0]->setViewport(0, 0, width  * pixelRatio, height * pixelRatio);
	//cameras[1]->setViewport(width / 2 * pixelRatio, 0, width / 2 * pixelRatio, height * pixelRatio);
}

osgGA::EventQueue* OSGWidget::getEventQueue() const
{
	osgGA::EventQueue* eventQueue = graphicsWindow_->getEventQueue();

	if (eventQueue)
		return eventQueue;
	else
		throw std::runtime_error("Unable to obtain valid event queue");
}

void OSGWidget::processSelection()
{

}

void OSGWidget::grabContext()
{
	m_renderer->lockRenderer();
	QMutexLocker lock(m_renderer->grabMutex());
	context()->moveToThread(m_thread);
	m_renderer->grabCond()->wakeAll();
	m_renderer->unlockRenderer();
}

void OSGWidget::onAboutToCompose()
{
	m_renderer->lockRenderer();
}

void OSGWidget::onFrameSwapped()
{
	m_renderer->unlockRenderer();
	// Assuming a blocking swap, our animation is driven purely by the
	// vsync in this example.
	emit renderRequested();
}

void OSGWidget::onAboutToResize()
{
	m_renderer->lockRenderer();
}

void OSGWidget::onResized()
{
	m_renderer->unlockRenderer();
}


Q_GLOBAL_STATIC(QMutex, initMutex)
Renderer::Renderer(OSGWidget *w) : m_inited(false),
m_glwidget(w),
m_exiting(false) {

}
void Renderer::render()
{
	if (m_exiting)
		return;

	QOpenGLContext *ctx = m_glwidget->context();

	if (!ctx) // QOpenGLWidget not yet initialized
		return;
	QSurfaceFormat format;
	format.setVersion(2, 1);
	format.setOption(QSurfaceFormat::DeprecatedFunctions, true);
	format.setProfile(QSurfaceFormat::CoreProfile);
	format.setSamples(8);
	ctx->setFormat(format);
	// Grab the context.
	m_grabMutex.lock();
	emit contextWanted();
	m_grabCond.wait(&m_grabMutex);
	QMutexLocker lock(&m_renderMutex);
	m_grabMutex.unlock();

	if (m_exiting)
		return;

	Q_ASSERT(ctx->thread() == QThread::currentThread());

	// Make the context (and an offscreen surface) current for this thread. The
	// QOpenGLWidget's fbo is bound in the context.
	m_glwidget->makeCurrent();

	if (!m_inited) {
		m_inited = true;
		initializeOpenGLFunctions();

		QMutexLocker initLock(initMutex());
	

		m_elapsed.start();
		// set set the default FBO-id. 
		// this id will be used when the rendering-backend is finished with RTT FBOs 
		GLuint defaultFboId = m_glwidget->defaultFramebufferObject();
		//mpGraphicsContext->setDefaultFboId(defaultFboId);
		m_glwidget->graphicsWindow_->setDefaultFboId(defaultFboId);
	}
	m_glwidget->viewer_->frame();
	// Make no context current on this thread and move the QOpenGLWidget's
	// context back to the gui thread.
	m_glwidget->doneCurrent();
	ctx->moveToThread(qGuiApp->thread());

	// Schedule composition. Note that this will use QueuedConnection, meaning
	// that update() will be invoked on the gui thread.
	QMetaObject::invokeMethod(m_glwidget, "update");
}

在使用过程中没有发现问题,并且可以改成线程模式,osg 的渲染在线程中渲染。

你可能感兴趣的:(OSG,QT,OSG,QOpenGLWidget)