I am attempting to be able to take some OpenGL code that draws objects from a vertex array and add it to a class file. However, the code only runs currently when I have it in my main.cpp file. I call init() from my main() function, before heading into my draw loop.
init(){
GLuint containerVAO, VBO;
glGenVertexArrays(1, &containerVAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(containerVAO);
// Position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
// Normal attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),(GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
}
The relevant code in my draw loop:
glUseProgram(noTextureShaderID);
glBindVertexArray(containerVAO);
///many different uniforms added here
glDrawArrays(GL_TRIANGLES, 0, 36);
This creates a cube no problem.
Now, when I replace the code inside my init() function (which initialises all objects, not just this one, I change it to this:
init(){
square.init(noTextureShaderID, vertices[], NULL, 36);
//Square is a global variable within my main.cpp file
}
And then I use this function:
void Mesh::init(const GLuint& shaderid, GLfloat vertices[], const char* tex_file, int num_vertices)
{
GLuint VBO;
vao = NULL; //This is a variable within the Mesh class
g_point_count = num_vertices;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(vao);
// Position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
// Normal attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
}
Then, in my draw function I call this instead:
glUseProgram(noTextureShaderID);
glBindVertexArray(square.vao);
///many different uniforms added here
glDrawArrays(GL_TRIANGLES, 0, g_point_count);
But even though both programs seem to have the same code, only the first version generates a cube. What am I missing in this regard?
Your code is not identical in both cases, and this has nothing to do with OpenGL:
void Mesh::init(const GLuint& shaderid, GLfloat vertices[], const char* tex_file, int num_vertices)
{
// ...
glBufferData(..., sizeof(vertices), ...);
}
vertices is passed by reference here, the inner function will never see the array, and sizeof(vertices) will be identical to sizeof(GLfloat*), which is typically 4 or 8 on todays machines. Hence, your buffer is just containing the first one or two floats.
You either have to explicitely provide the array size as an additional parameter, or you use some (reference to an) higher-level object like std:vector, which completely manages the array internally and allows you to query the size.
Related
I'm trying to draw a textured cube in OpengL.
At first, I initialize VBO and VAO in the class constructor as follows.
Block::Block(...)
{
glGenBuffers(1, &VBO);
glGenVertexArrays(1, &VAO);
//glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBindVertexArray(VAO);
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);
}
and in the method called renderBlock(), I render the cube like this
void Block::renderBlock()
{
setMatrix();
shader.use();
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBindVertexArray(VAO);
glActiveTexture(GL_TEXTURE0);
shader.setInt("tex", 0);
shader.use();
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
and if I call the block.renderBlock(), the program will crash at glDrawArrays(GL_TRIANGLES, 0, 6);. The error message says that 0xC0000005: Access conflict occurred at read position 0x0000000000000000.. But if I uncomment the //glBindBuffer(GL_ARRAY_BUFFER, VBO); in constructer, the program will run successfully. It's very confusing to me. Why should I bind the VBO before I edit the VAO. Is there any connection between them?Any advice would be greatly appreciated.
You must bind the VBO before calling glVertexAttribPointer. When glVertexAttribPointer is called, the buffer currently bound to the GL_ARRAY_BUFFER target is associated with the specified attribute index and the ID of the buffer object is stored in the state vector of the currently bound VAO. Therefore, the VAO and the VBO must be bound before calling glVertexAttribPointer.
It is my understanding from the OpenGL documentation that a VAO can be deleted (glDeleteVertexArrays), and then later regenerated (glGenVertexArrays). However, I have an issue when I am getting an OpenGL error when trying to re-use an existing VAO variable in a Chunk class (for a Minecraft clone). This only happens for some chunks and I cannot understand why. I output the VAO value (unsigned int type) and it doesn't seem to change after deleting with glDeleteVertexArrays. It was my understanding from the documentation that this value would be reset to zero after running this function. See Chunk class code below.
void Chunk::load()
{
// Update chunk state
loaded = true;
// Set up OpenGL buffers
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &vertexVBO);
glGenBuffers(1, &textureVBO);
glGenBuffers(1, &EBO);
// VAO bound before setting up buffer data
glBindVertexArray(VAO);
// Indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
indices.size() * sizeof(unsigned int),
&indices[0],
GL_DYNAMIC_DRAW);
// Vertices
glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
glBufferData(GL_ARRAY_BUFFER,
vertices.size() * sizeof(float),
&vertices[0],
GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(unsigned int), (void*)0);
glEnableVertexAttribArray(0);
// Texture Coordinates
glBindBuffer(GL_ARRAY_BUFFER, textureVBO);
glBufferData(GL_ARRAY_BUFFER,
texCoords.size() * sizeof(float),
&texCoords[0],
GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
// Unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void Chunk::unload()
{
// Update chunk state
loaded = false;
// Delete arrays/buffers.
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &vertexVBO);
glDeleteBuffers(1, &textureVBO);
glDeleteBuffers(1, &EBO);
}
Just as delete ptr; in C++ or free(ptr); in C does not actually change the pointer value of ptr variable, calling glDelete* on an OpenGL object does not change the value of the variables you give it. It is up to you to not use the variable again or to assign it to a neutral value.
That having been said, if your intent is to immediately create a new VAO... why bother? Just clear out the old one by disabling all of the attribute arrays and buffer bindings, and its ready to be used anew:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
GLint maxAttrib;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttrib);
for(int attribIx = 0; attribIx < maxAttrib; ++attribIx)
{
glDisableVertexAttribArray(attribIx);
glVertexAttribPointer(attribIx, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
glVertexAttribDivisor(attribIx, 0);
}
I create my buffers with the following code:
//generate buffers
glGenVertexArrays(1, &VAO);
//glGenBuffers(1, &EBO);
glGenBuffers(1, &VBO_vertices);
glGenBuffers(1, &VBO_colors);
glGenBuffers(1, &VBO_normals);
// Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).
glBindVertexArray(VAO);
// Copy our vertices array in a buffer for OpenGL to use
glBindBuffer(GL_ARRAY_BUFFER, VBO_vertices);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*vertices.size(), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, vTable.size() * sizeof(int), &vTable[0], GL_STATIC_DRAW);
// Position attribute
glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); //size = 3 (X,Y,Z)
glEnableVertexAttribArray(0);
//Buffer for color
glBindBuffer(GL_ARRAY_BUFFER, VBO_colors);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*vertices.size(), &v_color[0], GL_STATIC_DRAW);
// Color attribute
glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); //size = 3 (R,G,B)
glEnableVertexAttribArray(1);
//Buffer for normals
glBindBuffer(GL_ARRAY_BUFFER, VBO_normals);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*vertices.size(), &v_normals[0], GL_STATIC_DRAW);
//normal attribute
glVertexAttribPointer((GLuint)2, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); //size = 3 (R,G,B)
glEnableVertexAttribArray(2);
// Unbind the VAO
glBindVertexArray(0);
My data are :
vector<vec3> vertices, v_normals,v_color;
vector<int> vTable;
I have vertices, normals and colors per vertex and an index table with the index vertices of each triangle.
When I try to render this, nothing appears on the window.
glBindVertexArray(VAO); //Bind VAO
glDrawElements(GL_TRIANGLES, vTable.size(), GL_UNSIGNED_INT, &vTable[0]);
glBindVertexArray(0); //Unbind VAO
If I used this:
glDrawArrays(GL_TRIANGLES,0,vTable.size());
It draws something but an incomplete object, like in the link image.
image
Anybody knows what happens? Thanks in advance
Your glDrawElements call is wrong, the last parameter should be a byte offset into your GL_ELEMENT_ARRAY_BUFFER that holds the indices, not pointer to system memory.
glDrawElements(GL_TRIANGLES, vTable.size(), GL_UNSIGNED_INT, 0);
So I have some code that creates a buffer and puts some vertices in it:
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);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
I also bind it to a shader attribute:
glBindAttribLocation(programID, 0, "pos");
And, finally, draw it:
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glDrawArrays(GL_TRIANGLES, 0, 3);
Of course, there is other code, but all of this stuff runs fine (displays a red triangle on the screen)
However, the instant I try to factor this stuff out in a struct, nothing will display (here is one of the methods):
void loadVerts(GLfloat verts[], int indices)
{
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glVertexAttribPointer(indice, indices, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(indice);
indice++;
buffers.push_back(vertexbuffer);
}
I've quadruple checked this code, and I've also traced it to make sure it would match the code above whenever its called. My draw call is almost the same as my original:
void draw()
{
glBindBuffer(GL_ARRAY_BUFFER, buffers.at(0));
glDrawArrays(GL_TRIANGLES, 0, 3);
}
I've also tried making this a class, and adding/changing many parts of the code. buffers and indice are just some vars to keep track of buffers and attribute indexes. buffers is an std::vector<GLuint> FWIW.
The main problem is here:
void loadVerts(GLfloat verts[], int indices)
{
...
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
The type of the verts argument is a pointer to GLfloat. Your function signature is equivalent to:
void loadVerts(GLfloat* verts, int indices)
So sizeof(verts), which is used as the second argument to glBufferData(), is 4 on a 32-bit architecture, 8 on a 64-bit architecture.
You will need to pass the size as an additional argument to this function, and use that value as the second argument to glBufferData().
These statements also look somewhat confusing:
glVertexAttribPointer(indice, indices, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(indice);
I can't tell if there's a real problem, but you have two variables with very similar names that are used very differently. indice needs to be the location of the attribute in your vertex shader, while indices needs to be the number of components in the attribute.
Whenever I try to draw a mesh using glDrawElements, my program gets no errors, but it doesn't draw anything. I'm pretty sure that the issue is with glDrawElements since if I comment glDrawElements out and replace it with glDrawArrays, it works perfectly. So, my question is, can anyone help me figure out why this is happening?
Also glGetError returns no errors
#include "Mesh.h"
Mesh::Mesh(glm::vec2* vertices, glm::vec2* texCoords, GLushort* elements, int size)
{
m_size = size;
m_vertices = vertices;
m_elements = elements;
movementVector = glm::vec2(0.0f, 0.0f);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), vertices, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glGenBuffers(1, &tex_vbo);
glBindBuffer(GL_ARRAY_BUFFER, tex_vbo);
glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), texCoords, GL_STATIC_DRAW);
// note: I assume that vertex positions are location 0
int dimensions = 2; // 2d data for texture coords
glVertexAttribPointer(1, dimensions, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray (1); // don't forget this!
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
}
Mesh::~Mesh()
{
glDeleteVertexArrays(1, &vbo);
}
void Mesh::Draw()
{
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
//glDrawArrays(GL_TRIANGLES, 0, 8);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);
glDisableVertexAttribArray(0);
}
Here's a link to the code handling drawing objects to the screen, also, I have tested my shaders, they are fully functional and unless drawing via glDrawElements requires changes to the shaders as well, then there is nothing that is causing this with them.