Qt的Graphics-View框架和OpenGL结合详解

演示程序下载地址:这里

程序源代码下载地址:这里

这是一篇纯技术文,介绍了这一个月来我抽时间研究的成果。

Qt中有一个非常炫的例子:Boxes,它展示了Qt能够让其Graphics–View框架和Qt的OpenGL模块结合起来,渲染出非常出色的效果。其实我私自认为凭这个程序,已经有很多游戏开发者关注Qt了,因为游戏开发一个非常常见的模块就是UI,一般情况下游戏引擎提供的UI模块比较弱,基本上都是游戏引擎+第三方GUI库进行结合的。但是Qt以其Graphics–View框架能够非常轻松地将UI控件嵌入场景中,而且能够和OpenGL底层共存,更重要的是,凭借着Qt的qss,Qt可以定制许多GUI元素,这是非常具有吸引力的。所以说,如果大家对游戏开发感兴趣,那么不妨看一下Qt。

好了,下面介绍一下前几天我制作并发布的一个demo。这个demo是对Boxes这个例子进行模仿,结合学习《OpenGL超级宝典》,制作而成的,由于最近比较忙,所以总共花了将近一个月才完成,不得不说效率有点儿低。

首先从MainWindow.cpp这个文件说起吧,一开始是要初始化MainWindow类的,这个类是继承QMainWindow的,这里重点说说它的构造函数:

MainWindow::MainWindow(QWidget*pParent):
QMainWindow(pParent)
{
QGLWidget*pWidget=newQGLWidget(QGLFormat(QGL::SampleBuffers),this);
pWidget->makeCurrent();
//scene的内容
GraphicsScene*pScene=newGraphicsScene(this);
OpenGLView*pView=newOpenGLView(this);
pView->setViewport(pWidget);
pView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
pView->setScene(pScene);
//选择不同的着色器的时候进行着色器连接
connect(pScene,SIGNAL(SwitchShader(constQString&)),
pView,SLOT(SwitchShader(constQString&)));
connect(pScene,SIGNAL(SetLightPos(constQVector3D&)),
pView,SLOT(SetLightPos(constQVector3D&)));
setCentralWidget(pView);
setWindowTitle(tr("Lightforshader"));
resize(,);
}

首先在我们创建了一个QWidget,然后调用makeCurrent()成员函数,其实意思是让它的rendercontext设为当前的rendercontext。随后建立的是OpenGLView,这个OpenGLView是来自于QGraphicsView的,它的初始化和其祖先的并无二致,随后一句非常重要:setViewport(),它的作用是将QGLWidget设置为OpenGLView的viewport,这样的话背景的rendercontext不再是rastercontext而是OpenGLcontext了,否则场景的背景还是需要用CPU渲染的,效率低下。接着是两段建立连接的代码。最后设置的是窗口大小和标题什么的,一开始还是非常简单的。

接下来我们看看OpenGLView是怎么定义的:

classOpenGLView:publicQGraphicsView,
protectedQOpenGLFunctions
{
Q_OBJECT
public:
OpenGLView(QWidget*pParent=);
virtual~OpenGLView(void);
voidsetScene(GraphicsScene*pScene);
publicslots:
boolSwitchShader(constQString&shaderFileName);
voidSetLightPos(constQVector3D&lightPos=QVector3D());
protected:
voidresizeEvent(QResizeEvent*pEvent);
voidmousePressEvent(QMouseEvent*pEvent);
voidmouseReleaseEvent(QMouseEvent*pEvent);
voidmouseMoveEvent(QMouseEvent*pEvent);
voidwheelEvent(QWheelEvent*pEvent);
voiddrawBackground(QPainter*pPainter,constQRectF&rect);
private:
voidInitGL(void);
voidResizeGL(intwidth,intheight);
voidPaintGL(void);
voidDrawAxis(void);
boolSetupShaders(void);
Cameram_Camera;
Format3DSm_3DS;
//着色器
QOpenGLShader*m_pVertexShader;
QOpenGLShaderProgram*m_pShaderProgram;
};

这里我们在它的成员中添加了一个摄像机,一个3ds模型实例,还有一个顶点着色器和着色器程序类。在上次的博客中讲到了在这种情况最好使用类指针而不是类成员作为数据成员,这里我索性把着色器程序类也做成了指针成员了。

由于OpenGLView类实现比较长,这里我着重说一下其中的几个函数。下面是drawBackground()函数的实现:

