0
votes

Bear in mind that I have openGL 3 and glsl version 1.30, therefore, I don't have dynamic indexing nor glBindTextureUnit(). I have seen people batch render multiple textures in one draw call by simply activating the texture slot, binding the texture then setting the uniform, like so:

glUniform1i(glGetUniformLocation(program, "u_texture0"), 0);
glUniform1i(glGetUniformLocation(program, "u_texture1"), 1);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture1);

However, when I do this:

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char* readFile(const char filePath[])
{
    FILE* file;
    file = fopen(filePath, "r");

    if(file == NULL)
    {
        perror("unable to load file");
        exit(1);
    }

    fseek(file, 0l, SEEK_END);

    char* buffer = (char*)malloc(ftell(file));

    rewind(file);

    char c;
    unsigned int i = 0;

    while((c = fgetc(file)) != EOF)
        buffer[i++] = c;

    fclose(file);

    return buffer;
}

GLuint loadShader(const char* src, GLenum type)
{
    GLuint shader = glCreateShader(type);

    glShaderSource(shader, 1, &src, NULL);
    glCompileShader(shader);

    // shader error handling
    int result;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
    if(result == GL_FALSE)
    {
        int length;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
        char* message = (char*)alloca(length * sizeof(char));
        glGetShaderInfoLog(shader, length, &length, message);
        puts(message);
    }

    return shader;
}
GLuint loadTexture(const char* filePath)
{
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

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

    unsigned char* buffer;
    int width, height;

    stbi_set_flip_vertically_on_load(1);
    buffer = stbi_load(filePath, &width, &height, NULL, STBI_rgb_alpha);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

    if(buffer)
        stbi_image_free(buffer);

    return texture;
}

void resetViewport(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); }

int main()
{
    // initialising GLFW
    if(!glfwInit())
    {
        perror("error with glfw!");
        exit(1);
    }

    // window creation
    GLFWwindow* window;

    window = glfwCreateWindow(800, 500, "window", NULL, NULL);
    glfwSetWindowSizeCallback(window, resetViewport);

    // binding renderring context
    glfwMakeContextCurrent(window);

    // initialising glew
    if(glewInit() != GLEW_OK)
        perror("error with glee!");

    // enabling blend
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // creating program and shaders
    GLuint program = glCreateProgram();

    char* vertexShaderSource = readFile("vertex.glsl");
    char* fragmentShaderSource = readFile("fragment.glsl");
    GLuint vertexShader = loadShader(vertexShaderSource, GL_VERTEX_SHADER);
    GLuint fragmentShader = loadShader(fragmentShaderSource, GL_FRAGMENT_SHADER);

    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);

    // deleting shaders
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    free(vertexShaderSource);
    free(fragmentShaderSource);

    // compiling and binding program
    glLinkProgram(program);
    glUseProgram(program);

    // creating vertex array
    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    // creating vertex buffer
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    // vertex buffer layout
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), NULL);
    glBindAttribLocation(program, 0, "a_position");
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void*)(2 * sizeof(float)));
    glBindAttribLocation(program, 1, "a_textureCoordinate");
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void*)(4 * sizeof(float)));
    glBindAttribLocation(program, 2, "a_index");

    // creating texture
    GLuint texture0 = loadTexture("image0.jpeg");
    GLuint texture1 = loadTexture("image1.jpeg");

    // vertex data
    float vertexes[] =
    {
    //   x      y       u     v         i (index)
        -1.0f, -1.0f,   0.0f, 0.0f,     0.0f,
        -1.0f,  0.0f,   0.0f, 1.0f,     0.0f,
         0.0f,  0.0f,   1.0f, 1.0f,     0.0f,
         0.0f, -1.0f,   1.0f, 0.0f,     0.0f,

         0.0f,  0.0f,   0.0f, 0.0f,     1.0f,
         0.0f, +1.0f,   0.0f, 1.0f,     1.0f,
        +1.0f, +1.0f,   1.0f, 1.0f,     1.0f,
        +1.0f,  0.0f,   1.0f, 0.0f,     1.0f,
    };

    // unbinding just incase
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // rebinding
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    // binding texture to texture slot

    glUniform1i(glGetUniformLocation(program, "u_texture0"), 0);
    glUniform1i(glGetUniformLocation(program, "u_texture1"), 1);
    
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture1);

    // sending data
    glBufferData(GL_ARRAY_BUFFER, 8 * 5 * sizeof(float), vertexes, GL_STATIC_DRAW);

    glLinkProgram(program);

    while(!glfwWindowShouldClose(window))
    {
        glfwPollEvents();

        // clear screen
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // draw call
        glDrawArrays(GL_QUADS, 0, 8);

        // updating window's buffer
        glfwSwapBuffers(window);
    }

    // error logging
    GLenum error = glGetError();
    if(error != GL_NO_ERROR)
        printf("error: %u\n", error);

    // termination
    glDeleteTextures(1, &texture0);
    glDeleteTextures(1, &texture1);
    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);
    glDeleteProgram(program);

    glfwTerminate();
}

