I am trying to create a way to load all the images which I plan to use in OpenGL and then render them all on the screen. It seems OpenGL seems to be very code repetitive in it's process of creating VAO,VBA, and EBOs.
Why is this only rendering one image? I thought the image object would be stored in the VAO.
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
#include "ogl_shaders.h"
const int MAX_TEXTURES = 255;
GLuint vaos[MAX_TEXTURES] = {0};
GLuint s_textures[MAX_TEXTURES] = {0};
int x_pos[] = {0, 300, 600};
int y_pox[] = {0, 100, 50};
int w_size[] = {200,200, 200};
int h_wize[] = {200, 200, 200};
std::vector<std::string> g_image_paths = { "image.png", "image2.png", "image3.png" };
struct Vec2 {
float x;
float y;
};
Vec2 toNDC(int pixel_x, int pixel_y, int window_width, int window_height) {
Vec2 vec;
vec.x = (static_cast<float>(pixel_x) / window_width) * 2.0f - 1.0f;
vec.y = (static_cast<float>(pixel_y) / window_height) * 2.0f - 1.0f;
return vec;
}
// I assume the problem is here <<<
GLuint setupImageRectangle(int posX, int posY, int sizeX, int sizeY, GLuint shaderProgram, GLuint texture) {
// Convert position to NDC
Vec2 vec = toNDC(posX, posY, 800, 600);
float ndcPosX = vec.x;
float ndcPosY = vec.y;
// Convert size to NDC
float ndcSizeX = static_cast<float>(sizeX) / (800 / 2.0f);
float ndcSizeY = static_cast<float>(sizeY) / (600 / 2.0f);
// Vertex data for the rectangle
float vertices[] = {
ndcPosX, ndcPosY, 0.0f, 0.0f, // Bottom-left
ndcPosX + ndcSizeX, ndcPosY, 1.0f, 0.0f, // Bottom-right
ndcPosX + ndcSizeX, ndcPosY + ndcSizeY, 1.0f, 1.0f, // Top-right
ndcPosX, ndcPosY + ndcSizeY, 0.0f, 1.0f // Top-left
};
GLuint elements[] = {0, 1, 2, 2, 3, 0};
// Create and bind the VAO
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Load the vertex data into a VBO
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Load the element data into an EBO
GLuint ebo;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
// Specify the layout of the vertex data
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
GLint texAttrib = glGetAttribLocation(shaderProgram, "texcoord");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
// Bind the texture to the rectangle
//glBindTexture(GL_TEXTURE_2D, texture);
return vao;
}
void CreateTexture(std::vector<std::string> image_path) {
for (int image = 0; image < image_path.size(); image++) {
// Generate a texture ID
glGenTextures(1, &s_textures[image]);
// Bind the texture ID
glBindTexture(GL_TEXTURE_2D, s_textures[image]);
// Set texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Load the image data
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true); // Flip image
unsigned char* data = stbi_load(image_path[image].c_str(), &width, &height, &nrChannels, STBI_rgb_alpha);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
} else {
std::cout << "Failed to load texture: " << image_path[image] << std::endl;
}
// Free the image data
stbi_image_free(data);
}
}
void helper_gl_bindTextures(GLuint vaos[], int size) {
for (int i = 0; i < size && vaos[i] != 0; i++){
glActiveTexture(GL_TEXTURE0 + i); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, s_textures[i]);
glBindVertexArray(vaos[i]);
std::cout << "image of vaos loaded " << i << std::endl;
}
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
int main()
{
glfwInit();
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", nullptr, nullptr);
glfwMakeContextCurrent(window);
glewInit();
// Compile and activate shaders
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, nullptr);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, nullptr);
glCompileShader(fragmentShader);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
CreateTexture(g_image_paths);
for (int idx = 0 ; idx < g_image_paths.size(); idx++){
vaos[idx] = setupImageRectangle(x_pos[idx], y_pox[idx], w_size[idx], h_wize[idx], shaderProgram, s_textures[idx]);
}
while(!glfwWindowShouldClose(window))
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the texture on the screen
helper_gl_bindTextures(vaos, sizeof(vaos) / sizeof(vaos[0]));
// Swap buffers and poll window events
glfwSwapBuffers(window);
glfwPollEvents();
}
// Cleanup and exit
for (int idx = 0 ; idx < sizeof(s_textures); idx++){
glDeleteTextures(1, &s_textures[idx]);
}
glfwTerminate();
return 0;
}
"I thought the image object would be stored in the VAO."
If you mean texture binding, the answer is no. You must bind the texture before you draw the mesh. The Vertex Array Object stores only the vertex specification and the IDs of the vertex buffers and the index buffer.
Apart from that, you must draw each mesh separately and therefore make a "draw" call for each mesh.
void helper_gl_bindTextures(GLuint vaos[], int size) {
for (int i = 0; i < size && vaos[i] != 0; i++){
glActiveTexture(GL_TEXTURE0 + i); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, s_textures[i]);
glBindVertexArray(vaos[i]);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
}