Jump to content

  • Log In with Google      Sign In   
  • Create Account


Geometry Clipmaps Terrain Tutorial with Source


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
13 replies to this topic

#1 spacerat   Members   -  Reputation: 713

Like
10Likes
Like

Posted 29 January 2014 - 04:00 AM

After browsing the web for a usable terrain lib, I ended up writing one myself as the others were simply too bloated.

I am sharing the result now as a tutorial and hope it will help the one or other of you to get started with terrain rendering.

The main code as only 200 lines, so is easy to understand.

Performance is around 1000 fps.

 

Key settings:
Space : wireframe view
Enter : Top-down view

 

You can fetch the zip with all required includes from here or from the attachment.

 

Changelog: 

Bugfix applied: glDrawArrays( GL_LINES, 0, vert.size()); (old) -> glDrawArrays( GL_LINES, 0, vert.size()/3); (new / ok)

///////////////////////////////////////////
int grid= 64;			// patch resolution
int levels=5;			// LOD levels
int width=2048,height=2048;     // heightmap dimensions
///////////////////////////////////////////

void DrawScene()
{
	if ( GetAsyncKeyState(VK_ESCAPE) )  exit(0);

	POINT cursor;
	GetCursorPos(&cursor); // mouse pointer position

	bool	wireframe= GetAsyncKeyState(VK_SPACE);	// render wireframe
	bool	topdown	 = GetAsyncKeyState(VK_RETURN);	// view top-down
	float	viewangle= float(cursor.x)/5.0;
	vec3f	viewpos ( (timeGetTime()>>2)&((1<<17)-1) , -(float(cursor.y)/1000.0)* 0.1-0.01 , 0 );

	glClearDepth(1.0f);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_FRONT);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

	static int tex_heightmap=0;
	static int tex_terrain=0;

	static bool init=true;
	static Shader shader("Shader");

	static int vbo=0;
	static std::vector<float> vert;

	if(init)
	{
		/*+++++++++++++++++++++++++++++++++++++*/
		// terrain heightmap
		Bmp bmp(width,height,32);
		loopj(0,height) loopi(0,width)
		{
			float x= float(i)/float(width);
			float y= float(j)/float(height);
			float h = (sin(4*M_PI*x)+sin(4*M_PI*y)+sin(16*M_PI*x)*sin(16*M_PI*y))*0.125+0.5;
			((float*)bmp.data)[i+j*width]=h;
		}	
		//bmp.load_float("../result.f32"); // <-- use this for loading raw float map from file
		tex_heightmap = ogl_tex_new(width,height,GL_LINEAR_MIPMAP_LINEAR,GL_REPEAT,GL_LUMINANCE16F_ARB,GL_LUMINANCE,bmp.data, GL_FLOAT);
		/*+++++++++++++++++++++++++++++++++++++*/
		// terrain texture
		loopj(0,height)	loopi(0,width) loopk(0,3)
		{
			bmp.data[(i+j*width)*3+k]=i^j^(k*192);
		}
		tex_terrain = ogl_tex_new(width,height,GL_LINEAR_MIPMAP_LINEAR,GL_REPEAT,GL_RGB,GL_RGB,bmp.data, GL_UNSIGNED_BYTE);
		/*+++++++++++++++++++++++++++++++++++++*/
		// driver info
		std::cout << "GL_VERSION: " << glGetString(GL_VERSION) << std::endl;
		std::cout << "GL_RENDERER: " << glGetString(GL_RENDERER) << std::endl;
		std::cout << "GL_VENDOR: " << glGetString(GL_VENDOR) << std::endl;
		std::cout << "GLU_VERSION: " << gluGetString(GLU_VERSION) << std::endl;
		std::cout << "GLUT_API_VERSION: " << GLUT_API_VERSION << std::endl;
		/*+++++++++++++++++++++++++++++++++++++*/
		// load shaders
		shader.attach(GL_VERTEX_SHADER,"../shader/vs.txt");
		shader.attach(GL_FRAGMENT_SHADER,"../shader/frag.txt");
		shader.link();
		/*+++++++++++++++++++++++++++++++++++++*/
		// make vbo quad patch
		loopj(0,grid+1)
		loopi(0,grid+2)
		{
			loopk(0, ((i==0) ? 2 : 1) )
			{
				vert.push_back(float(i)/grid);
				vert.push_back(float(j)/grid);
				vert.push_back(0);
			}			
			++j;
			loopk(0, ((i==grid+1) ? 2 : 1) )
			{
				vert.push_back(float(i)/grid);
				vert.push_back(float(j)/grid);
				vert.push_back(0);
			}
			--j;
		}
		/*+++++++++++++++++++++++++++++++++++++*/
		glGenBuffers(1, (GLuint *)(&vbo));
		glBindBuffer(GL_ARRAY_BUFFER, vbo);
		glBufferData(GL_ARRAY_BUFFER, sizeof(float)*vert.size(),&vert[0], GL_DYNAMIC_DRAW_ARB );
		/*+++++++++++++++++++++++++++++++++++++*/
		init=false;
		/*+++++++++++++++++++++++++++++++++++++*/
	}
	glMatrixMode( GL_PROJECTION);
	glLoadIdentity();

	if (topdown)
	{
		glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
		glRotatef(180,1,0,0);
		wireframe^=1;
	}
	else		 
	{
		int vp[4];
		glGetIntegerv(GL_VIEWPORT, vp);
		gluPerspective(90.0,float(vp[2])/float(vp[3]) , 0.0001, 1.0);
		glTranslatef(0,viewpos.y,0);	// set height
		glRotatef(130,1,0,0);		
		glRotatef(viewangle,0,0,1);	// set rotation
	}

	matrix44 mat;
	glGetFloatv(GL_PROJECTION_MATRIX, &mat.m[0][0]);		
	
	// Enable VBO
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo);				
	glEnableClientState(GL_VERTEX_ARRAY);					
	glVertexPointer  ( 3, GL_FLOAT,0, (char *) 0);			

	glEnable(GL_TEXTURE_2D);
	glActiveTextureARB( GL_TEXTURE0 );
	glBindTexture(GL_TEXTURE_2D, tex_heightmap);
	glEnable(GL_TEXTURE_2D);
	glActiveTextureARB( GL_TEXTURE1 );
	glBindTexture(GL_TEXTURE_2D, tex_terrain);

	// Triangle Mesh
	shader.begin();
	shader.setUniform1i("tex_heightmap",0);
	shader.setUniform1i("tex_terrain",1);

	float sxy=2; // scale x/y
	shader.setUniform4f("map_position", 
		-viewpos.x/float(2*512*grid),
		-viewpos.z/float(2*512*grid),0,0);

	loopi(0,levels)
	{
		float ox=(int(viewpos.x*(1<<i))&511)/float(512*grid);
		float oy=(int(viewpos.z*(1<<i))&511)/float(512*grid);

		vec3f scale	(sxy*0.25,sxy*0.25,1);
		shader.setUniform4f("scale" , scale.x,scale.y,1,1);	

		loopk(-2,2) loopj(-2,2) // each level has 4x4 patches
		{
			if(i!=levels-1) if(k==-1||k==0) if(j==-1||j==0) continue;

			vec3f offset(ox+float(j),oy+float(k),0);
			if(k>=0) offset.y-=1.0/float(grid); // adjust offset for proper overlapping
			if(j>=0) offset.x-=1.0/float(grid); // adjust offset for proper overlapping

			//cull
			int xp=0,xm=0,yp=0,ym=0,zp=0;
			looplmn(0,0,0,2,2,2)
			{
				vec3f v = scale*(offset+vec3f(l,m,float(-n)*0.05)); // bbox vector
				vec4f cs = mat * vec4f(v.x,v.y,v.z,1); // clipspace
				if(cs.z< cs.w) zp++;				
				if(cs.x<-cs.w) xm++;	if(cs.x>cs.w) xp++;
				if(cs.y<-cs.w) ym++;	if(cs.y>cs.w) yp++;
			}
			if(zp==0 || xm==8 || xp==8 || ym==8 || yp==8)continue; // skip if invisible
			
			//render
			shader.setUniform4f("offset", offset.x,offset.y,0,0);
			if(wireframe)	glDrawArrays( GL_LINES, 0, vert.size()/3);
			else		glDrawArrays( GL_TRIANGLE_STRIP, 0, vert.size()/3);
		}
		sxy*=0.5;
	}	
	shader.end();

	// Disable VBO
	glDisableClientState(GL_VERTEX_ARRAY);									
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);								
	glutSwapBuffers();
}
///////////////////////////////////////////
int main(int argc, char **argv) 
{ 
  glutInit(&argc, argv);  
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);  
  glutInitWindowSize(1024, 512);  
  glutInitWindowPosition(0, 0);  
  glutCreateWindow("Geometry Clipmaps Example");
  glutDisplayFunc(DrawScene);
  glutIdleFunc(DrawScene);
  glewInit();
  wglSwapIntervalEXT(0);
  glutMainLoop();  
  return 0;
}
///////////////////////////////////////////

