I am building a simple test to see if I can render multiple separate objects using multiple EBOs, VAOs, and VBOs. I've gotten this to work in the past with just quads, but now that I am using a cube and a quad, only the quad will be drawn to the screen, no matter the order in which I draw them. I am using the stb_image
, cglm
, glad
, glfw
, and OpenGL
libraries along with pure C, no C++.
#define _CRT_SECURE_NO_WARNINGS
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include "stb/stb_image.h"
#include <cglm/cglm.h>
typedef struct WindowData {
GLFWwindow* window;
int status;
} WindowData;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
unsigned int setUpShader(const char* vertSource, const char* fragSource);
WindowData buildWindow();
unsigned int setUpTexture();
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
// shader sources
const char* vertexShaderSource3 = "#version 330 core\n" // vert // projection shader
"layout(location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"layout(location = 2) in vec2 aTexCoord;\n"
"out vec3 vertexColor;\n"
"out vec2 TexCoord;\n"
"out vec3 vertexPos;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"void main()\n"
"{\n"
" vertexColor = aColor; \n"
" vertexPos = aPos;\n"
" gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
" TexCoord = aTexCoord;\n"
"}\n";
const char* fragmentShaderSource4 = "#version 330 core\n" //frag // texture RGB shader
"out vec4 FragColor;\n"
"in vec3 vertexColor;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D ourTexture;\n"
"void main()\n"
"{\n"
" FragColor = texture(ourTexture, TexCoord) * vec4(vertexColor, 1.0);\n"
"}\n\0";
int main(int argc, char* argv[])
{
// SETUP WINDOW
WindowData windowBuildResult = buildWindow();
if(windowBuildResult.status == -1) {
return -1;
}
GLFWwindow* window = windowBuildResult.window;
// GLAD: LOAD ALL OpenGL FUNCTION POINTERS
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
printf("Failed to initialize GLAD\n");
return -1;
}
// SET UP SHADERS AND TEXTURES
unsigned int modelShader = setUpShader(vertexShaderSource3, fragmentShaderSource4);
// SET UP VERTEX DATA AND VBOs/VAOs/EBO
float plane1verts[] = {
//---position--------/----color--------/texture coordinates
//X------Y------Z----/R-----G-----B----/X-----Y----/
-0.75f, -1.0f, -0.75f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // 0
0.75f, -1.0f, -0.75f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // 1
0.75f, -1.0f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // 2
-0.75f, -1.0f, 0.75f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 3
};
float planeIndices[] = {
0, 1, 3, // 1st triangle
1, 2, 3, // 2nd triangle
};
float cube1verts[] = {
//---position--------/----color--------/texture coordinates
//X------Y------Z----/R-----G-----B----/X-----Y----/
// back face
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 0 // RED // top right
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 1 // YELLOW // bottom right
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, // 2 // GREEN // bottom left
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // 3 // BLUE // top left
// front face
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // 4
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 5
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 6
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // 7
// left face
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // 8
-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // 9
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // 10
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 11
// right face // working on color correction
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 12
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // 13
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, // 14
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 15
// bottom face
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // 16
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // 17
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // 18
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 19
// top face
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // 20
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 21
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 22
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // 23
};
unsigned int cubeIndices[] = {
//back face
0, 1, 3, // 1st triangle
1, 2, 3, // 2nd triangle
//front face
4, 5, 6, // 3rd triangle
6, 7, 4, // 4th triangle
//left face
8, 9, 11, // 5th triangle
9, 10, 11, // 6th triangle
//right face
12, 13, 15, // 7th triangle
13, 14, 15, // 8th triangle
//bottom face
16, 17, 19, // 9th triangle
17, 18, 19, // 10th triangle
//top face
20, 21, 23, // 11th triangle
21, 22, 23, // 12th triangle
};
vec3 cubePositions[] = {
{ 0.0f, 0.0f, 0.0f },
{ 2.0f, 5.0f, -15.0f },
{ -1.5f, -2.2f, -2.5f },
{ -3.8f, -2.0f, -12.3f },
{ 2.4f, -0.4f, -3.5f },
{ -1.7f, 3.0f, -7.5f },
{ 1.3f, -2.0f, -2.5f },
{ 1.5f, 2.0f, -2.5f },
{ 1.5f, 0.2f, -1.5f },
{ -1.3f, 1.0f, -1.5f }
};
unsigned int VBOs[2], VAOs[2], EBOs[2];
glGenVertexArrays(2, VAOs);
glGenBuffers(2, VBOs);
glGenBuffers(2, EBOs);
// SETUP TEXTURES
unsigned int texture = setUpTexture();
// SETUP RENDERING
// CUBE
glBindVertexArray(VAOs[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube1verts), cube1verts, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[0]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// PLANE
glBindVertexArray(VAOs[1]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(plane1verts), plane1verts, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(planeIndices), planeIndices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// To draw in triangle as a wireframe:
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glLineWidth(2.0f);
glEnable(GL_DEPTH_TEST);
// RENDER LOOP
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/*--------------------------------------------------------------------------------------*/
// SETUP CAMERA/FOV
// initialize view and projection matrices
mat4 view = GLM_MAT4_IDENTITY_INIT; // camera
mat4 projection = GLM_MAT4_IDENTITY_INIT; // FOV
// do view and projection transforms
float viewSinMovement = (float)sin(glfwGetTime());
glm_rotate(view, glm_rad(0.0f), (vec3) { 0.0f, 1.0f, 0.0f });
glm_translate(view, (vec3) { viewSinMovement, -0.25f, -3.0f });
glm_perspective(glm_rad(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f, projection); // FOV, aspect ratio, near Z, far Z, projection matrix
/*--------------------------------------------------------------------------------------*/
// DRAW TEXTURED CUBE IN PERSPECTIVE
// setup cube textures, shaders, and EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUseProgram(modelShader);
// assign view and projection matrices to shader
unsigned int viewLoc1 = glGetUniformLocation(modelShader, "view");
unsigned int projectionLoc1 = glGetUniformLocation(modelShader, "projection");
glUniformMatrix4fv(viewLoc1, 1, GL_FALSE, (const GLfloat*) view);
glUniformMatrix4fv(projectionLoc1, 1, GL_FALSE, (const GLfloat*) projection);
// bind VAO
glBindVertexArray(VAOs[0]);
for (int i = 0; i < (sizeof(cubePositions)/sizeof(vec3)); i++) {
// initialize model matrix
mat4 model1 = GLM_MAT4_IDENTITY_INIT;
// do model matrix transforms
glm_translate(model1, cubePositions[i]);
glm_rotate(model1, glm_rad((float)glfwGetTime() * 10.0f * i), (vec3) { 1.0f, 0.5f, 0.0f });
// assign model matrix to shader
unsigned int modelLoc2 = glGetUniformLocation(modelShader, "model");
glUniformMatrix4fv(modelLoc2, 1, GL_FALSE, (const GLfloat*)model1);
// draw cube
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
}
/*--------------------------------------------------------------------------------------*/
// DRAW PLANE IN PERSPECTIVE
// setup plane textures, shaders, and EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[1]);
glBindTexture(GL_TEXTURE_2D, texture);
glUseProgram(modelShader);
// initialize model matrix
mat4 model2 = GLM_MAT4_IDENTITY_INIT;
// do model matrix transforms
glm_translate(model2, (vec3) { 0.0f, 0.0f, 0.0f });
glm_rotate(model2, 0.0f, (vec3) { 0.0f, 0.0f, 0.0f });
glm_scale(model2, (vec3) { 1.0f, 1.0f, 1.0f });
// assign model matrix to shader
unsigned int modelLoc1 = glGetUniformLocation(modelShader, "model");
glUniformMatrix4fv(modelLoc1, 1, GL_FALSE, (const GLfloat*)model2);
// assign view and projection matrices to shader
unsigned int viewLoc2 = glGetUniformLocation(modelShader, "view");
unsigned int projectionLoc2 = glGetUniformLocation(modelShader, "projection");
glUniformMatrix4fv(viewLoc2, 1, GL_FALSE, (const GLfloat*)view);
glUniformMatrix4fv(projectionLoc2, 1, GL_FALSE, (const GLfloat*)projection);
// draw plane
glBindVertexArray(VAOs[1]);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// SWAP BUFFERS AND CHECK FOR USER INPUTS
glfwSwapBuffers(window);
glfwPollEvents();
}
// DE-ALLOCATE RESOURCES
glDeleteVertexArrays(2, VAOs);
glDeleteBuffers(2, VBOs);
glDeleteBuffers(2, EBOs);
glDeleteProgram(modelShader);
// CLEAR ALL RESOURCES AND STOP OpenGL
glfwTerminate();
return 0;
}
// CHANGE WINDOW SIZE AND VIEWPORT
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, 1);
}
}
unsigned int setUpShader(const char* vertSource, const char* fragSource) {
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertSource, NULL);
glCompileShader(vertexShader);
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
}
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
}
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return shaderProgram;
}
WindowData buildWindow() {
// INIT GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// CHANGE SETTINGS BASED ON OS
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// BUILD WINDOW
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
WindowData windowData = { window, 0 };
if (window == NULL)
{
printf("Failed to create GLFW window\n");
glfwTerminate();
windowData.status = -1;
return windowData;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
return windowData;
}
unsigned int setUpTexture(){
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_LINEAR);
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(1);
unsigned char* data = stbi_load("container.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 {
printf("ERROR::TEXTURE::LOAD::FAILED\n");
}
stbi_image_free(data);
return texture;
}
What I have verified is correct:
modelShader
(both the vert shader and frag shader)GL_DEPTH_TEST
being enabledview
and projection
matricesmodelShader
on both the cube and quadThe only thing that is incorrect, is when I set the render mode to wireframe
, the cube(s) only have a single line being drawn, while the entire quad is being drawn properly (all of its lines show up)
Index arrays (such as planeIndices[]
and cubeIndices[]
) need to be unsigned int
and not float
On line 171 I declared planeIndices[]
as a float
and not an unsigned int
array, hence why OpenGL did not properly render the plane.
This does not actively break the render cycle, which is why it took so long to find it.
Double check the types used for all your object data as well as the vertex data and indices if you are having trouble getting an object to render.
PS: Also make sure you are binding a texture to your object if the shader utilizes one. (modelShader
uses a texture and the plane
object did not have the any texture bound to it)