Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
1
Question by sevensixtytwo · Sep 11, 2014 at 07:20 AM · terrain

Turn a Terrain into a Contoured Map

Is there a way to create a height-contoured map from a Unity Terrain other than to manually paint over it? Something like Arma to this effect:

alt text

I've tried using an exported heightmap but Photoshop-fu can only take one so far.

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

4 Replies

· Add your reply
  • Sort: 
avatar image
5
Best Answer Wiki

Answer by Dave29483 · Sep 11, 2014 at 07:49 AM

Updated Post

Figured I may aswell try it in Unity... (Output shown on plane. Uses alpha channel so lines can be overlaid)

Code below image.

alt text

Listing for HeightmapToContourMap.cs

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
 using System;
 
 public class ContourMap
 {
     //Creates contour map from Raw 16bpp heightmap data as Texture2D
     //Returns null on failure
     //If neither width nor height specified, then it's POT size will be guessed
     public static Texture2D FromRawHeightmap16bpp( string fileName, int width = 0, int height = 0 )
     {
         if ( !File.Exists( fileName ) )
         {
             Debug.Log( "Heightmap not found " + fileName );
             return null;
         }
 
         //dimensions
         int _width = width;
         int _height = height;
 
         Color32 bandColor =  new Color32(255, 255, 255, 255);
         Color32 bkgColor = new Color32(0, 0, 0, 0);
         
         //Output
         Texture2D topoMap;
 
         //Read raw 16bit heightmap
         byte[] rawBytes = System.IO.File.ReadAllBytes( fileName );
         short[] rawImage = new short[rawBytes.Length / 2];
 
         //Create slice buffer
         bool[] slice = new bool[rawImage.Length];
 
         //Convert to bytes to short
         Buffer.BlockCopy( rawBytes, 0, rawImage, 0, rawBytes.Length );
             
         //Create Texture2D with estimated or specified width
         if ( _width == 0 || _height == 0 )
         {
             _width = (int)Math.Sqrt( rawImage.Length ); //Estimated width/height
             _height = _width;
             topoMap = new Texture2D( _width, _height );
         }
         else
         {
             topoMap = new Texture2D( _width, _height );
         }
 
         topoMap.anisoLevel = 16;
 
         //Set background
         for (int x = 0; x < _width; x++)
         {
             for (int y = 0; y < _height; y++)
             {
                 topoMap.SetPixel( x, y, bkgColor );
             }
         }
             
         //Initial Min/Max values for signed 16bit value
         int minHeight = 32767;
         int maxHeight = -32767;
 
         //Find lowest and highest points
         for ( int i = 0; i < rawImage.Length; i++ )
         {
             if ( rawImage[ i ] < minHeight )
             {
                 minHeight = rawImage[ i ];
             }
             if ( rawImage[ i ] > maxHeight )
             {
                 maxHeight = rawImage[ i ];
             }
         }
 
         Debug.Log("Min: " + minHeight.ToString() + ", Max: " + maxHeight.ToString());
 
         //Create height band list
         int bandDistance = maxHeight / 12; //Number of height bands to create
             
         List<int> bands = new List<int>();
             
         //Get ranges
         int r = minHeight + bandDistance;
         while ( r < maxHeight )
         {
             bands.Add( r );
             r += bandDistance;
         }            
             
         //Draw bands
         for ( int b = 0; b < bands.Count; b++ )
         {
                 
             //Get Slice
             for ( int i = 0; i < rawImage.Length; i++ )
             {
                 if ( rawImage[ i ] >= bands[ b ] )
                 {
                     slice[ i ] = true;
                 }
                 else
                 {
                     slice[ i ] = false;
                 }
             }
                 
             //Detect edges on slice and write to output
             for ( int y = 1; y < _height - 1; y++ )
             {
                 for ( int x = 1; x < _width - 1; x++ )
                 {
                     if ( slice[ y * _width + x ] == true )
                     {
                         if ( slice[ y * _width + ( x - 1 ) ] == false || slice[ y * _width + ( x + 1 ) ] == false || slice[ ( y - 1 ) * _width + x ] == false || slice[ ( y + 1 ) * _width + x ] == false )
                         {
                             topoMap.SetPixel( x, y, bandColor );
                         }
                     }
                 }
             }
                 
         }
 
         topoMap.Apply();
             
         //Return result
         return topoMap;
     }
 }

Listing for TopoLines.cs

 using UnityEngine;
 using System.Collections;
 
 public class TopoLines : MonoBehaviour
 {
     public string heightmapPath = "/Users/dave/desktop/terrain.raw";
 
     public Texture2D topoMap;
 
     public Material outputMaterial;
     
     void Start()
     {
         topoMap = ContourMap.FromRawHeightmap16bpp(heightmapPath);
 
         if (topoMap == null)
         {
             Debug.Log("Creation of topomap failed.");
         }
         else
         {
             Debug.Log("Creation of topomap was successful.");
         }
 
         if (outputMaterial != null)
         {
             outputMaterial.mainTexture = topoMap;
         }
     }
 }


