Monday, 29 July 2013

Faster Terrain Render

In the previous post about the basics of terrain rendering you saw a simple way to render a terrain in OpenGL.   In this post you’ll see how to improve the performance of the render procedure.   On my computer, the original procedure measured 590 frames per second; and with the improvements shown here it measured a whopping 1017 frames per second.   That is nearly a 100% improvement.

Let me start by reviewing the original plan: it is shown in Listing 1.  This procedure traverses through all elements, calculates and sends each vertex to OpenGL.  The CPU does all the calculation work – and it does this every time the scene is rendered.  To be fair, the procedure is not all bad: it uses very little memory. 

But what exactly is our memory overhead?  A float is 4 bytes.  As shown in the previous post, a 50x50 heightmap produces 5 000 vertices (during the triangular traverse).  So, if we store each vertex, we would need less than 60 kb (5 000 * 3 * 4) of memory on the GPU.   As a side effect: once we have the vertices on the GPU memory, we do not have to make the 5 000 calls o OpenGL.  And voila, we gain a lot of speed!

Listing 1:


The first task is to calculate the vertices.  This is done once: before this first render.  The idea is simple: store the calculations to a std::vector.  The code is shown in Listing 2.  Note that the space for the vector is allocated on its constructor – because we know how many elements we need. 
 Listing 2:


Now that you have the values in main memory, you need to copy the data to the GPU.  This is done in three steps (see Listing 3).  First use glGenBuffers to create a handle to a new buffer.  Then, glBindBuffer tells the OpenGL state machine that you want to use this buffer.  And then glBufferData does the real work: it copies the data from the main to the GPU memory.  The last line tells OpenGL that we are not using the buffer anymore.   
Listing 3:



All that remains for you is to render the scene using the buffer.  This is done in Listing 4.  This procedure must be called inside the game loop for every render.  As before you tell OpenGL you want to use the buffer identified by the previously allocated handle. The calls to glEnableVertexAttribArray and glVertexAttribPointer tells OpenGL what the format is of the data in the buffer:  essentially we store 3 float values per vertex.  Then the call to glDrawArrays uses the data definition and the data in the buffer to render the terrain.  It is called for each column.  You use the heightmap dimensions to calculate prims, the number of vertices per column.  This value is then used to find the offset in the data for the given column.
Listing 4:



You now know a little bit more about OpenGL buffers.  You get the same picture as before – its just faster. 

Saturday, 13 July 2013

The basics of terrain rendering

screenIn the previous post called The heightmap: from concept to template  you saw how to create a C++ class template for a heightmap.  This post is the next step: you’ll gain an understanding of how to use OpenGL to render a terrain that uses that heightmap.  The screenshot on the right shows what you’ll have if you follow this post carefully.  The screenshot is produced from the heightmap concept and the BMP file introduced in the previous post.  

Broadly speaking, the terrain is rendered in two steps: 1) create the geometry 2) send the geometry to OpenGL.   The first step in technology agnostic and and could be of use even if you do not like OpenGL.   

Let’s start with step one. Terrain geometry needs vertices, so you need to use each element in the heightmap matrix to create a three-dimensional vertex.  This is not difficult because a matrix element can be represented as the triple: {c,r,h} where c is the column, r is the row and h is the value of the element.  Imagine h is zero for all matrix elements, then the terrain is a flat rectangular grid, composed of equality sized squares. If there are m columns and n rows in the heightmap there are m * n vertices, and consequently you’ll have (m-1) * (n-1) squares in your terrain.  You can easily see how it works if the heights are not zero imagining that the corners of the squares are ‘pulled up’ based on the value of h.  Clearly your imagination with not be sufficient in itself: you need a function that maps each triple {c,r,h} to a vertex {x,y,z}. I call this function the transformer

Before you can define the transformer, you need to commit to a meaning for the components of the vertex {x,y,z}.  When talking about terrains, I find it useful to think in terms of the cardinal directions.   Orientate your terrain so that the top row of squares lies north and the left column of squares is on the west side.  OpenGL does not know the meaning of the cardinal directions, so we need to decide on a Cartesian coordinate system that maps nicely to these directions.  Let x increase from east to west, y increase from south to north and z increase upwards.  Take a moment to consider what this means. You should also decide on an origin for the vertex on the north-west corner: let that be {0,0,h’} where h’ is a mapping from the h value of the matrix element at {0,0}.

The transformer needs to know the size of your terrain.  OpenGL does not define units, but it is a good idea for you to tie meaning to the floating point numbers sent to the rendering engine.  For convenience I always use the rule: one meter = 1.0f.  Let square_length be the length of the side of a square.  So, if your terrain stretches 10km west to east and you have 20 squares in a row, then your square_length is 500.0f.   Using this value, the transformer can calculate two components: x = r * square_length and y = c * square_length * (-1).  Notice that the sign for y is changed because r increases towards the south while the coordinate system’s y increases towards the north.

