Can I universally raise my terrain to allow greater sculpting depth while preserving details?

I created a terrain and sculpted mountains and haills and such, and was preparing to create a deep river with a dam object sunk down into it when I realized that I could only make the river so deep before it reached “0”…

Is there any way to allow Terrain to become deeper than “0” (typing negative numbers fails to be recognized, and defaults back to “0”)? If not, is there any way to universally raise the altitude of the entire Terrain all at once while preserving the already-sculpted details?

Help would be really appreciated, to avoid having to start the terrain over again…


Note - if you’re just starting a NEW terrain, it begins uselessly at all-zero, so you cannot do “down”

To change that (again this is only when starting fresh…)

[36135-screen+shot+2014-11-30+at+16.35.40.png|36135]

Edit (2016): new script with undo functionality, and editor script for inspector buttons.

Disclaimer : use at your own risk. Back up your terrain before using. I won’t be held responsible for lost terrain ! (though this version does have undo which should hopefully cover any accidents)

STEP ONE : save your terrain! Export it by right-clicking on it in the Project Window, then select Export Package…

#How to use this :#

This script can be run in the Editor without playing. Attach the script to the terrain or an empty gameobject.

Assign the terrain to be modified in the inspector.

Set the amount in units to raise or lower the terrain by. If the height of the tallest peak is greater than the max height of the terrain, these peaks will become flattened. Same if the height is lower than the min height.

Click the ‘Raise/Lower Terrain’ button.

If there are previous heights stored, an Undo button will show.

please note: delete the script and add a new script for editing a different terrain. I havn’t included anything for checking if a different terrain is assigned in the inspector, so please delete this script and add a new script when editing a different terrain, otherwise the undo will write the previous terrains stored undo heights, and that would be very bad.

TerrainRaiseLowerHeightmap.cs :

//--------------------------------//
//  TerrainRaiseLowerHeightmap.cs //
//  Written by Jay Kay            //
//  2016/4/8                      //
//--------------------------------//


using UnityEngine;
using System.Collections;
using System.Collections.Generic;


public class TerrainRaiseLowerHeightmap : MonoBehaviour 
{
	public Terrain terrain;
	
	public float changeHeightInUnits = 0f;
	
	[HideInInspector] public List< float[,] > undoHeights = new List< float[,] >();
	
	
	//	-------------------------------------------------------  Modify Heightmap Functions
	
	
	public void ModifyTerrainHeightmap() 
	{
		// check if terrain is assigned in inspector
		if ( !terrain )
		{
			Debug.LogError( gameObject.name + " has no terrain assigned in the inspector" );
			return;
		}
		
		// get reference variables
		TerrainData terrainData = terrain.terrainData;
		int heightmapWidth = terrainData.heightmapWidth;
		int heightmapHeight = terrainData.heightmapHeight;
		
		// copy current heights
		float[,] currentHeights = terrainData.GetHeights( 0, 0, heightmapWidth, heightmapHeight );
		undoHeights.Add( currentHeights );
		
		// make new height array
		float[,] newHeights = new float[ heightmapWidth, heightmapHeight ];
		float terrainHeight = terrainData.size.y;
		
		for ( int y = 0; y < heightmapWidth; y++ ) 
		{
			for ( int x = 0; x < heightmapHeight; x++ ) 
			{
				newHeights[ y, x ] = Mathf.Clamp01( currentHeights[ y, x ] + ( changeHeightInUnits / terrainHeight ) );
			}
		}
		
		// apply to terrain
		terrainData.SetHeights( 0, 0, newHeights );
		
		Debug.Log( "Raise/Lower Heights completed" );
	}
	
	
	//	-------------------------------------------------------  Undo Functions
	
	
	public void UndoModifyTerrainHeightmap() 
	{
		// check if terrain is assigned in inspector
		if ( !terrain )
		{
			Debug.LogError( gameObject.name + " has no terrain assigned in the inspector" );
			return;
		}
		
		// get last heights
		float[,] newHeights = undoHeights[ undoHeights.Count - 1 ];
		
		// apply to terrain
		terrain.terrainData.SetHeights( 0, 0, newHeights );
		
		// remove from list
		undoHeights.RemoveAt( undoHeights.Count - 1 );
		
		Debug.Log( "Undo Heights completed" );
	}
}

