
In previous posts, we generated a plane, added noise and noise layers to it. Now, it’s time to add texture to our terrain finally! ?
If you haven’t seen previous posts, I would recommend doing so, as the content of this post is based on them.
Do you remember that we added UV to our terrain mesh? We did it in the first post on plane generation. Thanks to that, we don’t need to worry about it now. But to remind ourself which part is responsible for assigning UV to our mesh, let’s put it here.
using System.Collections.Generic; using UnityEngine; /// <summary> /// Script that generates a plane with width and depth. /// </summary> public class PlaneGenerator : MonoBehaviour { ... /// <summary> /// Method which generates plane. /// </summary> private void GeneratePlane() { ... // Defining UV. Vector2[] uv = new Vector2[(width + 1) * (depth + 1)]; i = 0; for (int d = 0; d <= depth; d++) { for (int w = 0; w <= width; w++) { uv[i] = new Vector2(w / (float)width, d / (float)depth); i++; } } ... } ... }
If you want to remind yourself how UV works and how it’s assigned, you can check it in the post about generating quad. ?
Now let’s add a base for today’s topic. We have to get the reference to the renderer, create a new texture, and assign it to the material in the renderer.
using System.Collections.Generic; using UnityEngine; /// <summary> /// Script that generates a plane with width and depth. /// </summary> public class PlaneGenerator : MonoBehaviour { ... // Reference to the mesh renderer. private MeshRenderer meshRenderer; ... /// <summary> /// Unity method called on first frame. /// </summary> void Start() { ... meshRenderer = GetComponent<MeshRenderer>(); } /// <summary> /// Method which generates plane. /// </summary> private void GeneratePlane() { // Creating a terrain texture. Texture2D terrainTexture = new Texture2D(width, depth); ... // Configuring texture parameters. terrainTexture.filterMode = FilterMode.Bilinear; terrainTexture.wrapMode = TextureWrapMode.Clamp; // Applying changes to the texture. terrainTexture.Apply(); // Assigning texture to the material. meshRenderer.material.mainTexture = terrainTexture; } ... }
We also have to configure some parameters in the texture to render it properly and apply all the changes!
Our next step is to define layers for the terrain with color and minimal height on which color occurs.
using UnityEngine; /// <summary> /// Terrain layer. /// </summary> [System.Serializable] public class TerrainLayer { // Terrain minimum elevation. public float minHeight; // Terrain color. public Color terrainColor; }
We don’t need more data there, but it would be great if we have something that has a list of terrain layers and would tell us the terrain color for a given height. So let’s make another class that will do that!
using System.Collections.Generic; using UnityEngine; /// <summary> /// Terrain settings. /// </summary> [System.Serializable] public class TerrainSettings { // Different terrain types defined as layers. [SerializeField] private List<TerrainLayer> terrainLayers = new List<TerrainLayer>(); /// <summary> /// Evaluates the color for the terrain. /// </summary> /// <returns>The terrain color.</returns> /// <param name="height">Terrain height.</param> public Color EvaluateColor(float height) { // Looking for the proper layer for the provided height. int i = terrainLayers.Count - 1; while (terrainLayers[i].minHeight > height) { i--; } if (i < 0) i = 0; // Returning terrain color. return terrainLayers[i].terrainColor; } }
In the future, this class can be extended with some other methods and parameters, but for now, this is enough.
Now, let’s add these settings to our generator so we can configure terrain layers.
using System.Collections.Generic; using UnityEngine; /// <summary> /// Script that generates a plane with width and depth. /// </summary> public class PlaneGenerator : MonoBehaviour { ... [Header("Terrain")] // Terrain settings. [SerializeField] private TerrainSettings terrainSettings = new TerrainSettings(); ... }

Now the only thing left to do is to assign colors to the texture! ??
We can add logic for it in the same place where we are calculating terrain elevation from the noise layers.
using System.Collections.Generic; using UnityEngine; /// <summary> /// Script that generates a plane with width and depth. /// </summary> public class PlaneGenerator : MonoBehaviour { ... /// <summary> /// Method which generates plane. /// </summary> private void GeneratePlane() { ... int i = 0; for (int d = 0; d <= depth; d++) { for (int w = 0; w <= width; w++) { // Setting vertice position. vertices[i] = new Vector3(w, 0, d) - new Vector3(width / 2f, 0, depth / 2f); // Calculating elevation from noise layers. float elevation = 0; for (int l = 0; l < noiseLayers.Count; l++) { elevation += noiseLayers[l].Evaluate(w / (float)width, d / (float)depth); } // Assigning terrain color to the texture. Color terrainColor = terrainSettings.EvaluateColor(elevation); terrainTexture.SetPixel(w, d, terrainColor); // Adding elevation from perlin noise. vertices[i].y = elevation; i++; } } ... } ... }
With all the steps done, now we can check the results on the terrain!

What do you think about it? Have you generated texture before? Let me know in the comment section below!
If you want to get notified on future content, sign up for the newsletter!
As always, the whole project is available at my public repository. ?
And I hope to see you next time! ?