Terrain tutorial
Preface
This tutorial is based on my – I have to admit: few – experiences with Unity’s built in terrain engine. I have tried to proof as many of my statements as possible but I can not rule out that it still contains some mistakes.
So please feel free to edit and correct any part of this tutorial. And if you would like to add something new do not hesitate to do so. I really like the idea of this place becoming some kind of central knowledge database for all questions on the terrain engine.
Step 1: Resolution settings
Some questions that might help to find the right setup
- What size should the terrain have?
- How do the boundaries of the terrain look like? Is it an island? Or are there some mountains or walls?
- What would be the max height of the terrain?
- Which details would i like to be part of the terrain? Hills, mountains, paths...
- Do I really need a terrain? Wouldn't some modeled meshes do a better job? [Have a look at: Note: Restrictions on Terrains]
This first section is about finding the right setup for creating your terrain – about balancing performance and the level of detail. And as it isn’t easy at all to change any of the basic resolution parameters afterwards [except from the resolution of the heightmap] without losing a lot of your work, you really should pay some attention to do the setup carefully.
From the performance’s point of view less is more. So first we will have a look at the different resolution settings and how they will affect your "freedom of design" and the performance – better: the number of draw calls and memory usage:
Heightmap resolution
Note:
If you don’t need very high mountains just set the height as low as possible because this will give you more accuracy on the y-axis.
Let’s say we will set up a terrain with the dimensions of 1000 x 120 x 1000m.
Common resolutions for a heightmap are 257px / 513px or even 1025px.
Note:
Any calculation or number below depends on dimensions of 1000 x 120 x 1000m.
Having a terrain with the dimensions of 2000 x 120 x 2000m the relations would look like this:
- 257px heightmap resolution: 1px = 7.8m x 7.8m
- 513px heightmap resolution: 1px = 3.89m x 3.89m
- 1025px heightmap resolution: 1px = 1.95m x 1.95m
So on our terrain each pixel of the heightmap will represent:
- 257px heightmap resolution: 1px = 3.9m x 3.9m
- 513px heightmap resolution: 1px = 1.95m x 1.95m
- 1025px heightmap resolution: 1px = 0.98m x 0.98m
Note: Restrictions on Terrains
- Due to the 2d heightmap you won’t be able to build things like cliffs or caves.
- You will always have to deal with the terrain’s chequerboard grid which might not fit to all the geometry you would like to have [e.g. nicely curved roads]
- "...it can be tempting to make a heightmap have super-fine detail, but if you crank it up too high -- say below 1 m/pixel, you will have performance problems and the terrain will get all spiky as the polys get too small and close together. The smoother the terrain can be, the lower res the heightmap, and the smoother it will be to walk around on." [Source:]
Of course it would be great to have a super-fine resolution but is it worth spending all the gpu power on the terrain? Does a high resolution heightmap help you create things you want to bake to the terrain?
Note:
The 257 and the 1025px resolution heightmaps have just been made by resampling the 513px heightmap using Photoshop. They are not manually optimized or reworked.
Trail and error
So let’s have a look at a pretty common example: Digging a path into your terrain.
In order to do that you will need at least 3 pixels of the heightmap: 1 for the left border or slope, 1 for the ground floor or the path itself and 1 for the right slope. [In case you don’t want to make your path follow the terrain’s grid you might even need 4 pixels.]
So a path [including slopes] will have following min. dimensions:
- 257px heightmap resolution: 3px = 11.7m width
- 513px heightmap resolution: 3px = 5.85m width
- 1025px heightmap resolution: 3px = 2.94m
Comparing the 3 examples shown in the images on the right you will find out:
- The 257px heightmap resolution won’t let you draw a path onto the terrain which could really be recognized as a path [adding textures would help of course].
- The 513px heightmap resolution gives you a pretty nice result, but doubles the number of draw calls.
- The 1025px heightmap is even nicer and more detailed of course, but with 216 draw calls it eats nearly half the number of draw calls suggested by Unity for casual games [which is 500].
Conclusion
- Using a 1025px heightmap for a 1000 x 120 x 1000m terrain is no option. A 513px heightmap gives us a pretty nice level of detail at an affordable price.
- So a resolution of 1px ≈ 2m x 2m might be a rough rule of thumb and should fit most of your needs.
- Due to the large number of draw calls of a high resolution heightmap you should think about putting higher detailed geometry to
- extra game objects or
- maybe even a second terrain [in case you have a rather smooth landscape [terrain_1] and only some high and rough mountains [terrain_2]]
Detail resolution
Note: What does detail resolution mean?
"Details are the generic name for grass and small things to render close but not far away. So this is the resolution of the map. The bigger your terrain is or the finer the groupings you want of items, the bigger your detail map should be." – DocSWAB
[Source:]
Does a higher detail resolution end up in a higher number of draw calls? Yes and no: No in case you do not use large clusters of grass but just a few smaller plants like in the examples on the right.
- A higher detail resolution will help you to place details like small plants or rocks much more precisely [have look at the example "placement of plants"].
- In case you paint massive clusters like grass the number of draw calls will increase by factor 2 to 4.
So for our example I suggest to choose a 1024px resolution for the detail map.
"Each 64x64 chunk of the Detail Map is one draw call" – aNTeNNa trEE
Example: Placement of plants
As you can see, the 512px detail resolution map won’t let you place small plants exactly at the slopes of the path, whereas higher resolutions will give you much more control without raising the number of draw calls significantly – at least in this case.
Although I would expect a higher usage of VRAM working with higher resolution detail maps, it pretty much keeps the same... Any explanation for this?
Control Texture Resolution
The control texture resolution describes the resolution of the splat-textures used for painting textures on the terrain.
"I haven't noticed any particular slow-downs with larger splatmap resolutions...what you want to avoid is going over 4 textures if possible, as each set of 4 (or partial set) requires an additional splatmap which causes the terrain to be drawn again I think, but that's a separate issue." – Eric5h5
So let’s have a look at different solutions:
The 512px resolution won’t allow us to draw a well defined path onto the terrain: A 513px heightmap in combination with a 512px control texture resolution would end up in a pretty poor terrain... Choosing a 1024px control texture resolution would result in each pixel of the splatmap representing 0.5 x 0.5m of the terrain which still is quite a lot. But as the VRAM usage gets very high working with a 2048px resolution we will take the 1024px resolution in our example.
As Eric5h5 wrote above: Each Splatmap is capable of containing 4 textures. So using more than 4 textures will add a second splatmap which not only means 1 additional shader pass but also
- Control Texture Resolution: 512px: 1.3MB more VRAM usage
- Control Texture Resolution: 1024px: 5.3MB more VRAM usage
- Control Texture Resolution: 1024px: 21.2MB more VRAM usage
[All values for uncompressed splatmaps. But "splatmaps don't get compressed, since DXT compression artifacts would render the splatmap unusable to some extent." – Eric5h5
Anyway – the splatmap resolutions that are used at runtime can be controlled by your project’s quality settings: Texture Quality to fit older graphic cards ]
Base Texture Resolution
"The "base texture" is the fully composited terrain texture that is used in the distance or on old hardware that does not support fully detailed terrain rendering." – Aras Pranckevicius
Well, I don’t use this...
Step 2: Modeling the terrain
Terrain Editors:
I haven’t tested these editors but you will find some words about them on Unity’s forum.
Unity Terrain Toolkit:
Grome:
World Machine:
Terragen:
I would strongly recommend using Sandor Moldan's excellent Unity Terrain toolkit, which plugs straight into the unity editor, and can quickly produce some very believable results.
Being built into the Unity editor, means there are no export/import issues, the resulting height-field can be edited in Unity (with both the standard terrain tools and new erosion brushes provided by the toolkit), or exported to your favorite art program for fine tweaking.
The toolkit also provides a procedural texturing function which saves hours of tedious work, allowing you to perform a full terrain texture pass in seconds, and then just focus on areas of detail. Best of all it's free! There is an informative short video tutorial here:
Failing that there are lot of external terrain editors around, but I have no experiences with any of those. I just use Photoshop or Fireworks to get a first draft of the terrain – most about dimensions and proportions: Where is a mountain, where a river or lake, what will the coast line look like... All other work I do with the built in tools which is not as comfortable as I would like it to be... I really miss a visual feedback of where you are drawing e.g.
Exporting a terrain from a 3d App
You can also use a common 3d app to model your terrain in case your are able to export heightmaps in the appropriate format. See the Blender tutorial dealing with this topic.
World Machine is another great tool for creating realistic heightmaps and texture alphamaps. It exports in 16 or 8 bit RAW format (Windows byte order).
With the TerrainImporter script, you can import height and texture maps very much like normal asset importing.
Exporting a terrain from unity
To export your terrain as .OBJ in case you want to modify it with an 3d app, you can use the TerrainObjExporter script.
Step 3: Setting up the basic lighting
It might be a bit confusing to set up the lighting at this point of work, but this is this is the way I do because all textures we apply to the terrain will be affected by the light and should fit the surrounding atmosphere.
Some words about fog
As I have seen a lot of projects not having fog applied I guess I should mention that I think adding fog is absolutely essential:
- Fog adds much more realism to your scene.
- Fog helps to cover the far clipping plane of the view frustrum and the fading of terrain details like grass.
So more fog please.
Some further information and images on fog [which is called aerial perspective in traditional painting] can be found here:
Getting the right Gamma etc.
"To ensure that your game looks good on a wide range of computers, especially PCs, it's important to have your monitor properly calibrated, and then adjust the lighting levels and texture brightness." Read the full article here:
So after having placed an avatar on the terrain [which we will need to check lighting]:
- import your skybox and apply it to the scene using the render settings.
- Create a directional light which will represent the sun. Make its position fit the lighting or even visual sun in the skybox.
- Back to render settings: Choose a color for the ambient light which will affect the darkness of the shadows on any game object.
- Adjust fog color and fog density so the terrain smoothly fades with the sky.
- In case you have the pro version: Make the directional light drop shadows, this helps a lot [later on we will turn them off...].
- Readjust all parameters untill you like the result.
This might lead to something like this: A bright sunny day – somewhere in the desert.
In fact I would not suggest building a terrain like this, as each detail, each blade of grass would drop a hard and well defined shadow on the terrain which is impossible to reach with Unity. And, as we are going to use a lightmap on the terrain, even large objects like trees can’t drop a hard shadow... anyway.
Step 4: Adding the first textures
Creating tiling textures
It is all about highpass filtering. A tutorial on highpass filtering can be found here:
A good texture tool which includes highpass filtering is luxology’s imagesynth.
I love details
As I want the grass texture to fit the avatars size I use a rather detailed image... but the base texture doesn’t need a resolution higher than 512px just the detail resolution comes with 1024px in order to reduce tiling effects...
Adjusting color and brightness
As I mentioned before: In order to achieve a pretty good looking terrain you should work on the coloring and brightness of all textures to have them match your lighting. I do this by adding a lot of adjustment layers in photoshop. This gives my the possibility to apply changes afterwards without losing any information stored in the original image. It will also help to do different versions of the same texture in case you want to use it in another scenery [with different lighting...].
Getting the right Gamma etc.
"To ensure that your game looks good on a wide range of computers, especially PCs, it's important to have your monitor properly calibrated, and then adjust the lighting levels and texture brightness." Read the full article here:
Texture resources
How to modify the built in Terrain shader
Right now the built in terrain shader doesn’t support specular or bump mapping. But there is a trick to replace the built in shaders:
As we are going to build a terrain with some smooth grassy hills we choose a grass texture that tiles very well, as our base texture.
This texture was made using high pass filtering in order to avoid any visible tiling effects. Turned out as tiling perfectly but pretty boring.
So our 2nd texture is made from the same source material – but with less highpass filtering. This gets us a very detailed and lively texture but with high tiling effects, which is not practical for texturing large areas:
Mixing both textures using different brushes and opacity values gives us both: nice tiling and lively grass.
The third texture is a sandy one to paint paths with and to add more details to the grass:
The next texture would be rocks I guess.... always rocks. Hmmm, we will see. Let us just skip it for the moment, but keep in mind: We still have one texture for free [Remember: 4 textures for each splatmap].
Update: See also -> Terrain_tutorial#Abusing_the_lightmap for more tips on texturing the terrain.
Step 5: Adding trees and some more details
Now it is time to bring in some trees and plants.
Preparing trees
Adding more variety to trees
Unity's terrain engine allows you to paint trees with different sizes and colors all based on the same model or prefab. It doesn’t allow you to paint trees with different rotation angles due to the internal billboarding system.
For this reason a forest just made with one tree model looks pretty strange. But instead of adding more and more different trees I would suggest to use 2 or 3 different models of the same tree [The terrain asset ships with 3 different models of the scotchpine: wonderful work]. In order to get different models you can just duplicate the model and rotate the duplicate in a 3d app. Of course you can also do a completely different version of the model – which is quite a lot of work unless you don’t have the original model. [The demo package will ship with 2 rough versions of sycamore, 2 slightly different version of alder and some more.]
Of course you may also place a rotated tree manually [as game object] but this tree won’t billboard as it isn’t controlled by the terrain engine.
Precise placement of trees
In order to paint a tree more or less exactly where you want choose a "Brush Size" of 1 and set "Tree Density" to 100. This will give you most control.
First import your trees and make sure they are placed in a folder whose name contains "Ambient-Occlusion":
 that folder and reimport it, Unity will calculate soft ambient occlusion specialized for trees. The "Nature/Soft Occlusion" shaders need this information. If you don't follow the naming conventions the tree will look weird with completely black parts." – Unity documentation
