Monday, March 16, 2015

Environment in Savage Lands: Part 2 - Terrain Composer & Splat Textures in Unity 3D

Outside the village Argo
screnshot from Savage Lands

In my last post I talked about the concept phase, and the tools & procedural sculpting of our height map.

Moving to the next step of the pipeline, I will attempt to describe how we established the environment in Unity using Terrain Composer. The first stage is the terrain texture application.

Booting Up

Unity 4 already has built-in terrain & tree systems, but we were looking for a way to really maximize the treatment & organic feel of the environment space. Terrain Composer was recommended by a colleague who showed me it's unified interface that manages a complex application of textures, vegetation, and objects scattered on the terrain.

Actually using TC was quite another thing. The tool is exceptionally deep & powerful, but the majority of it is undocumented. The videos help a little, but don't go into much depth on the specifics of what every control in the interface actually does. And the interface itself isn't the pinnacle of "artist-friendly".

Here is the setup for the displacement of the terrain mesh:

Height Map setup in Terrain Composer
Height Map setup in Terrain Composer

Generating the terrain mesh was straight forward. You apply the height map texture (16-bit) to a new terrain mesh, the adjust scale and resolution. We experimented with multi-mesh (tiled) terrains for a while, but couldn't really see any advantage to that approach, in fact it made the data more complicated to manage. So we settled on one terrain mesh for the whole world.

Base Texturing

So, it took weeks of experimenting to arrive at a point where I felt I could control the output when it came to splat maps, texture blending, and layer controls. Eventually I went back and started with a stripped down scene in unity, and applying a set of test textures that were color coded and labeled with a unique number.

Then, many knobs were twiddled. Over & over again to understand filters, curves, masks, blend ranges, and how all these things interact. This really helped to see what the tool was actually doing.

The result of this research phase was the decision to use 8 texture sets for the terrain, consisting of 5 snow textures distributed by altitude (ranging from muddy snow to grassy snow to pure snow), 2 rock textures on cliffs, and 1 muddy ground texture that goes underneath villages and the water.

In each texture slot you can set the tile amount & offset under Settings. You can input a diffuse and normal texture map for each channel. I found out much later that you can put a Specular texture into the alpha of the diffuse! Never saw that in the documentation, and it made a huge difference.

Main texture index for terrain
Main texture index for terrain

Splat Maps

A "splat" map is usually an RBGA image, you store one mask into each channel for a total of 4 masks in a splat. These masks control where each texture channel is applied on the terrain. This is how Unity likes the data. Terrain Composer generates these in the final step.

But the layers are where the real power is. They let you reference a single mask texture for a given feature, and you can use 16-bit textures for this. Here are each of the layer & filter setups for our terrain textures in TC:

Snow Layer Height Curve & Mask
Snow Layer Height Curve & Mask
Cliffs with Steepness Curve
Cliffs with Steepness Curve
Paths with Mask Only
Paths with Mask Only

In the above images you can see 3 layers of application:

1) Snow - curve based on height (altitude). A filter is added with a mask to put snow only on flatter areas of the terrain.

2) Cliffs - curve based on slope steepness. The curve represents degrees, so you have to arrange your index or do some crafty curve editing to get the right texture on the right angle.

3) Paths - no curve variation, just straight application with a filter mask that was painted to localize application to village areas.

For the first layer (snow), it's important to note that the "height" curve represents the full range of a height map, so if the highest point in your height map is 0.5 luminance, you need to adjust the curve max point accordingly.

For each layer you specify the index of textures used, and set a "mix rate" which is the overlap area where two textures are blended together. This really helps provide more variation. You can also set sliders for each texture channel to bias it against the curve.

In this image you can see the Snow textures, and how the mud is pushed down to the lowest altitude so it appears only under the water.

Snow layer blending & bias
Snow layer blending & bias

Go Time

Once you've defined your textures, layers, and filters, you can generate the final splat maps and TC will apply all this to your terrain. After that we apply the global normal map which adds a lot of nice shading detail. It's crucial to bake the normals from the height map using the same overall terrain height you set in Terrain Composer.

Here is a screenshot showing how the filter masks create nice smooth blends between texture areas.

Lessons and Thoughts

Once you decode the Rubik's cube that is Terrain Composer, it becomes a really great way to manage your terrain features across a huge play area. I would love to contribute to a documentation effort that would save others the countless hours I spent arriving at sane baselines for this part of the pipeline.

There are other tools like Relief Terrain Pack (RTP) that add another level of realism to the terrain shaders & textures. We gave it a try early on, but abandoned it because the added complexity made it tough to get predictable results. Like TC, it's a powerful set of tools that require loads of time to really understand. I may revisit it at a later date, as I've seen some people get amazing results with RTP.

Next, time to fill the world with vegetation!

Part 3 - Vegetation and Chopping Down Trees

Thanks for reading!!