With the fragment shader code being:

#version 130

in vec2 v_textureCoordinate;
flat in uint v_index;

uniform sampler2D u_texture0;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
uniform sampler2D u_texture3;
uniform sampler2D u_texture4;
uniform sampler2D u_texture5;
uniform sampler2D u_texture6;
uniform sampler2D u_texture7;

out vec4 o_color;

void main()
{
    // dynamic indexing is not suppourted in GLSL version 1.30 therefore the most suitable solution is to switch case between them
    switch(v_index)
    {
        case 0u:
            o_color = texture2D(u_texture0, v_textureCoordinate);
            return;
        case 1u:
            o_color = texture2D(u_texture1, v_textureCoordinate);
            return;
        case 2u:
            o_color = texture2D(u_texture2, v_textureCoordinate);
            return;
        case 3u:
            o_color = texture2D(u_texture3, v_textureCoordinate);
            return;
        case 4u:
            o_color = texture2D(u_texture4, v_textureCoordinate);
            return;
        case 5u:
            o_color = texture2D(u_texture5, v_textureCoordinate);
            return;
        case 6u:
            o_color = texture2D(u_texture6, v_textureCoordinate);
            return;
        case 7u:
            o_color = texture2D(u_texture7, v_textureCoordinate);
            return;
        default:
            return;
    }
}

and the vertex shader code being:

#version 130

attribute vec4 a_position;
attribute vec2 a_textureCoordinate;
attribute float a_index;

out vec2 v_textureCoordinate;
flat out uint v_index;

void main()
{
    gl_Position = a_position;
    v_textureCoordinate = a_textureCoordinate;
    v_index = uint(a_index);
}

It renders both quads with the texture texture0, Am I doing something wrong? I am getting the impression that binding the second texture is unbinding the first texture. What am I missing?

1
...why are you calling glEnable() & glBlendFunc() before you have a current GL context (glfwMakeContextCurrent())? Why aren't you querying the vertex attribute locations?genpfault
@genpfault What about vertex attribute locations? My problem is with the texture bindingMaximV
You're assuming they're at fixed locations in the glEnableVertexAttribArray() & glVertexAttribPointer() calls without a corresponding layout qualifiers in the GLSL.genpfault
I As I have GLSL version 1.30 I don't have layout locations, therefore I have to bind them manually in the C source code using glBindAttribLocation(), if you're saying that because I forgot to do that for the texture index, I've updated it now.MaximV
You have to call glBindAttribLocation() before you link the shader program, not after.genpfault

1 Answers

2
votes

glBindAttribLocation() has to be called before shader program linking to have any effect:

GLuint prog = glCreateProgram();
AttachShader( prog, GL_VERTEX_SHADER, vert );
AttachShader( prog, GL_FRAGMENT_SHADER, frag );
glBindAttribLocation(prog, 0, "a_position");
glBindAttribLocation(prog, 1, "a_textureCoordinate");
glBindAttribLocation(prog, 2, "a_index");
glLinkProgram( prog );
CheckStatus( prog, false );
glUseProgram( prog );

All together:

screenshot of output

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>

void CheckStatus( GLuint obj, bool isShader )
{
    GLint status = GL_FALSE, log[ 1 << 11 ] = { 0 };
    ( isShader ? glGetShaderiv : glGetProgramiv )( obj, isShader ? GL_COMPILE_STATUS : GL_LINK_STATUS, &status );
    if( status == GL_TRUE ) return;
    ( isShader ? glGetShaderInfoLog : glGetProgramInfoLog )( obj, sizeof( log ), NULL, (GLchar*)log );
    std::cerr << (GLchar*)log << "\n";
    std::exit( EXIT_FAILURE );
}

void AttachShader( GLuint program, GLenum type, const char* src )
{
    GLuint shader = glCreateShader( type );
    glShaderSource( shader, 1, &src, NULL );
    glCompileShader( shader );
    CheckStatus( shader, true );
    glAttachShader( program, shader );
    glDeleteShader( shader );
}

const char* const vert = R"GLSL(
#version 130

attribute vec4 a_position;
attribute vec2 a_textureCoordinate;
attribute float a_index;

out vec2 v_textureCoordinate;
flat out uint v_index;

void main()
{
    gl_Position = a_position;
    v_textureCoordinate = a_textureCoordinate;
    v_index = uint(a_index);
}    
)GLSL";