The next steps would be:
- First of all: Switch the terrain from vertex lighting to lightmapped and create the terrain’s lightmap using the built in "calc. lightmap..." function [A resolution of 512px will be enough this time]. This is important for the lightmap controls the lighting of trees, grass and detail meshes.
- Drag your tree into one of the preview panels next to your avatar.
- In case the tree doesn’t fit the avatar’s size you will have to scale the tree by editing the "Mesh Scale Factor" in the import settings of the original mesh [not the prefab]. Scaling the mesh or the prefab using the scale handles will corrupt its lighting.
- Adjust its "brightness" by playing with the sliders for baselight, ambient and direct occlusion and make it fit your scene’s lighting.
In case you can not reach an appropriate lighting on the tree [leaves or trunk are too dark e.g.] think of additionally adjusting the textures in Photoshop.
- Add a capsule collider and make its dimensions fit the trunk’s size.
- Then create a new prefab and drag the tree from the "Hierarchy view" onto it.
- Add a new tree to the terrain and assign the just created prefab to it.
- Paint some trees on the terrain.
- You might want to readjust the "brightness" of the prefab [Don’t forget to select "Refresh Tree and Detail Prototypes" from the terrain menu in order to make your changes be applied to the terrain’s trees.
- Finally delete your prefab from the "Hierarchy view".
Advanced editing of trees
Painting trees onto the terrain works pretty well as far as the terrain is flat or the tree’s trunk is very tall like the alder’s one [from the original terrain asset]. But as we use the sycamore tree with a rather wide trunk we will get some "floating" trees when painting on rough edges of the terrain.
Floating trees [nearly fixed in unity 2.6.1 / unity 3 beta]
There are several strategies to avoid those floating trees:
- The cheapest one: Do not paint trees on rough edges... no option to me.
- Edit the tree model in a 3d app and raise the pivot. This will stop mesh trees from floating but leads to floating billboards... [nearly fixed in unity 2.6.1 / unity 3 beta]
- The only solution I could find to get both – non-floating billboard trees and non-floating mesh trees – is to work with 2 terrains: the first [and lower] one just for the trees [it is lowered by something around -0.5m/-1m], and a second one for texturing and all details like grass etc. This solution works very well but costs some extra draw calls.
Further details can be found here:
- Is there any reason you can't just edit the tree meshes in your favorite modeling program? Simply extrude the trunk 0.5m so it goes below the pivot point, which stays the same. You will need to UV-unwrap the new polygons so they correctly display the bark texture. Alternatively, just select the bottom vertices of the trunk mesh and drag them down 0.5m. Note that this will stretch the texture a bit, but if you also move the next highest edge loop, or perhaps make a new edge loop halfway up the trunk and manipulate it, you can average out the stretching so it's barely noticeable. For bonus points, you could actually model the bottom of the trunk to look like roots, so trees near a cliff have their roots exposed a little bit. I don't know exactly how billboarding works, but I assume if you don't move or edit the branches or the pivot point, it will look the same. -WarpZone
In case you can’t afford to work with 2 terrains I would suggest combining route 1 and 2: avoid to paint on rough terrain and raise the pivot just a litte bit.
But as we have been very stingy as far as the heightmap resolution is concerned we can afford a second terrain.
Working with 2 terrains [nearly obsolete since unity 2.6.1 / unity 3 beta]
Creating the second terrain:
- So just create a second terrain and make its size and heightmap resolution settings fit the settings of the first terrain. As we neither paint textures nor details on the 2nd terrain, set detail, control texture and basemap resolution to: 513 / 64 / 64.
- Select the first terrain and export its heightmap.
- Import this heightmap to the second terrain.
- Lower the second terrain by something around 0.5m using the inspector
- Add just the base texture and the tree to the second terrain and start painting trees again.
Note: On the image below the second terrain is colored grey and hasn’t been assigned the base texture
Getting rid of floating grass
Using the built in and common grass features ["Add Grass Texture" in the "Paint Foliage" tab] of the terrain engine will lead to problems equal to those of the trees: floating grass. For this reason I hardly use any grass textures [at least not on rough terrain] but mostly detail meshes whose pivot is raised so they "sink" into the terrain and "climb" slopes pretty nicely:
Construction view
As you can see the grass’ pivot is pretty high, so half of it will sink into the terrain.
This model comes with a "high" number of polys which won’t increase the number of draw calls but the number of drawn polys of course.
Update: Using grass made from detail meshes can easily raise the number of polys up to 1 million [terrain + trees + details [grass]]. On modern graphics hardware this should not be a problem – but older gpus will get a really hard job. So in case you are addressing a wide range of graphic cards I suggest to mix usual "grass" rendered as "grass texture" [on rather flat terrain] and "grass" rendered as "detail mesh" – just for the slopes and rough terrain.
Is there any posibility to just add grass as "detail mesh" and maybe switch to "grass texture" on runtime if necessary?
Getting rid of alpha transparency problems [updated]
I often had problems with alpha transparency using the grass- or nature/Soft Occlusion Leaves shader: Small dark borders at the edge of the texture or geometry. In order to get [mostly] rid of those first set the texture from "repeat" to "clamp".
Update: Make sure that, in the import settings, you have chosen the original size as "Max Texture Size". If you want to use a lower resolution version of the texture go to Photoshop and resize the texture there.
Additionally make sure that the uvs don’t touch the borders of your texture. Doing so will improve any detail mesh rendered as "Grass".
Update: On older graphics hardware [tested on ATI Mobility Radeon 9700 / 64 MB] you probably will still have those borders whereas the same player on more advanced hardware [ATI Radeon X1600 / 128 MB] won’t show any.
Optimizing grass’ coloring
As we already have grass painted on the terrain I like the mesh grass’s color matching the color of the base terrain texture. This really helps to cover the fade in / fade out of detail meshes and gives us a nice looking lawn.
Do so by adjusting colors and brightness of your grass texture in Photoshop and applying color values to "Healthy Color" and "Dry Color" in the "Edit Detail Mesh" dialog which are pretty much white or grey.
Detail meshes and wind
Detail meshes won’t be affected by wind as far as any bending is concerned. Instead of bending they will do some color cycling which doesn’t look convincing and totally messes up our coloring.
I don’t know how to influence or deactivate this color cycling. So I strongly recommend to turn off wind for the detail terrain by setting all sliders to zero. Trees however still will be affected by wind as they are placed on the second terrain...
Any other idea?
Optimized painting of grass
Not working with "low contrast grass" [see paragraph above] might lead to a visible pattern of the detail resolution map.
In order to reduce this visual pattern you also should not try to paint large areas with low density of grass. Due to our low resolution detail map this always will produce that kind of pattern.
Paint several clusters each with a high density of grass instead.Do so by selecting one of the "rough" brushes and setting opacity to 80 – 100.
Adding more details
Adding more details just works the same way like adding grass:
- Import the model and texture.
- Add it to the "terrain_details" using "Add Detail Mesh" in the "Paint Foliage" tab.
- Make sure you choose color values for "Healthy Color" and "Dry Color" which are pretty much white or grey.
- Paint some examples of the new detail mesh on the terrain.
- Adjust color and contrast of the texture using Photoshop in order to make them fit your lighting and all other colors.
Detail meshes vs. Trees
As we use detail meshes to paint grass on our terrain we should reduce the "Detail Distance" in the "Terrain Settings" tab to something between 30 – 90 metres in order to get an acceptable number of draw calls and polys. As these values do not represent a pretty far viewing distance and any detail beyond the detail distance just won’t be drawn I would recommend to use "grass textures" and "detail meshes" only for rather small or low plants.
Higher plants might be drawn as trees even if they are no trees at all because trees have their own distance slider and support billboarding. Next to this there are some more differences between trees an detail meshes:
Let’s have a look at an example: weed.
Weed usually has a height between 1 – 2 meters. Due to its height it
- should bend in the wind
- and be visible over a long viewing distance.
For these reasons we will add weed as tree:
Just look at the weed and do not pay much attention to the water please...
As the "Nature/Soft Occlusion Bark" shader seems not to produce nice results on such tiny geometry like the weed’s trunk one, both trunk and leaves are rendered using the "Nature/Soft Occlusion Leaves" shader. This will affect the bending but in an unimportant way.
As trees don’t support rotated instances of the original model our model does not contain just one single trunk and some leaves but comes as cluster of several trunks and leaves.
Step 6: Final lighting
In order to make our terrain more realistic, it is essential to add shadows.
Bake tree shadows
Hand-drawn tree shadows
Drawing tree shadows by hand is not much fun but it is really possible as long as you don’t have too many trees. And the result might even look much better than that using the script mentioned on the left.
In order to bake tree shadow to the terrain’s lightmap we need some scripts:
- "Assets/Editor/LightmapExport.cs" from the Island Demo
And in case you have Unity Pro:
- "Terrain/CustomLightmap" from the wiki:
If you don’t have the Pro version – well, then you will have to draw all shadows by hand...
Prepare the lightmaps
It doesn’t matter whether you draw or calculate tree shadows: The first step would be to create the terrain’s original lightmap in the wanted resolution [in our example: 2048px which will give us a shadow resolution of 0.5 x 0.5 meters]
Unfortunately the script "CustomLightmap" doesn’t handle higher resolutions than 2048px. Additionally it does not compute any coloring of lights. So we will have use some workaround to create our lightmap:
- Calculate the terrain lightmap [if you work with 2 terrains: the lightmap of the upper detail terrain] using the built in "Create Lightmap" function from the terrain settings tab. Don’t tick the checkbox "Shadows".
- Duplicate the lightmap from the project view using the "Assets/Editor/LightmapExport.cs" script.
- rename the duplicated lightmap
- [if you work with 2 terrains]
- Turn off the upper terrain.
- Select the lower terrain and raise it up to 0 meters.
- [endif you work with 2 terrains]
- Choose "Terrain/CustomLightmap" and render it. As we have already rendered a lightmap for the terrain’s geometry we will just render the tree’s shadow. Do so by choosing a high value for "Heightmap shading contrast" e.g. 100.
- Duplicate the lightmap from the project view using the "Assets/Editor/LightmapExport.cs" script.
- rename the duplicated lightmap
- [if you work with 2 terrains]
- Lower the tree’s terrain back to the initial value.
- Turn on the upper terrain.
- [endif you work with 2 terrains]
In case you want or have to draw shadows by hand:
- Open the basic lightmap in Photoshop.
- Add a new layer.
- Start drawing shadows on the new layer.
- Save the lightmap and assign it to the terrain [see next paragraph].
- Check and correct the position of your shadows by switching between Unity and Photoshop while doing the adjustments...
Combining the lightmaps
Now we have 2 lightmaps: One for the terrain’s geometry and one just for the shadows of the trees.
Combine them by using Photoshop. Copy the tree’s shadow on top of the terrain lightmap and change the blending mode to "multiply". Adjust the darkness of the shadows using the opacity slider. Then save and import the combined lightmap.
Note: In the "Import Settings" choose "Texture Format: RGB 24 bit". Otherwise the lightmap will get corrupted on exporting.
Note: The custom lightmap could contain some shadow "artifacts" [from the terrain’s geometry] that you will have to erase.
In order to apply the new combined lightmap
- choose the [upper] terrain from the project view [icon with palm...].
- Then drag the new lightmap on the appropriate slot in the "Inspector".
That’s it. The result looks much better now – but still not good enough...
Adding advanced shadows
Looking at the picture above you will notice that:
- tree shadows are a bit too dark.
- detail meshes don’t drop any shadow.
- the coloring of the detail meshes especially that of "ground foliage" is pretty much the same and looks a bit boring.
- trees that are in the shadow of another tree still have the same brightness.
Now we are going to correct this.
Improving tree shadows
As the overall tree shadows of the combined terrain lightmap appear a bit too dark we start by brightening them using Photoshop.
Then we manually add special shadows just to the trunk of each tree. Do so by:
- drawing a rectangle and adding a gradient from black/100% opacity to black/0% opacity.
- Scale, rotate and position it in an appropriate way.
- Save the lightmap and switch to Unity to check your adjustments.
Adding shadows to detail meshes
Adding manually drawn shadows will make the ground foliage drop shadows on the terrain and influence its coloring/lighting for this depends on the terrain’s lightmap. So you won’t just get shadows but also some more lively coloring and lighting of the ground foliage.
Unfortunately our lightmap resolution of 2048px is pretty low compared to the size of the terrain which is 1000x1000m. So each pixel of the lightmap just represents a area of about 0.5 x 0.5m.
For this reason I will give you some more examples from another terrain which has the dimensions of 500x500m and a lightmap resolution of 2048px:
Bushes dropping shadows.
Reed [not the one rendered as tree as mentioned above but a different model rendered as detail mesh] dropping shadows.Please note the differences of lighting on the the reed: this is the result of manually adding detail shadows.
Improving the lighting of trees
As you can see: the both trees on the right side are pretty bright – especially their trunks – although they should receive shadows from the left tree.
Lighting of trees can be controlled using lightmaps. But, as the terrain’s lightmap already contains the trees’ shadows, all trees would be black in case we used this lightmap: The lightmap color at the pivot of each tree nearly is black because of the trees’ shadows.
For this reason we will have to create a special lightmap just to light the trees:
- Copy the combined lightmap which includes the terrain’s lightmap and the trees’ shadows.
- Resize it to e.g. 512 x 512px as we don’t need much accuracy.
- Brighten the terrain’s lightmap – otherwise all trees would become too dark.
- Use the trees’ shadows as "landmark" to find where to add some dark spots which will "light" the trees in the shadow of other trees. [see the green boxes in the left half of the image].
- Turn off the layer with the trees’ shadows and save the new lightmap.
- Import the new lightmap to Unity and apply it to the trees using the script "Terrain/Update Tree Lightmap Color" from the island demo if you work with just one terrain.
- In case you work with two terrains the demo package will ship with a special script which allows you to choose the terrain and the lightmap you want to apply.
Update tree color using 2 terrains
- Turn off the upper terrain.
- Choose "Terrain" -> "MyUdateTreeLightmapColor"
- Drag the lower terrain to the slot "Terrain" and the tree’s lightmap to the slot "Tex".
- Hit run.
- Then turn on the upper terrain.
The result might look like this.
Note: This approach won’t add "real" shadows to trees of course – it just darkens a whole tree...
Abusing the lightmap
In order to make the terrain texture more lively without adding more terrain textures you can abuse the lightmap by adding e.g. some colored noise.
1. Normal terrain texture.
2. Terrain texture with colored noise in the lightmap.
3. Lightmap with added colored noise.
Just try yourself!
Step 7: Dropping shadows on top of the player
Right now the player doesn’t receive any shadows from the trees or any other game object which would be high enough.
We will change that by adding a projector:
- Add a projector.
- Make it "Orthographic" by ticking the checkboxes
- Choose 500 [=half of the terrain’s size] as "Orthographic Size"
- Set the Position to 500/60/500 [=middle of our terrain]
- Set the "Far Clip Plane" to 60 [= Y-position of the projector – in case you have a higher terrain adjust those values]
- Assign the default grey ramp as "FallOff"
Now we have to create an appropriate cookie. Well: we can use the "Custom lightmap" as a cookie as it contains just the trees’ shadows and maybe some shadows from other game objects. So:
- open the combined lightmap
- Copy the trees’ shadows layer to a new file.
- Resize it to let’s say 512px – we don’t need much accuracy here.
- Then create a new alpha channel and copy the trees’ shadows layer to it.
- Save and import the cookie.
- Assign the cookie to the projector.
Now you should have projected shadow on top of the lightmapped shadows.
Next step will be to restrict the projector just to our player or any other moving game object:
- Select the player in the "hierarchy view".
- Select its layer and choose "add a new layer".
- Name it something like "receives_projected_shadows"
- Assign this layer to the player.
- Switch back to the projector and choose "Ignore Layers" = "Everything" but "receives_projected_shadows"
That’s it.
Step 8: Making the player itself dropping shadows
I think there are 3 ways to achieve this goal:
- Adding a blob shadow projector to the player.
This would be rather cheap but wouldn’t give us a nice and accurate result.
- Adding realtime shadows [Unity Pro only]. This would mean to assign pixel-lighting to the terrain... Even lowering the shadow distance to something around 15 or 20 meters [as we just want the player to drop real time shadows] this would be pretty expensive to render. Furthermore the blending between realtime shadows and the terrain’s lightmap at the edge of the shadow distance doesn’t look very nice.
- Using some other method to project realtime shadows: The Character Shadow script from the wiki [Unity Pro only]: This one will let us combine the lightmapped terrain [and other lightmapped game objects] with our moving player, smoothly, so we will take this one.
Character Shadow script
Just attaching the original script to the directional light of our scene does not work well: On the backside of e.g. hills [seen from the directional light’s position] it produced very weird shadows even pointing to a complete wrong direction. So just assign the script to a game object that follows the player [but doesn’t] rotate. The demo package ships with a customized "ThirdPersonController"-Script that will do the job. So:
- Create an empty gameobject and name it: "001 Character_Shadow" – this name is used by the "ThirdPersonController"-Script to address this object.
- Place it next to your player between player and the main light.
- Assign the Character Shadow script to this gameobject and drag your Player (or one of the player’s submodels) on the appropriate slot called "Target"
- Setup all other settings
- Run the scene to see the script in action
- Readjust the position of the "001 Character_Shadow" gameobject to make the projected shadow fit the lighting. Note: The closer you place it next to your player the fewer problems you will get on uneven terrain and geometries like cubes or walls.
Note: Make sure your Fadeout Texture is set to "clamp" and has black pixels both on the right and on the left side. Otherwise the projected shadow won’t be limited to the area just next to the player. See:
- Adjust the starting point of the projected shadow by adding, or subtracting, black pixels to the left side of the Fadeout Texture.
- You might want to exclude more layers from receiving the projected shadow [right now only the "water" layer is excluded]. Do so by editing line 121 of "CharacterShadow.cs"
---> myignoreLayer = (1<<targetLayer) | (1<<mywaterlayer);
The demo package
Download
Get the unitypackage [55 MB] at:
The demo package contains:
- all specific scripts mentioned above
- all models and textures shown in the webplayer and even some more
Most models and textures that ship with the demo package are free to use in any kind of project as far as it is done using Unity. But there are some restricted models which are taken from a crysis mod [You will find them in the folders "restricted models"]. They are free to use in non-commercial projects. In case you want to use them in a commercial project, please contact the original author, who offers a package containing more, and advanced, models:
Note: All lightmaps are edited with Fireworks and saved as png. Opening those pngs in Photoshop will give you just one flat layer. In order to get access to the different layers and objects you will have to use fireworks...
Real time lighted terrains
to be continued...