1

I'm using C++ and OpenGL 4.6 with SDL2 and glew. I have a class Texture:

#include "stb_image/stb_image.h"
class Texture {
private:
    unsigned int m_RendererID;
    std::string m_FilePath;
    unsigned char *m_LocalBuffer;
    int m_Width, m_Height, m_BPP;
public:
    Texture(const std::string &path);
    ~Texture();

    void Bind(unsigned int slot = 0) const;
    void Unbind() const;

    inline int GetWidth() const { return m_Width; }
    inline int GetHeight() const { return m_Height; }
    inline int GetID() const { return m_RendererID; }
};

Texture::Texture(const std::string &path) 
    : m_RendererID(0), m_FilePath(path), m_LocalBuffer(nullptr), m_Width(0), m_Height(0), m_BPP(0) 
{
    stbi_set_flip_vertically_on_load(1); //Flip vertically because OpenGL renders texture in the opposite way
    m_LocalBuffer = stbi_load(path.c_str(), &m_Width, &m_Height, &m_BPP, 4);

    GLCall(glGenTextures(1, &m_RendererID));
    GLCall(glBindTexture(GL_TEXTURE_2D, m_RendererID));

    GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
    GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
    GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
    GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));

    GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_Width, m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_LocalBuffer));
    GLCall(glBindTexture(GL_TEXTURE_2D, 0));

    if (m_LocalBuffer) {
        stbi_image_free(m_LocalBuffer);
    }
}

Texture::~Texture() {
    GLCall(glDeleteTextures(1, &m_RendererID));
}

void Texture::Bind(unsigned int slot) const {
    GLCall(glActiveTexture(GL_TEXTURE0 + slot));
    GLCall(glBindTexture(GL_TEXTURE_2D, m_RendererID));
}

void Texture::Unbind() const {
    GLCall(glBindTexture(GL_TEXTURE_2D, 0));
}

If I create one texture the texture shows:

Texture texture("ship.png");
texture.Bind(0);
shader.Bind();
va.Bind();
ib.Bind();
shader.SetUniform1i("u_Texture", 0);
glDrawElements(GL_TRIANGLES, ib.GetCount(), GL_UNSIGNED_INT, nullptr);

But if I create two textures I get only black screen:

Texture texture("ship.png");
texture.Bind(0);
Texture texture2("ship2.png");
texture2.Bind(1);
shader.Bind();
va.Bind();
ib.Bind();
shader.SetUniform1i("u_Texture", 0);
glDrawElements(GL_TRIANGLES, ib.GetCount(), GL_UNSIGNED_INT, nullptr);

This should work. I was searching on the internet for 2 hours and I can't seem to find the reason as to why this won't work. Could you guys help me out?

Tohomas
  • 13
  • 2
  • Try to bind both textures _after_ both are created. – tkausl Mar 02 '19 at 21:46
  • @tohomas: There's an unrelated bug in your code. See [OpenGL object in C++ RAII class no longer works](https://stackoverflow.com/q/46839586/734069) for details. Also, `m_FilePath` and `m_LocalBuffer` are only used by the constructor. Why do you keep them around as members? – Nicol Bolas Mar 02 '19 at 23:37

1 Answers1

0

At the end of the following lines of code

 Texture texture("ship.png");
 texture.Bind(0);
 Texture texture2("ship2.png");
 texture2.Bind(1);

the 2nd texture object ("ship2.png") is bound to texture unit 1, but to texture unit 0 the default texture object (0) is bound.

Note when Texture texture("ship.png"); is called then the texture object is created and bound to the current texture unit, after that the default texture object (0) is bound to the current texture unit:

 Texture::Texture(const std::string &path) {

     GLCall(glBindTexture(GL_TEXTURE_2D, m_RendererID));

     // [...]

     GLCall(glBindTexture(GL_TEXTURE_2D, 0));
 }

Note, what you call "unbind", doesn't unbind anything. glBindTexture(GL_TEXTURE_2D, 0) binds the default texture object (0) to the texture unit which is currently active. There is no necessity to do that.


Simply change the order of the instructions to solve your issue. Create and load the texture objects first. Then bind the texture objects to the texture units:

Texture texture("ship.png");
Texture texture2("ship2.png");

texture.Bind(0);
texture2.Bind(1);

shader.Bind();
va.Bind();
ib.Bind();
shader.SetUniform1i("u_Texture", 0);
glDrawElements(GL_TRIANGLES, ib.GetCount(), GL_UNSIGNED_INT, nullptr);

As an alternative you can avoid to use the texture unit 0. Use the texture unit 0 for creating the texture object only:

Texture::Texture(const std::string &path, unsigned int slot) {

    GLCall(glActiveTexture(GL_TEXTURE0));

    // [...]
}
Texture texture("ship.png");
texture.Bind(1);                     // <--- 1
Texture texture2("ship2.png");
texture2.Bind(2);                    // <--- 2
shader.Bind();
va.Bind();
ib.Bind();
shader.SetUniform1i("u_Texture", 1); // <--- 1
glDrawElements(GL_TRIANGLES, ib.GetCount(), GL_UNSIGNED_INT, nullptr);

Or add a parameter for the texture unit to the constructor of the class Texture:

Texture::Texture(const std::string &path, unsigned int slot) {

    GLCall(glGenTextures(1, &m_RendererID));

    GLCall(glActiveTexture(GL_TEXTURE0 + slot));
    GLCall(glBindTexture(GL_TEXTURE_2D, m_RendererID));

    // [...]

    // GLCall(glBindTexture(GL_TEXTURE_2D, 0)); <--- delete
}
Texture texture("ship.png", 0);
Texture texture2("ship2.png", 1);
Rabbid76
  • 202,892
  • 27
  • 131
  • 174