docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Custom Terrain Tool shaders

    If you don't want to use the built-in painting Material, you need to create your own shader. You can then use that shader to create a Material, and use that Material instead to modify Terrain Texture data.

    Here is an example Terrain Tool shader.

    Shader "TerrainTool/CustomTerrainTool"
    {
        Properties { _MainTex ("Texture", any) = "" {} }
    
        SubShader
        {
            ZTest Always Cull Off ZWrite Off
    
            HLSLINCLUDE
    
            #include "UnityCG.cginc"
            #include "Packages/com.unity.terrain-tools/Shaders/TerrainTools.hlsl"
    
            sampler2D _MainTex;
            float4 _MainTex_TexelSize;      // 1/width, 1/height, width, height
    
            sampler2D _BrushTex;
    
            float4 _BrushParams;
            #define BRUSH_STRENGTH      (_BrushParams[0])
            #define BRUSH_TARGETHEIGHT  (_BrushParams[1])
            #define kMaxHeight          (32766.0f/65535.0f)
    
            struct appdata_t
            {
                float4 vertex : POSITION;
                float2 pcUV : TEXCOORD0;
            };
    
            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 pcUV : TEXCOORD0;
            };
    
            v2f vert(appdata_t v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.pcUV = v.pcUV;
                return o;
            }
    
            ENDHLSL
    
            Pass
            {
                Name "CustomTerrainTool"
    
                HLSLPROGRAM
                
                #pragma vertex vert
                #pragma fragment frag
    
                float4 frag(v2f i) : SV_Target
                {
                    float2 brushUV = PaintContextUVToBrushUV(i.pcUV);
    
                    // out of bounds multiplier
                    float oob = all(saturate(brushUV) == brushUV) ? 1.0f : 0.0f;
    
                    // Sample the MainTex, which should be a region of the source Heightmap texture, to get the current height value at the provided UV
                    // UnpackHeightmap is necessary here because it unpacks the height value from R and G channels if the current platform/graphics device does not support R16_UNorm texture formats. If R16_UNorm formats are supported, UnpackHeightmap just reads from the R channel.
                    float height = UnpackHeightmap(tex2D(_MainTex, i.pcUV));
                    // Calculate the influence from the brush mask at this fragment
                    float brushShape = oob * UnpackHeightmap(tex2D(_BrushTex, brushUV));
                    // Calculate the new height value
                    height = height + BRUSH_STRENGTH * brushShape;
    
                    // Store the new height into the destination RenderTexture. Clamp between 0.0f and 0.5f because the Heightmap itself is signed but is treated as an unsigned texture when rendering the Terrain
                    // PackHeightmap is necessary here because it packs the height value into R and G channels if the current platform/graphics device does not support R16_UNorm texture formats. If R16_UNorm formats are supported, PackHeightmap just writes to the R channel.
                    return PackHeightmap(clamp(height, 0, kMaxHeight));
                }
    
                ENDHLSL
            }
        }
    }
    
    In This Article
    Back to top
    Copyright © 2025 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)