topo.png (192.6 kB)
Comment
Add comment · Show 14 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image sevensixtytwo · Sep 11, 2014 at 08:14 AM 0
Share

How would this be implemented? While building the level? During runtime?

Also regarding drawing the overlay, were you referring to a graphics program? or can unity edit images through scripting? Sorry about all the questions and thanks for the reply.

avatar image Dave29483 · Sep 11, 2014 at 04:14 PM 0
Share

Updated post with example for you :)

avatar image sevensixtytwo · Sep 12, 2014 at 02:22 AM 0
Share

Interesting. I'll see if I can get something similar working for Unity and check back here. $$anonymous$$uch appreciated.

avatar image Dave29483 · Sep 12, 2014 at 02:12 PM 0
Share

Had a bit of free time so did another example which uses 16bit raw height map data which is a more likely scenario. It is also considerably faster.

avatar image Dave29483 · Sep 13, 2014 at 01:00 AM 1
Share

Figured it out...

After creating Texture2D clear background;

 //Set background
         for (int x = 0; x < _width; x++)
         {
             for (int y = 0; y < _height; y++)
             {
                 topo$$anonymous$$ap.SetPixel( x, y, bkgColor );
             }
         }

Then remove the else section from edge detection so it looks like so;

 if ( slice[ y * _width + ( x - 1 ) ] == false || slice[ y * _width + ( x + 1 ) ] == false || slice[ ( y - 1 ) * _width + x ] == false || slice[ ( y + 1 ) * _width + x ] == false )
                         {
                             topo$$anonymous$$ap.SetPixel( x, y, bandColor );
                         }



I also updated code listing above with fix and removed the unnecessary slice allocation in the band loop. Same slice buffer can be reused.

Show more comments
avatar image
0

Answer by AlucardJay · Mar 25, 2016 at 05:05 PM

This is a modified script from the excellent answer by Dave29483 for creating a contour map texture from a terrain. Add the function to that class, or just use the class as below.

Call with :

 ContourMap.FromTerrain( terrain ) // use default number of bands and colours
 ContourMap.FromTerrain( terrain, numberOfBands ) // assign number of bands, use default colours
 ContourMap.FromTerrain( terrain, numberOfBands, bandColor, bkgColor ) // assign number of bands and colours

modified ContourMap.cs :

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 
 public class ContourMap : MonoBehaviour 
 {
     // Creates contour map from terrain heightmap data as Texture2D
     
     // use default colours and optional parameter numberOfBands
     public static Texture2D FromTerrain( Terrain terrain, int numberOfBands = 12 ) 
     {
         return FromTerrain( terrain, numberOfBands, Color.white, Color.clear );
     }
     
     // define all parameters
     public static Texture2D FromTerrain( Terrain terrain, int numberOfBands, Color bandColor, Color bkgColor ) 
     {
         // dimensions
         int width = terrain.terrainData.heightmapWidth;
         int height = terrain.terrainData.heightmapHeight;
         
         // heightmap data
         float[,] heightmap = terrain.terrainData.GetHeights( 0, 0, width, height );
         
         // Create Output Texture2D with heightmap dimensions
         Texture2D topoMap = new Texture2D( width, height );
         topoMap.anisoLevel = 16;
         
         // array for storing colours to be applied to texture
         Color[] colourArray = new Color[ width * height ];
         
         // Set background
         for ( int y = 0; y < height; y++ )
         {
             for ( int x = 0; x < width; x++ )
             {
                 colourArray[ (y * width) + x ] = bkgColor;
             }
         }
         
         // Initial Min/Max values for normalized terrain heightmap values
         float minHeight = 1f;
         float maxHeight = 0;
         
         // Find lowest and highest points
         for ( int y = 0; y < height; y++ )
         {
             for ( int x = 0; x < width; x++ )
             {
                 if ( minHeight > heightmap[ y, x ] )
                 {
                     minHeight = heightmap[ y, x ];
                 }
                 if ( maxHeight < heightmap[ y, x ] )
                 {
                     maxHeight = heightmap[ y, x ];
                 }
             }
         }
         
         // Create height band list
         float bandDistance = ( maxHeight - minHeight ) / (float)numberOfBands; // Number of height bands to create
         
         List< float > bands = new List< float >();
         
         // Get ranges
         float r = minHeight + bandDistance;
         while ( r < maxHeight )
         {
             bands.Add( r );
             r += bandDistance;
         }
         
         // Create slice buffer
         bool[,] slice = new bool[ width, height ];
         
         // Draw bands
         for ( int b = 0; b < bands.Count; b++ )
         {
             // Get Slice
             for ( int y = 0; y < height; y++ )
             {
                 for ( int x = 0; x < width; x++ )
                 {
                     if ( heightmap[ y, x ] >= bands[ b ] )
                     {
                         slice[ x, y ] = true;
                     }
                     else
                     {
                         slice[ x, y ] = false;
                     }
                 }
             }
             
             // Detect edges on slice and write to output
             for ( int y = 1; y < height - 1; y++ )
             {
                 for ( int x = 1; x < width - 1; x++ )
                 {
                     if ( slice[ x, y ] == true )
                     {
                         if ( 
                             slice[ x - 1, y ] == false || 
                             slice[ x + 1, y ] == false || 
                             slice[ x, y - 1 ] == false || 
                             slice[ x, y + 1 ] == false )
                         {
                             // heightmap is read y,x from bottom left
                             // texture is read x,y from top left
                             // magic equation to find correct array index
                             int ind = ( ( height - y - 1 ) * width ) + ( width - x - 1 );
                             
                             colourArray[ ind ] = bandColor;
                         }
                     }
                 }
             }
             
         }
         
         // apply colour array to texture
         topoMap.SetPixels( colourArray );
         topoMap.Apply();
         
         // Return result
         return topoMap;
     }
 }

