There is very little information online on how to assign terrain textures via code on Unity (there are a couple of posts like this one or that one but they’re several years old and they don’t explain fully what’s what). And Unity’s own documentation on the topic is very poor.
The following code allows you to assign as many textures as you want to your terrain and to define how they appear based on factors such as altitude or steepness (e.g. a snow texture should only appear at high altitude where it’s not too steep). Let me first share the code with you and I will tell you afterwards how to use it.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Linq;
public class TextureModifier : MonoBehaviour {
// Using Serializable allows us to embed a class with sub properties in the inspector.
[Serializable]
public class TextureAttributes{
public string name;
public int index;
public bool defaultTexture = false;
[Range(0.0f,1.0f)]
public float minSteepness;
[Range(0.0f,1.0f)]
public float maxSteepness;
[Range(0.0f,1.0f)]
public float minAltitude;
[Range(0.0f,1.0f)]
public float maxAltitude;
}
public List<TextureAttributes> listTextures = new List<TextureAttributes> ();
private Terrain terrain;
private TerrainData terrainData;
private int indexOfDefaultTexture;
void Start () {
// Get the attached terrain component
terrain = GetComponent<Terrain>();
// Get a reference to the terrain data
terrainData = terrain.terrainData;
//This is the # of textures you have added in the terrain editor
int nbTextures = terrainData.alphamapLayers;
//See below for the definition of GetMaxHeight
float maxHeight = GetMaxHeight(terrainData, terrainData.heightmapWidth);
// Your texture data (i.e. Splatmap) is stored internally as a 3d array of floats with x and y location as the first 2 dimensions of the array and the index of the texture to be used as the 3rd dimension
float[, ,] splatmapData = new float[terrainData.alphamapWidth, terrainData.alphamapHeight, terrainData.alphamapLayers];
//This is just in case someone mixed up min and max when completing the inspector for this script
for (int i = 0; i < listTextures.Count; i++) {
if (listTextures [i].minAltitude > listTextures [i].maxAltitude) {
float temp = listTextures [i].minAltitude;
listTextures [i].minAltitude = listTextures [i].maxAltitude;
listTextures [i].maxAltitude = temp;
}
if (listTextures [i].minSteepness > listTextures [i].maxSteepness) {
float temp2 = listTextures [i].minSteepness;
listTextures [i].minSteepness = listTextures [i].maxSteepness;
listTextures [i].maxSteepness = temp2;
}
}
//For some reason you need a default texture in Unity
for (int i = 0; i < listTextures.Count; i++) {
if (listTextures [i].defaultTexture) {
indexOfDefaultTexture = listTextures [i].index;
}
}
for (int y = 0; y < terrainData.alphamapHeight; y++)
{
for (int x = 0; x < terrainData.alphamapWidth; x++)
{
// Normalise x/y coordinates to range 0-1
float y_01 = (float)y/(float)terrainData.alphamapHeight;
float x_01 = (float)x/(float)terrainData.alphamapWidth;
// Sample the height at this location (note GetHeight expects int coordinates corresponding to locations in the heightmap array)
float height = terrainData.GetHeight(Mathf.RoundToInt(y_01 * terrainData.heightmapHeight),Mathf.RoundToInt(x_01 * terrainData.heightmapWidth) );
//Normalise the height by dividing it by maxHeight
float normHeight = height / maxHeight;
// Calculate the steepness of the terrain at this location
float steepness = terrainData.GetSteepness(y_01,x_01);
// Normalise the steepness by dividing it by the maximum steepness: 90 degrees
float normSteepness = steepness / 90.0f;
//Erase existing splatmap at this point
for(int i = 0; i<terrainData.alphamapLayers; i++){
splatmapData [x, y, i] = 0.0f;
}
// Setup an array to record the mix of texture weights at this point
float[] splatWeights = new float[terrainData.alphamapLayers];
for (int i = 0; i < listTextures.Count; i++) {
//The rules you defined in the inspector are being applied for each texture
if (normHeight >= listTextures [i].minAltitude && normHeight <= listTextures [i].maxAltitude && normSteepness >= listTextures [i].minSteepness && normSteepness <= listTextures [i].maxSteepness) {
splatWeights [listTextures [i].index] = 1.0f;
}
}
// Sum of all textures weights must add to 1, so calculate normalization factor from sum of weights
float z = splatWeights.Sum();
//If z is 0 at this location (i.e. no texture was applied), put default texture
if(Mathf.Approximately(z, 0.0f)){
splatWeights [indexOfDefaultTexture] = 1.0f;
}
// Loop through each terrain texture
for(int i = 0; i<terrainData.alphamapLayers; i++){
// Normalize so that sum of all texture weights = 1
splatWeights[i] /= z;
// Assign this point to the splatmap array
splatmapData[x, y, i] = splatWeights[i];
}
}
}
// Finally assign the new splatmap to the terrainData:
terrainData.SetAlphamaps(0, 0, splatmapData);
}
//This is to get the maximum altitude of your terrain. For some reason TerrainData.
private float GetMaxHeight(TerrainData tData, int heightmapWidth){
float maxHeight = 0f;
for (int x = 0; x < heightmapWidth; x++) {
for (int y = 0; y < heightmapWidth; y++) {
if (tData.GetHeight (x, y) > maxHeight) {
maxHeight = tData.GetHeight (x, y);
}
}
}
return maxHeight;
}
}
When you attach this script to a terrain object, you get something like this:
Simply do as follows:
-
Precise the size on top (i.e. how many textures you want on your terrain)
-
For each texture:
-
Precise the name (you can put whatever name you wish, there is no logic that derives from this in the code)
-
Precise if it’s your default texture, a must-have on Unity for some reason. In other words if for a specific location no texture should appear based on the steepness/altitude rules you defined, which texture should be there?
-
Precise the index, i.e. in which order it appears in your terrain inspector. For instance in the image below my texture with index “0” (zero) is a sort of dried grass, the one with index “3” is a sort of greyish snow, the one with index “5” a greener grass, etc.

-
Define the rules for steepness and altitude. For instance if I want my snow texture to only appear in high altitudes and one not-to-steep areas, I could define Min Altitude as 0.6 and Max Altitude at 1 which means it will only appear in the top 40% highest altitudes. For steepness if I put Min Steepness at 0 and Max Steepness at 0.6 it means it will only appear in places with a steepness degree from 0 degree to 0.6 times 90 (maximum amount of degrees) = 54 degrees.
There you go, I hope it helps some people!
