Help with multiple VAO/VBO/EBO

Hello. I’m a game design student and I’m a little bit desperate to get this code working, since I’ve tried getting help from my fellow classmates, with no luck and I’m getting out of time.

Well, I’m supposed to make a very simple Space Invaders game using OpenGL and Visual Studio, in C++.
My main problem is: I have to draw at least 3 elements on screen, but only one is being rendered.

This is my code:

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <string.h>
#include <time.h>
#define GL_LOG_FILE "gl.log"
#include <iostream>
#include "gl_utils.h"
//#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
using namespace std;


int g_gl_width = 640;
int g_gl_height = 480;
GLFWwindow *g_window = NULL;
int Pos = 10;
float x;
int distanceToPlayer = 0;
int enemyPos = 1;
int enemyDir = 1;
int enemyVel = 60;
int shot = 3;
int points = 0;
int enemyMovement = 0;
bool goDown = false;

glm::mat4 transform(1.0);
glm::mat4 transform2(1.0);
glm::mat4 transform3(1.0);


void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	static bool gWireframe = 0;




	if (key == GLFW_KEY_LEFT && action == GLFW_PRESS)
	{
		if (Pos > 1)
		{
			transform = glm::translate(transform, glm::vec3(-0.1, 0.0, 0.0f));
			Pos--;
		}

	}
	else if (key == GLFW_KEY_RIGHT && action == GLFW_PRESS)
	{
		if (Pos < 19)
		{
			transform = glm::translate(transform, glm::vec3(0.1, 0.0, 0.0f));
			Pos++;
		}
	}
	if ((key == GLFW_KEY_SPACE) && action == GLFW_PRESS)
	{

		shot = 9;
		if (Pos == enemyPos)
		{
			glm::mat4 aux(1.0f);
			transform3 = glm::translate(aux, glm::vec3(-0.9f, 0.9f, 0.0f));
			enemyPos = 1;
			enemyDir = 1;
			distanceToPlayer = 0;
			goDown = false;

			if (enemyVel > 5)
				enemyVel -= 5;
			else
			{
				enemyVel--;
			}
			points += 1;
			cout << "Pontos: " << points << endl;
		}
	}
	else { shot = 3; }

}
int main() {
	restart_gl_log();
	// all the GLFW and GLEW start-up code is moved to here in gl_utils.cpp
	start_gl("Space Invaders");
	// tell GL to only draw onto a pixel if the shape is closer to the viewer
	//glEnable( GL_DEPTH_TEST ); // enable depth-testing
	//glDepthFunc( GL_LESS );		 // depth-testing interprets a smaller value as "closer"

	
	char vertex_shader[1024 * 256];
	char fragment_shader[1024 * 256];
	parse_file_into_str("test_vs.glsl", vertex_shader, 1024 * 256);
	parse_file_into_str("test_fs.glsl", fragment_shader, 1024 * 256);

	GLuint vs = glCreateShader(GL_VERTEX_SHADER);
	const GLchar *p = (const GLchar *)vertex_shader;
	glShaderSource(vs, 1, &p, NULL);
	glCompileShader(vs);

	// check for compile errors
	int params = -1;
	glGetShaderiv(vs, GL_COMPILE_STATUS, &params);
	if (GL_TRUE != params) {
		fprintf(stderr, "ERROR: GL shader index %i did not compile
", vs);
		print_shader_info_log(vs);
		return 1; // or exit or something
	}

	GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
	p = (const GLchar *)fragment_shader;
	glShaderSource(fs, 1, &p, NULL);
	glCompileShader(fs);

	// check for compile errors
	glGetShaderiv(fs, GL_COMPILE_STATUS, &params);
	if (GL_TRUE != params) {
		fprintf(stderr, "ERROR: GL shader index %i did not compile
", fs);
		print_shader_info_log(fs);
		return 1; // or exit or something
	}

	GLuint shader_programme = glCreateProgram();
	glAttachShader(shader_programme, fs);
	glAttachShader(shader_programme, vs);
	glLinkProgram(shader_programme);

	glGetProgramiv(shader_programme, GL_LINK_STATUS, &params);
	if (GL_TRUE != params) {
		fprintf(stderr, "ERROR: could not link shader programme GL index %i
",
			shader_programme);
		print_programme_info_log(shader_programme);
		return false;
	}


	// set up vertex data (and buffer(s)) and configure vertex attributes
	// ------------------------------JOGADOR------------------------------------
	float vertices2[] = {
		// positions          // colors           // texture coords
		0.08f,  0.08f, 0.0f,   1.0f, 1.0f, 1.0f,   1.0f, 1.0f, // top right
		0.08f, -0.08f, 0.0f,   1.0f, 1.0f, 1.0f,   1.0f, 0.0f, // bottom right
		-0.08f, -0.08f, 0.0f,   1.0f, 1.0f, 1.0f,   0.0f, 0.0f, // bottom left
		-0.08f,  0.08f, 0.0f,   1.0f, 1.0f, 1.0f,   0.0f, 1.0f,  // top left

	};
	unsigned int indices2[] = {
		0, 1, 3, // first triangle
		1, 2, 3,  // second triangle
	};

	unsigned int VBO2, VAO2, EBO2;
	glGenVertexArrays(1, &VAO2);
	glGenBuffers(1, &VBO2);
	glGenBuffers(1, &EBO2);

	glBindVertexArray(VAO2);

	glBindBuffer(GL_ARRAY_BUFFER, VBO2);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO2);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, 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);

	////////////////////////////////////////////////////////INIMIGOS///////////////////////////////////////////////////////////

	float vertices_Inimigos[] = {
		// positions          // colors           // texture coords
		0.08f,  0.08f, 0.0f,   1.0f, 1.0f,1.0f,   1.0f, 1.0f, // top right
		0.08f, -0.08f, 0.0f,   1.0f, 1.0f, 1.0f,   1.0f, 0.0f, // bottom right
		-0.08f, -0.08f, 0.0f,   1.0f, 1.0f, 1.0f,   0.0f, 0.0f, // bottom left
		-0.08f,  0.08f, 0.0f,   1.0f, 1.0f, 1.0f,   0.0f, 1.0f, // top left

	};
	unsigned int indices_Inimigos[] = {
		0, 1, 3, // first triangle
		1, 2, 3,  // second triangle
	};

	unsigned int VBO1, VAO1, EBO1;
	glGenVertexArrays(1, &VAO1);
	glGenBuffers(1, &VBO1);
	glGenBuffers(1, &EBO1);

	glBindVertexArray(VAO1);

	glBindBuffer(GL_ARRAY_BUFFER, VBO1);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_Inimigos), vertices_Inimigos, GL_STATIC_DRAW);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO1);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices_Inimigos), indices_Inimigos, 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);


	// -----------------------TIRO-------------------------------------------
	float vertices3[] = {
		// positions          // colors           // texture coords
		 0.05f,  0.05f, 0.0f,   1.0f, 1.0f, 1.0f,   1.0f, 1.0f, // top right
		 0.05f, -0.05f, 0.0f,   1.0f, 1.0f, 1.0f,   1.0f, 0.0f, // bottom right
		-0.05f, -0.05f, 0.0f,   1.0f, 1.0f, 1.0f,   0.0f, 0.0f, // bottom left
		-0.05f,  0.05f, 0.0f,   1.0f, 1.0f, 1.0f,   0.0f, 1.0f  // top left 
	};
	unsigned int indices3[] = {
		0, 1, 3, // first triangle
		1, 2, 3  // second triangle
	};


	unsigned int VBO3, VAO3, EBO3;
	glGenVertexArrays(1, &VAO3);
	glGenBuffers(1, &VBO3);
	glGenBuffers(1, &EBO3);

	glBindVertexArray(VAO3);

	glBindBuffer(GL_ARRAY_BUFFER, VBO3);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices3), vertices3, GL_STATIC_DRAW);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO3);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices3), indices3, 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);




	// load and create a texture
	// -------------------------
	unsigned int texture1, texture2, texture3;
	glUseProgram(shader_programme);
	glUniform1i(glGetUniformLocation(shader_programme, "texture1"), 1);
	glUniform1i(glGetUniformLocation(shader_programme, "texture2"), 2);
	glUniform1i(glGetUniformLocation(shader_programme, "texture3"), 3);
	
	glGenTextures(1, &texture1);
	
	glBindTexture(GL_TEXTURE_2D, texture1);
	
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	int width, height, nrChannels;

	//stbi_set_flip_vertically_on_load(true);
	unsigned char *data = stbi_load("Nave.jpg", &width, &height, &nrChannels, 0);
	if (data)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
	{
		std::cout << "Failed to load texture" << std::endl;
	}
	stbi_image_free(data);

	// texture 2
	// ---------
	glGenTextures(1, &texture2);
	
	glBindTexture(GL_TEXTURE_2D, texture2);
	// set the texture wrapping parameters
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// set texture wrapping to GL_REPEAT (default wrapping method)
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	// set texture filtering parameters
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	// load image, create texture and generate mipmaps


	data = stbi_load("Alien.jpg", &width, &height, &nrChannels, 0);
	if (data)
	{
		// note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBA
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
	{
		std::cout << "Failed to load texture" << std::endl;
	}
	stbi_image_free(data);

	// texture 3
	// ---------
	glGenTextures(1, &texture3);
	
	glBindTexture(GL_TEXTURE_2D, texture3);
	// set the texture wrapping parameters
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// set texture wrapping to GL_REPEAT (default wrapping method)
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	// set texture filtering parameters
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	// load image, create texture and generate mipmaps
	
	data = stbi_load("Shot.jpg", &width, &height, &nrChannels, 0);
	if (data)
	{
		// note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBA
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else
	{
		std::cout << "Failed to load texture" << std::endl;
	}
	stbi_image_free(data);

	

	glEnable(GL_CULL_FACE); // cull face
	glCullFace(GL_BACK);		// cull back face
	glFrontFace(GL_CW);			// GL_CCW for counter clock-wise

	transform = glm::translate(transform, glm::vec3(0.0, -0.85, 0.0f));
	transform2 = glm::translate(transform2, glm::vec3(-0.9f, 0.9f, 0.0f));



	while (!glfwWindowShouldClose(g_window)) {

		// wipe the drawing surface clear

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glfwSetKeyCallback(g_window, key_callback);

		unsigned int transformLoc = glGetUniformLocation(shader_programme, "transform");

		glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));

		glUseProgram(shader_programme);

		// bind textures on corresponding texture units and render
		glActiveTexture(GL_TEXTURE2);
		glBindTexture(GL_TEXTURE_2D, texture2);
		glBindVertexArray(VAO1);
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, texture1);
		glBindVertexArray(VAO2);
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

		glActiveTexture(GL_TEXTURE3);
		glBindTexture(GL_TEXTURE_2D, texture3);
		glBindVertexArray(VAO3);
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
		
		

	

		enemyMovement++;
		
		if (enemyMovement >= enemyVel) {
			if (goDown == false)
				transform2 = glm::translate(transform2, glm::vec3(0.1f*enemyDir, 0.0f, 0.0f));
			else {
				transform2 = glm::translate(transform2, glm::vec3(0.0f, -0.1f, 0.0f));
				distanceToPlayer++;
				cout << "Distância: " << distanceToPlayer << endl;
				goDown = false;
				//17 dies
			}
			enemyPos += enemyDir;
			if ((enemyPos == 19 || enemyPos == 0) && goDown == false) {
				enemyDir = -enemyDir;
				goDown = true;
			}

			enemyMovement = 0;
		}
		glViewport(0, 0, g_gl_width, g_gl_height);


		glfwPollEvents();
		if (GLFW_PRESS == glfwGetKey(g_window, GLFW_KEY_ESCAPE)) {
			glfwSetWindowShouldClose(g_window, 1);
		}
		// put the stuff we've been drawing onto the display
		if (distanceToPlayer == 17) {
			glfwSetWindowShouldClose(g_window, 1);
		}
		glfwSwapBuffers(g_window);
	}

	// close GL context and any other GLFW resources
	glfwTerminate();
	return 0;
}

You see, the objects with textures for Alien.jpg and Shot.jpg are not appearing on screen. Only the Player with the Nave.jpg texture is being rendered.

You didn’t show your fragment shader, but it must include uniforms for the 2nd and third textures.

If you use just one texture, the sampler2d that you use in the fragment shader defaults to the first texture bound. However, if you use more than one texture, you must explicitly use a sampler2d for each texture.

Regards,

Jeff

P.S. I just recently learned this myself on one of my current hobby projects.

P.S #2 - Check out my youtube channel on opengl programming!
https://www.youtube.com/channel/UCzx8alrxVELz5h1dfCdkdfg?view_as=subscriber