I am working through an OpenGL Tutorial, and am stuck. I am trying to draw two triangles to make a rectangle, and this works fine when I include glVertexAttribPointer and glEnableVertexAttribArray in the main function, but once I try to abstract it into the VertexArray class nothing is drawn on the screen. I suspect that this is because all points are being drawn at 0,0 because when I change the draw mode to points instead of triangles it draws one point at 0,0. Note that this is not included in the code below, which is still set to triangles.
the main method
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include "Renderer.h"
#include "VertexArray.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
struct ShaderProgramSource
{
std::string VertexSource;
std::string FragmentSource;
};
static ShaderProgramSource ParseShader(const std::string& filePath)
{
//open file at filePath
std::ifstream stream(filePath);
enum class ShaderType
{
NONE = -1, VERTEX = 0, FRAGMENT = 1
};
ShaderType type = ShaderType::NONE;
std::string line;
std::stringstream ss[2];
while (getline(stream, line))
{
//if #shader is found in line
if (line.find("#shader") != std::string::npos)
{
//set type to vertex or fragment depending on #shader line
if (line.find("vertex") != std::string::npos)
{
//set the mode to vertex
type = ShaderType::VERTEX;
}
else if (line.find("fragment") != std::string::npos)
{
//set the mode to fragment
type = ShaderType::FRAGMENT;
}
}
// add the line to a string at the index of the type
else
ss[(int)type % 2] << line << "\n";
}
return { ss[0].str(), ss[1].str() };
}
static unsigned int CompileShader(unsigned int type, const std::string& source)
{
unsigned int id = glCreateShader(type);
const char* src = source.c_str();
glShaderSource(id, 1, &src, nullptr);
glCompileShader(id);
//retrieves the result of glCompileShader
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
int length;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = (char*)alloca(length * sizeof(char));
glGetShaderInfoLog(id, length, &length, message);
std::cout << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << " shader" << std::endl;
std::cout << message << std::endl;
glDeleteShader(id);
return 0;
}
return id;
}
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{
unsigned int program = glCreateProgram();
unsigned int vShader = CompileShader(GL_VERTEX_SHADER, vertexShader);
unsigned int fShader = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
glAttachShader(program, vShader);
glAttachShader(program, fShader);
glLinkProgram(program);
glValidateProgram(program);
glDeleteShader(vShader);
glDeleteShader(fShader);
return program;
}
int main(void)
{
GLFWwindow* window;
/* Initialize the library */
if (!glfwInit())
return -1;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
/* Create a windowed mode window and its OpenGL context */
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
if (glewInit() != GLEW_OK)
std::cout << "Error Initilizing GLEW" << std::endl;
{
float positions[] = {
-0.5f, -0.5f,
0.5f, -0.5f,
0.5f, 0.5f,
-0.5f, 0.5f
};
unsigned int indices[] = {
0, 1, 2,
2, 3, 0
};
VertexArray VAO;
VertexBufferLayout layout;
layout.Push<float>(2);
VertexBuffer VB(positions, 4 * 2 * sizeof(float));
VAO.AddBuffer(VB, layout);
IndexBuffer IB(indices, 6);
ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");
unsigned int shader = CreateShader(source.VertexSource, source.FragmentSource);
glUseProgram(shader);
//returning values from a shader
GLCall(int location = glGetUniformLocation(shader, "u_Color"));
ASSERT(location != -1);
GLCall(glUniform4f(location, 0.8f, 0.3f, 0.8f, 1.0f));
GLCall(glBindVertexArray(0));
GLCall(glUseProgram(0));
GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0));
GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
float r = 0.0f;
float increment = 0.05f;
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
GLCall(glClear(GL_COLOR_BUFFER_BIT));
GLCall(glUseProgram(shader));
GLCall(glUniform4f(location, r, 0.3f, 0.8f, 1.0f));
VAO.Bind();
GLCall(IB.Bind());
GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));
if (r > 1.0f)
increment = -0.05f;
else if (r < 0.0f)
increment = 0.05;
r += increment;
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
glDeleteProgram(shader);
}
glfwTerminate();
return 0;
}
Vertex Array
#include "VertexArray.h"
#include "Renderer.h"
VertexArray::VertexArray()
{
GLCall(glGenVertexArrays(1, &m_RendererID));
Bind();
}
VertexArray::~VertexArray()
{
GLCall(glDeleteVertexArrays(1, &m_RendererID));
}
void VertexArray::AddBuffer(VertexBuffer VB, VertexBufferLayout& layout)
{
Bind();
VB.Bind();
const auto& elements = layout.getElements();
unsigned int offset = 0;
for (unsigned int i = 0; i < elements.size(); i++)
{
const auto& element = elements[i];
GLCall(glEnableVertexAttribArray(i));
GLCall(glVertexAttribPointer(i, element.count, element.type, element.normalized, layout.GetStride(), (const void*)offset));
offset += element.count * VertexBufferElement::GetSizeofType(element.type);
}
VB.Unbind();
}
void VertexArray::Bind()
{
// Make the VAO current by binding it
glBindVertexArray(m_RendererID);
}
void VertexArray::Unbind()
{
glBindVertexArray(0);
}
Vertex Buffer
#include "VertexBuffer.h"
#include "Renderer.h"
VertexBuffer::VertexBuffer(const void* data, unsigned int size)
{
//Returns a vacant Buffer object which can after v THIS v line can be referenced to OpenGL by uint ID
glGenBuffers(1, &m_RendererID);
//Binds the buffer to GL_ARRAY_BUFFER, so GL knows which to modify when BufferData vv is called or when a buffer drawn
glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
//Fills GL_ARRAY_BUFFER with data
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}
VertexBuffer::~VertexBuffer()
{
GLCall(glDeleteBuffers(1, &m_RendererID));
}
void VertexBuffer::Bind() const
{
//Binds the buffer to GL_ARRAY_BUFFER, so GL knows which to modify when BufferData vv is called or when a buffer drawn
GLCall(glBindBuffer(GL_ARRAY_BUFFER, m_RendererID));
}
void VertexBuffer::Unbind() const
{
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Vertex Buffer Layout:
#pragma once
#include <vector>
#include <GL/glew.h>
#include "Renderer.h"
struct VertexBufferElement
{
unsigned int type;
unsigned int count;
unsigned char normalized;
static unsigned int GetSizeofType(unsigned int type)
{
switch (type)
{
case GL_FLOAT: return 4;
case GL_UNSIGNED_INT: return 4;
case GL_UNSIGNED_BYTE: return 1;
}
ASSERT(false)
return 0;
}
};
class VertexBufferLayout
{
private:
std::vector<VertexBufferElement> m_Elements;
unsigned int m_Stride;
public:
VertexBufferLayout()
:m_Stride(0) {}
template<typename T>
void Push(unsigned int count)
{
static_assert(false);
}
template<>
void Push<float>(unsigned int count)
{
VertexBufferElement vbe = { GL_FLOAT, count, GL_FALSE };
m_Elements.push_back(vbe);
m_Stride += VertexBufferElement::GetSizeofType(GL_FLOAT) * count;
}
template<>
void Push<unsigned int>(unsigned int count)
{
VertexBufferElement vbe = { GL_UNSIGNED_INT, count, GL_FALSE };
m_Elements.push_back(vbe);
m_Stride += VertexBufferElement::GetSizeofType(GL_UNSIGNED_INT) * count;
}
template<>
void Push<unsigned char>(unsigned int count)
{
VertexBufferElement vbe = { GL_UNSIGNED_BYTE, count, GL_TRUE };
m_Elements.push_back(vbe);
m_Stride += VertexBufferElement::GetSizeofType(GL_UNSIGNED_BYTE) * count;
}
inline const std::vector<VertexBufferElement> getElements() const { return m_Elements; }
inline unsigned int GetStride() const { return m_Stride; }
};
The renderer which currently just handles errors
#include "Renderer.h"
#include <iostream>
void GLClearError()
{
while (glGetError() != GL_NO_ERROR)
{
}
}
bool GLLogCall(const char* function, const char* file, int line)
{
while (GLenum error = glGetError())
{
std::cout << "[OpenGL Error] (" << error << "):" << function << " " << file << ":" << line << std::endl;
return false;
}
return true;
}
Index Buffer
#include "IndexBuffer.h"
#include "Renderer.h"
IndexBuffer::IndexBuffer(const unsigned int* data, unsigned int count)
: m_Count(count)
{
ASSERT(sizeof(unsigned int) == sizeof(GLuint));
GLCall(glGenBuffers(1, &m_RendererID));
GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID));
GLCall(glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(unsigned int), data, GL_STATIC_DRAW));
}
IndexBuffer::~IndexBuffer()
{
GLCall(glDeleteBuffers(1, &m_RendererID));
}
void IndexBuffer::Bind() const
{
GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID));
}
void IndexBuffer::Unbind() const
{
GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
}
And finally shaders
#shader vertex
#version 330 core
layout(location = 0) in vec4 aPos;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);
}
#shader fragment
#version 330 core
layout(location = 0) out vec4 color;
uniform vec4 u_Color;
void main()
{
color = u_Color;
}
The rectangle draws properly if the two GLCall lines from VertexArray::AddBuffer is in the main method, and through debugging I was able to confirm that the values at the lines were the same.
UPDATE: I needed to pass the VB object in as a reference. Oops. I was creating a stack-allocated copy of VB when I passed it into AddBuffer.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.