The mapping from h to z should also be uncomplicated.  If you use a byte for h the value of h ranges from 0 to 255.  One way to use this range is to decide on the maximum and minimum height you want for your terrain. Let’s call those max_h and min_h. Keep in mind that min_h could be negative and that a negative value for h could mean below water level.  From these bounds you calculate height_scale = (max_h – min_h) / 255, and you get the function z = h * height_scale which calculates the final component of the vertex.     

Listing 1 shows the functor template called terrain::Transformer.  This class implements the component calculations in C++.  The subclass called terrain::TransformerByte can be used for a heightmap with byte elements. It simply provides a convenient constructor that takes the bounds of h as arguments.  The basic Byte, Scalar and Vector types used in the listing are defined in GameEx.

Listing 1:



That wraps up step one: you have geometry.  Now we decide how render the geometry.  Let’s create a triangle strip for each column of squares.  Consider the squares in the west-most column. The vertices on left (west) side of those squares have c = 0, and those on the right hand side c = 1.  For this column you create the triangle strip by walking the following  {c,r} sequence: {1,0}, {0,0}, {1,1}, {0,1}, {1,2}, {0.2}, {1,3} …. {1,m},{0.m}.  This traversal through the elements in a heightmap, can be implemented on the terrain::Heightmap class template.  Listing 2 shows the new traverse_triangles method.  This method takes a function as argument.  It calls this function for each triple {c,r,h} it visits while walking in the desired sequence column by column.  It emits a sentinel triple {-1,-1,0} to indicate the end of a column has been reached.

Listing 2:



Listing 3 combines the geometry and rendering approach in a class template called TerrainObject.  Take a close look at the draw method: it first draws solid triangles and then red lines ‘over’ those triangles.  Notice how traverse_triangles is called in line 19: it uses a C++ lambda function.  

Listing 3:


Listing 4 shows how these concepts are brought together in the GameEx framework.  If you use another game library this part would obviously be vastly different.  The lines in the listing is all that is needed to show the terrain and have a crude camera to explore your creation a bit.

Listing 4:



This concludes the post. Using the BMP file as as input,  you created a three-dimensional image that has 4900 triangles.  And maybe you leant a bit more of the new C++ language features.

After thoughts:

The post called Faster Terrain Render show you how to use buffers to draw this terrain.

Tuesday, 2 July 2013

The heightmap: from concept to template

If you want to render a terrain, a heightmap is a handy tool to have at your disposal.  This post explores one way to create an abstraction of this concept using the new C++ standard.    
In its essence, a heightmap is simply a matrix of numbers where an element at {c,r} indicates the height of the terrain at some location.  It is a handy data structure and is also a compact way to store the details of a terrain.   The image on the right hand side is an example of a heightmap shown as a 2D image (taken from Wikipedia). The height is determined by the value of the white colour: a whiter pixel indicates a higher level for the terrain at that point.  You should be able to imagine where the hills and valleys are by looking at this image long enough.
As you can expect, compact storage comes with a cost: less data equals less detail.  The granularity of the terrain is determined by the size of the matrix, and also by the size of the data type used for its elements.  For instance if the elements are stored as a bytes the terrain height ranges from 0 to 255.  The height cannot be 23.5 … there is only 256 levels to choose from. 
Getting back to the abstraction: It seems reasonable that a heightmap needs at least three parameters.  These are: (a) the number of columns m, (b) the number of rows n and (c) the data type of the elements, elemT.   We should now be able to implement this abstract concept as a  C++ class template
First, we need is to choose a data structure for the matrix data.  We want a structure that is fixed in memory and stores consecutive elements in sequence.  This constraint will make the heightmap very useful for rendering in OpenGL.   The std::array container is well suited for this purpose. 
Often we want to address the elements in the array using the {c,r} pair as index.  For this reason, I provided an overloaded () operator that can throw std::out_of_range. The code for our class template Heightmap, is shown in Listing 1. 
Listing 1:



Fine and well you say, the meaning is clear, but what is the use of it all?  Ok, let’s develop a more concrete concept that actually does do something.  How about loading the heightmap from an image file, and writing it back? 
Consider the image shown above.  Using my favourite image editor paint.net, I converted this image to an 24-bit BMP file and resized it to be 50 x 50 pixels.  I want a BMP file because this is a file format SDL handles very nicely; the size is an arbitrary choice. 
The fact is, the handy SDL library provides all the heavy lifting we need.  The SDL_LoadBMP function creates an SDL_Surface which contains the pixel data in row order.  The pixel data depends on the pixel format declared in the BMP file.  The 24 bits means the data contains 3 bytes per pixel.  Note that the 3 bytes represents an RGB value where R = G = B. Here 0 is black and 255 is white. Clearly, the R, G and B values are not all needed in the matrix: one byte is enough. 
Let’s call our new abstraction HeightmapWithByte: it is a Heightmap with elemT = unsigned char.   The code for this abstraction is shown in Listing 2.   The read and write members of this new class template allows us to  swap heightmap data from and to a BMP file. 
Listing 2:



