I was trying to draw a Triangle with a custom FragmentShader for a gradient effect on the triangle.
I was trying to use Indexed-Drawing instead of Drawing Arrays.
Although glDrawArrays(...)
works just fine, glDrawElements(...)
doesn't work for me. I just get a blank screen with indexed-drawing as opposed to a normal output (Screenshots are attached below).
Could someone tell me where I'm going wrong?
(Here's my code)
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
// Vertex Shader source code
const char* vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"out vec3 outColor;\n"
"void main() {\n"
" gl_Position = vec4(aPos, 1.0);\n"
" outColor = aColor;\n"
"}\n\0";
// Fragment Shader source code
const char* fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 outColor;\n"
"void main() {\n"
" FragColor = vec4(outColor, 1.0f);\n"
"}\n\0";
const int width{ 800 };
const int height{ 600 };
bool WIREFRAME_MODE = false;
int main() {
glfwInit();
#pragma region Creating a Window
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(width, height, "Triangle Exercise", nullptr, nullptr);
if (!window) {
std::cout << " >> Failed to create window!" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
#pragma endregion
gladLoadGL();
glViewport(0, 0, width, height);
#pragma region Shader Compilation
// Vertex Shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glCompileShader(vertexShader);
// Fragment Shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);
// Shader Program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// Delete the shaders
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
#pragma endregion
#pragma region Vertex and Index data
// Vertex data
GLfloat vertices[] = {
// positions // colors
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom left
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // top
};
// Index data
GLuint indices[] = {
0, 1, 2
};
#pragma endregion
#pragma region VAO, VBO and EBO
GLuint VAO; // Vertex Attribute Object ref
GLuint VBO; // Vertex Buffer Object ref
GLuint EBO; // Element Buffer Object ref
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Point to the X,Y,Z vertex data in the VBO (at layout location 0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * (sizeof(GLfloat)), (void*)0);
glEnableVertexAttribArray(0);
// Point to the RGB color data in the VBO (at layout location 1)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * (sizeof(GLfloat)), (void*)(3*sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0); // unbind VBO
glBindVertexArray(0); // unbind VAO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // unbind EBO (conatined in VAO)
#pragma endregion
// While window isn't closed
while (!glfwWindowShouldClose(window)) {
if (WIREFRAME_MODE) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Wireframe mode
}
else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Fill mode
}
glClearColor(0, 0, 0, 1); // black background
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
// WORKS FINE
glDrawArrays(GL_TRIANGLES, 0, sizeof(vertices) / sizeof(GLfloat));
// DOESN'T WORK?
//
//glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(GLuint), GL_UNSIGNED_INT, indices);
//
glfwSwapBuffers(window);
glfwPollEvents();
}
#pragma region Cleanup
glDeleteProgram(shaderProgram);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &VAO);
glDeleteBuffers(1, &EBO);
glfwDestroyWindow(window);
#pragma endregion
glfwTerminate();
return 0;
}
Output screenshot with glDrawArrays()
:
Output screenshot with glDrawElements()
:
I tried checking if my EBO's parameters were wrong, but they seem fine to me.
The last parameter of glDrawElements
("indices") should not be a pointer to the indices array, but "an offset of the first index in the array in the data store of the buffer" (ref), then cast to a GLvoid*
. Here, the draw call should use all 3 indices, so the offset should be 0:
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(GLuint), GL_UNSIGNED_INT, static_cast<GLvoid*>(0));
(Other, roughly equivalent expressions are also possible, such as 0
, NULL
and nullptr
.)
The confusion may have come from documentation such as this, where the parameter is incorrectly described as "a pointer to the location where the indices are stored".
I suspect that this how glDrawElements
worked in older OpenGL versions (e.g. OpenGL 1.1) and that since the use of the function has changed while the function prototype has remained the same, hence the oddity of having to cast byte offset to a void pointer.