7. Terrain Level Of Detail
Jump To:
heightfield.h Source
heightfield.cpp Source
Heightfield.H File:
This is a really simple change I am going to implement, which will give us a crisper looking terrain, while also giving us a constant 60 frames per second 😀 I am going to add a variable here which will be called hLOD:
// int hLOD;
This is going to be a public variable which we are going to set before we create our terrain. It is going to be a number which is a power of 2. Eg: 2, 4, 8, 16. I am going to use it to draw every nth vertex, where n is the hLOD variable. So we are going to draw every hLOD vertex if that makes sense. The reason this speeds up the terrain rendering should be obvious, the less we have to draw, the faster it will go. But it makes it crisper as well, because if you run the previous tutorials and get up close to the terrain, you will find that it is really jagged. This also smooths out the terrain surface for us.
1. 2. 3. 4. 5. 14. 23. 32. 41. |
#include <windows.h>
class Vert{ float y; class TexCoord{ public: class SwiftHeightField { private: int vhVertexCount; TexCoord *vhTexCoords; unsigned int vhVBOVertices; unsigned int tID[2]; bool Init(void); public: void Render(void); int hLOD; BYTE hHeightField[1024][1024]; }; |
Heightfield.CPP File:
In our heightfield.cpp file, all our changes are going to take place in the Create function. First, we need to change the vertex count that we will be using throughout to tell us how many vertices we need, we do this by changing the following line:
// vhVertexCount = (int)(hmHeight * hHeight * 6) / (1 * 1);
to:
// vhVertexCount = (int)(hmHeight * hHeight * 6) / (hLOD * hLOD);
The next change we need to make is to the increments in our loops. Instead of incrementing by 1 for every vertex, we need to increment by every hLOD to get every hLOD’th vertex like so:
for (int hMapX = 0; hMapX < hmWidth; hMapX+=hLOD){
for (int hMapZ = 0; hMapZ < hmHeight; hMapZ+=hLOD){
After we have our loop setup, we need to make sure that our polygons span over every hLOD vertex, so instead of incrementing our vertices X and Z positions by 1, we need to increment them by our hLOD value as follows:
flX = (float)hMapX + ((nTri == 1 || nTri == 2 || nTri == 5) ? hLOD : 0);
flZ = (float)hMapZ + ((nTri == 2 || nTri == 4 || nTri == 5) ? hLOD : 0);
Next we are going to move onto our main.cpp file and set a value for our hLOD variable.
1. 2. 3. 4. 5. 6. 7. 8. 17. 26. 35. 44. 53. 62. 71. 80. 89. |
#include <stdio.h>
#include <GL/glew.h>
#include “jpeg.h” bool SwiftHeightField::Init(void){ glGenBuffersARB(1, &vhVBOVertices); glGenBuffersARB(1, &vhVBOTexCoords); glBufferDataARB(GL_ARRAY_BUFFER_ARB, vhVertexCount * delete [] vhVertices; delete [] vhTexCoords; vhTexCoords = NULL; return true; bool SwiftHeightField::Create(char *hFileName, const hmHeight = hHeight; FILE *fp; fp = fopen(hFileName, “rb”); fread(hHeightField, 1, hWidth * hHeight, fp); fclose(fp); vhVertexCount = (int)(hmHeight * hHeight * 6) / (hLOD * hLOD vhVertices = new Vert[vhVertexCount]; int nIndex = 0; for (int hMapX = 0; hMapX < hmWidth; hMapX+ =hLOD){for (int hMapZ = 0; hMapZ < hmHeight; hMapZ+=hLOD){ flX = (float)hMapX + ((nTri == 1 || nTri == 2 || nTri == 5) ? hLOD : 0); flZ = (float)hMapZ + ((nTri == 2 || nTri == 4 || nTri == 5) ? hLOD : 0); vhVertices[nIndex].x = flX; vhVertices[nIndex].z = flZ; vhTexCoords[nIndex].u = flX / 1024; vhTexCoords[nIndex].v = flZ / 1024; } SwiftTextureJpeg(tID, “texture.jpg”, 0); Init(); return true; void SwiftHeightField::Render(void){ glEnableClientState(GL_TEXTURE_COORD_ARRAY); glBindBufferARB(GL_ARRAY_BUFFER_ARB, vhVBOTexCoords); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_TRIANGLES, 0, vhVertexCount); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_TEXTURE_2D); |
Main.CPP File:
We are only going to make 1 change to our main.cpp file in this tutorial. This is the addition of the line:
// hField.hLOD = 8;
In our Init function, right before we call the creation of our heightfield. I chose the number 8 because after testing different numbers, I found 8 to give good frame rates, and still look really nice.
And that is it for now. Check out the next tutorial on adding a bump map texture to the heightfield for dynamic lighting on the terrain here *coming soon*. 🙂
If you have any questions, just email me at swiftless@gmail.com
1. 2. 3. 4. 13. 22. 31. 40. 49. 58. 67. 76. 85. 94. 103. 112. 121. 130. 139. |
#include <GL/glew.h> #include <GL/gl.h> #include <GL/glext.h> #include <GL/GLUT.h> #include <math.h> #include <windows.h> #include <stdio.h> #include “heightfield.h” #pragma comment(lib,“glew32.lib”) float xpos = 851.078, ypos = 351.594, zpos = 281.033, xrot = float lastx, lasty; float bounce; SwiftHeightField hField; void camera (void) { int posZ = (int)zpos; glRotatef(xrot,1.0,0.0,0.0); glTranslated(–xpos,–ypos,–zpos); void display (void) { glClearColor (0.0,0.0,0.0,1.0); glLoadIdentity(); glPushMatrix(); glPopMatrix(); glutSwapBuffers(); void initExtensions(void){ glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress(“glGenBuffersARB”); glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress(“glBufferDataARB”); } void Init (void) { glDepthFunc(GL_LEQUAL); initExtensions(); hField.hLOD = 8; hField.Create(“heightField.raw”, 1024, 1024); #include <iostream.h> void mouseMovement(int x, int y) { int diffy=y–lasty; yrot += (float) diffx; void keyboard (unsigned char key, int x, int y) { if (key == ‘w’) yrotrad = (yrot / 180 * 3.141592654f); xpos += float(sin(yrotrad)) * cScale; ypos –= float(sin(xrotrad)) ; } if (key == ‘s’) yrotrad = (yrot / 180 * 3.141592654f); xpos –= float(sin(yrotrad)) * cScale; ypos += float(sin(xrotrad)); } if (key == ‘d’) yrotrad = (yrot / 180 * 3.141592654f); zpos += float(sin(yrotrad)) * cScale; if (key == ‘a’) yrotrad = (yrot / 180 * 3.141592654f); zpos –= float(sin(yrotrad)) * cScale; } void reshape (int w, int h) { glMatrixMode (GL_PROJECTION); glMatrixMode (GL_MODELVIEW); int main (int argc, char **argv) { glutInit(&argc, argv); glutInitWindowSize(500, 500); Init(); glutReshapeFunc(reshape); glutMainLoop (); |
Download:
Download heightfield.h Source Code for this Tutorial
Download heightfield.cpp Source Code for this Tutorial
Download main.cpp Source Code for this Tutorial
Download heightfield.raw for this Tutorial
Download texture.jpg for this Tutorial
Download jpeg.h Source Code for this Tutorial
Could you tell me why I’m getting this error? Can’t fix it…
1>—— Build started: Project: Terrain Vertex Buffer Objects, Configuration: Debug Win32 ——
1> Main.cpp
1>HeightField.obj : error LNK2001: unresolved external symbol __imp____glewBindBufferARB
1>Main.obj : error LNK2001: unresolved external symbol __imp____glewBindBufferARB
1>HeightField.obj : error LNK2001: unresolved external symbol __imp____glewBufferDataARB
1>Main.obj : error LNK2001: unresolved external symbol __imp____glewBufferDataARB
1>HeightField.obj : error LNK2001: unresolved external symbol __imp____glewGenBuffersARB
1>Main.obj : error LNK2001: unresolved external symbol __imp____glewGenBuffersARB
1>Main.obj : error LNK2001: unresolved external symbol __imp____glewDeleteBuffersARB
1>jpeg.lib(jerror.obj) : error LNK2019: unresolved external symbol __iob referenced in function _output_message
1>C:\Users\Tomas\Desktop\OpenGl\Terrain Vertex Buffer Objects\Debug\Terrain Vertex Buffer Objects.exe : fatal error LNK1120: 5 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Where would I start if I wanted to add lighting to this?
sorry it was actually the first triangle,
flX = (float)hMapX + ((nTri == 1 || nTri == 2 || nTri == 5) ? LOD : 0);
flZ = (float)hMapZ + ((nTri == 1 || nTri == 4 || nTri == 5) ? LOD : 0);
solves this problem
Did you ever try this with back face culling?
the order of verts on the second triangle( think it’s the second) is wrong so when you try to cull it, it will appear to have holes.
Wonderful!