C++ - Qt Framework - Using OpenGL texture with index array

 Playing with colors can be fun but what about textures?

What about something really realistic?

In this OpenGL tutorial for Qt we are going to apply a texture on our dear triangles.

Yes in OpenGL there are only triangles, no other kinds of shapes available but with some variations though (the famous primitives).

First of all

We are going to see how to create a struct named StructVertex to handle a vertex and its texture in one object.

So as we need 4 vertex (4 borders) to display the texture image we are going to define 4 objects in a StructVertex array.

Each object is delimited by square brackets (in the array) to have a clear vision of where an object starts and where it finishes.

One important thing is that in this example, we are going to use 2 arrays:

  1. One for the StructVertex
  2. One for the indexes.

It really necessary to specifiy the type of this indexes array in order to let OpenGL and Qt understand that this array is a special one, used only for indexes.

We will specify it directly in our constructor to set it automatically.

So why do we need that kind of array?

Because it'll save us a lot of space memory.

Indeed with this mechanism we are going to select each vertex we want to draw.

So if we need to use again a vertex already allocated, we don’t need to allocate it again.

We have just to say we want to use the index of this vertex in the first array with StructVertex.

Another nice trick is to use the glDrawElements() function that renders primitives from an array.

This function takes 4 parameters:

  1. The mode parameter of our example will be a GL_TRIANGLE_STRIP.
  2. The count, specifying the number of elements to be rendered.
  3. The type of our element in the indexes array (we chose GL_UNSIGNED_SHORT).
  4. The indices specifying the byte offset of the array. As we already specified the indexes array as "index" type then OpenGL already knows this information, so in our case we have to pass "0" (zero) as parameter because OpenGL is going to use it (there is only one array index at a time).

For example we are going to specify that we need 4 vertex from our array1 linked with the 4 indexes of array2 and OpenGL will draw for us not 1 triangle but 2.

Both triangles will donate the illusion of a square on which to apply our texture.

Not so obvious at first, don’t worry if this concept is a bit vague.

Well almost everything is settled, it’s time to code a bit.

 

Code

The main program is taken from a previous tutorial.

So just replace the following files with the code below:

  • BadprogDisplay.cpp
  • BadprogDisplay.h
  • badprog.qrc
  • The both shader files (vertex and fragment).

BadprogDisplay.cpp

#include "badprogDisplay.h"

#include <QtGui/QOpenGLShaderProgram>
#include <QOpenGLTexture>

// BadproG.com

// ============================================================================
//
// ============================================================================
BadprogDisplay::BadprogDisplay()
    : _program(nullptr),
      _vbo1_index(QOpenGLBuffer::IndexBuffer)
{
}

// ============================================================================
//
// ============================================================================
void BadprogDisplay::initTextures() {
    // Loading the image
    _texture = new QOpenGLTexture(QImage(":/your_image").mirrored());
}

// ============================================================================
//
// ============================================================================
void BadprogDisplay::initialize() {
    // Program.
    _program = new QOpenGLShaderProgram(this);
    _program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":vertex");
    _program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":fragment");

    // Linking.
    _program->link();
    _program->bind();

    // Texture.
    initTextures();

    // Attributes.
    _attribute1_position    = _program->attributeLocation("layoutPosition");
    _attribute1_texture     = _program->attributeLocation("layoutTexture");

    // Uniforms.
    _uniformForShader1_matrix    = _program->uniformLocation("shaderMatrix");

    // Enabling the point size modification.
    glEnable(GL_PROGRAM_POINT_SIZE);

    // Array of vertex
    StructVertex arrayVertex[] = {
        // First triangle                  // Texture
        {QVector3D(-1.0f, -1.0f,  1.0f), QVector2D(0.0f0.0f)},
        {QVector3D1.0f, -1.0f,  1.0f), QVector2D(1.0f0.0f)},
        {QVector3D(-1.0f,  1.0f,  1.0f), QVector2D(0.0f1.0f)},
        {QVector3D1.0f,  1.0f,  1.0f), QVector2D(1.0f1.0f)},

    };

    // Array index
    GLushort arrayIndex1[] = {
        0123
    };
    // ------------------------------------------
    // ------------------------------------------
    // ------------------------------------------
    // VAO 1
    // ------------------------------------------
    // ------------------------------------------
    // ------------------------------------------
    _vao1.create();
    _vao1.bind();

    // ------------------------------------------
    // ------------------------------------------
    // VBO 1
    // ------------------------------------------
    // ------------------------------------------

    // ------------------------------------------
    // Buffer 1
    // ------------------------------------------
    _vbo1_position.create();
    _vbo1_position.bind();
    _vbo1_position.allocate(arrayVertex, sizeof (StructVertex) * 4);

    // ------------------------------------------
    // Buffer 2
    // ------------------------------------------
    _vbo1_index.create();
    _vbo1_index.bind();
    _vbo1_index.allocate(arrayIndex1, sizeof (GLushort) * 4);
}

