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