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?
glEnable()
&glBlendFunc()
before you have a current GL context (glfwMakeContextCurrent()
)? Why aren't you querying the vertex attribute locations? – genpfaultglEnableVertexAttribArray()
&glVertexAttribPointer()
calls without a correspondinglayout
qualifiers in the GLSL. – genpfaultglBindAttribLocation()
, if you're saying that because I forgot to do that for the texture index, I've updated it now. – MaximVglBindAttribLocation()
before you link the shader program, not after. – genpfault