TerrainRaiseLowerHeightmapEditor.cs - IMPORTANT- place this script in an Editor folder :

//--------------------------------//
//  TerrainRaiseLowerHeightmap.cs //
//  Written by Jay Kay            //
//  2016/4/8                      //
//--------------------------------//


using UnityEditor;
using UnityEngine;


[ CustomEditor( typeof( TerrainRaiseLowerHeightmap ) ) ]
public class TerrainRaiseLowerHeightmapEditor : Editor 
{
	private GameObject obj;
	private TerrainRaiseLowerHeightmap objScript;
	
	void OnEnable()
	{
		obj = Selection.activeGameObject;
		objScript = obj.GetComponent< TerrainRaiseLowerHeightmap >();
	}
	
	public override void OnInspectorGUI()
	{
		DrawDefaultInspector();
		
		// spacing between buttons
		EditorGUILayout.Space();
		
		// check if there is a terrain
		if ( objScript.terrain == null )
		{
			EditorGUILayout.BeginHorizontal();
			GUILayout.Label( "Assign a terrain to modify", GUILayout.MinWidth( 80 ), GUILayout.MaxWidth( 350 ) );
			EditorGUILayout.EndHorizontal();
			
			return;
		}
		
		// raise/lower button
		EditorGUILayout.BeginHorizontal();
		if ( GUILayout.Button( "Raise/Lower Terrain", GUILayout.MinWidth( 80 ), GUILayout.MaxWidth( 350 ) ) )
		{
			objScript.ModifyTerrainHeightmap();
		}
		EditorGUILayout.EndHorizontal();
		
		// check if there is an undo array
		if ( objScript.undoHeights.Count > 0 )
		{
			// spacing between buttons
			EditorGUILayout.Space();
			
			EditorGUILayout.BeginHorizontal();
			if ( GUILayout.Button( "UNDO", GUILayout.MinWidth( 80 ), GUILayout.MaxWidth( 350 ) ) )
			{
				objScript.UndoModifyTerrainHeightmap();
			}
			EditorGUILayout.EndHorizontal();
		}
	}
}

This script is part of an upcoming Terrain Tools package I’m currently working on =]


Original Answer (2014):

While the OP has done an excellent job of answering their own question, I would like to provide another answer using scripting.

Disclaimer : use at your own risk. Back up your terrain before using. I won’t be held responsible for lost terrain !

STEP ONE : save your terrain! Export it by right-clicking on it in the Project Window, then select Export Package…

#How to use this :#

This script can be run in the Editor without playing. Attach the script to the terrain.

Right-click on the script component in the Inspector, then a drop-down selection box appears. Example :

alt text

There should be an option Raise Terrain Heightmap, click on this. Now the script will run and show a console message when complete.

The amount the height is raised by uses world-space values :

public var raiseHeightInUnits : float = 20.0;

so this would raise the terrain by 20 units.

Warning : if the height of the tallest peak is greater than the max height of the terrain, these peaks will become flattened. That’s why it’s important to backup the terrain first, in-case of any mistakes. I could write in some code to make sure this doesn’t happen, but with careful implementation and gradual increments to the raiseHeightInUnits, there should be no problems =]

#pragma strict


#if UNITY_EDITOR
@ContextMenu( "Raise Terrain Heightmap" )
function RaiseTerrainHeightmap() 
{
	RaiseHeights();
}
#endif


public var myTerrain : Terrain;
private var terrainData : TerrainData;
private var heightmapWidth : int;
private var heightmapHeight : int;
private var heightmapData : float[,];

public var raiseHeightInUnits : float = 20.0;


