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
|
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: http://www.qt-project.org/
**
** This file is part of the QtQuick3D documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** 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.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms
** and conditions contained in a signed written agreement between you
** and Nokia.
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\ingroup qt3d
\ingroup qt3d::math
\since 4.8
\title Camera and View Frustum
\page qt3d-camera-frustum.html
\keyword Camera View Frustum
\brief Basis for the vamera and view frustum culling test.
The QGLCamera class provides useful viewing functionality, such as being able
to position and orient view point within a scene. In combination with the
QGLPainter::isCullable() function, basic view frustum culling is available.
To understand in more depth these concepts, or to verify the implementation
more details of these are presented here. To simply use the functionality
read the documentation.
\list
\o QGLCamera
\o QGLPainter::isCullable()
\endlist
\section1 The Default Camera
Qt3D uses the OpenGL co-ordinate system, that is a right-handed system. In
this system you can think of X as pointing to the right, Y as pointing up and
Z as pointing toward you. If you hold your right-hand so that the thumb,
index and middle fingers are all at right angles to each other, then
\list
\o Thumb - X axis - points to the right
\o Index - Y axis - points upward
\o Middle - Z axis - points toward you
\endlist
When you first create a QGLCamera object, by default it has
\code
QGLCamera camera;
qDebug() camera;
--
// displays:
QGLCamera
projection: Perspective -- viewsize: 2 x 2
near-plane: 5 -- far-plane: 1000 -- field-of-view: 0
rotation: 0 -- motion adjust: QVector3D(0, 0, 1) -- aspect adjust: true
eye: QVector3D(0, 0, 10) -- center: QVector3D(0, 0, 0) -- up: QVector3D(0, 1, 0)
\endcode
In the visualisation just mentioned, with positive Z pointing toward you the
camera is sitting at the tip of your middle finger, looking in the same
direction you are, that is down toward the negative end of the Z axis.
\section1 Positioning a Custom Camera.
In this image we have a camera positioned with the following settings:
\code
camera.setNearPlane(2.2f);
camera.setFarPlane(14.0f);
camera.setViewSize(QSizeF(1.257f, 1.257f));
camera.setEye(QVector3D(0, 0, -8.0f));
\endcode
This screen capture from a 3D modelling program shows how this camera is
set up in a scene, where its viewing a cube. The image shows a purple area
which indicates the \i{view frustum}, which you can think of for
now as the area captured by the camera. Actually its more correct to think
of the volume captured by the camera, and imagine that the purple indication
in the image is the shadow cast by this volume.
\image view-frustum-angle-shot.png
The odd value for view size comes from the far plane size which was set at
8 x 8 when creating the diagram: by similar triangles (8 / 14) x 2.2 = 1.257.
There is no API call for setting the size of the far-plane, same as in OpenGL.
So to get this 8 x 8 far plane we had to work out the near plane size.
\image view-frustum-origin.png
This plan view shows the parameters of the camera by reference to a grid.
You can see that the camera is situated - by QGLCamera::setEye() - to a point
8 units back along the negative z axis. The far plane is 14 units away from
the eye - set by QGLCamera::setFarPlane().
Note that here also the camera is sitting down the negative z axis, looking
back toward us - the opposite of the default. Its near plane and far plane
are also much closer. The camera is still pointing to the origin - where a
3D cube is situated, ready to be viewed by the camera.
\section1 The View Frustum
The camera can only see a \bold part of the scene - not all of it. Anything
too far to the camera's right or left is out of the range of its view, and
even though that part of the scene may contain geometry that is sent to the GPU
for processing, those triangles will not be visible on the screen. The GPU
will clip them away - but it still has to do work to do this.
The part of the scene which the camera can see, and which will get displayed
is called the \bold{View Frustum}. A Frustum is a geometric shape, like a
pyramid with its top cut off. It has sloping sides due to the perspective
transformation performed by OpenGL, when simulating the effect of the camera
in the 3D scene.
The cube in this scene has 6 sides, just like the view frustum, so we can
compare the names of these:
\table
\header
\o Cube
\o View Frustum
\row
\o Front
\o Near Plane
\row
\o Back
\o Far Plane
\row
\o Left side
\o Left side
\row
\o Right side
\o Right side
\row
\o Top
\o Top
\row
\o Bottom
\o Bottom
\endtable
Like the cube, the frustum is a solid, and can contain things. You can think of
it as having a set of bounds. Anything inside these bounds is rendered by OpenGL.
\image view-frustum-10-degrees-angle-shot.png
In this image, we see the camera as a black-line wire-frame. The rectangle of the
front of the camera represents the \bold{aperture}.
The triangular purple colored area on the floor corresponds to the camera's eye
and spreads out, as determined by the size of the camera's aperture. If you extended
the lines from the camera's eye to the four corners of its aperture, those lines would
define the view frustum. That view frustum would be the same size as the purple
view triangle, except the part behind the camera aperture is sliced off.
Anything that falls inside the black wire-frame of the camera itself \bold{does not
get displayed}.
Because the scene needs to be projected onto a camera aperture of a non-zero size,
this means there is this area between the eye of the camera and its aperture which
cannot be displayed. Notice that the camera aperture and the Near Plane of the view
frustum are the same thing.
We can also say that anything which falls outside the fiew frustum \bold{does not get
displayed} including anything in front of the near plane. Poor camera positioning
in a scene can often result in this near plane culling which can produce odd visual
effects as geometry appears and disappears when it passes in front and behind of
the near plane.
\section1 View Frustum Culling
Carefully examine the next 3 screenshots, which show the same plan view of the
camera, cube and view - except the cube is rotated arount the eye of the camera
by 10 degrees each time.
\image view-frustum-10-degrees.png
In this image the cube is slightly out of the view frustum. It still must be sent
to the GPU, but some of its pixels will be clipped off during rendering.
\image view-frustum-20-degrees.png
In this image the cube is mostly out of the view frustum. It still must be sent
to the GPU, but most of its pixels will be clipped off during rendering.
\image view-frustum-30-degrees.png
In this image the cube is completey out of the view frustum. Performance could be
improved by not sending the cubes geometry to the GPU at all. This test must
be made on the CPU side, so therefore it must be very cheap to do in order to beat
the cost of just sending it anyway and allowing the GPU to clip away all of it.
The optimisation technique where you detect this case is called \bold{View Frustum
Culling} and it can a simple and valuable performance enhancement. Qt3D implements
view frustum culling via the QGLPainter::isCullable() function.
\section1 Aspect-Ratio Correction
The default camera has a viewport which is 2 x 2 in size, as discussed above. However
when the camera is used to display a scene on a QGLAbstractSurface, for example a
QGLWidgetSurface, the \bold{aspect ratio} of that surface is taken into account in
the View Frustum. Specifically code in the QGLCamera::projectionMatrix() function
takes the aspect ratio and expands the default viewport to match. If the surface is
wider than it is high, the aspect ratio is greater than 1 and the viewport width is
multiplied by the aspect ratio to make it larger. If the surface is higher than it
is wide, the aspect ratio is less than 1 and the viewport height is divided by the
aspect ratio to make it larger. The result is the viewport aspect ratio matches that
of the surface it is to be displayed on.
\code
QGLWidget widget; // creates a 640 x 480 widget by default
QGLPainter painter(&widget) // internally makes a 640 x 480 QGLWidgetSurface
qreal asp = painter.currentSurface()->aspectRatio();
// asp == 1.3333 which is (640 / 480)
QGLCamera camera; // default camera as above
painter.setCamera(&camera); // applies the aspect ratio to the perspective projection
QMatrix4x4 p = painter.projectionMatrix().top();
QMatrix4x4 q;
q.frustum(-asp, asp, -1, 1, camera.nearPlane(), camera.farPlane());
qDebug() << q == p; // prints "true"
\endcode
One result of this is that clipping to the view frustum can only be done with the
knowledge of the surface because more of the scene is visible to the left and right
than would be suggested by the default camera settings.
*/
|