The code for the functions invoked on line 5 and line 11 are shown below in Listing 3.   Both functions assume the the 24-bit per pixel format.  Line 2 refers to a simple wrapper class in GameEx (a Github repo of mine)  that manages the SDL_Surface.  The pitch of an SDL surface is a notable concept.  It is the number of bytes in a render line. It is the next number after the image width that is divisible by 4.   For example if you have 50 pixels per row, the SDL surface has a pitch is 52.  The consequence is that each row has 2 padding bytes appended in the pixel data of the surface.  So, be extra careful if your fingers itch to unroll that for-loop ;-).        
Listing 3:



my_hm
Let's put it all together is a small example program. The snippet in Listing 4 reads the input bitmap, inverts the heights so that the highs are low and lows are high. Then it writes the heightmap to an output BMP file. The image on the right hand side is an enlargement of the output produced by this snippet.  If you compare with the original you’d notice the pixilation effect brought about by the radical transformation of the original input (described above).
Listing 4:



By the way, that for_each call in the snippet is a very handy macro defined in GameEx.  It works very well with all standard containers, including the new std::array
There you have it: a few lines of C++ to chew on.  Have fun!
 
After thoughts:
There is follow-on post about terrain rendering you can read.

Tuesday, 25 June 2013

Screenshot in SDL/OpenGL

I am using OpenGL and SDL, mostly to learn more of OpenGL. At some time it would be nice to take a screenshot. Pleasant was my surprise that this is not difficult to do. The code shown below is all you need. The _width and _height is your screen size in pixels.

Listing 1:



There is a small caveat. The 'y' axis is swapped when you move from SDL to OpenGL. So you have to flip the SDL image vertically. The function in the code below does exactly that.

Listing 2:



Saturday, 4 February 2012

Coding for fun

In many news groups and game development forums there are always general questions from newbies that would like to create games.  The oldies that frequent these sites give good advice, but the utility of this advice depends the assumption the oldie makes about the experience of the newbie.  A newbie may be disheartened  by an answer that goes into too much depth, or feel that the advice is too superficial.

I think the question we newbies should ask ourselves is: Why do I want to create a game? There are many possible reasons.  For example, you may want to pursue a career in the gaming industry; impress someone with your technical aptitude; explore your own creative genius; learn a new language; or you may simply want to create games for your own personal enjoyment (a.k.a. fun).

Whatever it is, be sure you know the particular motivation that drove you to the task of creating a game.  And then stay true to yourself in that regard.  For example. if you want to join the gaming industry, my advice is to make sure you explore all areas that is of interest to you, then choose a speciality. Focus all your efforts on learning the current state-of-the-art tools and techniques in that speciality.  If you don't like it, switch to another area - and so on.   I would also argue that you would do well to ignore me and ask advice from someone that has actually tried to make it in the industry.

For a person like myself that create games only for the fun of it; any advice that does not directly pertain to fun should be taken with a good dose of salt.  Now, if we talk about fun - that is a different story. About that we can talk for hours. Soon we might conclude that  fun is not objective and it is a very personal thing.  But, all is not lost, there is one absolute truth about fun: you are the only judge of your fun.  If you say 'that was fun', no one can disagree; and if you say 'that was not fun' no one can disagree that you did not have fun.

So, if fun is your motivator, I propose that you are the only judge of your game.  If the game was fun to create, it was a successful, otherwise it was an utter waste of your time.  This proposal presupposes that you have in fact completed a game.  Clearly, on the road to completion you may have to spend some time (hopefully not too much) that may not be fun.  But take heart; this process is like any other worthwhile human endeavour. The acquiring of fun is usually preceded by the attainment of a reasonable level of skill.  The pursuit of the latter is filled with toils and troubles that are overcome only by an adequate level of personal commitment.

Of course, when your aim is to have fun, your level of commitment is relatively low.  You quickly recognise problems that are far beyond your current skill and promptly avoid solving them.  This is a fine approach, but it may be problematic.  This is mostly because creating a game for fun is not a competitive sport, like tennis for example.  You may enjoy playing tennis without ever thinking about becoming a tennis pro.  You like to play tennis with other people that are more or less on your skill level; and as they get better, your game may also improve.  If your competitors always beat the living crap out of you, tennis won't be much fun..

Likewise, when you create a game, and you evaluate your product with the product of a professional game studio, creating games won't be much fun.  You need a good competitor.  Realise that the only competitor you have - and the one you must always strive to beat - is no one else but the previous version of yourself.  So it is very advisable to start with Tic-Tac-Toe or some other simple game.  This way you create a worthy opponent; an opponent that has drawn a line in the sand, and dares you to cross that line.  For your next game, you must focus on giving that opponent a real beating - and then draw a new line in the sand.

Before asking on a forum which is the best language, the best platform, the best graphics engine for creating games, or the best game idea, first ask yourself what is more fun for you.  For me, it is currently more fun to program in C++ than in Java or in C#.  It is more fun to use OpenGL than it is to use Direct X.  It is more fun for me to use SDL than it is to Orge3D.  These are assertions I make because I have explored these (and many other) choices.  And I had fun doing it.

Go ahead, start working towards that vision of a game you always wanted to write.  But start by creating a lesser game - a game you know you can write.  Take that first step; who knows where to it might lead. It could even be fun!

Wednesday, 16 February 2011

Boogaloo

It has been some time since I did a bit of game development. Recently, a local game competition inspired me to do another little game. After puddling around a bit, I decided to stop development and to call the game finished [a.k.a. version 1.0].

The game itself is the simple snake-eats-apple game with an interesting twist: you have to control two snakes at the same time. Surprisingly, this small variation to the original idea adds a little bit of puzzle and a lot of fun.

I built the game using Scala, Swing and Java2D. I also made use of an indie game library (GTGE). The game is very simple, so the services I used from the library were minimal. However, I must say I liked the simplicity of the library API and I would gladly use GTGE again.

It is the first time I tried the JAVA VM to develop a game. As an experience, it was not as bad as I expected. The deployment package is a single JAR file that you should be able to start playing with a double-click on the jar file (on most modern PC platforms).

Why not give it a try -- download the executable jar file from box.net.

If you get more than 600 points, you should really consider quiting that day job ;-).

Saturday, 18 April 2009

BreakOut clone updated

Although I am working on one or two other ideas, I figured I should not let my first (and only) completed XNA game rust away. So, I picked up ye old shovel and upgraded some code to VS2008 and XNA 3.0. The upgrade process was quite painless - apart from an issue I picked up with XACT.

In fact XACT does not seem to be working on my machine at all. Even the XACT tool fails to play any media. I googled a bit for an answer, but could not find any good reasons for this behaviour. Luck was on my side: XNA 3.0 offers an alternative to XACT for audio processing. The game is quite simple, so it was easy to remove XACT from the code, and plug in the new pieces.

I do not know what advantage XACT has for a simple game like this one. XACT took a bit of figuring out; while the new method is very straight forward. My crystal ball says most new developers will sidestep XACT while they can.

Anyways if you are interested in trying out this very basic clone, get the zip from the release on codeplex. But calm down those high expectations -- I suspect reading the code is more interesting than playing the game :).

Sunday, 1 February 2009

Teaming Up

It is clear that collaboration does not come free. Compared to the work of a solo developer, there is definitely additional effort required to start and maintain a successful collaborative project. Collaboration is driven by the need to be part of something great, something cool, or even something profitable. Even though total satisfaction is not guaranteed, one thing cannot be denied: the team is stronger than the individual.
A few days ago, I posed a question at SA game dev to get some thoughts on the team issue. The feedback was very interesting. Here are some of the key points that were highlighted.

A "main dude" is needed for success.
* This is the guy with the full picture in his mind. He defines the project, sets it up and invites other to join. He is possibly the key contributor, and he directs and controls the work done by other (possibly part-time) contributors.
* If you want to be this dude, your attitude is important. Setup you collaborative site, but assume that you will do all the work yourself. The success of the project hinges not on the contributions of others, but on your ability to see it through.

Your idea must be pragmatic.
* The game idea must be clear. You must know how to implement the idea. Think about breaking down the idea into small milestones so that each can be reached relatively quickly.
* An alternative is to start with a small idea and finish it with some collaboration. Then come up with a more complex idea that will take the willing participants to the next level. Finish that as well. Gradually gaining collaborative momentum.

Consider the team issues before you start.
* Make sure you ideas and plans are communicated clearly. The team will function properly only if they work from the same knowledge base.
* Think about how team members will participate. Perhaps a person can choose which discipline he wants to be involved with. Maybe the idea lends itself to episodes or mini-games - giving your team members more autonomy.
* Choose tools that promotes collaboration amongst developers and also amongst artists.
* Keep in mind that some potential team members are less technical, and they may need custom written tools before they will help you. For instance, an artist might become more interested when he can view his creation in a setting that resembles the game in some way.

Saturday, 3 January 2009

Those jagged edges

After patching together the crater (described previously), I noticed a deformation at the edges of the crater's upturn. Initially, I gave it little thought and blamed it on a side-effect associated with terrains with sharp edges.

However, the subconscious mind is apparently very persistent, and a possible fix dawned on me. The four vertexes used to create the two triangles for the terrain do not (in all likelihood) line up to form a plane. The quadrilateral create by the points A,B,C and D is divided into two triangles using either the diagonal AC or the diagonal BD. In the terrain construction, the same diagonal is always chosen. The picture above illustrates the consequence -- the edges of the crater is very jagged.

Instead of a fixed choice, a better result is obtained when the diagonal is chosen with more care. Here is the rule I applied: take the diagonal that is the highest. This means that the quadrilateral will be always be convex (as seen from the outside) -- and some concave aspects of the generated terrain will disappear. In other words the terrain will have less jagged edges. To compute the highest diagonal I take the diagonal that has the largest sum of the Y component of its vertexes. Other methods also seem reasonable: for instance take the diagonal with the highest Y.

The second picture shows the same view as the first using the largest sum method. Definitely a noticeable improvement.

