4

I'm practicing textures in OpenGL, applying a simple texture defined as white colors onto a quad. The code compiles and runs fine, but the color of the quad is simply black and not white as I have specified.

I think it is because my fragment shader does not do the right kind of sampling, it's like the data it returns from the texture function is all zero, thus the texture of the quad becomes black. The reason I suspect so is because when I run the program, I can make a clear color of white and move one of the vertices on the quad a bit as to see the background (since it usually fills the entire surface). I can then clearly see the white background there, with the black quad on top. So some output color is produced from the fragment shader, it's just the wrong one, since the small pixel array I provided is only white color. The problem is I don't know exactly why it would produce wrong results from the code I have. I'm attempting to follow along in a book, which use or less the same code. I've also tried looking it up on different tutorials but they all seem to use the same approach, so I am stuck.

[EDIT]

I have tracked down some of the problem. Doing some error tracking using glGetError(), I figured out that this line of my code:

glTexStorage2D(GL_TEXTURE_2D, 0, GL_RGB8, 4, 4);

returns error code 1282, (Invalid Operation), due to the second parameter level being 0. If I set this to 1 the error dissapears. The problem then becomes this line:

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_R, GL_FLOAT, myTexture);

Which returns the same error, though does not get fixed by changing the level parameter.

This is the fragment and vertex shader I use:

Fragment Shader

// FRAGMENT SHADER
#version 330

uniform sampler2D tex;

in vec2 vs_tex_coord;

layout (location = 0) out vec4 outputColor;

void main(void)
{
    outputColor = texture(tex, vs_tex_coord);
}

Vertex Shader

// VERTEX SHADER
#version 330

layout(location = 0) in vec4 position;
layout(location = 1) in vec2 in_tex_coord;

uniform vec2 offset;

out vec2 vs_tex_coord;

void main(void)
{
    vec4 finalOffset = vec4(offset.x, offset.y, 0.0, 0.0);
    gl_Position = position + finalOffset;
    vs_tex_coord = in_tex_coord;
}

The code I use for texture object generation and storage is pretty simple:

glGenTextures(1, &textureHandle);

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

glTexStorage2D(GL_TEXTURE_2D, 0, GL_RGB8, 4, 4);

const GLubyte myTexture[] = {0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,    0xFF, 0xFF, 0xFF,
                             0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,    0xFF, 0xFF, 0xFF,
                             0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,    0xFF, 0xFF, 0xFF,
                             0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF,    0xFF, 0xFF, 0xFF};

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_RGB, GL_UNSIGNED_BYTE, myTexture);

myTexture simply contains the 4x4 texture in an internal format of RGB with each component having 4 bits. I've set them all to white.

Then I simply generate a default sampler like so:

glGenSamplers(1, &mySampler);

glBindSampler(0, mySampler);

And finally I render:

glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

glUseProgram(theProgram);

GLuint samplerLoc = glGetUniformLocation(theProgram, "tex");

if (samplerLoc == -1)
    printf("Location name could not be found...\n");

glUniform1i(samplerLoc, 0);

glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);

glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(16 * sizeof(float)));

glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

The array which I upload to a VBO is:

const float vertexPositions[] =
{
    // Vertex position
    -0.8f, -1.0f, 0.0f, 1.0f,
     1.0f, -1.0f, 0.0f, 1.0f,
     1.0f,  1.0f, 0.0f, 1.0f,
    -1.0f,  1.0f, 0.0f, 1.0f,

    // Texture coordinates
    0.0f, 0.0f,
    1.0f, 0.0f,
    1.0f, 1.0f,
    0.0f, 1.0f
};

Is it because I am using the sampler objects incorrectly? Do I need to set some sort of settings on them before I can get it to work correctly? Do I miss some intermediate step?

For anyone interested, the visual result is this: enter image description here

You can see the white cleared background, with me having purposefully moved one of the vertex positions a bit so you can see the black quad against the white background.

CodingBeagle
  • 1,888
  • 2
  • 24
  • 52
  • *`glTexStorage2D`* +1 for the use of immutable textures. – Nicol Bolas Aug 09 '13 at 18:02
  • 1
    I don't see the part where you bind the texture to the context for use by the shader. I see where you *initialize* the texture, but are you binding it when you actually render? Or are you just assuming that nothing else will unbind the texture between initialization and usage? – Nicol Bolas Aug 09 '13 at 18:04
  • Yeah I was just assuming that nothing else will unbind the texture between initialization and usage (since the first time I bind it to modify it). That said, I just tried to to call "glBindTexture(GL_TEXTURE_2D, textureHandle);" inside the actual game loop before calling "glDrawArrays". Unfortunately didn't seem to have an effect :( – CodingBeagle Aug 09 '13 at 18:09
  • 2
    btw, if you are the author of http://www.arcsynthesis.org/gltut/ (which your profile could indicate), just wanted to give you a quick thank you :) It was a much much needed beginner introduction to graphics programming and a bit of OpenGL, it filled a void that meant I got started with all this in the first place. I'm still following along :) – CodingBeagle Aug 09 '13 at 18:10
  • "*nothing else will unbind the texture between initialization and usage*" It won't. But when you're trying to find bugs, it's best to be specific; you never know when an errant `glBindTexture` might happen and you don't know about it. – Nicol Bolas Aug 09 '13 at 18:13
  • Good point :) Anyways I moved the glBindTexture down into the rendering routine and unfortunately it didn't solve it, though at least now that part seems more clear. – CodingBeagle Aug 09 '13 at 18:16
  • So I have found something interesting. I tried to actually do some error checking with OpenGL (lesson freaking learned! Always do error checking...!). Anyways, what I found was that on the line "glTexStorage2D(GL_TEXTURE_2D, 0, GL_RGB8, 4, 4);", using "glGetError", I get an error code returned of 1282. I found this to be "GL_INVALID_OPERATION". Although I haven't been able to figure out why, other than it has to do with the "Levels" parameter being 0. If I set it to 1 I get no error returned from it. – CodingBeagle Aug 09 '13 at 19:46
  • Then I instead get the same error code at "glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_R, GL_FLOAT, myTexture);", even if I change that level paramater to 1. Something odd is going on there... – CodingBeagle Aug 09 '13 at 19:46

1 Answers1

4

Alright, so after some bug tracking I finally found the problem! :) For anyone who gets into this problem in the future, here's the (very simple... doh!) solution.

First of all, this line right here:

glTexStorage2D(GL_TEXTURE_2D, 0, GL_RGB8, 4, 4);

Returned an OpenGL error of 1281 (Invalid Operation), is because the second parameter levels has to be at the very least 1, to store enough memory for the base image no matter if you want to use mipmaps or not (which I thought should be 0). So changing it from 0 to 1 fixed it.

Secondly, this line:

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_R, GL_FLOAT, myTexture);

Had a problem at the format parameter, where it says GL_R in the above snippet. This was just plain wrong. Looking at the official documention for the function, it states this about it:

format Specifies the format of the pixel data. The following symbolic values are accepted: GL_RED, GL_RG, GL_RGB, GL_BGR, GL_RGBA, GL_BGRA, GL_DEPTH_COMPONENT, and GL_STENCIL_INDEX.

The type I previous specified in glTextStorage2D uses GL_RGB8, which has a base internal format of GL_RGB, this the correct version is:

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_RGB, GL_UNSIGNED_BYTE, myTexture);

Fixing these two issues caused the textured result to show up :)

CodingBeagle
  • 1,888
  • 2
  • 24
  • 52