Terrain_GeometryClipmaps_Tutorial.png

 

Clipboard02.png

Attached Files


Edited by spacerat, 31 January 2014 - 12:17 PM.


Sponsor:

#2 4wesome   Members   -  Reputation: 102

Like
0Likes
Like

Posted 30 January 2014 - 05:35 PM

This is going to prove invaluable to me thanks

 

I spent the last 2 evenings looking for some form of source code to look for GPUGCM but this is all I could find!

 

I'm still noobie to it all but this will be studied hard. :)



#3 spacerat   Members   -  Reputation: 713

Like
0Likes
Like

Posted 11 February 2014 - 05:53 AM

I am glad that it is helpful. 

I have actually continued the development and now combined it with the procedural heightmap generation; you can find a demo on my page.

 

t.png


Edited by spacerat, 11 February 2014 - 05:53 AM.


#4 NineYearCycle   Members   -  Reputation: 884

Like
0Likes
Like

Posted 21 February 2014 - 07:09 AM

This is very cool, are you going to be uploading new demos as you go on or updating the example?


"Ars longa, vita brevis, occasio praeceps, experimentum periculosum, iudicium difficile"

"Life is short, [the] craft long, opportunity fleeting, experiment treacherous, judgement difficult."


#5 dpadam450   Members   -  Reputation: 842

