Importing DEM Terrain Heightmaps for Unity using GDAL

I know that some folks reading my blog are from the spatial/mapping community, and may have been disappointed that my posts of late have been more influenced by game development and Unity than by spatial data and Bing Maps. Well, good news, spatial fans – this post is about mapping terrain from DEM data! (to create terrain for a Unity game… )

I’ve written a few posts in the past (such as here and here) that have made use of Digital Elevation Model data, such as collected by the SRTM or GTOPO30 datasets, via GDAL into Bing Maps, WPF etc. In this post I’ll be running through a similar workflow to that which I’ve used before, but this time with the target output being a Unity terrain object.

So, here goes:

1.) Get a GeoTIFF file of DEM data. For whole-world STRM coverage, use Google Earth browse of, or download direct from e.g. (although I note that King’s have disabled directory browsing on their server so you’ll have to know the name of the SRTM file you want to access)

If you want to preview the data in the DEM file, you can open it up in MicroDEM (File –> Open –> Open DEM –> Select GeoTiff file). It should appear something like this:


Note that although you can load up GeoTIFF files in image applications such as Photoshop or GIMP, they won’t visualise the geographic data encoded in the file, and you’ll probably just see this:



2.) (Optional) Use gdalwarp to transform the data to an alternative projection and/or crop it to a particular area of interest. Like all forms of spatial data, DEM data can be provided in various different projections. The STRM data I’m using here is provided in WGS84 decimal degrees, but as it’s data for Great Britain, I’d like to reproject it to the National Grid of Great Britain (EPSG:27700) instead:

gdalwarp –multi –t_srs EPSG:27700 srtm_36_02.tif srtm_36_02_warped.tif

I’ll then crop a 100km2 area from the warped image so that it covers the Ordnance Survey Grid Square “SH”, which includes North Wales and Snowdonia National Park as shown in the red square here:


Here’s the gdalwarp command to crop the image:

gdalwarp srtm_36_02_warped.tif –te 200000 300000 300000 400000 srtm_36_02_warped_cropped.tif

If you were to view the warped, cropped image in MicroDEM again now, it would look like:



3.) Use gdal_translate to convert the GeoTIFF file to the raw heightmap format expected by Unity

gdal_translate –ot UInt16 –scale –of ENVI –outsize 1025 1025 srtm_36_02_warped_cropped.tif heightmap.raw

The settings used here are as follows:

  • -ot UInt16 Use 16 bit channel. Most graphics applications use 32 bit colour images, consisting of four channels (R,G,B,A), each one having 8 bits. That means that, in any given channel, you can only represent an integer value between 0-255. We’ll be encoding the heightmap data in a single channel, so having only 256 unique values would not give us very precise resolution. Instead. we’ll specify a 16bit channel that gives 65,536 unique values instead.
  • –scale To make the most of our 16 bit channel, we need to scale the height values from their original range to fill the full range of values available in the 16 bit channel (0 – 65535). Specifying –scale with no parameters automatically does this.
  • -of ENVI  This outputs the result in the raw binary output file format Unity expects.
  • -outsize 1025 1025 Unity terrain maps must have dimensions equal to a power of 2 + 1. i.e. 65×65, 129×129, 257×257, 513×513, 1025×1025, 2049×2049 etc.


4.) Import into Unity

Create a new terrain object and, from the settings tab of the Inspector, click the button to Import Raw heightmap. If you browse to select the heightmap.raw file created in the previous step, it should populate the correct settings automatically (note that, even when running under Windows, gdal appears to use Mac Byte Order). Note that Width and Height need to match the Heightmap Resolution field in the terrain inspector, not the Width and Height of the terrain itself:


If all goes well, your blank terrain should magically update to reflect the heightmap and you’ll see the following:



5.) Correct the orientation

The observant amongst you will notice one slight problem with the previous image – the heightmap has been rotated anti-clockwise by 90 degrees. It turns out that Unity treats heightmap coordinates with (0,0) at the bottom-left corner, whereas most other applications, including gdal, interpret (0,0) at the top-left corner. Fortunately the fix is quite simple. Just attach the following script to the terrain object and click play (changes will be saved to the terrain, so script can be disabled/deleted once used once)

using UnityEngine;
using System.Collections;