Thursday, 1 January 2009

Riemers Components 1.1.0

A little bit of functionality has been added since the last time I published the code, and time for a version update has arrived.

The new stuff in Version 1.1.0 is briefly described in related blog entries:
  1. The terrain weights are adjusted not only based on height, based also on the normal.
  2. A new class was added to generate heightmaps using Perlin Noise.
  3. The ability to create a larger heightmap from a small one through tiling was added.
  4. Placing volcanoes and craters on the heightmap is also possible.

If you would like to use these components in your own game, I suspect the best way is to copy code from my project to your project, and make changes to the classes as you see fit. The components were not created to be generic or to be a reusable library of sorts. It simply presents you with an approach to combine the various aspects of creating a terrain into a reasonable set of classes.

The interesting thing about class design is that it is a little bit of an artform. Like other art, not everybody like what they see, but the artist is likely to stay true to himself. Sometimes, the artist might even cut off his ear.

However, class design can also be a little bit of a science form. There is a lot to learn, and I need my ear for that, thank you very much. You may have better ideas on the class composition I did here and I invite you to share your ideas and feedback with me if you get some time.

I published the code in a zip file that can be downloaded here.

Saturday, 27 December 2008

Geomorphic landforms

The previous blog entry was about cosmetic changes. This one is about adding landforms to the terrain. The Perlin terrain works well for generating your basic (perhaps flattish) terrain, but I want to be able to place specific obstacles at specific locations. Not obstacles that are placed on the terrain: but rather obstacles that are part of the terrain.

I am thinking about volcanoes and craters. A volcano is a cone shaped mountain with a hole in the middle. The cone surface is made less smooth with randomised adjustments. Although a crater is simply a large hole with an elevated rim at its edge; it gets slightly complicated when you consider that the rim must fit nicely with the original landscape.

The implementation starts with a definition for the ILandform interface. Instances of this interface is added to an object that inherits from HeightMap via the Landformations member on this class. Right after the heightmap itself is established each of the landforms in this collection are initialised. The lanform has full access to the heightmap data. The classes LandformVolcano and LandformCrater embodies the volcano and crater functionality. Not really much code there; but some experimentation was needed to get things just right.

I must admit, gross shortcuts were taken. There must be more geomorphologically correct ways to build these landforms (and I cannot help but feel my volcano can do with some smoke). Alas, these artifacts are good enough for my current expectation. The image above shows a crater next to a volcano. I am sure you can spot the difference between the crater pool and the Perlin pool.

Friday, 26 December 2008

Black water debunked on better beaches

When moving away from the terrain, I noticed that the water turned black. You can see what I mean if you look at the image in the previous post. This is a side effect of Riemers Skydome. In order to minimise the problem a bit, I did two things. The first was to change the background colour to something other than black (made it a blend between white smoke and sky blue). This small change made the problem much less apparent.

But, I was not yet convinced, so I took a closer look at the RiemersSkyDome class. In order to determine the place of the dome in the world, a matrix is calculated as follows:
wMatrix = Matrix.CreateTranslation(0, -0.30f, 0)
* Matrix.CreateScale(100)
* Matrix.CreateTranslation(camera.Position);


Our needs can be addressed by adjusting the value of the first translation's Y component (-0.30) and the scale value. A higher value of Y component lifts up the dome making the gradient effect more visible from the terrain surface. The scale adjusts the distance to the horizon of the dome. A scale factor of 300 produced the desired effect.

On a different note, I also refined the island terrain trimmer (the HeightMapIslandTrim class) produced more pleasing terrains. I added two options: the one is a shape and the other is a trim method. The shape is either a circle or a square.

The trim method is more interesting: it has two options: a fill option and a merge option. The fill option is the one described in the previous post. It simply fills in ground until the beach is perfect according to specification. The problem with this is that it creates an unnatural edge for the beach; except if that's what you were going for, you may not like it.

The merge option is much smarter. It takes the beach line (this is where the water hits the sand) and applies the fill method there. As it moves away from this line, the original form of the heightmap is gradually adopted with more vigor. Again this uses our old friend the lerp function.

The image shown here is a terrain built from a mirror of Riemer's original heightmap with the island modifier applied to it. Notice how the merge option created puddles on the beach.

Wednesday, 24 December 2008

Fairly odd stitching

In my previous blog I explained the need for a fair playground. Developing a fair heightmap from an unfair one is quite easy. The key is to tile the original on a 2 x 2 grid such that the edges are mirrors of one another. This way the resulting playground is not only fair, but the edges where the tiles meet are seamless.

This idea was implemented in a class called HeightMapMirror that takes an IHeightMap as argument. This class also implements the IHeightMap
interface. This means a mirror can now be created from any other height map on the fly - whether that map is generated or whether it is loaded from an image file.

Obviously you can feed the constructor of the new class another instance of a HeightMapMirror effectively creating a 4 x 4 tile (who wouldn't give that a try?). Surprisingly, from an almost featureless patch of a HeightMapPerlin terrain an interesting and attractive fair playground is produced using this plan . The image shown above is produced that way. Notice the repeating pattern of water and hills.

The other thing I did today was to create a HeightMapIslandTrim class. Its function is simply to put a coastline on the edge of the heightmap. Like HeightMapMirror (the other modifier of heightmaps) this class constructor also takes another map as source.

First I tried a circle island and did not like the loss of real estate. I then tried creating a square island and did not like the look of it too much. I'll experiment more with this when creating a terrain in the context of a game.

The basic idea of adding a coastline is simple: the height is calculated using a linear interpolation that is based on the distance from the edge of the island. This class can also be (ab)used to create a wall around the terrain by specifying impossible values for waterlevel or shoreheight.

This concludes today's blog. Not much added, but at least some progress has been made.

Tuesday, 23 December 2008

Perlin's magic on terrain generation

After building the terrain from a fixed heightmap and fixing the slopes, terrain generation seems to be a good next step. There seems to be quite a number of ways to do generate terrain. I think the selection of a method depends primarily on the look you are going for.

I decided on three initial characteristics: a) The landscape must be a fair playground, b) it must be reproducible, and c) it must be relatively smooth.

The idea behind a fair playground is to have a two player arena such that the terrain gives no advantage to either player. This means the terrain must be a mirror of sorts. A player in the north looking toward the center will see the same effective terrain as a player looking from the south.

The term 'effective terrain' refers to those aspects of the terrain that are not cosmetic. For example: having different vegetation in the north and the south may influence the look dramatically. If it is not used in the game logic the choice of vegetation is not part of the effective terrain. This separation of effective and cosmetic concerns on your drawable objects could be an import design consideration that would simplify many aspects of the game design and implementation.

A terrain is reproducible when its form can be recreated from its input. In other words, although I might need a lot of terrains for my game, I would like to be sure that the same terrain is presented for the same scenario. This reproducibility also give me a handle on the generated terrains, and allows me to handpick scenarios by tweaking the input parameters. The idea here is to use predictable random numbers.

After some googling I decided to start with Perlin Noise based generator (Perlin was already used before to create the animating clouds). It seems that I can get a smooth terrain using this method. For the implementation I used an article on the Nerdy Inverse Network as a starting point.

During my previous blog, I created a class called Terrain, originally copied from RiemersTexturedTerrain. Now, we need to separate the height map from the terrain, and IHeightMap together with the reference implementation HeightMapLoaded emerged.

The new class, called HeightMapPerlin addresses expectation (b) and (c) described above. Here is its constructor: public HeightMapPerlin(Game g, int width, int length, float minHeight, float xHeight, int seed, float persistence, int octaves)

The seed produces the same terrain for any given seed value. The value of persistence influence how pointy the hills are. Low values produce a less pointry terrain. The number of hills are influenced by the value of octaves. More octaves, more hills. The slope is influenced by the value of minHeight and maxHeight. Some experimentation with these values is nessasary to get the right kind of terrain to suit a particular need. The published image shows a rendering with persistence = 2.9f and octaves = 4.

I can now generate a terrain with given properties, but the issue of fairness has not been addressed yet.

Thursday, 18 December 2008

Slippery slopes

I could not help but see an opportunity to fix the degradation that occurs at the steep slopes in the terrain created in the previous blog. This degradation occurs because the UV coordinates specify a small piece of texture (as seen from the top) must fit on a large piece of real estate (as seen from the side). Aha, I thought, the solution is simple - just fix the coordinates and all should be fine. After doing the work, the flat areas of the terrain become seriously damaged. This is because all the adjustments adds up and the sum causes a distortion of the textures on the flat areas in the terrain. I'm sure there is a simple mathematical explanation for this, but you'd have to ask someone else for it ;-).

Little did I know this was a know issue. See this forum topic for a small discussion and a better description of the problem. So I reverted back to the original UV coordinates. However, I decided to change the multi-texture decision so that the rock texture is preferred when the gradient reaches some threshold. The gradient is easy to measure -- the smaller the Y component of the normal, the steeper is the slope.

Choosing the rock texture as the one for steep slopes gives you this code: TexWeights.Z = 1.0f - _vertices[x + y * _width].Normal.Y;. As you can see from the image on the right, the slopes look a bit more realistic.

On another topic -- the download of the code I published from my previous blog did not work well. I republished the code here. The available code does not contain the changes mentioned here. Maybe I'll publish again later.

Tuesday, 16 December 2008

XNA Terrain Rendering - lesson 5


I am now at step 10 of Riemers advanced terrain tutorial. Instead of using complex wave mathematics, Riemer uses a bump map. A bump map contains normal vectors in the RGB of each pixel. Using new inputs to the HLSL effect, waveLength and waveHeight, the pixels in the mirror is perturbed with the pixels in the bump map.

Step 11 works in the refraction map created earlier into the scene. Based on the angle of camera and the normal, the amount of reflection and refraction is adjusted. This is called a Fresnel term, and is calculated using the dot product of the two vectors. In addition, this step adds a bit of dullness to the output to make the water surface less shiny.