Like
0Likes
Like

Posted 21 February 2014 - 04:32 PM

Why is the wireframe not actually wireframe? I see GL_LINES but when I draw my terrain that way, it still comes out as real wireframe.

What does the green/brown/blue/red designate?

You should comment your code even if it is for yourself. That looks impossible to maintain if you come back to it a year later or add a ton more code. Almost not comments and not very meaningful variable names.



#6 spacerat   Members   -  Reputation: 713

Like
0Likes
Like

Posted 03 March 2014 - 09:09 PM

 

This is very cool, are you going to be uploading new demos as you go on or updating the example?

 

Thank you ! 

I might release further demos or tutorials (like the skeletal animation one),

but since I plan to use the terrain for my turrican clone, the further development is closed source for now. 

 

 

Why is the wireframe not actually wireframe? I see GL_LINES but when I draw my terrain that way, it still comes out as real wireframe.

What does the green/brown/blue/red designate?

You should comment your code even if it is for yourself. That looks impossible to maintain if you come back to it a year later or add a ton more code. Almost not comments and not very meaningful variable names.

 

The wireframe does look like lines, as the geometry is rendered as triangle strips.

You can change GL_LINES to GL_LINE_STRIP, which will give the look closer to triangles. However, you will get error lines at the patch borders due to the way the mesh is generated with invalidating border triangles by using the same vertex twice.

 

The colors red etc are the result of the xor function used to create the sample texture. There is no meaning.

 

As for the comments, well , I have added as much comments as i need for myself when I get back to the code lateron.


Edited by spacerat, 04 March 2014 - 05:18 AM.


#7 public3NEMY   Members   -  Reputation: 105

Like
0Likes
Like

Posted 09 March 2014 - 10:03 PM

Just wanna say thanks for the demo.

#8 CDProp   Members   -  Reputation: 890

Like
0Likes
Like

Posted 10 March 2014 - 12:17 AM

What are you using for your sky?



#9 spacerat   Members   -  Reputation: 713

Like
0Likes
Like

Posted 14 March 2014 - 07:05 PM

 

Just wanna say thanks for the demo.

 

 

Glad I could help.

 

 

What are you using for your sky?

 

Its a simple skybox.

If you google for "skybox texture" you can find many alternatives.



#10 Hodgman   Moderators   -  Reputation: 27622

Like
0Likes
Like

Posted 14 March 2014 - 08:53 PM


The wireframe does look like lines, as the geometry is rendered as triangle strips.
You can change GL_LINES to GL_LINE_STRIP, which will give the look closer to triangles. However, you will get error lines at the patch borders due to the way the mesh is generated with invalidating border triangles by using the same vertex twice.
You can leave it as drawing triangle strips, but set glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); to get wire-frame triangles.

#11 dpadam450   Members   -  Reputation: 842

Like
0Likes
Like

Posted 15 March 2014 - 08:37 PM

 

You can leave it as drawing triangle strips, but set glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); to get wire-frame triangles.

I do that with my terrain and it kills the performance. I haven't thought about why as there isn't much overwrie on my terrain (about 0 triangles mapping ot the same pixel to make overdraw).  I assume that the polygonMode stll runs the pixel shader on all triangles pixels and not just the edges, but then later discards writing in depth/color for the non edge pixels. Only explanation I can think of off the top of my head. Goes from like 150FPS to 20 or something. so the inner triangle pixels would be overwritten a TON of times in my case.


Edited by dpadam450, 15 March 2014 - 08:42 PM.


#12 spacerat   Members   -  Reputation: 713

Like
0Likes
Like

Posted 18 March 2014 - 01:38 AM

I just tried to implement GL_FRONT_AND_BACK in the terrain tutorial - however it doesnt give the desired result. The problem is that I used degenerated triangles at the boundaries of the quad mesh - therefore there are lots of "error-lines" on the screen. To solve this, one would have to create an alternative quad mesh that is not using degenerated triangles.


Edited by spacerat, 18 March 2014 - 01:39 AM.


#13 rouncer   Members   -  Reputation: 297

Like
0Likes
Like

Posted 18 March 2014 - 01:52 PM

excuse me, what is a clip map in this context?  ive never used the term before.



#14 L. Spiro   Crossbones+   -  Reputation: 12231

Like
0Likes
Like

Posted 18 March 2014 - 03:36 PM

GeoClipmaps.

 

 

L. Spiro


It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS