Search Unity

  1. Looking for a job or to hire someone for a project? Check out the re-opened job forums.
    Dismiss Notice
  2. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Fast alternative to SetAlphamaps?

Discussion in 'World Building' started by Lesnikus5, Feb 14, 2021.

  1. Lesnikus5

    Lesnikus5

    Joined:
    May 20, 2016
    Posts:
    117
    I am looking for an alternative to the slow SetAlphamaps for realtime terrain editing/painting. I've heard that you can use CopyActiveRenderTextureToTexture to paint on the terrain. But there are no examples of how to implement this. Any help?
     
  2. nicolasdemauroy

    nicolasdemauroy

    Joined:
    Nov 11, 2020
    Posts:
    3
    I have exactly the problem that setAlphaMap is very very slow (around 10ms per call). In my game, there are many sequences where heavy editing is done on the terrain (when earthwork is done...), and this makes the framerate drop significantly.

    A first step is consolidating all the setAlphaMaps into a single call per frame (though the zone passed may be bigger). But ideally, there would be other tricks to go fast.
     
  3. Lesnikus5

    Lesnikus5

    Joined:
    May 20, 2016
    Posts:
    117
    Editing performance is highly dependent on the size of the heightmap. So try decreasing the terrain resolution and using a few tiles of terrain. I use 512x512 and when editing an area at the junction of 4 terrains at once, function call is about 7 ms. When editing in the center of one tile terrain, respectively, about 2ms. If you have a mobile game, you may need an even lower heightmap resolution, then 512.

    Coloring also adds its own overhead and depends on the number of layers on the current terrain. I keep 8 layers, which is enough for most types of games. 4 layers would be faster, but too little variety.

    Over the past months, I haven’t found a more streamlined way to edit terrain. Probably not worth trying to look for.

    I also use MicroSplat shader. There is a "Custom Splatmaps" function that allows you to manually manipulate layer textures. And remove the UpdateMaterial overhead, which becomes a big problem with a large number of terrain tiles (more than 100). However, I need to check this, I will need to edit the plugin code. But it should work.
     
    Last edited: Apr 26, 2021
  4. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    369
    I'd have a go at modifying the Terrain alphamaps on the GPU. You can use the PaintContext and TerrainPaintUtility APIs to modify subregions of the different Terrain textures.

    Let's say you want to modify a region starting at X,Y in world space using one Terrain Layer at a time. You can do something like this:

    Code (CSharp):
    1. var terrainLayer = GetDesiredTerrainLayer(); // this is the terrain layer you want to "paint" onto the Terrain
    2. var material = GetMaterial(); // the material/shader you are going to use to modify the terrain texture
    3. var uv = ConvertWorldPosToLocalTerrainPos(terrain, x, y); // you'll want to do this yourself. if you're using physics raycasts, you can use the texcoord value from RaycastHit.texcoord as your uv value (we do this for the painting tools)
    4. var xform = TerrainPaintUtility.CalculateBrushTransform(terrain, uv, sizeOfRegion, 0f); // 0f is for rotation. probably won't need any rotation. sizeOfRegion is going to be the world-space width and height of the area you want to modify. the function will convert that to local terrain units and figure stuff out for ya
    5. var ctx = TerrainPaintUtility.BeginPaintTexture(terrain, xform.GetBrushXYBounds(), terrainLayer, 0);
    6. TerrainPaintUtility.SetupTerrainToolMaterialProperties(ctx, xform, material);
    7. Graphics.Blit(ctx.sourceRenderTexture, ctx.destinationRenderTexture, material); // actually modify the texture on the GPU
    8. TerrainPaintUtility.EndPaintTexture(ctx, "end paint"); // second param won't be used at runtime (afaik). it's just for tracking undo in editor
    and your shader should follow this structure

    Code (CSharp):
    1.  
    2. #pragma vertex vert
    3. #pragma fragment frag
    4.  
    5. #include "UnityCG.cginc"
    6. #include "TerrainTool.cginc"
    7.  
    8. struct appdata_t
    9. {
    10.     float4 vertex : POSITION;
    11.     float2 pcUV : TEXCOORD0; // pcUV stands for PaintContext UV
    12. };
    13.  
    14. struct v2f {
    15.     float4 vertex : SV_POSITION;
    16.     float2 pcUV : TEXCOORD0;
    17. };
    18.  
    19. v2f vert(appdata_t v)
    20. {
    21.     v2f o;
    22.     o.vertex = UnityObjectToClipPos(v.vertex);
    23.     o.pcUV = v.pcUV;
    24.     return o;
    25. }
    26.  
    27. float4 frag(v2f i) : SV_Target
    28. {
    29.     float2 uv = PaintContextUVToBrushUV(i.pcUV); // gets you the correct uv into the source and destination rendertexture based on properties set by TerrainPaintUtility.SetupTerrainToolMaterialProperties(...). useful if you want to sample an additional mask for your terrain modification
    30.  
    31.     float weight = tex2D(_MainTex, i.pcUV).r; // get the current splat weight for the Terrain Layer
    32.  
    33.     // do something to the splat weight
    34.  
    35.     return weight; // unity will re-normalize splat weights for you when you call EndPaintTexture
    36. }
    37.  
    38.  
    If you want to modify multiple splats at a time, I'd use the CopyActiveRenderTextureToTexture APIs to get your subregion of the splatmap(s) and modify those on the GPU (less setup for this i think). You'll want to handle normalization of the splat weights once you're done modifying the texture
     
    Last edited: May 6, 2021 at 9:01 AM
unityunity