Trouble with loading textures.

I am trying to load a texture but it doesn’t want to load in. I have tried it with both SOIL2 and stb_image. They both show no data. I have been stuck at this for a few days now and do not see what the main problem is.

You can download my project at https://github.com/Geomazzix/2D-OpenGL-platformer
You can find the problem in the Texture.h and the Texture.cpp
They get called from the Mesh.cpp and Mesh.h

Hope you can help me.

could you post some actual code here that shows your problem ?

Sure, there are multiple places where it can go wrong.

First one is the texture itself, only the init function does something really big:
(Problem: he doesn’t want to log any image data)


//Initialize the texture.
void Texture::Init(const std::string filepath)
{
    //Read image.
    int textureWidth, textureHeight, components;
    unsigned char* imageData = stbi_load(("../2D Platformer/Textures/" + filepath).c_str(), &textureWidth, &textureHeight, &components, 4);
    if (imageData == NULL)
    {
        std::cout << "Image failed to load in." << std::endl;
        FatalError();
    }

    //Create texture.
    glGenTextures(1, &_Texture);
    glBindTexture(GL_TEXTURE_2D, _Texture);


    //Set the sampling for the S(u) and T(v) of the image.
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    //Set the filterering for the image. (min: smaller image) (mag: bigger image).
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


    //Set texture data.
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
    stbi_image_free(imageData);
}


Secondly the mesh itself:
Mesh::Mesh()
{
    //Vertex Positions
    _Vertices[0].position.x = -0.5f;
    _Vertices[0].position.y = -0.5f;
    _Vertices[0].position.z = 0.0f;

    _Vertices[1].position.x = -0.5f;
    _Vertices[1].position.y = 0.5f;
    _Vertices[1].position.z = 0.0f;

    _Vertices[2].position.x = 0.5f;
    _Vertices[2].position.y = 0.5f;
    _Vertices[2].position.z = 0.0f;

    _Vertices[3].position.x = 0.5f;
    _Vertices[3].position.y = -0.5f;
    _Vertices[3].position.z = 0.0f;


    //Vertex Colours
    _Vertices[0].colour.r = 0;
    _Vertices[0].colour.g = 255;
    _Vertices[0].colour.b = 0;
    _Vertices[0].colour.a = 255;

    _Vertices[1].colour.r = 0;
    _Vertices[1].colour.g = 255;
    _Vertices[1].colour.b = 255;
    _Vertices[1].colour.a = 255;

    _Vertices[2].colour.r = 0;
    _Vertices[2].colour.g = 255;
    _Vertices[2].colour.b = 0;
    _Vertices[2].colour.a = 255;

    _Vertices[3].colour.r = 0;
    _Vertices[3].colour.g = 255;
    _Vertices[3].colour.b = 255;
    _Vertices[3].colour.a = 255;


    //Set UV coordinates.
    _Vertices[0].uv.u = 0.0f;
    _Vertices[0].uv.v = 0.0f;

    _Vertices[1].uv.u = 1.0f;
    _Vertices[1].uv.v = 0.0f;

    _Vertices[2].uv.u = 1.0f;
    _Vertices[2].uv.v = 1.0f;

    _Vertices[3].uv.u = 0.0f;
    _Vertices[3].uv.v = 1.0f;
}


//Initialize all mesh components.
void Mesh::Init()
{
    //Create and enable the vertrex array object.
    glGenVertexArrays(1, &_VAO);
    glBindVertexArray(_VAO);

    //Create and enable the vertex object buffer.
    glGenBuffers(1, &_VBO);
    glBindBuffer(GL_ARRAY_BUFFER, _VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(_Vertices), _Vertices, GL_STATIC_DRAW);

    //Assign the vertex attributes.
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
    glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, colour));
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, uv));

    //Create a vertex element array for so the program can reuse vertices.
    glGenBuffers(1, &_EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_Triangles), _Triangles, GL_STATIC_DRAW);

    _Texture.Init("cat.png");
}