example TopoLines.cs :

 using UnityEngine;
 using System.Collections;
 
 
 public class TopoLines : MonoBehaviour 
 {
     public Terrain terrain;
     
     public int numberOfBands = 12;
     
     public Color bandColor = Color.white;
     public Color bkgColor = Color.clear;
     
     public Renderer outputPlain;
     
     public Texture2D topoMap;
     
     
     void Start() 
     {
         GenerateTopoLines();
     }
     
     void Update() 
     {
         if ( Input.GetMouseButtonDown(0) )
         {
             GenerateTopoLines();
         }
     }
     
     void GenerateTopoLines() 
     {
         //topoMap = ContourMap.FromTerrain( terrain );
         //topoMap = ContourMap.FromTerrain( terrain, numberOfBands );
         topoMap = ContourMap.FromTerrain( terrain, numberOfBands, bandColor, bkgColor );
         
         if ( outputPlain )
         {
             outputPlain.material.mainTexture = topoMap;
         }
     }
 }

Image from test. Change values in runtime and left-mouse in scene to observe :

alt text


topolines-example-scaled.jpg (211.3 kB)
Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image buronix · Mar 25, 2016 at 08:06 PM 0
Share

Thanks a lot !!

This end with weird odd texture resolution but it works like a charm !!

avatar image AlucardJay buronix · Mar 25, 2016 at 08:26 PM 0
Share

The odd texture resolution is because Unity terrain heightmaps are 2^n + 1 (eg 257, 513, 1025), more info : http://docs.unity3d.com/$$anonymous$$anual/terrain-OtherSettings.html (scroll down to Resolution)

Heightmap Resolution : Pixel resolution of the terrain’s heightmap (should be a power of two plus one, eg, 513 = 512 + 1).

I chose to leave it, to return a texture with the true full range of heights. However if you wish the texture to be 2^n (256, 512, 1024) then the lines that set the width and height can be modified like this :

 // dimensions
 int width = terrain.terrainData.heightmapWidth - 1;
 int height = terrain.terrainData.heightmapHeight - 1;
avatar image AlucardJay AlucardJay · Mar 25, 2016 at 10:17 PM 1
Share

Unity is strange in how it uses different coordinate systems (screen, texture, heightmap all have different origins and axis?!!). I had some trouble myself with this while converting that script. You see the comment "// magic equation to find correct array index" ;)

All I can suggest is some trial and error to find the right index calculation. If both axis are flipped, try :

 int ind = (y * width) + x;

or other combinations thereof :

 int ind = ((height - y - 1) * width) + x; // flip y, keep x
 int ind = (y * width) + (width - x - 1); // keep y, flip x
Show more comments
avatar image
0

Answer by iconeo · Nov 10, 2016 at 07:37 PM

I am very new to unity but would like to implement this. Is there somewhere where I can get more instruction or even a sample project with this already setup?

Thanks.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by Circool · Mar 02, 2018 at 08:33 AM

I've made an asset "Wire Terrain" and it can create mesh contours from terrain: https://assetstore.unity.com/packages/tools/modeling/wireterrain-94895

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

29 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

How do I create a terrain with an asset? 0 Answers

How can I edit my terrain around a street curb? 1 Answer

I have trees prefabs, how to make sure that they can be set on the terrain 0 Answers

Terrain Tree Index Removed, But Sometimes Not And I got An Error 0 Answers

Updating the terrain collider after importing raw data 4 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges