We have learned how to draw simple 2D shapes onscreen in the previous section. However, to fully utilize the OpenGL API, we also need to learn how to use it to render 3D images. In a nutshell, 3D images are simply an illusion created using 2D shapes stacked in a way that makes them look like 3D.
The main ingredient here is the depth value, which determines which shapes should appear in front or at the back of the other shapes. The primitive shape that is positioned behind another surface (with a shallower depth than another shape) will not be rendered (or partially rendered). OpenGL provides a simple way to achieve this, without too much technical hassle.
QTimer
header to your mainwindow.h
:#include <QTimer>
MainWindow
class:private:
QOpenGLContext* context;
QOpenGLFunctions* openGLFunctions;
float rotation;
mainwindow.h
for later use:public slots: void updateAnimation();
glEnable(GL_DEPTH_TEST)
to the initializeGL()
function in mainwindow.cpp
:void MainWindow::initializeGL() { // Enable Z-buffer depth test glEnable(GL_DEPTH_TEST); resizeGL(this->width(), this->height()); }
resizeGL()
function so that it uses the perspective view instead of the orthogonal view:void MainWindow::resizeGL(int w, int h) { // Set the viewport glViewport(0, 0, w, h); qreal aspectRatio = qreal(w) / qreal(h); // Initialize Projection Matrix glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1 * aspectRatio, 1 * aspectRatio, -1, 1, 1, -1); gluPerspective(75, aspectRatio, 0.1, 400000000); // Initialize Modelview Matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }
paintGL()
function as well. First, add GL_DEPTH_BUFFER_BIT
to the glClear()
function, because we also need to clear the depth information for the previous frame before we proceed to render the next frame. Then, remove the code we used in the previous example, which rendered a quad and a triangle on the screen:void MainWindow::paintGL() { // Initialize clear color (cornflower blue) glClearColor(0.39f, 0.58f, 0.93f, 1.f); // Clear color buffer glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBegin(GL_QUADS); glColor3f(1.f, 0.f, 0.f); glVertex2f(-0.8f, -0.8f); glColor3f(1.f, 1.f, 0.f); glVertex2f(0.3f, -0.8f); glColor3f(0.f, 1.f, 0.f); glVertex2f(0.3f, 0.3f); glColor3f(0.f, 0.f, 1.f); glVertex2f(-0.8f, 0.3f); glEnd(); glBegin(GL_TRIANGLES); glColor3f(1.f, 0.f, 0.f); glVertex2f(-0.4f, -0.4f); glColor3f(0.f, 1.f, 0.f); glVertex2f(0.8f, -0.1f); glColor3f(0.f, 0.f, 1.f); glVertex2f(-0.1f, 0.8f); glEnd(); glFlush(); }
glFlush()
, we will add the following code to draw a 3D cube:// Reset modelview matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Transformations glTranslatef(0.0, 0.0, -3.0); glRotatef(rotation, 1.0, 1.0, 1.0); // FRONT glBegin(GL_POLYGON); glColor3f(0.0, 0.0, 0.0); glVertex3f(0.5, -0.5, -0.5); glVertex3f(0.5, 0.5, -0.5); glVertex3f(-0.5, 0.5, -0.5); glVertex3f(-0.5, -0.5, -0.5); glEnd(); // BACK glBegin(GL_POLYGON); glColor3f(0.0, 1.0, 0.0); glVertex3f(0.5, -0.5, 0.5); glVertex3f(0.5, 0.5, 0.5); glVertex3f(-0.5, 0.5, 0.5); glVertex3f(-0.5, -0.5, 0.5); glEnd(); // RIGHT glBegin(GL_POLYGON); glColor3f(1.0, 0.0, 1.0); glVertex3f(0.5, -0.5, -0.5); glVertex3f(0.5, 0.5, -0.5); glVertex3f(0.5, 0.5, 0.5); glVertex3f(0.5, -0.5, 0.5); glEnd(); // LEFT glBegin(GL_POLYGON); glColor3f(1.0, 1.0, 0.0); glVertex3f(-0.5, -0.5, 0.5); glVertex3f(-0.5, 0.5, 0.5); glVertex3f(-0.5, 0.5, -0.5); glVertex3f(-0.5, -0.5, -0.5); glEnd(); // TOP glBegin(GL_POLYGON); glColor3f(0.0, 0.0, 1.0); glVertex3f(0.5, 0.5, 0.5); glVertex3f(0.5, 0.5, -0.5); glVertex3f(-0.5, 0.5, -0.5); glVertex3f(-0.5, 0.5, 0.5); glEnd(); // BOTTOM glBegin(GL_POLYGON); glColor3f(1.0, 0.0, 0.0); glVertex3f(0.5, -0.5, -0.5); glVertex3f(0.5, -0.5, 0.5); glVertex3f(-0.5, -0.5, 0.5); glVertex3f(-0.5, -0.5, -0.5); glEnd();
MainWindow
class, like so:MainWindow::MainWindow(QWidget *parent) { setSurfaceType(QWindow::OpenGLSurface); QSurfaceFormat format; format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setVersion(2, 1); // OpenGL 2.1 setFormat(format); context = new QOpenGLContext; context->setFormat(format); context->create(); context->makeCurrent(this); openGLFunctions = context->functions(); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(updateAnimation())); timer->start(100); rotation = 0; }
updateAnimation()
slot is called by the timer. We also manually call the update()
function to update the screen:void MainWindow::updateAnimation() { rotation += 10; this->update(); }
In any 3D rendering, depth is very important and thus we need to enable the depth testing feature in OpenGL by calling glEnable(GL_DEPTH_TEST)
. When we clear the buffer, we also must specify GL_DEPH_BUFFER_BIT
so that the depth information is also being cleared, in order for the next image to be rendered correctly.
We use gluPerspective()
to set up a perspective projection matrix so that the graphics appear to have depth and distance. The opposite to the perspective view is the orthographic view, which is the default view in OpenGL, and we have used it in our previous example. Orthographic projection is a form of parallel projection where objects appear to be flat and do not suggest depth and distance:
In this example, we used a timer to increase the rotation value by 10 every 100 milliseconds (0.1 second). The rotation value is then applied to the cube by calling glRotatef()
before supplying the vertex data to OpenGL. We also called glTranslatef()
to move the cube slightly to the back so that it's not too close to the camera view.
Remember to call update()
manually so that the screen gets refreshed, otherwise the cube will not be animated.
3.139.83.151