OpenGL3 the wrong texture is displaying

I am trying to add a second sampler2DRect texture to my program. I'm using separate shader files for the second texture, otherwise I'm using the same code that worked for displaying the first one. However, when I try to display the second texture, it's showing the first one instead, and I can't seem to figure out why. I've probably done one of the API calls wrong, or maybe omitted something important, but nothing I try fixes the problem.

No GL errors are being reported, and gDEBugger doesn't show anything suspicious.

I guess the issue could be somewhere else in my code, but here's the section that I'm using to try to draw the second texture (which is almost identical to the code which works to draw the first texture):

Test_World_Pause_Menu::Test_World_Pause_Menu(int resolution_width, int resolution_height)
    : _shaders("shaders/pause_menu.vert", "shaders/pause_menu.frag"),
      _vao_id( new GLuint[1] ),
      _mvp_id( new GLint[1] ),
      _pm_tex_id( new GLuint[1] ),
      _mvp( glm::ortho(0.0f, (GLfloat)resolution_width, 0.0f, (GLfloat)resolution_height, -1.0f, 1.0f) ),
      _pm_vertices_buffer( new GLuint[1] ),
      _pm_vertices( new GLfloat[12] ),
      _pm_colors_buffer( new GLuint[1] ),
      _pm_colors( new GLfloat[16] ),
      _pm_tex_coords_buffer( new GLuint[1] ),
      _pm_tex_coords( new GLfloat[8] ),
      _pm_normals_buffer( new GLuint[1] ),
      _pm_normals( new GLfloat[12] )
{
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);  // set alpha blending mode

    // begin pause menu initialization
    _pm_image.loadFromFile("resources/levels/test_00/pause_menu/pause_menu.png");
    _pm_image.flipVertically();

    unsigned int pm_location_x = 0;
    unsigned int pm_location_y = _pm_image.getSize().y;
    unsigned int pm_width = _pm_image.getSize().x;
    unsigned int pm_height = _pm_image.getSize().y;

    _pm_vertices[0] = pm_location_x; _pm_vertices[1] = pm_location_y; _pm_vertices[2] = 0.0f;
    _pm_vertices[3] = pm_location_x; _pm_vertices[4] = pm_location_y - pm_height; _pm_vertices[5] = 0.0f;
    _pm_vertices[6] = pm_location_x + pm_width; _pm_vertices[7] = pm_location_y - pm_height; _pm_vertices[8] = 0.0f;
    _pm_vertices[9] = pm_location_x + pm_width; _pm_vertices[10] = pm_location_y; _pm_vertices[11] = 0.0f;

    _pm_colors[0] = 1.0f; _pm_colors[1] = 1.0f; _pm_colors[2] = 1.0f; _pm_colors[3] = 0.0f;
    _pm_colors[4] = 1.0f; _pm_colors[5] = 1.0f; _pm_colors[6] = 1.0f; _pm_colors[7] = 0.0f;
    _pm_colors[8] = 1.0f; _pm_colors[9] = 1.0f; _pm_colors[10] = 1.0f; _pm_colors[11] = 0.0f;
    _pm_colors[12] = 1.0f; _pm_colors[13] = 1.0f; _pm_colors[14] = 1.0f; _pm_colors[15] = 0.0f;

    _pm_tex_coords[0] = 0.0f; _pm_tex_coords[1] = pm_height;
    _pm_tex_coords[2] = 0.0f; _pm_tex_coords[3] = 0.0f;
    _pm_tex_coords[4] = pm_width; _pm_tex_coords[5] = 0.0f;
    _pm_tex_coords[6] = pm_width; _pm_tex_coords[7] = pm_height;

    _pm_normals[0] = 0.0f; _pm_normals[1] = 0.0f; _pm_normals[2] = 0.0f;
    _pm_normals[3] = 0.0f; _pm_normals[4] = 0.0f; _pm_normals[5] = 0.0f;
    _pm_normals[6] = 0.0f; _pm_normals[7] = 0.0f; _pm_normals[8] = 0.0f;
    _pm_normals[9] = 0.0f; _pm_normals[10] = 0.0f; _pm_normals[11] = 0.0f;

    glGenVertexArrays(1, &_vao_id[0]); // generate VAO
    glBindVertexArray(_vao_id[0]);     // bind VAO

    // vertices
    glEnableVertexAttribArray((GLuint)0);
    glGenBuffers(1, &_pm_vertices_buffer[0]);
    glBindBuffer(GL_ARRAY_BUFFER, _pm_vertices_buffer[0]);
    glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), _pm_vertices.get(), GL_STATIC_DRAW);
    glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glEnableVertexAttribArray(0);

    // normals (inexplicably needed even though it won't be used in the shaders)
    glEnableVertexAttribArray((GLuint)1);
    glGenBuffers(1, &_pm_normals_buffer[0]);
    glBindBuffer(GL_ARRAY_BUFFER, _pm_normals_buffer[0]);
    glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), _pm_normals.get(), GL_STATIC_DRAW);
    glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glEnableVertexAttribArray(0);

    // texture
    glEnableVertexAttribArray((GLuint)2);
    glGenBuffers(1, &_pm_tex_coords_buffer[0]);
    glBindBuffer(GL_ARRAY_BUFFER, _pm_tex_coords_buffer[0]);
    glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), _pm_tex_coords.get(), GL_STATIC_DRAW);
    glVertexAttribPointer((GLuint)2, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glGenTextures(1, &_pm_tex_id[0]);
    glBindTexture(GL_TEXTURE_RECTANGLE, _pm_tex_id[0]);

    glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, pm_width, pm_height,
            0, GL_RGBA, GL_UNSIGNED_BYTE, _pm_image.getPixelsPtr());

    glEnableVertexAttribArray(0);

    // colors
    glEnableVertexAttribArray((GLuint)3);
    glGenBuffers(1, &_pm_colors_buffer[0]);
    glBindBuffer(GL_ARRAY_BUFFER, _pm_colors_buffer[0]);
    glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), _pm_colors.get(), GL_STATIC_DRAW);
    glVertexAttribPointer((GLuint)3, 4, GL_FLOAT, GL_FALSE, 0, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glEnableVertexAttribArray(0);

    // unbind buffers
    glBindVertexArray(0);

    _mvp_id[0] = glGetUniformLocation(_shaders.id(), "mvpMatrix");
    _pm_tex_id[0] = glGetUniformLocation(_shaders.id(), "pauseMenuTex");
// end pause menu initialization
}