The next step makes the water move. Again lots of vector and HLSL tricks. Here a new xTime parameter is added to the parameters. Together with a parameters that shows the wind direction, the chosen coordinates in the bump map changes. I created a component called Environment to keep these aspects. This component updates the parameters in the effect when it draws.

In step 12, a specular effect is added to the water. As the light shines on the water the water sparkles when the sun is reflected on the surface. This is done by adding to the intensity of the RGB components on the pixel shader.

Trees are added in step 13 using a billboard technique. I created a component called BilboardTrees to make this work. I had to make the vertexes of the terrain a public property for the billboard trees to be placed on the terrain.

The next step cleans out some problems with the billboards by drawing non-transparent pixels only. Riemer explains a trick that avoids ordering the trees before drawing them. A downside of billboards becomes clear when the world is viewed from the top.

The second last step is about creating perlin noise. Almost the work is done in HLSL. I placed this into a new component called PerlinNoise. This component generates a texture that contains perlin noise. The noise gradually shifts as time passes -- making clouds that change over time. Very cool!

The final step is subtle but adds a lot to the realism. Here this sky gradient is changed in HLSL so that the sky on the bottom of the horizon is lighter than the sky on the top.

Now, its done, the terrain tutorials are great. I now have a collection of components to use in my own terrain. I published this work on Game Projects.
(Edit: Please note that there has been additional improvements on the published code that might be of interest to you. Read all about it on this blog entry).

Sunday, 14 December 2008

XNA Terrain rendering - lesson 4

The lesson starts with step 3 of Riemer's advanced terrain tutorial. Here multiple textures are introduced (we now have grass,rock,snow and sand). In order to get a smooth texture transition, each vertex carries a weight for each the four textures. The weight are calculates using fuzzy logic (rockiness, snowiness and so on). The values are normalised to have a total of one. The weights are stored in a Vector4 using TEXCOORD3 HLSL semantics. Each vector component (X,Y,Z and W) is used to stored the weight of the respective textures. In the pixel shader the output colour is determined by adding up the weighted values from the four texture samplers.

Step 4 is about the level of detail (or LOD) problem. Essentially the size of the texture must be bigger (i.e. more detailed) when viewed up close -- and smaller when viewed from a distance. The solution is to get the distance from the camera (i.e. the depth of a pixel). The solution is to get a blend factor (float from zero to one) determined by the depth. When this factor is zero, near texture coordinates are used, when its far, the normal tex coords are used. The near texture coordinates is a magnification of the normal coordinates. Linear interpolation (lerp) chooses the actual magnification level. All this is done in the shader code. Very neat.

The next step is about setting up a sky dome. This code was simple to implement as a new component called RiemersSkyDome. The sky dome must be drawn before the terrain, and it also needs access to the RiemersCamera instance. The sky dome itself is a model (.X file), and the effects in the model is imply replaced with a clone of Riemers customer effect. The clone is important because this effect has its own values for the effect parameters.

In step 6, Riemer explains the overview of creating water. Essentially, the reflection (from above) is combined with the refraction from below to determine the pixel colour. Then a ripple is added using a Fresnel method. As a final modification, some "dirtiness" is added to get a realistic effect.

Step 7 starts off with the refraction map. Here again, I created a component called a RefractionMap The basic idea is that the part of the scene that is below the surface of the water is drawn to a Texture2D This is done by setting a clip plane -- only pixels above the clip plane is drawn. The tricky part is to create the place based on the current camera orientation. For the component I created a collection property called RenderedComponents. The user of the component adds all the elements to this collection he wants refracted. For now I only added the terrain to this collection.

In step 8, the same technique as above is used to create a reflection map. After creating a base class for RefractionMap, I created another control of the same kind called ReflectionMap. The problem here is to "extract" the reflected image to a texture.

In the next step a mirror effect is created. Much of the code comes from the HLSL tutorials. Form a component viewpoint, the normal XNA draw method is not enough for the ReflectionMap to use. For reflection, the draw must be done via from a reflection matrix view; so the interface of components must be extended to contain a void Draw(Matrix viewMatrix).

This concludes the lesson. We have a terrain with perfect mirrored surfaces for water. And some interesting new components.

Saturday, 13 December 2008

XNA Terrain rendering - lesson 3

Riemers tutorial, step 9 is all about the basics of lighting. It starts by adding a normal to each surface. Given a normal, a light source and view point, the amount of light reflected by the surface is computed. A new vertex structure called VertexPositionNormalColored is created to keep the normal. The light source is send to Riemers effect. Riemers next step applies these changes to the terrain.

In my code, the RiemerTerrain component keeps and calculates the light information is kept on the RiemerCamera component.

The final step on Riemer's series is very interesting. It is an optimization. Essentially memory is allocated on the GPU and values are copied to the GPU only once. The call to the graphics primitive is updated to use that memory instead. This means during draw, there is not a lot of communication to the graphics card. A great technique for optimising the rendering of static geometry.