public class RotateTerrain : MonoBehaviour {
    void Start () {
        Terrain terrain = GetComponent<Terrain>();
        // Get a reference to the terrain
        TerrainData terrainData = terrain.terrainData;
        // Populate an array with current height data
        float[,] orgHeightData = terrainData.GetHeights(0,0,terrainData.heightmapWidth, terrainData.heightmapHeight);

        // Initialise a new array of same size to hold rotated data
          float[,] mirroredHeightData = new float[terrainData.heightmapWidth, terrainData.heightmapHeight];
        for (int y = 0; y < terrainData.heightmapHeight; y++)
            for (int x = 0; x < terrainData.heightmapWidth; x++)

                // Rotate each element clockwise
                mirroredHeightData[y,x] = orgHeightData[terrainData.heightmapHeight - y - 1, x];
        // Finally assign the new heightmap to the terrainData:
        terrainData.SetHeights(0, 0, mirroredHeightData);

And there you have it – DEM data of Wales imported and ready to play in Unity. Now, if you want, you can texture it using procedural splat mapping (or just paint textures on manually).



This entry was posted in Game Dev, Spatial and tagged , , , , . Bookmark the permalink.

21 Responses to Importing DEM Terrain Heightmaps for Unity using GDAL

  1. Excellent post – I’ve been wanting to see if this was possible for a while. Will bookmark and try this out later.

  2. Gordon says:

    Thank you for this awesome tutorial, very helpful!

  3. veddycent says:

    This is a daft question but where to use:
    gdal_translate –ot UInt32 –of ENVI –outsize 1025 1025 srtm_36_02_warped_cropped.tif heightmap.raw
    You don’t mention where you actually put/use this command.
    Is it used in cmd prompt, if so how?

    Thank you so much for this guide. I have had great difficulty converting a 32-bit TIFF in to 32-bit RAW.

  4. Pingback: Integration Experimentation – Unity 3D | SSHS Computer Club

  5. Seb says:

    Hi ! Really Excellent Article !

    Unfortunately the Uvs of the terrain are also flipped. As I cannot rotate the terrain, I’m looking to rotate my shader instead, but I failed to do that.

    The Shader I’m using is this one :×4096

    Or maybe is this possible to rotate the UVs directly on the terrain ?

    Please Could U help me ?

    Thanks in advance !

  6. Edwin Page says:

    i pretty new to all of this so i obviously have a question , i have used the command-line u gave in the GDAL command-line utility, with specific location to were my Geo tiff is located but i get a message back say ” FAILURE: Too many command options.”. what could a maybe be doing wrong?

  7. Martin says:

    What does the -scale parameter exactly do? Does it just split the available elevation data in the map into 16bit peaces? What shall I do if I want to splitt elevation values of -20m to 8848m into 16bit peaces?

    • alastaira says:

      No, -scale allows you to rescale the range of input values to a different range of output values. For example, -scale 0 10 0 100 would “stretch” the output so that input values between 0-10 were rescaled to range from 0-100.
      By using -stretch with no parameters, it defaults to rescaling the full input range from the source data to the full range of the output.

      • Apteryx K. says:

        If I want to scale a tif I should use this command ? gdal_translate –scale 0 100 0 50 srtm_36_02_warped_cropped.tif .
        This should make it 1/2 but when i type this it doesn’t work :/
        I would really appreciate your help.
        And I sincerely thank you because of the fact that you do this (help and explain people) whereas you don’t really have to, this helps us a lot :)

  8. Apteryx K. says:

    I have a problem, first I had trouble to find the link, now when I type the line it says : too many commands… What can I do ? my version is 1.9.1, is it outdated ? Help :( please

  9. tom barnes says:

    Very interesting article, i wonder if you found some similar idea about Dem in here

  10. igr says:

    I feel like this is also a stupid question, but my attempts to duplicate this have created terrains with two apparent heights – sea level and ground level – and no variation between the two. (Using NED19 data.) I’m using the -scale flag when converting. Am I missing something obvious?

  11. A Twat says:

    Thank you very much

  12. Ken says:

    I followed your tutorial above and I am trying to import a heightmap that is 16bit and uses Big Endian byte order into Unity. All heightmaps imported have large stair steps (kinda looks like Lego terrain) Any idea on what is causing this?

  13. Pingback: importing GIS data into unity | Electric Archaeology

  14. Great advice Alastair, thanks for sharing. I had grief until I manually set -scale since I was converting from 32bit float value DEM into 8bit/Byte type.
    For those using gdal utilities, use gdalinfo on the source file to get your min and max, then punch them into the -scale option like mentioned above. I also dump it to PNG format instead and use -ot Byte. Then use a Unity script to import the PNG instead of using RAW format:

    Hope that helps someone, thanks again!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s