2
votes

I'm trying to render my first texture with Qt & OpenGL.

This is the code.

initializeGL function

void OpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    glEnable(GL_DEBUG_OUTPUT);
    glDebugMessageCallback(debugCallback, nullptr);

    program = genProgram("../06_HelloTexture/vert.glsl", "../06_HelloTexture/frag.glsl");
    glUseProgram(program);

    glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
    glEnable(GL_DEPTH_TEST);

    // vertices is std::vector<QVector3D>
    vertices = {
        {-0.5f, -0.5f, 0.0f}, {0.0f, 0.0f, 0.0f},
        {-0.5f, +0.5f, 0.0f}, {0.0f, 1.0f, 0.0f},
        {+0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f},
        {+0.5f, +0.5f, 0.0f}, {1.0f, 1.0f, 0.0f},
    };

    // indices is std::vector<GLuint>
    indices = {
        0, 1, 2,
        1, 2, 3,
    };

    GLuint VAO, VBO, EBO;

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(QVector3D), &vertices[0], GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(GLuint), &indices[0], GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 2*sizeof(QVector3D), static_cast<void*>(nullptr));
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 2*sizeof(QVector3D), (void*)sizeof(QVector3D));
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    QImage image("../lightbulb-solid.svg");
    // QImage image("../test.png");
    qDebug() << image;
    // this outputs
    // QImage(QSize(352, 512),format=6,depth=32,devicePixelRatio=1,bytesPerLine=1408,sizeInBytes=720896)

    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
    // glGenerateMipmap(GL_TEXTURE_2D);

    lightbulbSolidLocation = glGetUniformLocation(program, "lightbulbSolid");
    glUniform1i(lightbulbSolidLocation, 0);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
}

paintGL function

void OpenGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indices.size()), GL_UNSIGNED_INT, nullptr);
}

vertex shader

#version 450 core
layout (location = 0) in vec3 vertPosition;
layout (location = 1) in vec3 vertTexCoord;

out vec3 fragTexCoord;

void main()
{
    gl_Position = vec4(vertPosition, 1.0);
    fragTexCoord = vertTexCoord;
}

fragment shader

#version 450 core
in vec3 fragTexCoord;
out vec4 pixelColor;

uniform sampler2D lightbulbSolid;

void main()
{
    // pixelColor = vec4(fragColor, 1.0f);
    // pixelColor = texture(lightbulbSolid, vec2(fragTexCoord.x, fragTexCoord.y));

    vec4 temp = texture(lightbulbSolid, vec2(fragTexCoord.x, fragTexCoord.y));

    if (!all(equal(temp.xyz, vec3(0.0f))))
    {
        pixelColor = temp;
    }
    else
    {
        pixelColor = vec4(fragTexCoord, 1.0f);
    }
}

And this is the result

enter image description here

As you can see, all the colors generated by the texture function in the fragment shader are black, so the if statement choose the texture coordinate as color rather than the real texture color.

I tried to catch some error, but the callback function didn't find something wrong in the code. I also tried with another image (of PNG format rather than SVG), but the result is the same.

1

1 Answers

1
votes

If you want to load a PNG file, then you can load it to a QImage directly,

QImage image("../test.png");

but if you want to render SVG file, then you have to use the QSvgRenderer and QPainter to paint the content to an QImage. You have to choose the format, the resolution and the background color of the target bitmap:

e.g.:

#include <Qtsvg/QSvgRenderer>

QSvgRenderer renderer(QString("../lightbulb-solid.svg"));

QImage image(512, 512, QImage::Format_RGBA8888);  // 512x512 RGBA
image.fill(0x00ffffff);                           // white background

QPainter painter(&image);
renderer.render(&painter);

const uchar *image_bits = image.constBits();
int          width      = image.width();
int          height     = image.height();

Note, you have to link qt5svgd.lib (debug) respectively qt5svg.lib (release).


The default minifying function parameter (GL_TEXTURE_MIN_FILTER) is GL_NEAREST_MIPMAP_LINEAR. See glTexParameteri

This means either you have to change the GL_TEXTURE_MIN_FILTER parameter to GL_LINEAR

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
             GL_RGBA, GL_UNSIGNED_BYTE, image_bits);

or you have to create mipmaps

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
             GL_RGBA, GL_UNSIGNED_BYTE, image_bits);

glGenerateMipmap(GL_TEXTURE_2D);

In the initializeGL method the texture is bound to texture unit 0, but it is not guaranteed, that this is still the current state in the paintGL method. You have to bind the texture in the paintGL method:

void OpenGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indices.size()), GL_UNSIGNED_INT, nullptr);
}