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.
Related
CONTEXT:
I am trying to create an open world nature-like scene in openGL. An array terrainVertices of length 3*NUM_OF_VERTICES contains the vertex data for the terrain, which is a noise generated heightmap. Each vertex has x,y,z coordinates, and based on the value of y a certain color is assigned to the vertex, producing a somewhat smooth transition between deep waters and mountain peaks.
PROBLEM:
The lakes are formed when a neighbourhood of vertices has a y value, such that y<0. The colouring is performed as expected, however the result is not realistic. It looks like a blue pit:
The way I decided to tackle this issue is by creating a layer of vertices, that appear over the lake, having y=0 and a light blue color with low alpha value, thus creating the illusion of a surface, beneath which lies the actual lake.
The terrainVertices array is indexed by an element array elements[NUM_OF_ELEMENTS]. I iterate over the element array and try to find triplets of indices, that correspond to vertices with y<0. I gather every triplet that matches this condition in a vector, then create a new vertex array object, with the same vertex data as the terrain, but with the new element buffer object I just described. The vertices that are "underwater" have their y value replaced by 0, so as to stick to the surface of the lake.
Here's the code I used to accomplish this:
std::vector<GLuint> waterElementsV;
//iterating over every triangle
for (int i = 0; i < NUM_OF_ELEMENTS / 3; i++) {
//accessing each vertex based on its index from the element array
//checking if its height is lower than zero
//elements[3*i+0, +1, +2] represents the indices for a triangle
//terrainVertices[index + 1] represents the height of the vertex
if (terrainVertices[3 * (elements[3 * i]) + 1] < 0 &&
terrainVertices[3 * (elements[3 * i + 1]) + 1] < 0 &&
terrainVertices[3 * (elements[3 * i + 2]) + 1] < 0) {
//since its a valid triangle, add its indices in the elements array.
waterElementsV.push_back(elements[3 * i]);
waterElementsV.push_back(elements[3 * i + 1]);
waterElementsV.push_back(elements[3 * i + 2]);
}
}
//iterating through the terrain vertices, and setting
//each appropriate vertex's y value to water surface level y = 0
for (unsigned int i = 0; i < waterElementsV.size(); i++) {
currentIndex = waterElementsV[i];
terrainVertices[3 * currentIndex + 1] = 0;
}
//get the vector's underlying array
waterElements = waterElementsV.data();
glGenVertexArrays(1, &waterVAO);
glBindVertexArray(waterVAO);
glGenBuffers(1, &waterVerticesVBO);
glBindBuffer(GL_ARRAY_BUFFER, waterVerticesVBO);
glBufferData(GL_ARRAY_BUFFER, NUM_OF_VERTICES, terrainVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);
glGenBuffers(1, &waterEBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, waterEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(waterElements), waterElements, GL_STATIC_DRAW);
The shaders are very simple, as I only need to assign a position and a colour. No world view projection matrices are used, because every object is drawn relative to the terrain anyway.
Vertex Shader:
#version 330 core
layout (location = 0) in vec3 pos;
void main()
{
gl_Position = vec4(pos, 1.0);
}
Fragment Shader:
#version 330 core
out vec4 fragColor;
void main()
{
fragColor = vec4(0, 0, 0, 1);
}
The result is exactly the same as in the above picture. I've been picking my brain about what is going on but I cannot seem to figure it out... Note that the lakes are spread out at random places, and are not necessarily adjacent to each other.
Any help or tips are greatly appreciated :)
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'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
I'm trying to draw a terrain with GL_TRIANGLE_STRIP and glDrawElements but I'm having a really hard time understanding the indices thing behind glDrawElements...
Here's what I have so far:
void Terrain::GenerateVertexBufferObjects(float ox, float oy, float oz) {
float startWidth, startLength, *vArray;
int vCount, vIndex = -1;
// width = length = 256
startWidth = (width / 2.0f) - width;
startLength = (length / 2.0f) - length;
vCount = 3 * width * length;
vArray = new float[vCount];
for(int z = 0; z < length; z++) {
// vIndex == vIndex + width * 3 || width * 3 = 256 * 3 = 768
for(int x = 0; x < width; x++) {
vArray[++vIndex] = ox + startWidth + (x * stepWidth);
vArray[++vIndex] = oy + heights[z][x];
vArray[++vIndex] = oz + startLength + (z * stepLength);
}
}
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vCount, vArray, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void Terrain::DrawVBO(unsigned int texID, float ox, float oy, float oz) {
float terrainLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };
if(!generatedVBOs) {
GenerateVertexBufferObjects(ox, oy, oz);
generatedVBOs = true;
}
unsigned int indices[] = { 0, 768, 3, 771 };
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * 4, indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexPointer(3, GL_FLOAT, 0, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, terrainLight);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
I believe my vArray is correct, I use the same values when drawing with glBegin(GL_TRIANGLE_STRIP)/glEnd which works just fine.
My guess was to use just the index of the x coordinate for each vertex. But I have no idea if that's the right way to use indices with glDrawElements.
0: Index of the x coordinate from the first vertex of the triangle. Location: (-128, -128).
768: Index of the x coordinate from the second vertex of the triangle. Location: (-128, -127)
3: Index of the x coordinate from the third vertex of the triangle. Location: (-127, -128)
771: Index of the x coordinate from the fourth vertex, which will draw a second triangle. Location: (-127, -127).
I think everything is making sense so far?
What's not working is that the location values above (which I doubled checked on vArray and they are correct) are not the same which glDrawElements is using. Two triangles are drawn but they are a lot bigger than what they should be. It starts correctly at (-128, -128) but it goes to something like (-125, -125) instead of (-127, -127).
I can't understand what I'm doing wrong here...
Using something like the following solves my problem:
unsigned int indices[] = { 0, 256, 1, 257 };
I think it's safe to assume that the index is the x coordinate and that OpenGL is expecting that to be followed by y and z but we shouldn't increase by 3 ourselves, the server does it for us.
And now that I think about it, glDrawElements has the word element on it, which in this case is a vertex with 3 coordinates as specified in glVertexPointer and we need to pass the indices to the element, not the vertex.
I feel so dumb now...