Qt5 OpenGL Texture Sampling

4.3k views Asked by At

I'm trying to render a QImage using OpenGL wrapper classes of Qt5 and shader programs. I have the following shaders and a 3.3 core context. I'm also using a VAO for the attributes. However, I keep getting a blank red frame (red is the background clear color that I set). I'm not sure if it is a problem with the MVP matrices or something else. Using a fragment shader which sets the output color to a certain fixed color (black) still resulted in a red frame. I'm totally lost here.

EDIT-1: I also noticed that attempting to get the location of texRGB uniform from the QOpenGLShaderProgram results in -1. But I'm not sure if that has anything to do with the problem I'm having. Uniforms defined in the vertex shader for the MVP matrices have the locations 0 and 1.

Vertex Shader

#version 330

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec2 inTexCoord;

out vec2 vTexCoord;

uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;


void main(void)
{
    gl_Position = projectionMatrix * modelViewMatrix * vec4(inPosition, 1.0);

    // pass the input texture coordinates to fragment shader
    vTexCoord = inTexCoord;    
}

Fragment Shader

#version 330

uniform sampler2DRect texRGB;

in vec2 vTexCoord;

out vec4 fColor;

void main(void)
{
    vec3 rgb = texture2DRect(texRGB, vTexCoord.st).rgb;
    fColor = vec4(rgb, 0.0);
}

OGLWindow.h

#include <QOpenGLWindow>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLTexture>

#include <QDebug>
#include <QString>

class OGLWindow : public QOpenGLWindow, protected QOpenGLFunctions
{
public:
    OGLWindow();
    ~OGLWindow();

    // OpenGL Events
    void initializeGL();
    void resizeGL(int width, int height);
    void paintGL();

    // a method for cleanup
    void teardownGL();

private:
    bool isInitialized;

    // OpenGL state information
    QOpenGLBuffer               m_vbo_position;
    QOpenGLBuffer               m_vbo_index;
    QOpenGLBuffer               m_vbo_tex_coord;
    QOpenGLVertexArrayObject    m_object;
    QOpenGLShaderProgram*       m_program;

    QImage                      m_image;
    QOpenGLTexture*             m_texture;

    QMatrix4x4                  m_projection_matrix;
    QMatrix4x4                  m_model_view_matrix;

};

OGLWindow.cpp

#include "OGLWindow.h"

// vertex data
static const QVector3D vertextData[] = {
                           QVector3D(-1.0f, -1.0f,  0.0f),
                           QVector3D( 1.0f, -1.0f,  0.0f),
                           QVector3D( 1.0f,  1.0f,  0.0f),
                           QVector3D(-1.0f,  1.0f,  0.0f)
};

// indices
static const GLushort indices[] = {
                           0,  1,  2,
                           0,  2,  3
};

OGLWindow::OGLWindow() :
    m_vbo_position      (QOpenGLBuffer::VertexBuffer),
    m_vbo_tex_coord     (QOpenGLBuffer::VertexBuffer),
    m_vbo_index         (QOpenGLBuffer::IndexBuffer),
    m_program           (nullptr),
    m_texture           (nullptr),
    isInitialized       (false)
{
}

OGLWindow::~OGLWindow()
{
    makeCurrent();
    teardownGL();
}

void OGLWindow::initializeGL()
{
    qDebug() << "initializeGL()";

    initializeOpenGLFunctions();
    isInitialized = true;

    QColor backgroundColor(Qt::red);
    glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), 1.0f);

    // load texture image
    m_image = QImage(":/images/cube.png");

    m_texture = new QOpenGLTexture(QOpenGLTexture::TargetRectangle);

    // set bilinear filtering mode for texture magnification and minification
    m_texture->setMinificationFilter(QOpenGLTexture::Nearest);
    m_texture->setMagnificationFilter(QOpenGLTexture::Nearest);

    // set the wrap mode
    m_texture->setWrapMode(QOpenGLTexture::ClampToEdge);

    m_texture->setData(m_image.mirrored(), QOpenGLTexture::MipMapGeneration::DontGenerateMipMaps);

    int imgWidth = m_image.width();
    int imgHeight = m_image.height();

    m_projection_matrix.setToIdentity();
    m_projection_matrix.ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