Although Riemers advanced terrain series assumes knowledge of the HLSL series; I will start with it without walking through the HLSL series (I did that one quite a while ago -- and I am now sorry I did not create a blog for it). This means I have to prepare my current code to be aligned with Riemers. This was actually quite easy -- there is a new height map and a new effects file.

The first advanced tutorial step is all about creating a better camera. This was relatively easy -- I created a new component called RiemersFirstPersonCamera by inheriting from the RiemersCamera class created a few days ago. Had to refactor a bit -- and moved the angle relates functionality to a another derivative of RiemersCamera called RiemersRotatingCamera. Quite easily done. Now we have a camera that allows us explore the terrain nicely. The good old WASD with mouse control.

The second step uses a texture to draw the terrain. I copied RiemersTerrain to RiemersTexturedTerrain and found a number of small differences. One notable difference was the removal of the indices and vertexes from the private member list of the class. There is no need to keep a copy of this data since it is generated right into the graphics card memory.

The idea of the texture is relatively simple. When seen from above, each dot on the map maps to some XY coordinate on the texture.

There was one more change that is important from the component perspective. Previously, the camera decided on the current technique of the effect. But now, the current technique is chosen by the terrain component. The primary reason for this change is that the terrain owns the texture, and the texture is a parameter for the technique (albeit implicitly). I am not convinced that this is in fact the best place for technique selection -- maybe I'll change it back later.

Again, the end of another lesson; and we have a nice look and feel. That is a little green patch to look at, and an intuitive camera that creates the feel.

Friday, 12 December 2008

XNA Terrain rendering - lesson 2

So far, so good. New we move to step 4 of Riemer's excellent tutorials. During this step, the triangle coordinates are adjusted and the camera is introduced.

So, what I did is create a RiemersCamera component that implements the camera. This is a drawable component, and when it draws, it sets up the view and projection of the effect. The important lesson here is that the order in which the components are created is significant. Create the camera first -- it sets up the effect. Components that follow the camera uses this effect. This way there is no reason for the other components to be aware of the camera. Neat!

A disadvantage of these two components is that they share an effect. The effect has only one instance, and changes to the effect is propagated from one control to another.

To mitigate this situation, I created a base control RiemersEffectComponent that encapsulates the loading of the effect. At least the process coupling is a bit more explicit.

Step 5 of Riemer's series is about rotating and translating the world matrix. Easily done on the RiemersCamera. And step 6 is all about introducing DrawUserIndexedPrimitives. For this I created a new component called RiemersIndices, starting with a copy of ReimersTriangle.

At last in step 6 we get a glimpse of terrain. Although the final drawing is not much. The idea is the following: the terrain is a number of dots, organised in a equally spaced matrix. Each dot has Y-value that indicates its height. The dots are then connected to draw the terrain. You guessed it -- all this is done in the RiemersTerrain component.

Step 7 shows how the simple code in the previous steps becomes much more interesting. The only change is to load the height data from a heightmap. Step 8 gives a bit more interactivity and allows you to control the view via the keyboard.

The last two steps raised an issue for my components. The camera controls the view, but the terrain knows its size. So I added a TranslationMatrix as a public property to the RiemerCamera. Then passed the camera to the constructor of RiemersTerrain. After the terrain loads the heightmap, it adjusts the view by specifying the tranlation matrix value.

Step 8 adds colour. To determine the water levels, the height of the heightmap is determined and releative water levels are set. There is also a need to clear the Z-Buffer (However, on my machine I did not notice the anomaly that is mentioned in the tutorial).

This is a good place to end the lesson. We have moved from triangles to heightmappped wire frame to a basic coloured terrain. Some simple game components are taking form, thanks to Riemer!

Thursday, 11 December 2008

XNA Terrain rendering - lesson 1

I am no XNA expert - but I thought it is time to figure out how to render a terrain. I could find no better place to start than Remiers Tutorials. If your looking for a tut, follow Riemer. This is a blog :-). I am using XNA version 3.0.

In the first step, the graphics device is initialised to a 500 x 500 view.

In the second step, the effects file is loaded and incorporated into Draw. In the draw method, Riemer uses device.Clear(Color.DarkSlateBlue). But there is a property on Game called GraphicsDevice that I am using instead. A technique in the effect file called Pretransformed is used -- but nothing is in fact drawn using this technique.

In the third step, a triangle is drawn. Now the meaning of Pretransformed becomes clear - it is used as a simple rendering method of the triangle. The technique avoids the need for a camera. The triangle is specified as a set of three vertices and rendered on the draw method.

At this point I deviated from Riemer a bit and I created a DrawableGameComponent called RiemersTriangle. Moving away from the single class approach followed in the tutorial. For the component, the VertextDeclaration cannot be done on Initialise, and I had to add an override for LoadContent and put it there. The GraphicsDevice is not available during Initialise Add the component to the Game.Components on Initialise before base.Initialise is called.

Anyhow, that concludes lesson 1 -- we have a pre-transformed triangle to show for our effort