// ============================================================================
//
// ============================================================================
void BadprogDisplay::render() {
    // Viewport
    glViewport(00width(), height());

    // Clear whole buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(0.30.30.31);

    //
    _texture->bind();

    //
    _program->bind();
    _vao1.bind();

    //
    int bpOffset  = 0;
    int bpTuple   = 3;
    int bpStride  = sizeof (StructVertex);

    //
    _program->enableAttributeArray(_attribute1_position);
    _program->setAttributeBuffer(_attribute1_position, GL_FLOAT, bpOffset, bpTuple, bpStride);

    //
    bpOffset    += sizeof (QVector3D);
    bpTuple     = 2;

    //
    _program->enableAttributeArray(_attribute1_texture);
    _program->setAttributeBuffer(_attribute1_texture, GL_FLOAT, bpOffset, bpTuple, bpStride);

    //
    QMatrix4x4 matrixFromCpp1;
    matrixFromCpp1.perspective(100.0f1.0f0.1f100.0f);
    matrixFromCpp1.translate(0.0f0.0f, -2.0f);
    
    //
    _program->setUniformValue(_uniformForShader1_matrix, matrixFromCpp1);

    //
    glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0);

    //
    _vao1.release();

    //
    _program->release();
}

 

BadprogDisplay.h

#ifndef BADPROG_DISPLAY_H
#define BADPROG_DISPLAY_H

// BadproG.com

#include "badprogOpenGLWindow.h"

class QOpenGLShaderProgram;
class QOpenGLTexture;

#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>

#include <QVector2D>
#include <QVector3D>

struct StructVertex
{
    QVector3D elementPosition;
    QVector2D elementTexture;
};

class BadprogDisplay : public BadprogOpenGLWindow
{
public:
    //
    BadprogDisplay();

    //
    void initialize() override;
    void render() override;
    void initTextures();

private:
    //
    GLuint _attribute1_position;
    GLuint _attribute1_texture;
    GLuint _uniformForShader1_matrix;

    //
    QOpenGLShaderProgram *_program;

    //
    QOpenGLVertexArrayObject _vao1;
    QOpenGLBuffer _vbo1_position;
    QOpenGLBuffer _vbo1_index;

    //
    QOpenGLTexture *_texture;
};

#endif // !BADPROG_DISPLAY_H

 

badprog.qrc

<RCC>

    <qresource prefix="/">

        <file alias="fragment">shader/shader_fragment.glsl</file>

        <file alias="vertex">shader/shader_vertex.glsl</file>

        <file alias="your_image">your_image.png</file>

    </qresource>

</RCC>
 

shader/shader_vertex.glsl

#version 330 core

// BadproG.com

// layout
layout (location = 0in vec3 layoutPosition;
layout (location = 2in vec3 layoutTexture;

// texture
out vec2 shaderTexture;

// uniform
uniform mat4 shaderMatrix;

// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
void main() {
    //
    gl_PointSize = 40;
    gl_Position = shaderMatrix * vec4(layoutPosition, 1.0);
    shaderTexture = vec2(layoutTexture.xlayoutTexture.y);
}
 

shader/shader_vertex.glsl

#version 330 core

// BadproG.com

// out
out vec4 FragColor;

// texture
in vec2 shaderTexture;

// uniform texture
uniform sampler2D uniformTexture;

// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
void main() {
    //
    FragColor = texture2D(uniformTexture, shaderTexture);
}
 

Conclusion

With this simple exercice we are now ready to apply texture on every shape of our choice.

Saving memory space with the index mechanism is really interesting and will help us to create nice 3D sofware.

Once again, good job, you did it. cool

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.