模仿QtOpenGL例子VowelCube时遇到的三维和Qt绘图引擎QPainter混合问题
《C++GUI Programming with Qt 4, secondedition》这本书介绍了OpenGL三维绘图引擎和QPainter混合例子:VowelCube。让我暗自高兴,我也可以实现一个类似的功能吧?结果测试了晚上和一上午,才弄懂例子的原理。
书上介绍Qt的绘图引擎可以和OpenGL渲染器混合在一个绘图上下文(RenderContext)的,但是需要注意的是,如果使用了这样的混合,那么我们就需要注意正确地切换渲染上下文。虽然书上有了相关的介绍,但我觉得还是有些疏忽——稍微介绍一下就完了,我们还不知道如果不这么做会显示不了图形。
首先要明确一个问题。制作这样混合的程序究竟是使用便利的initializeGL()、resizeGL()、paintGL()函数还是使用QWidget基类的构造函数、paintEvent()函数和resizeEvent()函数?我实验了下,觉得混合着用是最好的。如果大家之前学过了用glut构建OpenGL程序的方式,那么我想你会同意我的。
在构造QGLWidget子类的时候,我的做法是在相关初始化的代码既可以写入构造函数中,又可以写入initializeGL()函数中。随后绘制的部分我认为既然是混合OpenGL和QPainter的应用程序,那么一定要使用paintEvent()函数。因为如果使用paintGL()函数,那么只能绘制OpenGL图形了不是吗。而需要重绘的时候,既可以使用resizeGL()也可以重写resizeEvent(),取决你的需求,但是按照glut的思维,还是重写resizeGL()比较好。不过相信大家的实力还是自己可以写一个resizeGL()函数出来的。
随后为了让这OpenGL和QPainter都将图形绘制在一个渲染上下文中,需要手动关闭一些开关。下面两句代码意思就是关闭自动填充背景图片和自动交换缓存(缓冲区)。
setAutoBufferSwap( false ); setAutoFillBackground( false );
随后注意的是在用QPainter绘制二维图形的时候,一定要用QPainter的begin()和end()成员函数包裹(QPainter在构造的时候隐式调用了begin()函数),这样保证了二维图形绘制的完整性(其实Windows编程的GDI和DirectX和OpenGL都是这么做的)。在用OpenGL绘制三维图形以及设置OpenGL的时候,如果不是在initializeGL()、resizeGL()、paintGL()函数内,那么需要使用makeCurrent()将渲染上下文转为OpenGL绘制。此外在初始化OpenGL或者使用OpenGL函数进行渲染的时候,一定要记住设置顺时针为正面。即
glFrontFace( GL_CW );
或者在进行OpenGL渲染的时候,将OpenGL的所有位都压入堆栈,绘制完毕后弹出堆栈(例子程序VowelCube就是这么做的),使用下面两条语句包裹:
glPushAttrib( GL_ALL_ATTRIB_BITS ); glPopAttrib( );
如果不这么做的话,那么用QPainter绘制的二维图形是无法显示的。在以前的glut编程时没有遇到这个问题的我在这里折腾了很久。所以以后希望不要再这样出错了。
有了这些规则,我开始研究怎样制作丰富多样的QtOpenGL程序了。