简体   繁体   中英

Creating a textured Rectangle in OpenGL 2.1 with Qt 5.7 on Mac OSX el capitan

I am working on a project where I need to create a textured Rectangle in Qt 5.7 with OpenGL 2.1. I have written this minimal example using Qt's QOpenGLWidget:

#include <QDebug>
#include <string.h>
#include "openglwidget.h"

OpenGLWidget::OpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{

}

void OpenGLWidget::initializeGL() {
    initializeOpenGLFunctions();
    glClearColor(0, 0, 0, 1);

    /** Vertex Shader **/
    const char* vertexShader = "\
            attribute vec2 pos;\
            attribute vec2 texCoord;\
            varying vec2 TexCoord;\
            void main() {\
                gl_Position.xy = pos.xy;\
                gl_Position.zw = vec2(0, 1);\
                TexCoord = texCoord;\
            }\
                               ";
    GLint vertexSourceLength = strlen(vertexShader);
    GLuint vertexShaderObject = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShaderObject, 1, &vertexShader, &vertexSourceLength);
    glCompileShader(vertexShaderObject);
    GLint result;
    glGetShaderiv(vertexShaderObject, GL_COMPILE_STATUS, &result);
    if (result != GL_TRUE) {
        qDebug() << "Vertex Shader not compiled";
    }

    /** Fragment Shader **/
    const char* fragmentShader = "\
            uniform sampler2D texture;\
            varying vec2 TexCoord;\
            void main() {\
                vec4 texColor = texture2D(texture, TexCoord);\
                gl_FragColor = texColor;\
            }\
                                 ";
    GLint fragmentSourceLength = strlen(vertexShader);
    GLuint fragmentShaderObject = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShaderObject, 1, &fragmentShader, &fragmentSourceLength);
    glCompileShader(fragmentShaderObject);
    glGetShaderiv(fragmentShaderObject, GL_COMPILE_STATUS, &result);
    if (result != GL_TRUE) {
        qDebug() << "Fragment Shader not compiled";
    }

    /** Program **/
    GLuint program = glCreateProgram();
    glAttachShader(program, vertexShaderObject);
    glAttachShader(program, fragmentShaderObject);
    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &result);
    if (result != GL_TRUE) {
        qDebug() << "Program not linked.";
    }
    glUseProgram(program);

    /** Upload Buffer Data **/
    GLuint VBO;
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    GLfloat vertexData[] = {
        //Position      //TexCoord
        -0.5, -0.5,     0.0, 0.0,
         0.5, -0.5,     1.0, 0.0,
         0.5,  0.5,     1.0, 1.0,
        -0.5,  0.5,     0.0, 1.0
    };
    glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(GLfloat),vertexData, GL_STATIC_DRAW);

    /** Set Position Pointers **/

    GLint pos = glGetAttribLocation(program, "pos");
    if (pos == -1) {
        qDebug() << "Position attribute not found.";
    }
    glVertexAttribPointer(pos, 2, GL_FLOAT, GL_TRUE, 4 * sizeof(GLfloat), 0);
    glEnableVertexAttribArray(pos);

    /** Set TexCoord Pointers **/

    GLint texCoord = glGetAttribLocation(program, "texCoord");
    if (texCoord == -1) {
        qDebug() << "texCoord attribute not found.";
    }
    glVertexAttribPointer(texCoord, 2, GL_FLOAT, GL_TRUE, 4 * sizeof(GLfloat), (void *) (2 * sizeof(GLfloat)));
    glEnableVertexAttribArray(texCoord);

    /** Set Texture **/
    int width = 4;
    int height = 4;
    GLubyte pixels[] = {
        255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,
        255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,
        255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,
        255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255
    };
    glActiveTexture(GL_TEXTURE0);
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    glGenerateMipmap(GL_TEXTURE_2D);
    GLint textureLoc = glGetUniformLocation(program, "texture");
    if (textureLoc == -1) {
        qDebug() << "texture uniform not found.";
    }
    glUniform1i(textureLoc, 0);

    GLuint err = GL_NO_ERROR;
    while ((err = glGetError()) != GL_NO_ERROR) {
        qDebug() << "Error: " << (void *) err;
    }
}

void OpenGLWidget::resizeGL(int w, int h) {

}

void OpenGLWidget::paintGL() {
    glClear(GL_COLOR_BUFFER_BIT);
    glDrawArrays(GL_QUADS, 0, 4);
    GLuint err = GL_NO_ERROR;
    while ((err = glGetError()) != GL_NO_ERROR) {
        qDebug() << "Error: " << (void *) err;
    }
}

I am expecting this code to generate a red rectangle, instead, I am getting a black screen. I have looked at pretty much every question related to glTexImage2D on StackOverflow, and I could not seem to find an answer. I am beginning to think that it is a Qt/Mac specific problem, but unfortunately, I do not have the resources to try it on another Operating System, so I am not sure. It could just be something obvious that I am missing.

I know for a fact that the position vectors and texCoords are being sent correctly because if I change the fragment shader to this:

uniform sampler2D texture;
varying vec2 TexCoord;
void main() {
    vec4 texColor = texture2D(texture, TexCoord);
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    gl_FragColor.xy *= TexCoord;
}

I get

这个

which is what I would expect with that fragment shader. No errors are coming out of glGetShaderiv, glGetProgramiv, or glGetError. Any Ideas?

UPDATE Tried it with OpenGL 3.3, still didn't work. I tried the same test that I tried above, got the texCoords to work, and the vertices to send (with a triangle this time since GL_QUADS was deprecated, and with texture() in the fragment shader instead of texture2D, since texture was deprecated). So its not an issue with the version of OpenGL I am using.

UPDATE So on second thought, It doesn't make much sense that it would be an operating system problem, because I know Qt uses Apple's NeXTStep api under the hood, and barring that in mind, I tried an example of a textured OpenGL object from Apple's website (written in Objective-C and Cocoa). That worked. I looked through the source code and tried to use the same opengl calls for their textures (although they were mostly the same), and it still didn't seem to work. So it is either a problem with Qt, or something really stupid that I am missing/misunderstanding in my code.

The problem is with the data type you use for your texture data:

GLuint pixels[] = {
    255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,
    255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,
    255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,
    255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255,     255, 0, 0, 255
};

Since you use GLuint for the type, and the matching GL_UNSIGNED_INT as the type argument to glTexImage2D() , these values will be interpreted as 32-bit unsigned values. And 255 is really a very small 32-bit value, which explains why you're getting black.

In other words, glTexImage2D() interprets the values you pass in as normalized fixed-point values, based on the type . To obtain the actual normalized value in a [0.0, 1.0] range, it will divide the value you pass in by 2^32-1 in the case of 32-bit values. So the resulting value in your case will be 255/(2^32-1) = 0.00000005937. Which is close enough to 0 to be black.

To fix this, the easiest way is that you use bytes for the type:

GLubyte pixels[] = {
...
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

I found the answer to my question! As it turns out, you have to call glBindTexture right before glDrawArrays for it to work. Something happens between initializeGL and paintGL with Qt that unbinds the texture and binds a different one. For those who are interested, I found this out by debugging using glGetTexImage, which will be a very useful function for debugging (even though it is apparently slow).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM