Although a bit late to the party here, I’ve managed to get a working solution for runtime height deformation using TerrainPaintUtility and thought I’d post it here in case anyone else in future is struggling with Unity’s lacking documentation around the system.
One of the main issues I discovered was that the materials TerrainPaintUtility provides you with don’t seem to do anything, so instead I create materials using the shaders provided by Unity in the Terrain Tools package. These have a number of variables that need to be setup regarding the brush (Unity’s documentation mentions none of this), such as _BrushTex and _BrushParams.
If you have the Terrain Tools package in your project, you can open and view any of the scripts or shaders to see how they function. I would recommend checking out “Packages/Terrain Tools/Shaders/PaintHeight.shader” (which is the shader I use in the below example) which contains a variety of functions using different shader passes. You can also use some of the other shaders in that folder for different functionality, or write your own of course.
Compared to the Get and Set Heights implementation I was also using originally I was getting much better performance.
Anyway, here is my code for raising and lowering terrain. A negative strength can be used to lower terrain instead.
public void RaiseTerrain(Vector3 worldPosition, Texture2D brushTexture, float strength, float size, float rotation)
{
// Get the terrain you want to edit. This also works between neighbouring tiles, so only need the one terrain
Terrain terrain = this.terrain;
// Turn brush world position into terrain UV space (0-1)
Vector3 localPos = worldPosition - terrain.GetPosition();
float x = localPos.x / terrain.terrainData.size.x;
float y = localPos.z / terrain.terrainData.size.z;
Vector2 terrainSpacePos = new Vector2(x, y);
// Use brush position, size and rotation to generate bounds
var brushTransform = TerrainPaintUtility.CalculateBrushTransform(terrain, terrainSpacePos, size, rotation);
Rect bounds = brushTransform.GetBrushXYBounds();
// Begin painting
PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, bounds);
// Setup material for editing, including brush texture and other params
// This is the shader Unity uses for raise and lower and contains a number of other functions for other methods
// Such as flatten and smooth
Material material = new Material(Shader.Find("Hidden/TerrainEngine/PaintHeightTool"));
material.SetTexture("_BrushTex", brushTexture);
// Index 0: brush strength
// Index 1: target height (when flattening)
// Index 2 & 3: used for stamping the terrain for "BRUSH_STAMPHEIGHT" and "BLEND_AMOUNT"
material.SetVector("_BrushParams", new Vector4(strength, 0, 0, 0));
// Applies other material properties
TerrainPaintUtility.SetupTerrainToolMaterialProperties(paintContext, brushTransform, material);
// Apply brush to destination render texture from material
int passIndex = 0; // Pass index refers to which shader pass to use for painting (0 is raise/lower)
// See PaintHeight.shader for other options
Graphics.Blit(paintContext.sourceRenderTexture, paintContext.destinationRenderTexture, material, passIndex);
TerrainPaintUtility.EndPaintHeightmap(paintContext, "");
// Sync heightmap to terrain data
terrain.terrainData.SyncHeightmap();
}
Different shader passes may require some slight changes, or extra parameters. For example, for flattening terrain to a height, I instead use the “Hidden/TerrainTools/SetExactHeight” shader, and need to pass in the target height as the y value in the _BrushParams property (This seems to be a value between 0 and 0.5, for whatever reason).
For smoothing terrain, I used passIndex = 3 to call the correct shader pass and had to pass in an extra variable _SmoothWeights. This is taken directly from Unity’s own implementation in “Packages/Terrain Tools/Editor/TerrainTools/SmoothHeightTool.cs” with direction being a value between -1 and 1. At direction = -1 smoothing will only lower points, while 1 will only raise points, and 0 is either.
Vector4 smoothWeights = new Vector4(
Mathf.Clamp01(1.0f - Mathf.Abs(direction)), // centered
Mathf.Clamp01(-direction), // min
Mathf.Clamp01(direction), // max
0);
material.SetVector("_SmoothWeights", smoothWeights);
There is a separate shader you can use for smoothing which the package uses in it’s implementation, and requires a few extra parameters, but this shader worked for me so I stuck with it.
Last thing of note, brushTexture is expected to be a heightmap, and the shaders read it’s red value, so ensure your brush textures are set up correctly. Here is the texture I am using.

I haven’t tried painting anything but height adjustments with TerrainPaintUtility, but hopefully for anyone looking to paint textures or holes, this is enough to get started with. Again, you can check out Unity’s own implementation for their terrain editor in “Packages/Terrain Tools/Editor/TerrainTools”.