c++openglglslglfwstb-image

OpenGL texture mixup


So I have a texture class in opengl that basically get's a buffer and renders a 2D texture on the screen. On itself, it works perfectly fine and allows me to render as many textures as I want on the screen. But I want to add a transition function that gets a second texture along with the original texture and blends them together in a single rectangle.

On a standalone project, without the texture class, I managed to make it work with the help of fragment shaders. However, when I add the transition function the the texture class and create a second texture with it that's not a transition, everything goes weird, the second rectangle disappears and the first rectangle that should transition uses the second rectangles texture for some reason.

So here is what SHOULD happen: a rectangle with data1 that uses the render function should just be on the screen. Another rectangle with the transition function should be on the screen but uses data2 and data3 at the same time. Here is my code so far:

main.cpp

class Texture{
private:
    typedef struct transtex_s { 
        uint8_t* buff;
        int w, h;
    } transtex; 


    Shader ourShader;
    unsigned int texture;
    unsigned int texture2;
    unsigned int VBO, VAO, EBO;
public:
    transtex tex2;

    Texture(float pos, bool is_trans, std::string shader): ourShader("camera.vs", shader.c_str())
    {
    float vertices[] = {
        // positions          // colors           // texture coords
         pos,  pos, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // top right
         pos, -pos, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // bottom right
        -pos, -pos, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // bottom left
        -pos,  pos, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // top left 
    };
    unsigned int indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
    };

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    // texture coord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);


    ourShader.use(); 
    glUniform1i(glGetUniformLocation(ourShader.ID, "texture"), 1);

    if(is_trans)
    {
        ourShader.setInt("texture2", 1);
    }
    }

    void transition(int width1,int height1, uint8_t* data1, float progress)
    {
        ourShader.use();
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width1, height1, 0, GL_RGB, GL_UNSIGNED_BYTE, data1);
        glGenerateMipmap(GL_TEXTURE_2D);

        glGenTextures(1, &texture2);
        glBindTexture(GL_TEXTURE_2D, texture2);

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex2.w, tex2.h, 0, GL_RGB, GL_UNSIGNED_BYTE, tex2.buff);
        glGenerateMipmap(GL_TEXTURE_2D);

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

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1f(glGetUniformLocation(ourShader.ID, "progress"), progress);
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    }

    void render(int w, int h, uint8_t* buffer)
    {
        ourShader.use();
        glEnable(GL_BLEND);

        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);

        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
        glGenerateMipmap(GL_TEXTURE_2D);

        glActiveTexture(GL_TEXTURE2);
        
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    }

    ~Texture()
    {
        glDeleteVertexArrays(1, &VAO);
        glDeleteBuffers(1, &VBO);
        glDeleteBuffers(1, &EBO);
    }
};

int main()
{
    ... // init glfw, opengl and glad.
   
    // Read images as buffers using stbi image.
    int width1, height1, nrChannels1;
    stbi_set_flip_vertically_on_load(true);
    unsigned char *data1 = stbi_load("/home/tb/Desktop/Work/CameraTest/bin/ex2.jpeg", &width1, &height1, &nrChannels1, 0);

    int width2, height2, nrChannels2;
    unsigned char *data2 = stbi_load("/home/tb/Desktop/Work/CameraTest/bin/ex1.jpeg", &width2, &height2, &nrChannels2, 0);

    int width3, height3, nrChannels3;
    unsigned char *data3 = stbi_load("/home/tb/Desktop/Work/CameraTest/bin/WATERMARK.png", &width3, &height3, &nrChannels3, 0);

    Texture rect1 = Texture(0.5f, true, "camera.fs");
    Texture rect2 = Texture(1.0f, false, "normal.fs");

    float progress = 0.0f;

    while (!glfwWindowShouldClose(window))
    {
        rect1.tex2.buff = data2;
        rect1.tex2.w = width2;
        rect1.tex2.h = height2;

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

        rect2.render(width3, height3, data3);
        rect1.transition(width1, height1, data1, progress);

        progress += 0.01f;

        if(progress >= 1.0f) progress = 0.0f;

        glfwSwapBuffers(window);
    }

    stbi_image_free(data1);
    stbi_image_free(data2);
    return 0;
}

Vectors:

camera.fs

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D texture1;
uniform sampler2D texture2;
uniform float progress;

void main()
{
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), progress);
}

normal.fs

#version 330 core

in vec2 TexCoord;
out vec4 FragColor; 

uniform sampler2D texture1; 

void main()
{
    vec4 texColor = texture(texture1, TexCoord);

    FragColor = texColor;
}

On the line:

rect2.render(width3, height3, data3);
rect1.transition(width1, height1, data1, progress);

...when I try to comment one and try to render the other, each one works perfectly fine alone. But when I enable them at the same time, data1 which is supposed to just be on in screen is just not there. And rect, which is supposed to transition from data2 to data3 just stays as data2.

Why is this happening? How can I fix this?

Edit: Upon further insoection I've realised that ourShader.use(); in render (which is just basically gluseprogram(ID)) causes the problem. But I'm still unsure how to prevent it.

Edit 2: The issues regarding the transition function is now solved. However now, after the first few frames render function starts using data1 instead of data3.


Solution

  • So there were two errors in my code:

    1. Not using shaders first in each function (render and transition)

    2. Using GL_TEXTURE2 in render instead of GL_TEXTURE0.

    Fixing these errors gave me the desired output.