//    m_projection_matrix.ortho(0.0, (float) width(), (float) height(), 0.0f, -1.0f, 1.0f);

    m_model_view_matrix.setToIdentity();

    glViewport(0, 0, width(), height());

    m_program = new QOpenGLShaderProgram();
    m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vshader.glsl");
    m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fshader.glsl");
    m_program->link();
    m_program->bind();

    // texture coordinates
    static const QVector2D textureData[] = {
                               QVector2D(0.0f,              0.0f),
                               QVector2D((float) imgWidth,  0.0f),
                               QVector2D((float) imgWidth,  (float) imgHeight),
                               QVector2D(0.0f,              (float) imgHeight)
    };

    // create Vertex Array Object (VAO)
    m_object.create();
    m_object.bind();

    // create position VBO
    m_vbo_position.create();
    m_vbo_position.bind();
    m_vbo_position.setUsagePattern(QOpenGLBuffer::StaticDraw);
    m_vbo_position.allocate(vertextData, 4 * sizeof(QVector3D));

    // create texture coordinates VBO
    m_vbo_tex_coord.create();
    m_vbo_tex_coord.bind();
    m_vbo_tex_coord.setUsagePattern(QOpenGLBuffer::StaticDraw);
    m_vbo_tex_coord.allocate(textureData, 4 * sizeof(QVector2D));

    // create the index buffer
    m_vbo_index.create();
    m_vbo_index.bind();
    m_vbo_index.setUsagePattern(QOpenGLBuffer::StaticDraw);
    m_vbo_index.allocate(indices, 6 * sizeof(GLushort));

    // enable the two attributes that we have and set their buffers
    m_program->enableAttributeArray(0);
    m_program->enableAttributeArray(1);

    m_program->setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D));
    m_program->setAttributeBuffer(1, GL_FLOAT, 0, 2, sizeof(QVector2D));

    // Set modelview-projection matrix
    m_program->setUniformValue("projectionMatrix", m_projection_matrix);
    m_program->setUniformValue("modelViewMatrix", m_model_view_matrix);

    // use texture unit 0 which contains our frame
    m_program->setUniformValue("texRGB", 0);

    // release (unbind) all
    m_object.release();
    m_vbo_position.release();
    m_vbo_tex_coord.release();
    m_vbo_index.release();
    m_program->release();    
}

void OGLWindow::resizeGL(int width, int height)
{
    qDebug() << "resizeGL(): width =" << width << ", height=" << height;
    if (isInitialized) {

        // avoid division by zero
        if (height == 0) {
            height = 1;
        }

        m_projection_matrix.setToIdentity();
        m_projection_matrix.perspective(60.0, (float) width / (float) height, -1, 1);

        glViewport(0, 0, width, height);
    }

}

void OGLWindow::paintGL()
{
    // clear
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // render using our shader
    m_program->bind();
    {
        m_texture->bind();

        m_object.bind();
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0) );
        m_object.release();
    }
    m_program->release();
}

void OGLWindow::teardownGL()
{
    // actually destroy our OpenGL information
    m_object.destroy();
    m_vbo_position.destroy();
    m_vbo_color.destroy();
    delete m_program;    
}

EDIT-2: I'm creating the context as follows:

QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setVersion(3,3);
1

There are 1 answers

5
Reto Koradi On

This line in your fragment shader code is invalid:

vec3 rgb = texture2DRect(texRGB, vTexCoord.st).rgb;

texture2DRect() is not a built-in function.

Since you're using the GLSL 3.30 core profile (core is the default for the version unless compatibility is specified), you should be using the overloaded texture() function, which replaces the older type specific functions like texture2D() in the core profile.

Functions like texture2D() are still supported in GLSL 3.30 core unless a forward compatible core profile context is used. So depending on how the context is created, you can still use those functions.

However, sampler2DRect was only added as a sampler type in GLSL 1.40 as part of adding rectangular textures to the standard in OpenGL 3.1. At the time, the legacy sampling functions were already marked as deprecated, and only the new texture() function was defined for rectangular textures. This means that texture2DRect() does not exist in any GLSL version.

The correct call is:

vec3 rgb = texture(texRGB, vTexCoord.st).rgb;

Another part of your code that can prevent it from rendering anything is this projection matrix:

m_projection_matrix.perspective(60.0, (float) width / (float) height, -1, 1);

The near and far planes for a standard projection matrix both need to be positive. This call will set up a projection transformation with a "camera" on the origin, looking down the negative z-axis. The near and far values are distances from the origin. A valid call could look like this:

m_projection_matrix.perspective(60.0, (float) width / (float) height, 1.0f, 10.0f);

You will then also need to set the model matrix to transform the coordinates of the object into this range on the negative z-axis. You could for example apply a translation by (0.0f, 0.0f, -5.0f).

Or, if you just want to see something, the quad should also become visible if you simply use the identity matrix for the projection.