I'm working on a Voxel Engine and I'm using VBO.
My problem is that I don't know how to generate Vertices.
I need to generate vertices for a chunk, So create a Chunk of cubes (With one VBO) from X,Y,Z coords.
How Can I do this?
First you will need to create/load the data for the voxels. Something like the following could be used to generate a 10x10x10 field of binary voxels:
Note: All code is untested and probably not optimal.
class Field {
bool data[10*10*10];
public:
bool& at(int i, int j, int k) { return data[i*10*10 + j*10 + k]; }
};
Field aField;
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
for (int k = 0; k < 10; ++k) {
aField.at(i, j, k) = !(rand() % 2);
}
}
}
I'm assuming you are after Minecraft style voxels, otherwise you probably want to use something like Marching Cubes to process the binary voxel field and pass the result to the vertex buffer code.
The next step is for each occupied voxel in the field, draw the faces associated with the cube. For this we need to create a list of all vertices and (in this case) triangle indices.
std::vector< std::array<GLfloat, 3> > vecVerts;
std::vector< std::array<GLuint, 3> > vecTris;
for (int i,j,k from 0 to 10) {
if (aField.at(i,j,k)) {
// Add vertices for eight corners of cube
vecVerts.emplace_back(dWidth*(i ), dWidth*(j ), dWidth*(k ));
vecVerts.emplace_back(dWidth*(i+1), dWidth*(j ), dWidth*(k ));
vecVerts.emplace_back(dWidth*(i ), dWidth*(j+1), dWidth*(k ));
vecVerts.emplace_back(dWidth*(i+1), dWidth*(j+1), dWidth*(k ));
vecVerts.emplace_back(dWidth*(i ), dWidth*(j ), dWidth*(k+1));
vecVerts.emplace_back(dWidth*(i+1), dWidth*(j ), dWidth*(k+1));
vecVerts.emplace_back(dWidth*(i ), dWidth*(j+1), dWidth*(k+1));
vecVerts.emplace_back(dWidth*(i+1), dWidth*(j+1), dWidth*(k+1));
// Add triangle coordinates for each triangle
vecTris.emplace_back(0, 1, 3); vecTris.emplace_back(0, 2, 3);
vecTris.emplace_back(4, 5, 7); vecTris.emplace_back(4, 6, 7);
/* todo: add remaining triangles, should be 8 more */
}
}
The above code will create two arrays in memory storing your vertex locations and the indices for the triangles. You'll notice that the code always draws 12 triangles for each voxel, it would be better to do a check if there is an adjacent occupied voxel and remove faces between such voxels, but I'll leave that for you. I also recommend you consider using glm::vec3 for storing vertex data rather then just a array.
We can now pass our arrays to OpenGL:
GLuint unVertexArray;
glGenVertexArrays(1, &unVertexArray);
glBindVertexArray(unVertexArray);
GLuint unVertexBuffer;
glGenBuffers(1, &unVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, unVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, 3*vecVerts.size()*sizeof(GLfloat),
&vecVerts[0][0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
GLuint unIndiciesBuffer;
glGenBuffers(1, &unIndicesBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, unIndicesBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3*vecTris.size()*sizeof(GLuint),
&vecTris[0][0], GL_STATIC_DRAW);
glBindVertexArray(0);
Then finally, to draw our arrays:
glBindVertexArray(unVertexArray);
glDrawElements(GL_TRIANGLES, 3*vecTris.size(), GL_UNSIGNED_INT, NULL);
glBindVertexArray(0);
I might (am almost certain I) have forgotten a few things here, but this should give you a general outline of what you will need to do. For a proper explanation of most of the gl calls used above there are many online references, such as http://arcsynthesis.org/gltut/index.html
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I am building a renderer which takes in many objects and batch renders them to the screen. The full renderer code -> https://github.com/rob-DEV/OpenGL-Model-Viewer/tree/master/OpenGL-Model-Viewer/src/graphics/renderer
Pushing the object (all are cubes containing indexed vertices (8) and 36 indices) to the renderer on each frame call.
//load models
for (int j = 0; j < 5; j++)
{
models.push_back(ObjModel("test-models/box.obj", glm::vec3(i * 5, i, j * 5)));
}
renderer.begin();
for (int i = 0; i < models.size(); i++)
{
//add to render call
renderer.submit(models[i]);
}
renderer.end();
renderer.flush();
render init()
// Enable depth test
glEnable(GL_DEPTH_TEST);
// Accept fragment if it closer to the camera than the former one
glDepthFunc(GL_LESS);
glGenVertexArrays(1, &m_VAO);
glGenBuffers(1, &m_VBO);
glBindVertexArray(m_VAO);
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
glBufferData(GL_ARRAY_BUFFER, RENDERER_BUFFER_SIZE, NULL, GL_STATIC_DRAW);
glEnableVertexAttribArray(SHADER_VERTEX_INDEX);
glEnableVertexAttribArray(SHADER_COLOR_INDEX);
glVertexAttribPointer(SHADER_VERTEX_INDEX, 3, GL_FLOAT, GL_FALSE, RENDERER_VERTEX_SIZE, (const GLvoid*)0);
glVertexAttribPointer(SHADER_COLOR_INDEX, 4, GL_UNSIGNED_BYTE, GL_TRUE, RENDERER_VERTEX_SIZE, (const GLvoid*)(offsetof(VertexData, VertexData::colour)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
//setup element
glGenBuffers(1, &m_IBO);
glBindVertexArray(0);
submission of an object
void Renderer::submit(ObjModel& object)
{
//a vector of vec3 vertices // these are indexed all objects are cubes so 8 verts per object
std::vector<glm::vec3> vertices = object.get_vertices();
std::vector<unsigned int> indices = object.get_indices();
std::vector<unsigned int> colours = object.get_colours();
//adjust the postion and give each picture a random colour & add to buffer
for (unsigned int i = 0; i < vertices.size(); i++)
{
vertices[i] += object.position;
m_VertexBuffer->vertex = vertices[i];
m_VertexBuffer->colour = colours[i];
m_VertexBuffer++;
m_VertexDataCount++;
}
m_IndiceCount += indices.size();
indiceData.insert(indiceData.end(), indices.begin(), indices.end());
}
as you can see i put each vertex (adding the position in the world) and colour in to a simple struct (VertexData*) and I add the indices to a vector.
Here is the actual draw
void Renderer::flush()
{
glBindVertexArray(m_VBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_IndiceCount * sizeof(unsigned int), &indiceData[0], GL_STATIC_DRAW);
// Draw the triangles !
glDrawElements(
GL_TRIANGLES, // mode
m_IndiceCount, // count
GL_UNSIGNED_INT, // type
(void*)0 // element array buffer offset
);
indiceData.clear();
m_IndiceCount = 0;
m_VertexDataCount = 0;
}
I realise the length but my question is, why does the position only work for the first cube (either the are drawing on top of each other or only the first cube draws) I can't figure this out. Is there a better way I out of ideas :)
why does the position only work for the first cube (either the are drawing on top of each other or only the first cube draws)
Because you're only drawing these first vertices, just several times:
Pushing the object (all are cubes containing indexed vertices (8) and 36 indices) to the renderer on each frame call.
Every time you call submit, you add those 8 vertices at the end of the vertex array, and you just copy the 36 indices to the end of the index array:
m_IndiceCount += indices.size();
indiceData.insert(indiceData.end(), indices.begin(), indices.end());
For the first cube, the all indices will be in the range 0 to 7, that's fine. But for all following cubes, you also will only use the indices 0 to 7 (because that are the correct indices for the local object alone). However, the second cube's vertices are at index 8 to 15 in the global vertex array. So when you draw the two cubes at once, it will re-draw the first cube just twice.
So the solution should be easy to see: You just need to offset each index in the index array by the actual base offset of the vertices in the global vertex array.
I'm having my vertices clipped on the edged as shown on this album:
http://imgur.com/a/VkCrJ
When my terrain size if 400 x 400 i get clipping, yet at 40x40 or anything less, i don't get any clipping.
This is my code to fill the position and indices:
void Terrain::fillPosition()
{
//start from the top right and work your way down to 1,1
double x = -1, y = 1, z = 1;
float rowValue = static_cast<float>((1.0f / _rows) * 2.0); // .05 if 40
float colValue = static_cast<float>((1.0f / _columns) * 2.0); // .05 if 40
for (y; y > -1; y -= colValue)
{
for (x; x < 1; x += rowValue)
{
_vertexPosition.emplace_back(glm::vec3(x, y, z));
}
x = -1;
}
}
This properly sets my position, I've tested it with GL_POINTS. It works fine at 400x400 and 40x40 and other values in between.
Index code:
void Terrain::fillIndices()
{
glm::ivec3 triangle1, triangle2;
for (int y = 0; y < _columns - 1; y++)
{
for (int x = 0; x < _rows - 1; x++)
{
// Triangle 1
triangle1.x = x + y * _rows;
triangle1.y = x + (y + 1) * _rows;
triangle1.z =(x + 1) + y * _rows;
// Triangle 2
triangle2.x = triangle1.y;
triangle2.y = (x + 1) + (y + 1) * _rows;
triangle2.z = triangle1.z;
// add our data to the vector
_indices.emplace_back(triangle1.x);
_indices.emplace_back(triangle1.y);
_indices.emplace_back(triangle1.z);
_indices.emplace_back(triangle2.x);
_indices.emplace_back(triangle2.y);
_indices.emplace_back(triangle2.z);
}
}
}
_indices is std::vector.I'm not sure what's causing this, But I'm pretty sure it's the way I'm filling the indices for the mesh. I've re-written my algorhithm and it ends up with the same result, small values work perfectly fine, and large values over ~144 get clipped. I fill my buffers like this:
void Terrain::loadBuffers()
{
// generate the buffers and vertex arrays
glGenVertexArrays(1, &_vao);
glGenBuffers(1, &_vbo);
glGenBuffers(1, &_ebo);
// bind the vertex array
glBindVertexArray(_vao);
// bind the buffer to the vao
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _vertexPosition.size() * sizeof(_vertexPosition[0]), _vertexPosition.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices.size() * sizeof(_indices[0]), _indices.data(), GL_STATIC_DRAW);
// enable the shader locations
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
// unbind our data
glBindVertexArray(0);
}
and my draw call:
void Terrain::renderTerrain(ResourceManager& manager, ResourceIdTextures id)
{
// set the active texture
glActiveTexture(GL_TEXTURE0);
// bind our texture
glBindTexture(GL_TEXTURE_2D, manager.getTexture(id).getTexture());
_shaders.use();
// send data the our uniforms
glUniformMatrix4fv(_modelLoc, 1, GL_FALSE, glm::value_ptr(_model));
glUniformMatrix4fv(_viewLoc, 1, GL_FALSE, glm::value_ptr(_view));
glUniformMatrix4fv(_projectionLoc, 1, GL_FALSE, glm::value_ptr(_projection));
glUniform1i(_textureLoc, 0);
glBindVertexArray(_vao);
// Draw our terrain;
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawElements(GL_TRIANGLES, _indices.size(), GL_UNSIGNED_INT, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBindVertexArray(0);
_shaders.unuse();
}
I thought it was because of my transformations to the model, so i removed all transformations and it's the same result. I tried debugging by casting the glm::vec3 to_string but the data looks fine, My projectionMatrix is:
glm::perspective(glm::radians(_fov), _aspRatio, 0.1f, 1000.0f);
So i doubt it's my perspective doing the clipping. _aspRatio is 16/9.
It's really strange that it works fine with small rowsxcolumns and not large ones, I'm really not sure what the problem is.
I would check the length of _vertexPosition; I suspect the problem is that you are (depending on the number of _rows) generating an extra point at the end of your inner loop (and your outer loop too, depending on _columns).
The reason is that the termination condition of your vertex loops depends on the exact behavior of your floating point math. Specifically, you divide up the range [-1,1] into _rows segments, then add them together and use them as a termination test. It is unclear whether you expect a final point (yielding _rows+1 points per inner loop) or not (yielding a rectangle which doesn't cover the entire [-1,1] range). Unfortunately, floating point is not exact, so this is a recipe for unreliable behavior: depending on the direction of your floating point error, you might get one or the other.
For a larger number of _rows, you are adding more (and significantly smaller) numbers to the same initial value; this will aggravate your floating point error.
At any rate, in order to get reliable behavior, you should use integer loop variables to determine loop termination. Accumulate your floating point coordinates separately, so that exact accuracy is not required.
I have a class terrain which create a grid of Quads. I do it like this
for(int z=0; z<_length;z++){
for(int x=0; x<_width;x++){
vertices.push_back(vec3((float)x*250, 0.f, (float)z*250));
}
}
for(int z=0; z<(_length-1);++z){
for(int x=0; x<(_width-1);++x){
int index = z*_width+x;
Vertex _vertices[] = {
Vertex(vertices.at(index),vec3(0, 0, 0)),
Vertex(vertices.at(index+1),vec3(0, 0, 0)),
Vertex(vertices.at(index+_width),vec3(0, 0, 0)),
Vertex(vertices.at(index+1+_width),vec3(0,0,0))
};
unsigned short indices[]= {index,index + 1,index +
_width,index + 1,index + _width,index + _width + 1};
Quad quad(_vertices, 4, indices, 6);
squares.push_back(quad);
i++;
}
}
The vertices and the logic are correct, but the indices aren't, for some reason. here is the output for this code :
But when I change this indices to this :
unsigned short indices[]= {0,1,2,1,2,3};
It works great :
The problem is I don't understand why this line
unsigned short indices[]= {index,index + 1,index +
_width,index + 1,index + _width,index + _width + 1};
doesn't work. And if it worked, my grid would consume a lot less ressources. If someone could explain me why it doesn't work, It would be great, thanks you.
In case you need to know how I draw a Quad, here is the code :
class Quad{
public:
Quad(Vertex *_vertices, int _n, unsigned short * _indices, unsigned short _numIndices){
for(int i=0; i < _numIndices; i++){
indices.push_back(_indices[i]);
}
for(int i=0; i<_n; i++){
vec3 v = vec3(_vertices[i].position, _lengthPower);
position.push_back(v);
}
glGenVertexArrays(1, &mVertexArray);
glBindVertexArray(mVertexArray);
glGenBuffers(1, &mPositionBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mPositionBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vec3)*position.size(), position.data(), GL_STATIC_DRAW);
glGenBuffers(1, &mIndicesBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndicesBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short)*indices.size(), indices.data(), GL_STATIC_DRAW);
}
void draw(){
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mPositionBuffer);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndicesBuffer);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, 0);
glDisableVertexAttribArray(0);
}
~Quad(){
}
private:
std::vector<unsigned short> indices;
std::vector<vec3> position;
GLuint mVertexArray;
GLuint mPositionBuffer;
GLuint mIndicesBuffer;
};
I'm using, OpenGL, glm, glfw etc.
The posted code uses parts of two solutions to draw the terrain. Each one would work just fine by itself, but this is halfway between.
Separate Quads
Most of the code shown treats the terrain as a set of separate quads. In this case, you have length - 1 times width - 1 quad instances, each one with 4 vertices and 6 indices.
This is exactly what your Quad class implements. It stores 4 vertices and 6 indices in its own pair of VBOs, and sets up a VAO with the attribute state. Then you instantiate a Quad for each square in your terrain.
Since the vertices for each Quad are stored in its own buffers, the implication is that the indices reference vertices within this buffer. Which means that the indices are in the range 0 to 3, which is exactly what you found to be working.
The downside of this choice is that it will be inefficient for large terrains. You'll have a lot of objects (2 VBOs and 1 VBO for each quad), and need a separate draw call for rendering each quad. You're also not sharing vertices, having 4 copies of most vertices in your overall data structure.
You could actually drop the indices for this approach, and use a glDrawArrays(GL_TRIANGLE_STRIP, 4) call to draw the quad. But the other disadvantages remain.
Shared Vertices
A much more efficient approach is to store the entire terrain in a single VBO. Your code started doing that here:
for(int z=0; z<_length;z++){
for(int x=0; x<_width;x++){
vertices.push_back(vec3((float)x*250, 0.f, (float)z*250));
}
}
But then you do not follow through with it. What you need to do is store these vertices in a single VBO for the entire terrain.
Since the indices will then reference vertices by their position in this vertex buffer that contains all vertices in the terrain, the index calculations start to make much more sense:
int index = z*_width+x;
unsigned short indices[]= {index,index + 1,index +
_width,index + 1,index + _width,index + _width + 1};
This would be the indices for a quad within this overall VBO. You would want to change this to also build an index array for the entire terrain. This could look something like this:
for(int z = 0; z < _length - 1; ++z) {
for(int x = 0; x < _width; ++x) {
indices.push_back(z * width + x);
indices.push_back((z + 1) * width + x);
}
}
This can then be rendered using length - 1 triangle strips. with 2 * _width indices each. The common vertices are shared, which makes the whole thing much more efficient. You could reduce the rendering to a single draw call by using slightly more advanced features, like primitive restart.
The only downside is that it might seem less object oriented to not have objects for each quad. But that seems sort of artificial anyway. You have a terrain that consists of a grid of vertices. I see nothing wrong with having a terrain object containing this whole grid. And I like classes and objects as much as the next guy (some say too much...).
So, I'm rendering a bunch a cubes; like, there will be thousands of cubes on the screen at once. The problem is that calculating transformations matrices every frame for each cube, as it turns out, isn't very efficient: 1,000 cubes renders at 54 fps and 10,000 cubes at 6 fps. I need to be able to change these transformation matrices because the player will be able to traverse them.
Here's my rendering code:
void Tester::render()
{
for (int i = 0; i < numInstances; i++)
transforms[i] = Commons::PERSPECTIVE_MATRIX * translations[i];
glBindBuffer(GL_ARRAY_BUFFER, m_transformationMatrixBuffer);
glBufferData(GL_ARRAY_BUFFER, numInstances * sizeof(glm::mat4), &transforms[0], GL_DYNAMIC_DRAW);
for (int i = 0; i < 4; i++)
{
glVertexAttribPointer(MVP_LOC + i, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (const void*)(sizeof(float) * i * 4));
glEnableVertexAttribArray(MVP_LOC + i);
glVertexAttribDivisor(MVP_LOC + i, 1);
}
glDrawElementsInstanced(GL_TRIANGLES, m_numIndexDataElements, GL_UNSIGNED_SHORT, 0, numInstances);
}
Basically, my question is: how can I efficiently calculate transformation matrices for each cube instance?
There is one operation that will be slow: the first for loop. instead just pass Commons::PERSPECTIVE_MATRIX as a uniform and just the translation part in the attribute.
Then in the vertex shader you will multiply them an then apply to the vertex.
If you need to have a per-instance rotation as well I would suggest passing a quaternion for that.
I have a vector of floats, this vector describes a set of triangles each triangle is described using 18 floats, first 3 are the first vertex, next 3 decribes the normal of the last vertex, and so on 3 times until describe each triangle.
i am using
std::vector< GLfloat >
to store all numbers, and this is my code to render the triangles
void Visualizer::draw3dModel()
{
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glNormalPointer(GL_FLOAT, 6 * sizeof(GLfloat), this->vertexes->data() + 3);
glVertexPointer(3, GL_FLOAT, 6 * sizeof(GLfloat), this->vertexes->data());
glPushMatrix();
glScalef(0.05f, 0.05f, 0.05f);
glColor3f(1,1,1);
glDrawArrays(GL_TRIANGLES, 0, this->vertexes->size());
glPopMatrix();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
this code actually works, but in certain moments I recreate the vector of floats with new triangles, it can be more or less triangles
before creating new triangles I clear the vector of vertexes, using this functions
std::vector< GLfloat >* MarchingCubesThread::getTriangles(float minvalue_scale)
{
this->generateTriangles(minvalue_scale);
for(unsigned int i=0; i < this->num_triangles; i++)
{
for(int j=0; j < 3; j++)
{
this->vertexes.push_back(this->triangles[i].p[j].x);
this->vertexes.push_back(this->triangles[i].p[j].y);
this->vertexes.push_back(this->triangles[i].p[j].z);
this->vertexes.push_back(this->triangles[i].norm.x);
this->vertexes.push_back(this->triangles[i].norm.y);
this->vertexes.push_back(this->triangles[i].norm.z);
}
}
return &(this->vertexes);
}
void MarchingCubesThread::generateTriangles(float minvalue_scale)
{
this->vertexes.clear();
this->triangles.clear();
this->triangles = MarchingCubesDataset(this->dataset->getMaxVal() * minvalue_scale, *(this->dataset), LinearInterp, this->num_triangles);
}
After playing around creating a new set of new triangles, the OpenGL render updates the mesh well, but in certain moments, I get some garbage triangles, and/or triangles that are from last iteration, and they should be cleared by calling this:
this->vertexes.clear();
this->triangles.clear();
here some screenshots taken in consecutive times:
any clues about what is happening here?, thank you
PD: for a complete source code, this is the public git repository on github:
https://github.com/joecabezas/MemoriaJoeCabezasCode/tree/visualizer
glDrawArrays expects not count of floats in your buffer, but count of vertices.
So correct call should be:
glDrawArrays(GL_TRIANGLES, 0, this->vertexes->size() / 6);
By letting glDrawArrays to draw 6 times more vertices you were displaying garbage after end of valid vector data, and sometimes it was data from previous iteration.