I'm trying to encapsulate OpenGL rendering in a renderer class.
I'm pretty sure my window class works fine (I've checked for errors) but I think the error is in my sprite class but I have no idea what it is.
This is my sprite class:
sprite_renderer.hpp
#ifndef SPRITE_RENDERER_H
#define SPRITE_RENDERER_H
#include <GL/glew.h>
#include "shader.hpp"
class SpriteRenderer
{
public:
SpriteRenderer(Shader &shader);
~SpriteRenderer();
void DrawSprite();
private:
Shader shader;
GLuint quadVAO;
void initRenderData();
};
#endif
sprite_renderer.cpp
#include "sprite_renderer.hpp"
SpriteRenderer::SpriteRenderer(Shader &shader)
{
this->shader = shader;
this->initRenderData();
}
SpriteRenderer::~SpriteRenderer()
{
glDeleteVertexArrays(1, &this->quadVAO);
}
void SpriteRenderer::DrawSprite()
{
this->shader.Use();
glBindVertexArray(this->quadVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
void SpriteRenderer::initRenderData()
{
GLuint VBO;
GLfloat vertices[] = {
// Pos
0.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
};
glGenVertexArrays(1, &this->quadVAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(this->quadVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
I'm using it like this in my main.cpp:
int main()
{
Window window = Window();
Shader shader = ResourceManager::LoadShader("./assets/shaders/sprite.vert", "./assets/shaders/sprite.frag", "sprite");
SpriteRenderer* renderer = new SpriteRenderer(shader);
while (window.IsOpen())
{
window.BeginDraw();
renderer->DrawSprite();
window.EndDraw();
}
glfwTerminate();
return 0;
}
Where window.BeginDraw() and window.EndDraw() are just:
void Window::BeginDraw()
{
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
void Window::EndDraw()
{
glfwSwapBuffers(window);
glfwPollEvents();
}
Just to rule out the obvious, try sticking a print statement in the destructor of your Shader class. Judging by the design of your Shader class, you should find it extremely easy to end up calling glDeleteProgram prior to actually using the shader.
In general, if you have a wrapper around a GLSL shader or program, you probably want to delete the copy constructor and assignment operator (or implement a ref counting system).
\edit to delete the copy ctor & assignment operator....
Shader(const Shader&) = delete;
Shader& operator = (const Shader&) = delete;
You probably want to manage these shaders somewhere (possibly ref counted), and only store pointers to them within your SpriteRenderer.
I can't see any other problem with the code you've posted above, so the issue may be within your shader, or how you are passing any uniform variables you're using.
Related
Can someone show show me how I can modify my code so I can draw more than one triangle?
I'm hoping to accomplish something like the picture shown here:
// Include standard headers
#include <stdio.h>
#include <stdlib.h>
// Include GLEW
#include <GL/glew.h>
// Include GLFW
#include <GLFW/glfw3.h>
GLFWwindow* window;
// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;
#include <common/shader.hpp>
int main(void)
{
// Initialise GLFW
if (!glfwInit())
{
fprintf(stderr, "Failed to initialize GLFW\n");
getchar();
return -1;
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Open a window and create its OpenGL context
window = glfwCreateWindow(1024, 768, "Tutorial 04 - Colored Cube", NULL, NULL);
if (window == NULL) {
fprintf(stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
getchar();
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// Initialize GLEW
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
getchar();
glfwTerminate();
return -1;
}
// Ensure we can capture the escape key being pressed below
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
// Enable depth test
glEnable(GL_DEPTH_TEST);
// Accept fragment if it closer to the camera than the former one
glDepthFunc(GL_LESS);
GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
// Create and compile our GLSL program from the shaders
GLuint programID = LoadShaders("TransformVertexShader.vertexshader", "ColorFragmentShader.fragmentshader");
// Get a handle for our "MVP" uniform
GLuint MatrixID = glGetUniformLocation(programID, "MVP");
int verticeCount = 3;
static const GLfloat g_vertex_buffer_data[] = {
-0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};
static const GLfloat g_color_buffer_data[] = {
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
};
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
GLuint colorbuffer;
glGenBuffers(1, &colorbuffer);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);
do {
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use our shader
glUseProgram(programID);
glm::mat4 MVP = glm::mat4(1.0);
// Send our transformation to the currently bound shader,
// in the "MVP" uniform
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
0, // attribute. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// 2nd attribute buffer : colors
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glVertexAttribPointer(
1, // attribute. No particular reason for 1, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Draw the triangle !
glDrawArrays(GL_TRIANGLES, 0, verticeCount * 3);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
// Swap buffers
glfwSwapBuffers(window);
glfwPollEvents();
} // Check if the ESC key was pressed or the window was closed
while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
glfwWindowShouldClose(window) == 0);
// Cleanup VBO and shader
glDeleteBuffers(1, &vertexbuffer);
glDeleteBuffers(1, &colorbuffer);
glDeleteProgram(programID);
glDeleteVertexArrays(1, &VertexArrayID);
// Close OpenGL window and terminate GLFW
glfwTerminate();
return 0;
}
It's actually right in front of your nose.
Triangles are defined by 3 vertices.
static const GLfloat g_vertex_buffer_data[] = {
-0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};
Here, you initialized your vertex data array with 3, 3-dimensional vertices. That's 1 triangle.
Adding more triangles, i.e. 3 sets of 3 vertices, to this array is all you need to do, along with extending the g_color_buffer_data array and changing verticeCount accordingly.
So this:
static const GLfloat g_vertex_buffer_data[] = {
-0.50f, +0.50f, +0.00f,
+0.50f, +0.50f, +0.00f,
+0.50f, -0.50f, +0.00f,
-0.50f, -0.25f, +0.00f,
-0.50f, -0.50f, +0.00f,
-0.25f, -0.50f, +0.00f,
};
static const GLfloat g_color_buffer_data[] = {
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
};
would give you 2 triangles: 1 red, 1 blue.
You could also define verticeCount as:
int verticeCount = sizeof(g_vertex_buffer_data) / (sizeof(g_vertex_buffer_data[0]) * 3);
(Thread on finding the size of a C-style array in C++)
This way you won't manually have to change it.
You can always create a new vertex buffer, but it is usually a good idea to implement vertex and fragment shaders instead. If you choose to use shaders, you can draw the buffer, transform the vertex shader, then draw the buffer again. Fragment shaders also provide a better way of specifying color dynamically. You can read more and see some examples at https://learnopengl.com/Getting-started/Shaders
I am following along with a youtube tutorial concerning opengl (a graphics programming library). I've created classes called vertex, ShapeData and ShapeGenerator. The overall idea is I'm creating code which will hold data for any type of shape I decide to come up with and want to display to the screen. The problem is my program seems to crash once the first "delete[]" is hit within ShapeData.h in the cleanup() function. Here is the relevant code:
Vertex.h
#pragma once
#include "GLM/glm.hpp"
class Vertex
{
public:
Vertex();
Vertex(glm::vec3 thePosition, glm::vec3 theColor);
glm::vec3 position;
glm::vec3 color;
};
Vertex.cpp
#include "Vertex.h"
Vertex::Vertex()
{}
Vertex::Vertex(glm::vec3 thePosition, glm::vec3 theColor) :
position(thePosition),
color(theColor)
{
}
ShapeData.h
#pragma once
#include "Vertex.h"
#include "GL/glew.h"
struct ShapeData
{
ShapeData() :
verticies(0), numberOfVerts(0),
indicies(0), numberOfIndicies(0)
{}
Vertex* verticies;
GLuint numberOfVerts;
GLushort* indicies;
GLuint numberOfIndicies;
GLsizeiptr VertexBufferSize() const
{
return numberOfVerts * sizeof(Vertex);
}
GLsizeiptr IndexBufferSize() const
{
return numberOfIndicies * sizeof(GLushort);
}
void CleanUp()
{
delete[] verticies;
delete[] indicies;
verticies = 0;
indicies = 0;
numberOfIndicies = 0;
numberOfVerts = 0;
}
};
ShapeGenerator.cpp
#include "ShapeGenerator.h"
ShapeData ShapeGenerator::MakeTriangle()
{
Vertex triangle[] = {
Vertex(glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
Vertex(glm::vec3(-1.0f, -1.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
Vertex(glm::vec3(1.0f, -1.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f))
};
ShapeData shapeData;
shapeData.numberOfVerts = sizeof(triangle) / sizeof(*triangle);
shapeData.verticies = new Vertex[shapeData.numberOfVerts];
memcpy(shapeData.verticies, triangle, sizeof(triangle));
shapeData.verticies = triangle;
GLushort indicies[] = { 0,1,2 };
shapeData.numberOfIndicies = sizeof(indicies) / sizeof(*indicies);
shapeData.indicies = new GLushort[shapeData.numberOfIndicies];
memcpy(shapeData.indicies, indicies, sizeof(indicies));
return shapeData;
}
I'm trying to create a triangle and everything works fine without running the cleanup() function within main. Here is the portion where I'm calling Cleanup() in main:
main.cpp
ShapeData triangle = ShapeGenerator::MakeTriangle();
GLuint bufferID;
glGenBuffers(1, &bufferID);
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
glBufferData(GL_ARRAY_BUFFER, triangle.VertexBufferSize(), triangle.verticies, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (char*)(sizeof(float) * 3));
GLuint indexBufferID;
glGenBuffers(1, &indexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangle.IndexBufferSize(),triangle.indicies, GL_STATIC_DRAW);
triangle.CleanUp();
You are replacing the new[]'d pointer here. This causes a crash because triangle is not new[]'d.
shapeData.verticies = new Vertex[shapeData.numberOfVerts];
memcpy(shapeData.verticies, triangle, sizeof(triangle));
shapeData.verticies = triangle;
I have been fiddling around with making a game/rendering engine, and I have found that I can have a class for a shader object, but if I wrap a VAO in a class, it won't render.
The shaders return no errors, and the VAO and shaders are valid OpenGL objects.
UPDATE
The problem is this line:
glBufferData(GL_ARRAY_BUFFER, sizeof(arrFVertex), arrFVertex, GL_STATIC_DRAW);
As #BDL suggested in the comments, I thought about it and I realized, it should be:
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * intNumVertex * 3, arrFVertex, GL_STATIC_DRAW);
UPDATE 2
In response to being put on hold, here is a Minimum Complete and Verifiable Example:
#include <OpenGL/gl3.h>
#include <SDL2/SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
SDL_Window *window = NULL;
SDL_GLContext openGLRenderer;
bool bolRunning = true;
int intGLVersionMajor, intGLVersionMinor;
GLfloat arrFVertex[] = {
0.5f, 0.5f, 0.0f, // Top Right
0.5f, -0.5f, 0.0f, // Bottom Right
-0.5f, 0.5f, 0.0f, // Top Left
0.5f, -0.5f, 0.0f, // Bottom Right
-0.5f, -0.5f, 0.0f, // Bottom Left
-0.5f, 0.5f, 0.0f // Top Left
};
GLuint intVAO;
GLuint intVBO;
GLuint intShaderAttribPosition;
GLuint intShaderProgram;
GLuint intNumVertex = 6;
void loadShaders(const char *strVertexShaderSource, const char *strFragmentShaderSource) {
intShaderProgram = glCreateProgram();
GLuint intVertexShader;
intVertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(intVertexShader, 1, &strVertexShaderSource, NULL);
glCompileShader(intVertexShader);
GLuint intFragmentShader;
intFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(intFragmentShader, 1, &strFragmentShaderSource, NULL);
glCompileShader(intFragmentShader);
glAttachShader(intShaderProgram, intVertexShader);
glAttachShader(intShaderProgram, intFragmentShader);
glLinkProgram(intShaderProgram);
glDeleteShader(intVertexShader);
glDeleteShader(intFragmentShader);
}
void buildVAO(GLfloat *arrFVertex) {
intShaderAttribPosition = glGetAttribLocation(intShaderProgram, "f3Position");
glGenVertexArrays(1, &intVAO);
glBindVertexArray(intVAO);
glGenBuffers(1, &intVBO);
glBindBuffer(GL_ARRAY_BUFFER, intVBO);
glVertexAttribPointer(intShaderAttribPosition, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid *)0);
glEnableVertexAttribArray(intShaderAttribPosition);
glBufferData(GL_ARRAY_BUFFER, sizeof(arrFVertex), arrFVertex, GL_STATIC_DRAW);
glBindVertexArray(0);
}
int main(int argc, char **argv) {
SDL_Init(SDL_INIT_EVERYTHING);
window = SDL_CreateWindow("GSEngine",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480,
SDL_WINDOW_OPENGL);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
if (window == NULL) {
printf("Could not create window: %s\n", SDL_GetError());
exit(1);
}
openGLRenderer = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, openGLRenderer);
glViewport(0, 0, 640, 480);
loadShaders("#version 330 core\n\
in vec3 f3Position;\n\
void main() {\n\
gl_Position = vec4(f3Position, 1.0);\n\
}", "#version 330 core\n\
out vec4 f4Color;\n\
void main() {\n\
f4Color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n\
}");
buildVAO(arrFVertex);
while (bolRunning) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
bolRunning = false;
}
}
SDL_GL_MakeCurrent(window, openGLRenderer);
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(intShaderProgram);
glDrawArrays(GL_TRIANGLES, 0, intNumVertex);
SDL_GL_SwapWindow(window);
}
glDeleteBuffers(1, &intVBO);
glDeleteVertexArrays(1, &intVAO);
glDeleteShader(intShaderProgram);
SDL_GL_DeleteContext(openGLRenderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
The problem has nothing to do with the VAO, but with the VBO. Since you pass a pointer to the constructor:
void GSMesh::build(GLfloat *arrFVertex, GSShader *shader, int _intNumVertex)
{
glBufferData(GL_ARRAY_BUFFER, sizeof(arrFVertex), arrFVertex, GL_STATIC_DRAW);
}
sizeof(arrFVertex) = sizeof(GLfloat*) which is the size of the pointer, not the size of the array pointed to. The correct code will look like this:
glBufferData(GL_ARRAY_BUFFER,
sizeof(GLfloat) * _intNumVertex * 3, arrFVertex,
GL_STATIC_DRAW);
In general I have to add, that this is not the way how questions should be asked on SO. It would have been good if you would have included at least the relevant parts of the code in your question.
Despite what the spec says, with some drivers you must enable the shader before you can get the location of an attribute or uniform. This may be what is causing your problems.
In your code that would mean in your GSMesh::build method adding:
shader->use();
Before:
intShaderAttribPosition = glGetAttribLocation(shader->intShaderProgram, "f3Position");
Personally if the version of OpenGL that you are using has support for Vertex Attribute Indexes I'd use them instead.
In the vertex shader you could have something like:
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 tex_coords;
And then in your mesh class all you need is:
struct Vertex
{
glm::vec3 position;
glm::vec2 tex_coords;
};
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, position);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, tex_coords));
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.
I am starting to learn OpenGL. So far I am doing alright, the only problem is that I don't know exactly which is the "best" way to organize the program. For example, a lot of tutorials construct all of the code sequentially on the main() function, which I don't think is very nice. Others will build functions like render(), init(), and call them on the main() function.
Right now I am trying to mix things, I built a class to wrap GLFW and GLEW libraries, and I call them on the main(). Also I have init() and render() functions. The problem I am getting into is that I have to declare (GLuint program) as a global variable, which is not advisable. This is the only way I can get the render function to use also the variable.
Here is the code:
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "GLWrapper.h"
#include "GLShader.h"
#include <iostream>
#include <ctime>
using namespace std;
GLuint program;
void renderer() {
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
float time = float(clock()) / float(CLOCKS_PER_SEC);
GLint uniColor = glGetUniformLocation(program, "triangleColor");
glUniform3f(uniColor, (sin(time * 4.0f) + 1.0f) / 2.0f, cos(time*2), 0.0f);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
void init() {
float vertices[] = {
0.0f, 0.5f, // Vertex 1 (X, Y)
0.5f, -0.5f, // Vertex 2 (X, Y)
-0.5f, -0.5f // Vertex 3 (X, Y)
};
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo); // Generate 1 buffer
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
program = LoadShader("Shader.vert", "Shader.frag");
glUseProgram(program);
}
int main() {
GLWrapper *glw = new GLWrapper(800, 600, "Ola Mundo!");
glw->setRenderer(renderer);
init();
GLint posAttrib = glGetAttribLocation(program, "position");
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(posAttrib);
glw->glMainLoop();
delete(glw);
return 0;
}
So, I want to know If there is a better way of organizing this code (building a class perhaps?), because since I will be building bigger and bigger codes, I want them to be as organized as possible.
Hope I made myself clear.