function RaiseHeights() 
{
	// - GetTerrainData -
	
	if ( !myTerrain )
	{
		//myTerrain = Terrain.activeTerrain;
		Debug.LogError( gameObject.name + " has no terrain assigned in the inspector" );
	}
	
	terrainData = myTerrain.terrainData;
	heightmapWidth = myTerrain.terrainData.heightmapWidth;
	heightmapHeight = myTerrain.terrainData.heightmapHeight;
	
	// --
	
	// store old heightmap data
	heightmapData = terrainData.GetHeights( 0, 0, heightmapWidth, heightmapHeight );
	
	var terrainHeight : float = terrainData.size.y;
	
	// --
	
	var y : int = 0;
	var x : int = 0;
	
	// raise heights
	for ( y = 0; y < heightmapHeight; y ++ )
	{
		for ( x = 0; x < heightmapWidth; x ++ )
		{
			var newHeight : float = Mathf.Clamp01( heightmapData[ y, x ] + ( raiseHeightInUnits / terrainHeight ) );
			
			heightmapData[ y, x ] = newHeight;
		}
	}
	
	terrainData.SetHeights( 0, 0, heightmapData );
	
	Debug.Log( "RaiseHeights() completed" );
}

I’m coming back to answer my own question because I was finally able to fix the problem by exporting the Terrain’s height-map from Unity as a 16-bit Mac file (even though I work on a PC), and then open it up in Photoshop as an 8-bit file (with neither PC or Mac checked)… This opens a RAW file with an Alpha Channel. Leave the Alpha Channel alone, but feel free to edit the single background layer as you see fit.

In my case, since I wanted to raise the entire terrain equally higher while leaving my features intact, I simply added a fill layer of pure white, then decreased the opacity to make the entire thing slightly lighter, then collapsed the layers and imported it back into Unity… It worked perfectly! And all I had to do was grab everything except the terrain and move it back up to where it needed to be to intersect the terrain before it was raised…

Hope this helps other folks out there…

Here is a working version with unity 2019 and C#:


First you need the C# script, applied to any game object you wish to use to manage your terrain heights:


using UnityEngine;
using UnityEditor;


public class RaiseTerrainHeightmap : MonoBehaviour
{

    public Terrain myTerrain;
    private TerrainData terrainData;
    private int heightmapWidth;
    private int heightmapHeight;
    private float[,] heightmapData = new float[0, 0];

    public float raiseHeightInUnits = 20.0f;



    public void RaiseHeights()
    {
        // - GetTerrainData -

        if (!myTerrain)
        {
            //myTerrain = Terrain.activeTerrain;
            Debug.LogError(gameObject.name + " has no terrain assigned in the inspector");
        }

        terrainData = myTerrain.terrainData;
        heightmapWidth = myTerrain.terrainData.heightmapWidth;
        heightmapHeight = myTerrain.terrainData.heightmapHeight;

        // --

        // store old heightmap data
        heightmapData = terrainData.GetHeights(0, 0, heightmapWidth, heightmapHeight);

        float terrainHeight = terrainData.size.y;

        // --

        var y = 0;
        var x = 0;

        // raise heights
        for (y = 0; y < heightmapHeight; y++)
        {
            for (x = 0; x < heightmapWidth; x++)
            {
                float newHeight = Mathf.Clamp01(heightmapData[y, x] + (raiseHeightInUnits / terrainHeight));

                heightmapData[y, x] = newHeight;
            }
        }

        terrainData.SetHeights(0, 0, heightmapData);

        Debug.Log("RaiseHeights() completed");
    }


}

Then you need (Or I would prefer to use) an editor script (Which you save in your project directory under an Editor Folder.):

using UnityEngine;
using UnityEditor;


[CustomEditor(typeof(RaiseTerrainHeightmap))]
public class TerrainCustomEditor : Editor
{


    // public RaiseHeights raiseHeights;

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        RaiseTerrainHeightmap RaiseTerrain = (RaiseTerrainHeightmap)target;
        if (GUILayout.Button("Raise Terrain Heightmap"))
        { RaiseTerrain.RaiseHeights(); }
    }
}

I hope it helps someone out. Credit to past users in this thread for writing the UnityScript.

You could “Terrain → Flatten HeightMap” or “Terrain ->Set Resolution”.
The former would require starting over, the latter would mean changing a lot/most of the features to get them to the height you want.

Other than these, I don’t know how you would do that, but I hope you find what you need if my help doesn’t work.