I try to make the code cleaner by extracting the whole main Proramm in some classes. So I have a vertex buffer, index buffer and a shader class. Only the array buffer is in the main and it worked.
Now I created a layout class and a vertex array class to put different layouts in the vertex array and the renderfunction in my main stopped to work.
Here is my code:
vertexBuffer.cpp
#include "../headerData/VertexBuffer.h"
VertexBuffer::VertexBuffer(const void* data, unsigned int size) {
glGenBuffers(1, &bufferId);
glBindBuffer(GL_ARRAY_BUFFER, bufferId);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}
VertexBuffer::~VertexBuffer() {
glDeleteBuffers(1, &bufferId);
}
void VertexBuffer::bind() const {
glBindBuffer(GL_ARRAY_BUFFER, bufferId);
}
void VertexBuffer::unbind() const {
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
vertex Index cpp:
#include "../headerData/IndexBuffer.h"
IndexBuffer::IndexBuffer(const void* data, unsigned int count) {
glGenBuffers(1, &bufferId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, count, data, GL_STATIC_DRAW);
}
IndexBuffer::~IndexBuffer() {
glDeleteBuffers(1, &bufferId);
}
void IndexBuffer::bind() const {
glBindBuffer(GL_ARRAY_BUFFER, bufferId);
}
void IndexBuffer::unbind() const {
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
VertexLayout.h
#pragma once
#include <vector>
//Stubides Layout da vieles vorgegeben wird
struct ElementLayout {
unsigned int size; //anzahl an koordinaten die zählen
unsigned int stride; //Die größe der
};
class VertexLayout {
private:
std::vector<ElementLayout> elements;
public:
VertexLayout();
~VertexLayout();
void layoutPush(unsigned int size);
std::vector<ElementLayout> getElements() const;
};
vertexLayout.cpp:
#include"../headerData/VertexLayout.h"
VertexLayout::VertexLayout() {}
VertexLayout::~VertexLayout() {}
void VertexLayout::layoutPush(unsigned int size)
{
elements.push_back({ size, size * sizeof(float) });
}
std::vector<ElementLayout> VertexLayout::getElements() const {
return elements;
}
VertexArray.cpp
#include "../headerData/VertexArray.h"
#include <iostream>
VertexArray::VertexArray() {
glGenVertexArrays(1, &vertexArrayID);
}
VertexArray::~VertexArray() {
glDeleteVertexArrays(1, &vertexArrayID);
}
void VertexArray::bind() const {
glBindVertexArray(vertexArrayID);
}
void VertexArray::unbind() const {
glBindVertexArray(0);
}
//Vom Buffer soll auch das zugehörige Layout übergeben werden
void VertexArray::addBuffer(VertexBuffer& vb, VertexLayout& layout) {
bind();
vb.bind();
std::vector<ElementLayout> element = layout.getElements();
unsigned int offset = 0;
for (int i = 0; i < element.size(); i++) {
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, element[i].size, GL_FLOAT, GL_FALSE, element[i].stride, (void*)0);
offset += element[i].size * sizeof(float);
std::cout << GL_NO_ERROR;
}
std::cout << glGetError();
}
main.cpp:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "headerData/VertexBuffer.h"
#include "headerData/IndexBuffer.h"
#include "headerData/Shader.h"
#include "headerData/VertexLayout.h"
#include "headerData/VertexArray.h"
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(800, 800, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
Shader shader;
shader.bind();
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
0.5f, 0.5f, 0.0f, // top right
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, // top left
-1.0f, -1.0f, 0.0f //zum Testen
//dreieck
};
unsigned int indices[] = { // note that we start from 0!
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
float vertices2[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 0.0f, 0.0f
};
unsigned int test;
glGenVertexArrays(1, &test);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(test);
//glBindBuffer(GL_ARRAY_BUFFER, VBO);
//glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
VertexBuffer vb(vertices, 5 * 3 * sizeof(float));
IndexBuffer ib(indices, 6 * sizeof(unsigned int));
VertexLayout layout;
layout.layoutPush(3);
VertexArray va;
//va.addBuffer(vb, layout);
//va.addBuffer(vb, layout); when this is uncomment the render function doesn't work
//Parameter
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(0));
//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(0);
//glEnableVertexAttribArray(1);
// uncomment this call to draw in wireframe polygons.
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
shader.bind();
//glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, (void*)(sizeof(unsigned int) * 3));
//glDrawArrays(GL_TRIANGLES, 0, 3);
//glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, (void*)(sizeof(unsigned int) * 3));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// glBindVertexArray(0); // no need to unbind it every time
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &test);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
With glGetError() I get the number 1281 =>invalid value but where.
The index buffer (ELEMENT_ARRAY_BUFFER) binding is stored within the Vertex Array Object. When glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferId) is called a reference to the element buffer object is stored in the currently bound Vertex Array Object. Therefore the VAO must be bound before the element buffer with glBindVertexArray(vertexArrayID):
VertexArray va;
va.bind();
IndexBuffer ib(indices, 6 * sizeof(unsigned int));
Related
I'm trying to implement model loading, but I'm stuck with one problem. When i try to draw a mesh (a single textured quad written by hand for test purposes) for some reason duplicated data associated with the first vertex is passed to the vertex shader
(RenderDoc screen).
Here is an example using a stripped down version of my class that still exhibits this behaviour:
#define TEST_MESH
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
struct Vertex {
float position[3];
float color[3];
};
class Mesh {
public:
Mesh(std::vector<Vertex> vertices, std::vector<unsigned int> indices);
~Mesh();
Mesh(const Mesh&) = delete;
Mesh& operator=(const Mesh&) = delete;
Mesh(Mesh&& other);
Mesh& operator=(Mesh&& other);
void Draw(unsigned int program_id);
std::vector<Vertex> m_Vertices;
std::vector<unsigned int> m_Indices;
private:
unsigned int m_VAO, m_VBO, m_EBO;
void Setup();
void Release();
};
int main() {
//GLFW INIT
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//GLFW Window setup
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Mesh Test", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
//GLAD INIT
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
//Shader setup:
const char* vertex = "#version 460 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aCol;\n"
"out vec3 vColor;\n"
"void main() {\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
" vColor = aCol;\n"
"}\0";
const char* fragment = "#version 460 core\n"
"in vec3 vColor;\n"
"out vec4 FragColor;\n"
"void main() {\n"
" FragColor = vec4(vColor, 1.0);\n"
"}\0";
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertex, NULL);
glCompileShader(vertexShader);
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment, NULL);
glCompileShader(fragmentShader);
unsigned int program;
program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
//Data:
float floats[24] = {
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f,-0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f,-0.5f, 0.0f, 1.0f, 1.0f ,0.0f };
unsigned int uints[6] = { 0, 1, 2, 2, 3, 0 };
#ifdef TEST_MESH
//Mesh assembly
std::vector<Vertex> vertices;
for (int i = 0; i < 4; i++)
vertices.push_back(Vertex{ floats[6 * i], floats[6 * i + 1], floats[6 * i + 2],
floats[6 * i + 3], floats[6 * i + 4], floats[6 * i + 5] });
std::vector<unsigned int> indices{ 0, 1, 2, 2, 3, 0 };
Mesh mesh(vertices, indices);
#else
unsigned int VAO, VBO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(floats), &floats[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uints), &uints[0], GL_STATIC_DRAW);
//Positions
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//Colors
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
#endif
//Render loop
while (!glfwWindowShouldClose(window)) {
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
#ifdef TEST_MESH
mesh.Draw(program);
#else
glUseProgram(program);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
#endif
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
Mesh::Mesh(std::vector<Vertex> vertices, std::vector<unsigned int> indices) {
m_Vertices = vertices;
m_Indices = indices;
Setup();
}
Mesh::Mesh(Mesh&& other)
: m_VAO(other.m_VAO), m_VBO(other.m_VBO), m_EBO(other.m_EBO)
, m_Vertices(other.m_Vertices), m_Indices(other.m_Indices)
{
other.m_VAO = 0;
other.m_VBO = 0;
other.m_EBO = 0;
}
Mesh& Mesh::operator=(Mesh&& other) {
if (this != &other) {
Release();
std::swap(m_VAO, other.m_VAO);
std::swap(m_VBO, other.m_VBO);
std::swap(m_EBO, other.m_EBO);
m_Vertices = other.m_Vertices;
m_Indices = other.m_Indices;
}
return *this;
}
Mesh::~Mesh() {
Release();
}
void Mesh::Release() {
glDeleteVertexArrays(1, &m_VAO);
glDeleteBuffers(1, &m_VBO);
glDeleteBuffers(1, &m_EBO);
}
void Mesh::Setup() {
glGenVertexArrays(1, &m_VAO);
glGenBuffers(1, &m_VBO);
glGenBuffers(1, &m_EBO);
glBindVertexArray(m_VAO);
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
glBufferData(GL_ARRAY_BUFFER, m_Vertices.size() * sizeof(Vertex), &m_Vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Indices.size() * sizeof(unsigned int), &m_Indices[0], GL_STATIC_DRAW);
//Positions
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
glEnableVertexAttribArray(0);
//Colors
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, color));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void Mesh::Draw(unsigned int program_id) {
glUseProgram(program_id);
glBindVertexArray(m_VAO);
glDrawElements(GL_TRIANGLES, m_Indices.size(), GL_UNSIGNED_INT, 0);
}
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int
height) {
glViewport(0, 0, width, height);
}
In your Mesh::Setup you have this line at the end:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
This unbinds your EBO from your VAO. Your implementation seems to treat undefined reads as zero, therefore all you see is the 0th vertex replicated six times.
Once you bound the EBO you don't need to unbind it.
(Note: unbinding GL_ARRAY_BUFFER, on the other hand, is OK. This is because the VBO is attached to the VAO at the time you call any of the *Pointer functions, not at the time you bind it to GL_ARRAY_BUFFER.)
(Note: Since you use the latest OpenGL version, I strongly recommend that you use the Direct State Access (DSA) functions. In this case you would bind the EBO with
glVertexArrayElementBuffer(m_VAO, m_EBO);
call, which I think would make the mistake much more obvious. See a quick reference of VAO state and the recommended functions to use to manipulate it.)
I've been following this tutorial in a bid to learn OpenGL. I have something that works, but only if I use global variables:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
// float vertices[] = {
// -0.5f, -0.5f, 0.0f,
// 0.5f, -0.5f, 0.0f,
// 0.0f, 0.5f, 0.0f
// };
float rectVertices[] = {
0.5f, 0.5f, 0.0f, // Top Right
0.5f, -0.5f, 0.0f, // Bottom Right
-0.5f, -0.5f, 0.0f, // Bottom Left
-0.5f, 0.5f, 0.0f // Top Left
};
uint rectIndices[] = {
0, 1, 3, // First Triangle
1, 2, 3 // Second Triangle
};
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main() {\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\n";
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"void main() {\n"
"color = vec4(1.0, 0.5, 0.2, 1.0);\n"
"}\n";
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
void finalize() {
glfwTerminate();
}
void programExit(int code) {
finalize();
exit(code);
}
void enforce(int success, const char* msg, const char* info) {
if (!success) {
std::cerr << msg << info << std::endl;
exit(-1);
}
}
// Initialise
// GLFW and OpenGL
void initialiseGLFW(int vMajor, int vMinor) {
// Initialising GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, vMajor);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, vMinor);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
}
// Window Creation
GLFWwindow* createWindow(int width, int height, const char* title) {
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr, nullptr);
enforce(window != nullptr, "Failed to create GLFW window", nullptr);
return window;
}
void initialiseGLEW(bool experimental) {
glewExperimental = experimental;
enforce(glewInit() == GLEW_OK, "Failed to initialise GLEW", nullptr);
}
GLFWwindow* initialise(int width, int height, const char* title) {
initialiseGLFW(3, 3);
// Creating GLFW Window
GLFWwindow* window = createWindow(width, height, title);
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
// Initialising GLEW
initialiseGLEW(true);
glViewport(0, 0, 800, 600);
return window;
}
// Creating Shaders
void checkCompilationError(uint shader) {
int success;
char infoLog[512];
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
enforce(success, "Shader compilation error: ", infoLog);
}
void checkLinkingError(uint program) {
int success;
char infoLog[512];
glGetProgramiv(program, GL_LINK_STATUS, &success);
glGetProgramInfoLog(program, 512, nullptr, infoLog);
enforce(success, "Program linking error: ", infoLog);
}
uint compileShader(GLenum type, const char* source, ushort count, int* lengths) {
uint shader = glCreateShader(type);
glShaderSource(shader, count, &source, lengths);
glCompileShader(shader);
checkCompilationError(shader);
return shader;
}
uint createProgram(uint vShader, uint fShader) {
uint program = glCreateProgram();
glAttachShader(program, vShader);
glAttachShader(program, fShader);
glLinkProgram(program);
checkLinkingError(program);
return program;
}
// Subprocedure specific to this program
uint initialiseShaders(const char* vsSource, const char* fsSource) {
// Initialising shaders
uint vShader = compileShader(GL_VERTEX_SHADER, vsSource, 1, nullptr);
uint fShader = compileShader(GL_FRAGMENT_SHADER, fsSource, 1, nullptr);
// Link program
GLuint shaderProgram = createProgram(vShader, fShader);
// clean up
glDeleteShader(vShader);
glDeleteShader(fShader);
return shaderProgram;
}
void configureVBO(float* vertices) {
for (int i = 0; i < 12; i++) std::cout << vertices[i] << std::endl;
glBufferData(
GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW
);
// Tell OpenGL how to interpret the vertices
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*) 0);
glEnableVertexAttribArray(0);
}
void configureEBO(uint* indices) {
for (int i = 0; i < 6; i++) std::cout << indices[i] << std::endl;
glBufferData(
GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW
);
}
uint intialiseVAO(float* vertices, uint* indices) {
uint VAO, VBO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
configureVBO(vertices);
if (indices != nullptr) {
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
configureEBO(indices);
}
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return VAO;
}
void execGameLoop(GLFWwindow* window, uint shaderProgram, uint VAO) {
while(!glfwWindowShouldClose(window)) {
glfwPollEvents();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Set the program to be used
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
}
int main() {
// Config
int width = 800, height = 800;
const char* title = "Learn OpenGL";
// Initialise GLFW and GLEW
GLFWwindow* window = initialise(width, height, title);
// Initialise Shader program
uint shaderProgram = initialiseShaders(vertexShaderSource, fragmentShaderSource);
// Configuring VAO, VBO and EBO
uint VAO = intialiseVAO(rectVertices, rectIndices);
execGameLoop(window, shaderProgram, VAO);
finalize();
return 0;
}
My problem is specifically to do with:
void configureVBO(float* vertices) {
for (int i = 0; i < 12; i++) std::cout << vertices[i] << std::endl;
glBufferData(
GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW
);
// Tell OpenGL how to interpret the vertices
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*) 0);
glEnableVertexAttribArray(0);
}
void configureEBO(uint* indices) {
for (int i = 0; i < 6; i++) std::cout << indices[i] << std::endl;
glBufferData(
GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW
);
}
which works as expected when using the rectVertices/Indices global variables, but don't work when they're passed as parameters. The for-loops in each method prints the array, and they contain the values I expect them to. Why does OpenGL draw the rectangle when I use the global variables, but not when I use local parameters?
void configureVBO(float* vertices) {
for (int i = 0; i < 12; i++) std::cout << vertices[i] << std::endl;
glBufferData(
GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW
);
// Tell OpenGL how to interpret the vertices
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*) 0);
glEnableVertexAttribArray(0);
}
The problem here is that sizeof(vertices) is not doing what you think it does. It doesn't tell you how many vertices are in the array -- rather, it's telling you how many bytes a variable of type vertices occupies. Since vertices is a pointer, that number is likely to be either 4 or 8 depending on your system -- but importantly, it has nothing to do with how many vertices you're actually intending to upload.
Instead, what you need to do is tell OpenGL how big you want the buffer to be, in bytes. You calculate that by number of vertices * sizeof(vertex_type). So in your case that would be 12 * sizeof(float).
In general, you'll either need to include a second parameter to the function which includes the number of vertices, or use a std::vector<float> to hold your vertex data instead. For example, with a vector this would become:
void configureVBO(const std::vector<float>& vertices) {
for (auto v : vertices) std::cout << v << "\n";
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float),
vertices.data(), GL_STATIC_DRAW);
// etc...
}
The reason the code worked when you used a global variable is to do with the way C++ passes arrays to functions (which in turn, it inherited from C). When you pass an array, what the function receives is just a pointer to the start of the array -- the information about the length of the array has been lost, so sizeof(vertices) can't tell you how long the array actually is. However, if you use a global variable, then the compiler can see the definition float vertices[12] even within the configureVBO function, so in that case sizeof() will do what you expect.
This is a very common mix-up for people getting started with C and C++. I'd really recommend using std::vector instead (for many reasons, of which this is just one), but it's also worth reading up on how arrays and pointers work in C and C++.
im learning about OpenGL and i wrote the following code in C++ using this guide and this video.
Also i am using GLFW for context creation and GLEW for GL functions
Most of the Shader class is copied from the video up linked,
The problem is that using glDrawElements() to render inside the main loop gets me a segmentation fault:
Segmentation fault
------------------
(program exited with code: 139)
Press return to continue
while with glDrawArrays() i can draw with no problems.
Does anyone know what this could be caused by?
I think the error might depend on the implementation of the Shader class, because i used glDrawArrays() in other programs that did not used this class and that cared about shaders in the main function.
program.cpp
//INCLUDE AND DECLARATIONS
#include <iostream>
#include <fstream>
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
#include "Shader.h"
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
unsigned long getFileLength(std::ifstream& file);
int loadshader(char* filename, GLchar** ShaderSource, unsigned long* len);
const GLuint WIDTH = 800, HEIGHT = 600;
//VERTEX DATA
float data[] = {
// X Y R G B
-0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // Top-left
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // Top-right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // Bottom-right
-0.5f, -0.5f, 1.0f, 1.0f, 1.0f // Bottom-left
};
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
//main
int main()
{ //INIT GLFW AND WINDOW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
glewExperimental = GL_TRUE;
glewInit();
glViewport(0, 0, WIDTH, HEIGHT);
//ALLOCATE BUFFERS
//VERTEX ARRAY BUFFER
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
//ELEMENT ARRAY BUFFER
GLuint ebo;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
//CREATE SHADER
Shader shader("./shaders/basicShader");
// main loop
while (!glfwWindowShouldClose(window))
{
shader.Bind();
glfwPollEvents(); //window events
glClearColor(1.0f, 0.0f, 0.5f, 0.5f); //background
glClear(GL_COLOR_BUFFER_BIT);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window); //update window
}
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ebo);
glfwTerminate();
return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
Shader.h
#include <iostream>
#include <fstream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
class Shader
{
public:
Shader(const std::string& filepath);
~Shader();
void Bind();
private:
static const GLuint NUM_SHADERS = 2;
GLuint program;
GLuint shaders[NUM_SHADERS];
std::string LoadShader(const std::string& fileName);
void CheckShaderError(GLuint shader, GLuint flag, bool isProgram, const std::string& errorMessage);
GLuint CreateShader(const std::string& text, unsigned int type);
};
Shader.cpp
#include "Shader.h"
Shader::Shader(const std::string& filepath)
{
program = glCreateProgram();
shaders[0] = CreateShader(LoadShader(filepath + ".vs"), GL_VERTEX_SHADER);
shaders[1] = CreateShader(LoadShader(filepath + ".fs"), GL_FRAGMENT_SHADER);
for(unsigned int i = 0; i < NUM_SHADERS; i++)
{
glAttachShader(program, shaders[i]);
}
glBindAttribLocation(program, 0, "position");
glBindFragDataLocation(program, 0, "outColor");
glLinkProgram(program);
CheckShaderError(program, GL_LINK_STATUS, true, "Error linking shader program");
glValidateProgram(program);
CheckShaderError(program, GL_LINK_STATUS, true, "Invalid shader program");
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLint posAttrib = glGetAttribLocation(program, "position");
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), 0);
glEnableVertexAttribArray(posAttrib);
GLint AttribColor = glGetAttribLocation(program, "color");
glVertexAttribPointer(AttribColor, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)(2*sizeof(float)));
glEnableVertexAttribArray(AttribColor);
}
Shader::~Shader()
{
for(unsigned int i = 0; i < NUM_SHADERS; i++)
{
glDetachShader(program, shaders[i]);
glDeleteShader(shaders[i]);
}
glDeleteProgram(program);
}
void Shader::Bind()
{
glUseProgram(program);
}
//loads shaders from files
std::string Shader::LoadShader(const std::string& fileName)
{
std::ifstream file;
file.open((fileName).c_str());
std::string output;
std::string line;
if(file.is_open())
{
while(file.good())
{
getline(file, line);
output.append(line + "\n");
}
}
else
{
std::cerr << "Unable to load shader: " << fileName << std::endl;
}
return output;
}
//Checks for eventual errors in shaders
void Shader::CheckShaderError(GLuint shader, GLuint flag, bool isProgram, const std::string& errorMessage)
{
GLint success = 0;
GLchar error[1024] = { 0 };
if(isProgram)
glGetProgramiv(shader, flag, &success);
else
glGetShaderiv(shader, flag, &success);
if(success == GL_FALSE)
{
if(isProgram)
glGetProgramInfoLog(shader, sizeof(error), NULL, error);
else
glGetShaderInfoLog(shader, sizeof(error), NULL, error);
std::cerr << errorMessage << ": '" << error << "'" << std::endl;
}
}
GLuint Shader::CreateShader(const std::string& text, unsigned int type)
{
GLuint shader = glCreateShader(type);
if(shader == 0)
std::cerr << "error allocating shader" << std:: endl;
const GLchar* p[1];
p[0] = text.c_str();
GLint lengths[1];
lengths[0] = text.length();
glShaderSource(shader, 1, p, lengths);
glCompileShader(shader);
CheckShaderError(shader, GL_COMPILE_STATUS, false, "Error compiling shader!");
return shader;
}
The problem is with your index buffer binding. The index buffer (GL_ELEMENT_ARRAY_BUFFER) binding is part of the VAO state. Skipping the unrelated calls, you have the following overall sequence:
...
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
...
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
...
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
Since the GL_ELEMENT_ARRAY_BUFFER binding is part of the VAO state, the current index buffer binding is replaced with the index buffer binding in the VAO state when you call:
glBindVertexArray(vao);
Since you never bind an index buffer while the VAO is bound, this means that the index buffer binding in the VAO state is 0. As a result, you don't have an index buffer bound after this call.
What happens next is that you make the glDrawElements() call with the last argument 0. Without an index buffer bound, the last argument is interpreted as a pointer to CPU memory. So OpenGL tries to read index data at address 0, which causes the crash.
To fix this, you simply have to bind the index buffer while the VAO is bound. You can either do that by changing the order of your calls, and create/bind the VAO before you set up the index buffer. Or you can bind it again when you set up the vertex attribute state. For example, at the very end of the Shader constructor, after the glVertexAttribPointer() and related calls, you add this call:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
I'm very new to OpenGL and at the moment I'm trying to understand VAO and VBO.
The VAO is simply a collection of VBO.
Each VBO is an attribute of an object, coordinates of vertices of the object, color at each object's vertex, etc.
In the code below, the Vertex struct define 2 properties of a vertex, which is position and color of the vertex. Therefore, 2 VBOs are needed to store these information.
My problem is: I'm able to draw the triangle on the screen, but the color at each vertex doesn't seem to be drawn. Why does this happen?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
// create vertex buffer
GLuint vertexbuffer;
struct Vertex{
GLdouble position[3];
GLfloat color[3];
};
static const GLfloat vertex_data[] = {
-0.7f, -0.7f, 0.0f,
0.7f, -0.7f, 0.0f,
0.0f, 1.0f, 0.0f
};
void SetupGeometry(){
const struct Vertex triangle[3] = {
{{-0.7, -0.7, 0.0}, {1.0f, 0.0f, 0.0f}},
{{0.7, -0.7, 0.0}, {0.0f, 1.0f, 0.0f}},
{{0.0, 1.0, 0.0}, {0.0f, 0.0f, 1.0f}}
};
//GLuint VertexArrayID;
//glGenVertexArrays(1, &VertexArrayID);
//glBindVertexArray(VertexArrayID);
//generate 1 buffer, put the resulting identifier in vertex buffer
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
// give our vertices to OpenGL
glBufferData(GL_ARRAY_BUFFER, 3*sizeof(struct Vertex), triangle, GL_STATIC_DRAW);
// GLuint index, GLuint size, GLenum type, GLboolean normalized, GLsizei stride, const void *offset
glVertexAttribPointer(0, 3, GL_DOUBLE, GL_FALSE, sizeof(struct Vertex), (void*) offsetof(struct Vertex, position));
// any newly created VAO disables array access for all attributes
// array access is enabled by binding the VAO in SetupGeometry and calling:
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(struct Vertex), (void*) offsetof(struct Vertex, color));
glEnableVertexAttribArray(1);
}
void SetupShaders(void){
}
void Render(int i){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_LINE_LOOP, 0, 3);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){
if((key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) && action != GLFW_PRESS){
glfwSetWindowShouldClose(window, GL_TRUE);
}
}
int main( void ) {
/* Create a windowed mode window and its OpenGL context */
GLFWwindow* window;
if( !glfwInit() ) {
printf("Failed to start GLFW\n");
exit( EXIT_FAILURE );
}
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window) {
glfwTerminate();
printf("GLFW Failed to start\n");
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window); // IMPORTANT: Must be done so glew recognises OpenGL
glfwWindowHint(GLFW_SAMPLES, 4);
int err = glewInit();
if (glewInit() != GLEW_OK) {
/* Problem: glewInit failed, something is seriously wrong. */
fprintf(stderr, "Error initializing GLEW: %s\n", glewGetErrorString(err));
}
fprintf(stderr, "Glew done\n");
fprintf(stderr, "GL VERSION INFO %s\n", glGetString(GL_VERSION));
glfwSetKeyCallback(window, key_callback);
SetupGeometry();
while(!glfwWindowShouldClose(window)){
Render(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
}
You don't seem to have included any shaders. You'll need to use a fragment shader to set the colour values for the triangle. This link should help you.
So I'm trying to write a simple 3D rendering engine using GLFW and GLEW in C++. However the program crashes on glDrawArrays(GL_TRIANGLES, 0, model.indicesCount); call. I'm pretty sure I'm doing something wrong but I can't figure out where or what needs to be changed/altered. I'm actually rewriting a perfectly working engine from Java.
My code:
common.h:
#ifndef _COMMON
#define _COMMON
// standard stuff
#include <iostream>
#include <list>
// openGL stuff
#include "GL\glew.h"
#include "GLFW\glfw3.h"
// my stuff
#include "DisplayManager.h"
#include "RawModel.h"
#include "Loader.h"
#include "Renderer.h"
#endif
DisplayManager.h:
#pragma once
#include "common.h"
class DisplayManager{
private:
GLFWwindow* window;
public:
void create(int width = 1280, int height = 720, std::string title = "Untitled"){
if(!glfwInit()){
std::cerr << "GLFW init failed\n";
system("pause");
exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(width, height, title.c_str(), NULL, NULL);
glfwMakeContextCurrent(window);
if(!window){
std::cerr << "Failed to create a window\n";
system("pause");
exit(EXIT_FAILURE);
}
glewExperimental = GL_TRUE;
if(glewInit() != GLEW_OK){
std::cerr << "GLEW init failed\n";
system("pause");
glfwTerminate();
exit(EXIT_FAILURE);
}
}
void update(){
glfwSwapBuffers(window);
glfwPollEvents();
}
int isCloseRequested(){
return glfwWindowShouldClose(window);
}
void close(){
glfwDestroyWindow(window);
glfwTerminate();
}
};
RawModel.h:
#pragma once
struct RawModel{
public:
GLuint vaoID;
GLuint indicesCount;
RawModel(GLuint vaoID, GLuint indicesCount){
this->vaoID = vaoID;
this->indicesCount = indicesCount;
}
};
Loader.h:
#pragma once
#include "common.h"
#define VERTEX_ATTRIB_INDEX 0
#define VERTEX_SIZE 3
class Loader{
public:
// functions
RawModel loadModel(const GLfloat vertices[], GLuint verticesCount){
GLuint vaoID = createAndBindVao();
storeFloatDataInVAO(VERTEX_ATTRIB_INDEX, vertices, verticesCount, VERTEX_SIZE);
unbindVAO();
return RawModel(vaoID, verticesCount);
}
void cleanUp(){
std::list<GLuint>::iterator vao_it = vaos.begin();
for(; vao_it != vaos.end(); ++vao_it){
const GLuint vao = *vao_it;
glDeleteVertexArrays(1, &vao);
}
std::list<GLuint>::iterator vbo_it = vbos.begin();
for(; vbo_it != vbos.end(); ++vbo_it){
const GLuint vbo = *vbo_it;
glDeleteBuffers(1, &vbo);
}
}
private:
// variables
std::list<GLuint> vaos;
std::list<GLuint> vbos;
// functions
GLuint createAndBindVao(){
GLuint vaoID;
glGenVertexArrays(1, &vaoID);
vaos.push_back(vaoID);
glBindVertexArray(vaoID);
return vaoID;
}
void storeFloatDataInVAO(const GLuint attributeIndex, const GLfloat data[], const GLuint dataLength, const GLuint chunkSize){
GLuint vboID;
glGenBuffers(1, &vboID);
vbos.push_back(vboID);
glBindBuffer(GL_VERTEX_ARRAY, vboID);
glBufferData(GL_VERTEX_ARRAY, sizeof(GLfloat) * dataLength * chunkSize, data, GL_STATIC_DRAW);
glVertexAttribPointer(attributeIndex, chunkSize, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindBuffer(GL_VERTEX_ARRAY, 0);
}
void unbindVAO(){
glBindVertexArray(0);
}
};
Renderer.h:
#pragma once
#include "common.h"
#define BLACK 0.0f, 0.0f, 0.0f, 1.0f
#define WHITE 1.0f, 1.0f, 1.0f, 1.0f
#define RED 1.0f, 0.0f, 0.0f, 1.0f
#define GREEN 0.0f, 1.0f, 0.0f, 1.0f
#define BLUE 0.0f, 0.0f, 1.0f, 1.0f
#define YELLOW 1.0f, 1.0f, 0.0f, 1.0f
class Renderer{
public:
void prepare(){
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(YELLOW);
};
void render(RawModel model){
glBindVertexArray(model.vaoID);
glEnableVertexAttribArray(VERTEX_ATTRIB_INDEX);
glDrawArrays(GL_TRIANGLES, 0, model.indicesCount);
glDisableVertexAttribArray(VERTEX_ATTRIB_INDEX);
glBindVertexArray(0);
}
};
and the Source.cpp with the main function:
#include "common.h"
static const GLfloat VERTICES[] = {
// X Y Z
-0.5f, 0.5f, 0,
-0.5f, -0.5f, 0,
0.5f, 0.5f, 0
};
int main(){
DisplayManager display;
display.create();
Loader loader;
RawModel model = loader.loadModel(VERTICES, 3);
Renderer renderer;
// main loop
while(!display.isCloseRequested()){
renderer.prepare();
renderer.render(model);
display.update();
}
loader.cleanUp();
display.close();
return EXIT_SUCCESS;
}
If I comment out the glDrawArrays(GL_TRIANGLES, 0, model.indicesCount); it seams to be working and I get a green window.
The error is here:
glBindBuffer(GL_VERTEX_ARRAY, vboID);
glBufferData(GL_VERTEX_ARRAY, sizeof(GLfloat) * dataLength * chunkSize, data, GL_STATIC_DRAW);
glVertexAttribPointer(attributeIndex, chunkSize, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindBuffer(GL_VERTEX_ARRAY, 0);
GL_VERTEX_ARRAY is not a valid buffer target in OpenGL, the correct one is GL_ARRAY_BUFFER. As a consequence, these commands all should generate a GL error. The attrib pointer function will should generate GL_INVALID_OPERATION, since no GL_ARRAY_BUFFER is bound at the time of the call, the others should just generate GL_INVALID_ENUM. Now you have basically an uninitialized vertex attribute pointer, you later enable that attribute array and try to draw from it, resulting in the crash.
Another thing: I don't see any shaders in your code. Shaders are mandatory in the core profile, which you use. Now glDrawElements() actually should fail, although some implementors ignore that and use some trivial shaders in that scenario.