简体   繁体   English

在Mac OSX el capitan上使用Qt 5.7在OpenGL 2.1中创建纹理矩形

[英]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. 我正在开发一个项目,我需要在Qt 5.7中使用OpenGL 2.1创建一个纹理矩形。 I have written this minimal example using Qt's QOpenGLWidget: 我用Qt的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. 我几乎看到了与StackOverflow上的glTexImage2D相关的每个问题,我似乎无法找到答案。 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. 我开始认为这是一个特定于Qt / Mac的问题,但不幸的是,我没有资源在另一个操作系统上尝试它,所以我不确定。 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: 我知道一个事实是位置向量和texCoords正在被正确发送,因为如果我将片段着色器更改为:

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. 没有错误来自glGetShaderiv,glGetProgramiv或glGetError。 Any Ideas? 有任何想法吗?

UPDATE Tried it with OpenGL 3.3, still didn't work. 更新使用OpenGL 3.3尝试它仍然无法正常工作。 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). 我尝试了上面尝试的相同测试,让texCoords工作,并发送顶点(这次是三角形,因为GL_QUADS已被弃用,片段着色器中的texture()而不是texture2D,因为纹理已被弃用) 。 So its not an issue with the version of OpenGL I am using. 所以它不是我正在使用的OpenGL版本的问题。

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). 更新所以第二个想法,它没有多大意义,这将是一个操作系统问题,因为我知道Qt使用Apple的NeXTStep api引擎盖,并且考虑到这一点,我尝试了一个纹理OpenGL对象的例子来自Apple的网站(用Objective-C和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. 我仔细查看了源代码,并试图对它们的纹理使用相同的opengl调用(尽管它们大部分都是相同的),但它似乎仍然没有用。 So it is either a problem with Qt, or something really stupid that I am missing/misunderstanding in my code. 所以它是Qt的一个问题,或者是我在代码中遗漏/误解的一些非常愚蠢的东西。

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. 由于您使用GLuint作为类型,并将匹配的GL_UNSIGNED_INT用作glTexImage2D()类型参数,因此这些值将被解释为32位无符号值。 And 255 is really a very small 32-bit value, which explains why you're getting black. 255实际上是一个非常小的32位值,这解释了为什么你变黑了。

In other words, glTexImage2D() interprets the values you pass in as normalized fixed-point values, based on the type . 换句话说, glTexImage2D()根据类型将传入的值解释为规范化的定点值。 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. 要获得[0.0,1.0]范围内的实际归一化值,它将在32位值的情况下将传入的值除以2 ^ 32-1。 So the resulting value in your case will be 255/(2^32-1) = 0.00000005937. 因此,您的案例中的结果值将为255 /(2 ^ 32-1)= 0.00000005937。 Which is close enough to 0 to be black. 这足够接近0为黑色。

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. 事实证明,你必须在glDrawArrays之前调用glBindTexture才能工作。 Something happens between initializeGL and paintGL with Qt that unbinds the texture and binds a different one. 在initializeGL和paintGL与Qt之间发生了一些事情,它解除了纹理并绑定了另一个纹理。 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). 对于那些感兴趣的人,我通过使用glGetTexImage进行调试发现了这一点,这将是一个非常有用的调试功能(即使它显然很慢)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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