//Draws the mesh.
void Mesh:Draw()
{
    glBindVertexArray(_VAO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _EBO);
    _Texture.Bind(0);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

Leaving garbage disposal out of this

Lastly the shaders I use on the mesh:

*Vertexshader:


#version 450

layout(location=0) in vec2 position;
layout(location=1) in vec4 colour;
layout(location=2) in vec2 uv;

out vec4 Colour;
out vec2 UV;

void main()
{
    Colour = colour;
    UV = uv;
    gl_Position = vec4(position, 0.0, 1.0);
}

*Fragmentshader


#version 450

in vec4 Colour;
in vec2 UV;

out vec4 colour;

uniform sampler2D diffuse;

void main()
{
    colour = Colour;
    colour = texture(diffuse, UV) * Colour;
}

Greetings, I downloaded the entire project, but I can’t find this Texture::Init function anywhere, which file is it in? Do I need an updated version of the project?

Texture.h and texture.cpp are missing from the GitHub linked project.

Alrighty,

Jeff

No this is entirely my fault, apparently the last push failed to reach the server. I will push the new classes right away!

Few things to check:

If you give a wrong path to stbi_load (ie a name that does not point to any existing file), does it return an error ? This is just for ensuring that your file is actually correctly found and loaded.

What is

no data
? Is it all black ?

Do you make any call to glActiveTexture ? Do you set a specific value to the diffuse sampler2D ?

Even if your integer colors look to be well defined, have you tried to display only the texture in the shader ?

  • The data of the stbi_load is not equal to NULL, but when I log it, it doesn’t show anything (maybe he just logs a space).
  • No have nog checked glActiveTexture and no I did not set a specific value in the diffuse sampler2D (I have no clue how I would do that).
  • No I have no clue how I would do that.

“diffuse” is your uniform variable for the texture, but I can’t find it in your code. it’s only in the shader, are you sending that info to the shader?

Jeff

Also here’s a link to my youtube opengl tutorials, follow along as I learn opengl:
https://www.youtube.com/channel/UCzx8alrxVELz5h1dfCdkdfg?view_as=subscriber

P.S. I also wanted to comment, that I really like and admire your coding style, it’s very organized, with all the .cpp and .h files separate, and the class management in separate files, etc. Eventually, I hope to find myself coding like this, but I still find it slightly confusing to track where the code is going from start to finish. I might be too old school…! lol Thanks again also, for sharing your project on GitHub.

Well thanks for that compliment, if you want to get such a clean way of coding you should try to work with an game engine i.e. unity or unreal engine. They use a Object Oriented Programming approach (OOP). This is a commen way to code this type of programs. It basicly means every instance or component of an object has it’s own responsibility.

Oh by the way, what should I push into the sampler2D?

Nothing. By default it will be 0, which is the first texture unit. And since you don’t change the texture unit on CPU side, this is well.

For the fragment shader, just try this:


colour = texture(diffuse, UV)

If it still outputs nothing, try to put 255 everywhere in your texture data and see if the result becomes white.

Nope, it doesn’t change anything, which means he does asign the texture, but he doesn’t display it for some reason.

Here’s a function call from one of my projects:

glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);

Jeff

You tried both ?

Also, check for OpenGL errors (glGetError) and if such errors happen, try to figure where is the gl statement responsible of that error.

i dont see anywhere what “Texture::Bind(0)” actually does.

//Draws the mesh.
void Mesh:Draw()
{
    glBindVertexArray(_VAO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _EBO);
    _Texture.Bind(0);
...

just to reiterate:
to draw a cube in a transformed position/orientation

glUseProgram(myprogram);
glBindVertexArray(myvao);

mat4 mymatrix = mat4(1);//or some other transform * view * perspective
glUniformMatrix4fv(location, 1, GL_FALSE, value_ptr(mymatrix));

glDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_BYTE/*or shhort/int*/, nullptr);

//glBindVertexArray(0);
//glUseProgram(0);

thats all, to do texturing, your fragmentshader needs a “uniform sampler2D mytexture;”. in addition, you c/c++ code needs to tell the “myprogram” from what texture unit (lets say 3) it is sourced:

GLint location = glGetUniformLocation(myprogram, "mytexture");
if (location == -1)
    /* handle error*/;
else
    glUniform1i(location, 3);

and right before drawing (that is the “glDraw*whatever” command), you bind the necessary texture to that unit:

glBindTextureUnit(3, sometexture);

then your cube will be textured according to the texcoords that you’ve sent to the fragmentshader. note that you dont need to bind (again) the element buffer or the vertex buffer since that info is stored in the VAO. take a look at this example, or read any of the tutorials found on the internet.

thats part 1

part 2: texture data

if stbi_load(…) doen NOT return 0, then it has succeded, so you have to generate a texture, allocate memory for it and fill it with that data, as usual by calling:


GLuint texture = 0;
glGenTextures(1, &tetxure);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, ... etc ...);

for testing purposes, you can skip the loading part and just set a 2x2 texture with 4 different texel colors:

struct Texel {
    unsigned char r, g, b, a;
};

Texel texturedata[] = {
    //                blue                                    yellow
    { 0x00, 0x00, 0xFF, 0xFF }, { 0xFF, 0xFF, 0x00, 0xFF },
    //                red                                    green
    { 0xFF, 0x00, 0x00, 0xFF }, { 0x00, 0xFF, 0x00, 0xFF },
};

it’s a function in the Texture.cpp file, it binds the texture to a unit texture, after that it binds it completely.

I know I try to use them as frequently as possible.

I followed through the code of jhon_connor, but this did not change anything. I have no clue what the problem might be…

me neither …

so lets guess: what are the “symptoms” ? whats not working ? is your cube/rectangle completely black or what color has it ? in case it is black, replace the fragmentshader output with:

out_color = vec4(0, 1, 0, 1);

the cube/rectangle/whatever should be green, is it ?
if yes, then texturing doesnt work as supposed …

replace the fragmentshader output with:

out_color = texelFetch(mytexture, ivec2(0, 0), 0);

and put just 1 texel in your texture, as i did in the example code linked above, bind it …

what color has your cube now ?

in texture.cpp:

//Allocate texture memory.
	glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, textureWidth, textureHeight);

	//Set texture data.
glTexImage2D(GL_TEXTURE_2D, 0, 0, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

that allocates texture memory twice, you’re supposed to set the data with …
either …
glTexImage2D(…)
or …
glTexStorage2D(…) + glTexSubImage2D(…)

whats the difference ?
glTexStorage2D allocates an “immutable” texture, glTexImage2D doesnt …

https://www.khronos.org/opengl/wiki/Texture_Storage#Immutable_storage
https://www.khronos.org/opengl/wiki/Texture_Storage#Mutable_storage

have you tried?


glTexImage2D(GL_TEXTURE_2D, 0, 0, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, &imageData[0]);

Also what are the dimensions of the image you are trying to load? I know in 1.1 it had some problems with some images of certain sizes but I’m not sure if any other versions had the same issues.