1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
|
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in a
** written agreement between you and Nokia.
**
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of this
** file.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example opengl/overpainting
\title Overpainting Example
The Overpainting example shows how QPainter can be used
to overpaint a scene rendered using OpenGL in a QGLWidget.
\image overpainting-example.png
QGLWidget provides a widget with integrated OpenGL graphics support
that enables 3D graphics to be displayed using normal OpenGL calls,
yet also behaves like any other standard Qt widget with support for
signals and slots, properties, and Qt's action system.
Usually, QGLWidget is subclassed to display a pure 3D scene. The
developer reimplements \l{QGLWidget::initializeGL()}{initializeGL()}
to initialize any required resources, \l{QGLWidget::resizeGL()}{resizeGL()}
to set up the projection and viewport, and
\l{QGLWidget::paintGL()}{paintGL()} to perform the OpenGL calls needed
to render the scene. However, it is possible to subclass QGLWidget
differently to allow 2D graphics, drawn using QPainter, to be
painted over a scene rendered using OpenGL.
In this example, we demonstrate how this is done by reusing the code
from the \l{Hello GL Example}{Hello GL} example to provide a 3D scene,
and painting over it with some translucent 2D graphics. Instead of
examining each class in detail, we only cover the parts of the
\c GLWidget class that enable overpainting, and provide more detailed
discussion in the final section of this document.
\section1 GLWidget Class Definition
The \c GLWidget class is a subclass of QGLWidget, based on the one used
in the \l{Hello GL Example}{Hello GL} example. Rather than describe the
class as a whole, we show the first few lines of the class and only
discuss the changes we have made to the rest of it:
\snippet examples/opengl/overpainting/glwidget.h 0
\dots
\snippet examples/opengl/overpainting/glwidget.h 1
\dots
\snippet examples/opengl/overpainting/glwidget.h 4
As usual, the widget uses \l{QGLWidget::initializeGL()}{initializeGL()}
to set up geometry for our scene and perform OpenGL initialization tasks.
The \l{QGLWidget::resizeGL()}{resizeGL()} function is used to ensure that
the 3D graphics in the scene are transformed correctly to the 2D viewport
displayed in the widget.
Instead of implementing \l{QGLWidget::paintGL()}{paintGL()} to handle updates
to the widget, we implement a normal QWidget::paintEvent(). This
allows us to mix OpenGL calls and QPainter operations in a controlled way.
In this example, we also implement QWidget::showEvent() to help with the
initialization of the 2D graphics used.
The new private member functions and variables relate exclusively to the
2D graphics and animation. The \c animate() slot is called periodically by the
\c animationTimer to update the widget; the \c createBubbles() function
initializes the \c bubbles list with instances of a helper class used to
draw the animation; the \c drawInstructions() function is responsible for
a semi-transparent message that is also overpainted onto the OpenGL scene.
\section1 GLWidget Class Implementation
Again, we only show the parts of the \c GLWidget implementation that are
relevant to this example. In the constructor, we initialize a QTimer to
control the animation:
\snippet examples/opengl/overpainting/glwidget.cpp 0
We turn off the widget's \l{QWidget::autoFillBackground}{autoFillBackground} property to
instruct OpenGL not to paint a background for the widget when
\l{QPainter::begin()}{QPainter::begin()} is called.
As in the \l{Hello GL Example}{Hello GL} example, the destructor is responsible
for freeing any OpenGL-related resources:
\snippet examples/opengl/overpainting/glwidget.cpp 1
The \c initializeGL() function is fairly minimal, only setting up the QtLogo
object used in the scene. See the \l{Hello GL Example}{Hello GL} example
for details of the QtLogo class.
\snippet examples/opengl/overpainting/glwidget.cpp 2
To cooperate fully with QPainter, we defer matrix stack operations and attribute
initialization until the widget needs to be updated.
In this example, we implement \l{QWidget::paintEvent()}{paintEvent()} rather
than \l{QGLWidget::paintGL()}{paintGL()} to render
our scene. When drawing on a QGLWidget, the paint engine used by QPainter
performs certain operations that change the states of the OpenGL
implementation's matrix and property stacks. Therefore, it is necessary to
make all the OpenGL calls to display the 3D graphics before we construct
a QPainter to draw the 2D overlay.
We render a 3D scene by setting up model and projection transformations
and other attributes. We use an OpenGL stack operation to preserve the
original matrix state, allowing us to recover it later:
\snippet examples/opengl/overpainting/glwidget.cpp 4
We define a color to use for the widget's background, and set up various
attributes that define how the scene will be rendered.
\snippet examples/opengl/overpainting/glwidget.cpp 6
We call the \c setupViewport() private function to set up the
projection used for the scene. This is unnecessary in OpenGL
examples that implement the \l{QGLWidget::paintGL()}{paintGL()}
function because the matrix stacks are usually unmodified between
calls to \l{QGLWidget::resizeGL()}{resizeGL()} and
\l{QGLWidget::paintGL()}{paintGL()}.
Since the widget's background is not drawn by the system or by Qt, we use
an OpenGL call to paint it before positioning the object defined earlier
in the scene:
\snippet examples/opengl/overpainting/glwidget.cpp 7
Once the QtLogo object's draw method has been executed, the GL
states we changed and the matrix stack needs to be restored to its
original state at the start of this function before we can begin
overpainting:
\snippet examples/opengl/overpainting/glwidget.cpp 8
With the 3D graphics done, we construct a QPainter for use on the widget
and simply overpaint the widget with 2D graphics; in this case, using a
helper class to draw a number of translucent bubbles onto the widget,
and calling \c drawInstructions() to overlay some instructions:
\snippet examples/opengl/overpainting/glwidget.cpp 10
When QPainter::end() is called, suitable OpenGL-specific calls are made to
write the scene, and its additional contents, onto the widget.
With \l{QGLWidget::paintGL()}{paintGL()} the
\l{QGLWidget::swapBuffers()}{swapBuffers()} call is done for us. But an explicit
call to swapBuffers() is still not required because in the
\l{QWidget::paintEvent()}{paintEvent()} method the QPainter on the OpenGL
widget takes care of this for us.
The implementation of the \l{QGLWidget::resizeGL()}{resizeGL()} function
sets up the dimensions of the viewport and defines a projection
transformation:
\snippet examples/opengl/overpainting/glwidget.cpp 11
Ideally, we want to arrange the 2D graphics to suit the widget's dimensions.
To achieve this, we implement the \l{QWidget::showEvent()}{showEvent()} handler,
creating new graphic elements (bubbles) if necessary at appropriate positions
in the widget.
\snippet examples/opengl/overpainting/glwidget.cpp 12
This function only has an effect if less than 20 bubbles have already been
created.
The \c animate() slot is called every time the widget's \c animationTimer emits
the \l{QTimer::timeout()}{timeout()} signal. This keeps the bubbles moving
around.
\snippet examples/opengl/overpainting/glwidget.cpp 13
We simply iterate over the bubbles in the \c bubbles list, updating the
widget before and after each of them is moved.
The \c setupViewport() function is called from \c paintEvent()
and \c resizeGL().
\snippet examples/opengl/overpainting/glwidget.cpp 14
The \c drawInstructions() function is used to prepare some basic
instructions that will be painted with the other 2D graphics over
the 3D scene.
\snippet examples/opengl/overpainting/glwidget.cpp 15
\section1 Summary
When overpainting 2D content onto 3D content, we need to use a QPainter
\e and make OpenGL calls to achieve the desired effect. Since QPainter
itself uses OpenGL calls when used on a QGLWidget subclass, we need to
preserve the state of various OpenGL stacks when we perform our own
calls, using the following approach:
\list
\o Reimplement QGLWidget::initializeGL(), but only perform minimal
initialization. QPainter will perform its own initialization
routines, modifying the matrix and property stacks, so it is better
to defer certain initialization tasks until just before you render
the 3D scene.
\o Reimplement QGLWidget::resizeGL() as in the pure 3D case.
\o Reimplement QWidget::paintEvent() to draw both 2D and 3D graphics.
\endlist
The \l{QWidget::paintEvent()}{paintEvent()} implementation performs the
following tasks:
\list
\o Push the current OpenGL modelview matrix onto a stack.
\o Perform initialization tasks usually done in the
\l{QGLWidget::initializeGL()}{initializeGL()} function.
\o Perform code that would normally be located in the widget's
\l{QGLWidget::resizeGL()}{resizeGL()} function to set the correct
perspective transformation and set up the viewport.
\o Render the scene using OpenGL calls.
\o Pop the OpenGL modelview matrix off the stack.
\o Construct a QPainter object.
\o Initialize it for use on the widget with the QPainter::begin() function.
\o Draw primitives using QPainter's member functions.
\o Call QPainter::end() to finish painting.
\endlist
*/
|