voidOpenGLView::drawBackground(QPainter*pPainter,
constQRectF&)
{
pPainter->beginNativePainting();
glPushAttrib(GL_ALL_ATTRIB_BITS);
InitGL();
ResizeGL(pPainter->device()->width(),
pPainter->device()->height());
PaintGL();
glPopAttrib();
pPainter->endNativePainting();
}

为什么选择drawBackground()?因为我们想要得到一种效果,OpenGL在底层绘制,上面绘制控件,其实自从QPainter有了beginNativePainting()和endNativePainting()这两个函数,我们就可以进行纯OpenGL的绘制了。这里还要注意的是,因为绘制控件也是使用OpenGL的context,这样简单调用会让OpenGL的状态混乱,所以需要将各种状态通过glPushAttrib(GL_ALL_ATTRIB_BITS);保存起来,然后初始化我们的OpenGL状态,以及绘图,最后记得glPopAttrib();还原所有状态供2D绘制。这里就不像以往的GLWidget套路了,因为GLWidget里面的initializeGL()函数只调用一次,paintGL()函数调用多次,但是在这里,只要有刷新的消息(通过update()或repaint()触发),就必须调用InitGL()函数来进行OpenGL状态的设置,否则先前设置的所有状态都消失了。

接下来看看ResizeEvent()函数:

voidOpenGLView::resizeEvent(QResizeEvent*pEvent)
{
scene()->setSceneRect(0.0,
0.0,
pEvent->size().width(),
pEvent->size().height());
pEvent->accept();
}

这里由于我们已经设置了scene为GraphicsScene类的实例指针,因此scene()是非空的,我们将场景限制为view的大小,这样可以避免一些刷新的问题。

而paintGL()函数也是相当的简单。

voidOpenGLView::PaintGL(void)
{
glClear(GL_COLOR_BUFFER_BIT|
GL_DEPTH_BUFFER_BIT);
glPushMatrix();
m_Camera.Apply();
m_3DS.RenderGL();
if(g_ShowAxis)DrawAxis();
glPopMatrix();
}

接着我向大家介绍一下GraphicsScene类:

classGraphicsScene:publicQGraphicsScene
{
Q_OBJECT
public:
GraphicsScene(QObject*pParent=);
voidSetCamera(Camera*pCamera);
signals:
voidSwitchShader(constQString&shaderFileName);
voidSetLightPos(constQVector3D&pos);
protected:
voidmousePressEvent(QGraphicsSceneMouseEvent*pEvent);
voidmouseMoveEvent(QGraphicsSceneMouseEvent*pEvent);
voidmouseReleaseEvent(QGraphicsSceneMouseEvent*pEvent);
voidwheelEvent(QGraphicsSceneWheelEvent*pEvent);
privateslots:
voidFeedback(void);
private:
//鼠标事件需要
QPointFm_LastPos;
Camera*m_pCamera;
};

这里的GraphicsScene类保存的是来自view的Camera和一些信号以及事件的处理。在实现上也说一下它的构造函数吧。

GraphicsScene::GraphicsScene(QObject*pParent):
QGraphicsScene(pParent),m_pCamera(Q_NULLPTR)
{
ClickableTextItem*pTextItem=newClickableTextItem(Q_NULLPTR);
pTextItem->setPos(10.0,10.0);
pTextItem->setHtml(tr("<fontcolor=white>"
"MadeByJiangcaiyang<br>"
"CreatedinSeptember<br>"
"Clickforfeedback."
"</font>"));
connect(pTextItem,SIGNAL(Clicked()),
this,SLOT(Feedback()));
addItem(pTextItem);
ShaderOptionDialog*pDialog=newShaderOptionDialog;
connect(pDialog,SIGNAL(SwitchShader(constQString&)),
this,SIGNAL(SwitchShader(constQString&)));
connect(pDialog,SIGNAL(SetLightPos(constQVector3D&)),
this,SIGNAL(SetLightPos(constQVector3D&)));
QGraphicsProxyWidget*pProxy=addWidget(pDialog,Qt::Window|Qt::WindowTitleHint);
pProxy->setPos(,);
}

我们创建了一个ClickableTextItem类,它继承于QGraphicsTextItem,它被摆在左上角,显示三排可以点击的文字。随后添加了一个对话框,设置信号和槽完毕后就用代理放入场景中了。整个过程也是非常简单的。

我测试了下,整个程序在我的Ubuntu13.04下能够正常运行。只是由于显卡不同(Ubuntu下较难支持nvidia显卡,使用的是intel集显),specularOpt效果出不来。