void Test_World_Pause_Menu::display()
{
    glEnable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);

    _shaders.bind();

        glUniformMatrix4fv(_mvp_id[0], 1, GL_FALSE, &_mvp[0][0]);

// begin pause menu rendering
        glUniform1i(_pm_tex_id[0], 0);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, _pm_tex_id[0]);

        glBindVertexArray(_vao_id[0]);     // bind the VAO

        // draw the contents of the bound VAO
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

        glBindVertexArray(0); // unbind the VAO
// end pause menu rendering

    _shaders.unbind();

    glDisable(GL_BLEND);
    glEnable(GL_DEPTH_TEST);

    return;
}

Here are the shaders I'm using for this:

Vertex Shader

#version 150

in vec4 in_Position;
in vec3 in_Normal;
in vec2 in_UV;
in vec4 in_Color;

uniform mat4 mvpMatrix;

smooth out vec3 vVaryingNormal;
smooth out vec2 vVaryingTexCoord;
smooth out vec4 vVaryingColor;

void main(void) 
{
    vVaryingNormal = in_Normal;
    vVaryingTexCoord = in_UV;
    vVaryingColor = in_Color;

    gl_Position = mvpMatrix * in_Position;
}

Fragment Shader

#version 150

smooth in vec3 vVaryingNormal;
smooth in vec2 vVaryingTexCoord;
smooth in vec4 vVaryingColor;

uniform sampler2DRect pauseMenuTex;

out vec4 out_Color;

void main(void)
{ 
    out_Color.a = vVaryingColor.a;
    out_Color += texture(pauseMenuTex, vVaryingTexCoord);
}

My guess is I'm not properly shutting off some texture state in here somewhere, but like I said, nothing I try fixes the problem. I can post code for other parts of the program if necessary.

Edit: There's definitely some wrong use of the API in this C++ code, as well as the code for the first texture, because when I remove the initialization and rendering functions for the first texture, the second one displays just fine. Can someone help me pinpoint my incorrect OpenGL usage (it's probably in the constructor).

Another Edit: Upon further investigation, I've found that whichever of the two objects' constructors is run first is the one whose texture displays during both display routines.

3rd Edit: Something that may be relevant: Unbinding the first texture with glBindTexture(GL_TEXTURE_RECTANGLE, 0) after calling glTexImage2D() in the constructor, causes it to not display at all during either of the two display routines. To me this seems like a problem, and it might be related to my other problem.


Your problem is right here:

glGenTextures(1, &_pm_tex_id[0]);
...
_pm_tex_id[0] = glGetUniformLocation(_shaders.id(), "pauseMenuTex");

The first statement creates a texture object and stores it into _pm_tex_id[0] . The next statement overwrites the texture object with a uniform location. Thus, you lose the texture object. It's no different from calling new to get a pointer, then overwriting that pointer later; you can't get it back.

The reason why your code "works" with in your revised solution is because you kept it bound to the context (in a different slot). You bound it to fill it with data, but you never unbound it. Thus, the context still has that texture object you lost track of.

You need to store the texture name and the sampler uniform location separate. You should use better naming conventions than "id"; uniform locations shouldn't have the same kinds of naming convention as OpenGL object names. That way, you won't confuse them. I would suggest "unif" for uniforms instead of "id".


Other things that aren't wrong, just not necessarily useful.

You frequently call:

glEnableVertexAttribArray(0);

For no reason. You can't make attribute 0 more enabled than the first time you enabled it ;)

You also allocate a lot of arrays of 1 item. That's really unnecessary. If you only want one VAO, just make a GLuint variable. When you need to generate it, just take it's address.

Also, stop allocating so much memory period. If you need an array, use a std::vector (you can get a pointer to the array in the exact same way you currently do: &vec[0] ). This way, you won't have to remember to delete the memory; C++ will do it for you when the object is destroyed. You're already using C++, so you may as well actually use the language.


I see one bind to GL_TEXTURE_RECTANGLE, and a later bind to GL_TEXTURE_2D. Should these be the same?


First, I don't have enough reputation to post a comment and I didn't feel like editing your question for what I'm about to say so apologies in advance if this doesn't work.

Your solution (or workaround) gives each texture its own texture slot which shouldn't be necessary unless you want to pass multiple texture to the same shader, so there's definitely a conflict going on.

You can remove glUniform1i(_pm_tex_id[0], x); from the render procedure and put it in the constructor, as it only has to be set once after your shader program is (re)created.

After Tim spotted the different binding calls, did you change this? Form the OpenGl docs:

Once created, a named texture may be re-bound to its same original target as often as needed.

I would definitely change them so they are both identical (I would go for GL_TEXTURE_2D).

Are you doing any other GL_* calls related to textures elsewhere? Because this smells like a stale parameter still hanging around after the first object is drawn. Hopefully this is somewhat helpful.

链接地址: http://www.djcxy.com/p/6550.html

上一篇: Linux中的3个头文件(C / C ++)(带有nvidia驱动程序)

下一篇: OpenGL3显示错误的纹理