3

So I'm currently making a game using SDL2 with OpenGL (glew), and using SOIL to load images, and I'm using modern opengl techniques with vertex array objects and shaders and whatnot. I'm currently trying to just render just a texture to the window, but I can't seem to do it. I've looked up many tutorials and solutions, but I can't seem to understand how I'm supposed to do it properly. I'm unsure of whether it's the shader that's the problem, or my code itself. I'll post all the necessary code below, and any more is needed I'd be happy to supply it. Any answers and solutions are welcome. For future context, I have a class I use to store data for VAOs for convenience purposes. Code:

Here's the code to load the texture:

 void PXSprite::loadSprite() {

     glGenTextures(1, &textureID);
     glBindTexture(GL_TEXTURE_2D, textureID);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glGenerateMipmap(GL_TEXTURE_2D);

     int imagew, imageh;

     //The path is a class variable, and I haven't received any errors from this function, so I can only assume it's getting the texture file correctly.
     unsigned char* image = SOIL_load_image(path.c_str(), &imagew, &imageh, 0, SOIL_LOAD_RGB);
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imagew, imageh, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
     SOIL_free_image_data(image);
     glBindTexture(GL_TEXTURE_2D, 0);

     //Don't worry about this code. It's just to keep the buffer object data.
     //It works properly when rendering polygons.
     spriteVAO.clear();
     spriteVAO.addColor(PXColor::WHITE());
     spriteVAO.addColor(PXColor::WHITE());
     spriteVAO.addColor(PXColor::WHITE());
     spriteVAO.addColor(PXColor::WHITE());

     spriteVAO.addTextureCoordinate(0, 0);
     spriteVAO.addTextureCoordinate(1, 0);
     spriteVAO.addTextureCoordinate(1, 1);
     spriteVAO.addTextureCoordinate(0, 1);

     glGenVertexArrays(1, &spriteVAO.vaoID);
     glGenBuffers(1, &spriteVAO.posVBOid);
     glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.posVBOid);
     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*12, nullptr, GL_STATIC_DRAW);
     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
     glGenBuffers(1, &spriteVAO.colVBOid);
     glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.colVBOid);
     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, &spriteVAO.colors[0], GL_STATIC_DRAW);
     glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
     glGenBuffers(1, &spriteVAO.texVBOid);
     glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.texVBOid);
     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, &spriteVAO.texCoords[0], GL_STATIC_DRAW);
     glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);

     glBindTexture(GL_TEXTURE_2D, 0);
}

And Here's my code for rendering the texture:

void PXSprite::render(int x, int y) {
    spriteVAO.clear(PXVertexArrayObject::positionAttributeIndex);
    spriteVAO.addPosition(x, y);
    spriteVAO.addPosition(x+width, y);
    spriteVAO.addPosition(x, y+height);
    spriteVAO.addPosition(x+width, y+height);

    glBindTexture(GL_TEXTURE_2D, textureID);

    glBindVertexArray(spriteVAO.vaoID);
    glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.posVBOid);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat)*12, &spriteVAO.positions[0]);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    glBindTexture(GL_TEXTURE_2D, 0);
}

Here's my vertex shader:

#version 330 core

in vec3 in_Position;
in vec4 in_Color;
in vec2 in_TexCoord;

out vec4 outPosition;
out vec4 outColor;
out vec2 outTexCoord;

void main() {
gl_Position = vec4(in_Position.x, in_Position.y*-1.0, in_Position.z, 1.0);

outTexCoord = in_TexCoord;
outColor = in_Color;

}

Here's my fragment shader:

#version 330 core

in vec2 outTexCoord;
in vec4 outColor;

out vec4 glFragColor;
out vec4 glTexColor;

uniform sampler2D pxsampler;

void main() {
    vec4 texColor = texture(pxsampler, outTexCoord);
    //This outputs the color of polygons if I don't multiply outColor by texColor, but once I add texColor, no colors show up at all.
    glFragColor = texColor*outColor;

}

And lastly here's a bit of code to give reference to the VAO Attribute Pointers that I use at the right time when loading the shaders:

glBindAttribLocation(ProgramID, 0, "in_Position");
glBindAttribLocation(ProgramID, 1, "in_Color");
glBindAttribLocation(ProgramID, 2, "in_TexCoord");

Any help or suggestions are welcome. If any extra code is needed I'll add it. I'm sorry if this question seems redundant, but I couldn't find anything to help me. If someone could explain to me how exactly the code for rendering the textures works, that would be great, because from tutorials I've read they usually don't explain what does what so that I know how to do it again, so I'm not clear on what I'm doing exactly. Again, any help is appreciated. Thanks!

8
  • What happens with your current code? Is it rendering the polygon untextured? Or not rendering anything at all? Commented Jul 28, 2016 at 6:15
  • Oh, make sure that your shaders compile successfully. The ones you posted should fail to compile. Built-ins like gl_ProjectionMatrix are not available in the core profile. Commented Jul 28, 2016 at 6:17
  • @RetoKoradi Well, if I remove the texColor variable from glFragColor, the (untextured) polygons render as the color I set them to, but once I add the texColor variable nothing renders at all. And about the shaders, they don't fail to compile, it still works, I think the built-ins don't do anything at all. I removed the built ins (and the texColor variable for a second to get the polygons to render) and everything worked the same, so I'll just remove them since they do nothing. Commented Jul 28, 2016 at 6:23
  • 1
    Well it's odd that you call glGenerateMipmap before inserting any data, but that should not be an issue since you're not actually using mipmaps. Did you check if SOIL_load_image returns a non-null pointer and if the values of imagew and imagew are correct? Commented Jul 28, 2016 at 6:36
  • @PeterT Is it odd? Like I said, I'm not entirely sure what I'm doing because I generally find that the tutorials don't explain when to call things any why I'm calling them then. Well, I'm not entirely sure how to check if it returns at a nullptr or not, thought I'm sure I could figure it out, but I checked imagew and imageh and they are the correct values of the image file after calling SOIL_load_image, so I can definitely assume that it is loading the image correctly, at least I think. Commented Jul 28, 2016 at 6:46

2 Answers 2

1

I spotted a few problems in this code. Partly already mentioned in other answers/comments, partly not.

VAO binding

The main problem is that your VAO is not bound while you set up the vertex attributes. See this code sequence:

glGenVertexArrays(1, &spriteVAO.vaoID);
glGenBuffers(1, &spriteVAO.posVBOid);
glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.posVBOid);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*12, nullptr, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

Here, you create a VAO (or more precisely, the id for a VAO), but don't bind it. The state set up by the glVertexAttribPointer() call is stored in the currently bound VAO. This call should actually give you a GL_INVALID_OPERATION error in a core profile context, since you need to have a VAO bound when making it.

To fix this, bind the VAO after creating the id:

glGenVertexArrays(1, &spriteVAO.vaoID);
glBindVertexArray(spriteVAO.vaoID);
...

glGenerateMipmap() in wrong place

As pointed out in a comment, this call belongs after the glTexImage2D() call. It generate mipmaps based on the current texture content, meaning that you need to specify the texture data first.

This error does not cause immediate harm in your current code since you're not actually using mipmaps. But if you ever set the GL_TEXTURE_MIN_FILTER value to use mipmapping, this will matter.

glEnableVertexAttribArray() in wrong place

This needs to happen before the glDrawArray() call, because the attributes obviously have to be enabled for drawing.

Instead of just moving them before the draw call, it's even better to place them in the attribute setup code, along the glVertexAttribPointer() calls. The enabled/disabled state is tracked in the VAO, so there's no need to make these calls every time. Just set up all of this state once during setup, and then simply binding the VAO before the draw call will set all the necessary state again.

Unnecessary glVertexAttribPointer() call

Harmless, but this call in the render() method is redundant:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

Again, this state is tracked in the VAO, so making this call once during setup is enough, once you fix the problems listed above.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much! I think the main issue was that I forgot the bind call, and I'm so silly for missing that, so thanks so much for pointing it out. I also did the other things you suggested! I can now render textures, but I'm not able to draw the polygons anymore. I was drawing them with VAOs, but now that glFragColor = texColor*outColor; rather than glFragColor = outColor; They don't draw, or at least I can't see them anymore. I assume that to draw polygons with their own colors I'll need to create a separate fragment shader, but if I'm wrong, let me know. But again, thank you so much!
1

It's best to simplify your problem to track down the issue. For example, instead of loading an actual image, you could just allocate a width*heigth*3 buffer and fill it with 127 to get a grey image (or pink to make it more obvious). Try coloring your fragments with the uv coordinates instead of using the sampler to see whether these values are set correctly.

I think you should enable the vertex attribute arrays before the draw call:

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

Also, before binding a texture to a texture unit, you should specify what unit to use instead of relying on the default:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID);

And make sure you bind your sampler to that texture unit:

glUniform1i(glGetUniformLocation(ProgramID, "pxsampler"), 0);

For the sampler uniform, we're setting indices directly instead of the GL_TEXTURE* values. So GL_TEXTURE0 needs an index 0 when setting the uniform and not GL_TEXTURE0.

6 Comments

GL_TEXTURE0 is the default active texture unit, and 0 is the default value for uniform variables. So while it's good to make those calls to establish a pattern that will still work with multiple textures, omitting those parts shouldn't keep a simple example from running.
hmm...I just checked and that's indeed quite clear in the specification that samplers default to the first texture unit. The only other thing that I can see as problematic is the generation of MIPs before uploading the texture data. This might actually create textures from the uninitialized data and this is what the sampler might be using?
This may sound dumb, but I'm not sure how to color the fragments with the uv coordinates. And I tried all those things, and nothing changed. I feel like the issue is in the drawing, because I know the image is getting loaded correctly, but either something in my code is wrong in the shader or in the code for the vertex arrays. Sorry for being so clueless about this.
you can do something like glFragColor = vec4(outTexCoord, 0.0, 1.0) actually, now that I looked at that code, can you remove the out vec4 glTexColor; line from your fragment shader? I think this might prevent glFragColor to bind to the first output.
I tried changing glFragColor to that, but nothing changed, and I removed glTexColor, but the window is still black. Am I just stupid for not being able to get this to work?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.