const char* const frag = R"GLSL(
#version 130

in vec2 v_textureCoordinate;
flat in uint v_index;

uniform sampler2D u_texture0;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
uniform sampler2D u_texture3;
uniform sampler2D u_texture4;
uniform sampler2D u_texture5;
uniform sampler2D u_texture6;
uniform sampler2D u_texture7;

out vec4 o_color;

void main()
{
    // dynamic indexing is not suppourted in GLSL version 1.30 therefore the most suitable solution is to switch case between them
    switch(v_index)
    {
        case 0u:
            o_color = texture2D(u_texture0, v_textureCoordinate);
            return;
        case 1u:
            o_color = texture2D(u_texture1, v_textureCoordinate);
            return;
        case 2u:
            o_color = texture2D(u_texture2, v_textureCoordinate);
            return;
        case 3u:
            o_color = texture2D(u_texture3, v_textureCoordinate);
            return;
        case 4u:
            o_color = texture2D(u_texture4, v_textureCoordinate);
            return;
        case 5u:
            o_color = texture2D(u_texture5, v_textureCoordinate);
            return;
        case 6u:
            o_color = texture2D(u_texture6, v_textureCoordinate);
            return;
        case 7u:
            o_color = texture2D(u_texture7, v_textureCoordinate);
            return;
        default:
            return;
    }
}
)GLSL";

GLuint loadTexture(const unsigned char* image, int w, int h)
{
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);

    return texture;
}

void resetViewport(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

int main()
{
    // initialising GLFW
    if(!glfwInit())
    {
        perror("error with glfw!");
        exit(1);
    }

    // window creation
    GLFWwindow* window;

    window = glfwCreateWindow(800, 500, "window", NULL, NULL);
    glfwSetWindowSizeCallback(window, resetViewport);

    // binding renderring context
    glfwMakeContextCurrent(window);

    // initialising glew
    if(glewInit() != GLEW_OK)
        perror("error with glee!");

    // enabling blend
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // creating program and shaders
    GLuint prog = glCreateProgram();
    AttachShader( prog, GL_VERTEX_SHADER, vert );
    AttachShader( prog, GL_FRAGMENT_SHADER, frag );
    glBindAttribLocation(prog, 0, "a_position");
    glBindAttribLocation(prog, 1, "a_textureCoordinate");
    glBindAttribLocation(prog, 2, "a_index");
    glLinkProgram( prog );
    CheckStatus( prog, false );
    glUseProgram( prog );      
    
    // creating vertex array
    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    // creating vertex buffer
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    // vertex buffer layout
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), NULL);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void*)(2 * sizeof(float)));
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void*)(4 * sizeof(float)));

    // creating texture
    const unsigned char image1[] = { 255, 0, 0, 255 };
    GLuint texture0 = loadTexture( image1, 1, 1 );
    const unsigned char image2[] = { 0, 255, 0, 255 };
    GLuint texture1 = loadTexture( image2, 1, 1 );

    // vertex data
    float vertexes[] =
    {
    //   x      y       u     v         i (index)
        -1.0f, -1.0f,   0.0f, 0.0f,     0.0f,
        -1.0f,  0.0f,   0.0f, 1.0f,     0.0f,
         0.0f,  0.0f,   1.0f, 1.0f,     0.0f,
         0.0f, -1.0f,   1.0f, 0.0f,     0.0f,

         0.0f,  0.0f,   0.0f, 0.0f,     1.0f,
         0.0f, +1.0f,   0.0f, 1.0f,     1.0f,
        +1.0f, +1.0f,   1.0f, 1.0f,     1.0f,
        +1.0f,  0.0f,   1.0f, 0.0f,     1.0f,
    };

    // unbinding just incase
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // rebinding
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    // binding texture to texture slot

    glUniform1i(glGetUniformLocation(prog, "u_texture0"), 0);
    glUniform1i(glGetUniformLocation(prog, "u_texture1"), 1);
    
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture1);

    // sending data
    glBufferData(GL_ARRAY_BUFFER, 8 * 5 * sizeof(float), vertexes, GL_STATIC_DRAW);

    while(!glfwWindowShouldClose(window))
    {
        glfwPollEvents();

        // clear screen
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // draw call
        glDrawArrays(GL_QUADS, 0, 8);

        // updating window's buffer
        glfwSwapBuffers(window);
    }

    // error logging
    GLenum error = glGetError();
    if(error != GL_NO_ERROR)
        printf("error: %u\n", error);

    // termination
    glDeleteTextures(1, &texture0);
    glDeleteTextures(1, &texture1);
    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);
    glDeleteProgram(prog);

    glfwTerminate();
}