Qt的Graphics-View框架和OpenGL结合详解的更多相关文章

  1. Graphics View框架

    Qt4.2开始引入了Graphics View框架用来取代Qt3中的Canvas模块,并在很多地方作了改进,Graphics View框架实现了模型-视图结构的图形管理,能对大量图元进行管理,支持碰撞 ...

  2. Qt 之 Graphics View Framework 简介

    Graphics View Framework 交互式 2D 图形的 Graphics View 框架概述.自 Qt4.2 中引入了 Graphics View,以取代其前身 QCanvas.Grap ...

  3. Scrapy框架的命令行详解【转】

    Scrapy框架的命令行详解 请给作者点赞 --> 原文链接 这篇文章主要是对的scrapy命令行使用的一个介绍 创建爬虫项目 scrapy startproject 项目名例子如下: loca ...

  4. 【Qt】Qt Quick 之 QML 与 C++ 混合编程详解

    Qt Quick 之 QML 与 C++ 混合编程详解 - CSDN博客   专栏:Qt Quick简明教程 - CSDN博客   .

  5. 转载爱哥自定义View系列--Paint详解

    上图是paint中的各种set方法 这些属性大多我们都可以见名知意,很好理解,即便如此,哥还是带大家过一遍逐个剖析其用法,其中会不定穿插各种绘图类比如Canvas.Xfermode.ColorFilt ...

  6. Android查缺补漏(View篇)--自定义View利器Canvas和Paint详解

    上篇文章介绍了自定义View的创建流程,从宏观上给出了一个自定义View的创建步骤,本篇是上一篇文章的延续,介绍了自定义View中两个必不可少的工具Canvas和Paint,从细节上更进一步的讲解自定 ...

  7. Android 网络框架之Retrofit2使用详解及从源码中解析原理

    就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...

  8. 转载爱哥自定义View系列--文字详解

    FontMetrics FontMetrics意为字体测量,这么一说大家是不是瞬间感受到了这玩意的重要性?那这东西有什么用呢?我们通过源码追踪进去可以看到FontMetrics其实是Paint的一个内 ...

  9. .NET ORM框架 SqlSuagr4.0 功能详解与实践【开源】

    SqlSugar 4.0 ORM框架的优势 为了未来能够更好的支持多库分布式的存储,并行计算等功能,将SqlSugar3.x全部重写,现有的架构可以轻松扩展多库. 源码下载: https://gith ...

随机推荐

  1. 解决 iOS webkit 使用CSS动画时闪烁的问题

    -webkit-backface-visibility: hidden;

  2. mysql explain 命令解释

    转载http://bzyyc.happy.blog.163.com/blog/static/6143064720115102551554/ key实 际使用的索引.如果为NULL,则没有使用索引.很少 ...

  3. 引号 shell

    在学些shell的 grep, awk, sed 中,发现<Linux 与Unix Shell 编程 指南>书中用大多都是单引号. 一开始我总在寻思,为什么用单引号,明明双引号也是行的呀. ...

  4. Linux下批量替换文件内容方法

    1:查找find . -type f -name "*.html"|xargs grep ‘yourstring’ 2:查找并替换find -name '要查找的文件名' | xa ...

  5. iOS开发中NSURL的基本操作

    1.URL URL是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址.互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它. ...

  6. JQuery 中的Ajax

    JQuery 对 Ajax 操作进行了封装, 在 jQuery 中最底层的方法时 $.ajax(), 第二层是 load(), $.get() 和 $.post(), 第三层是 $.getScript ...

  7. PHPCMS 插件开发教程及经验谈

    虽说 PHPCMS 开源,但其它开发文档及参考资料实在少得可怜.进行二次开发时,自己还得慢慢去研究它的代码,实在让人郁闷. PHPCMS 的“Baibu/Google地图”实在有待改进,对于数据量比较 ...

  8. DEDE常见的错误(转)

    1:dedecms文章录入的时候,如何控制文章重复.   在dede/article_add.php里面,加入该程序就OK了    if($cfg_check_title == 'Y'){      ...

  9. MSP430单片机的中断

    这篇文章是从网上转载过来的,原文章地址:http://www.21ic.com/jichuzhishi/mcu/questions/2012-12-21/154794.html 中断是MSP430微处 ...

  10. smart pointer (auto_ptr)

    很多人听说过标准auto_ptr智能指针机制,但并不是每个人都天天使用它.这真是个遗憾,因为auto_ptr优雅地解决了C++设计和编码中常见的问题,正确地使用它可以生成健壮的代码.本文阐述了如何正确 ...