Search Unity

Looking for advice to programmitcally increase heightmap resolution

Discussion in 'World Building' started by kenncann, Mar 29, 2021.

  1. kenncann

    kenncann

    Joined:
    Jul 16, 2015
    Posts:
    17
    I have a lot of terrains that I'd like to loop over and increase the heightmap resolution similar to how you can select it here in the terrain settings:

    upload_2021-3-29_1-56-43.png

    When you do this the resolution of the terrain is increased without changes in terrain size or loss of the terrain. However when I try to do this by adjusting the terrain data seems to clear the terrain and make it bigger by whatever factor I'm trying to scale up to (ie if I wanna go from 129 to 2049 then it increases by a factor of 16).

    I found an old video that showed that something like this used to happen if you adjusted the heightmap resolution through the settings so I'm guessing at some point a feature was added to interpolate new points without loss of the terrain itself, but only if you select the resolution through the popup.

    So firstly I'd like to know if anyone has any advice on how to replicate this effect? It seems other people have asked about this in the past year but have not found an answer.
    Secondly I would like to request api access to whatever is happening behind the scenes when the popup selection is made.
     
  2. StaggartCreations

    StaggartCreations

    Joined:
    Feb 18, 2015
    Posts:
    2,266
  3. MichaelEGA

    MichaelEGA

    Joined:
    Oct 11, 2019
    Posts:
    40
    Old thread I know, but I haven't seen an answer so...

    I managed to hack something together using the code Staggart linked above. Not ideal though, because I don't really understand how it works, and I had to remove a bunch of stuff I also didn't understand.

    Code (CSharp):
    1. static void ResizeHeightmap(int newResolution, Terrain m_Terrain)
    2.     {
    3.         RenderTexture oldRT = RenderTexture.active;
    4.  
    5.         RenderTexture oldHeightmap = RenderTexture.GetTemporary(m_Terrain.terrainData.heightmapTexture.descriptor);
    6.         Graphics.Blit(m_Terrain.terrainData.heightmapTexture, oldHeightmap);
    7.  
    8.         int dWidth = m_Terrain.terrainData.heightmapResolution;
    9.         int sWidth = newResolution;
    10.  
    11.         Vector3 oldSize = m_Terrain.terrainData.size;
    12.         m_Terrain.terrainData.heightmapResolution = newResolution;
    13.         m_Terrain.terrainData.size = oldSize;
    14.  
    15.         oldHeightmap.filterMode = FilterMode.Bilinear;
    16.  
    17.         float k = (dWidth - 1.0f) / (sWidth - 1.0f) / dWidth;
    18.         float scaleX = (sWidth * k);
    19.         float offsetX = (float)(0.5 / dWidth - 0.5 * k);
    20.         Vector2 scale = new Vector2(scaleX, scaleX);
    21.         Vector2 offset = new Vector2(offsetX, offsetX);
    22.  
    23.         Graphics.Blit(oldHeightmap, m_Terrain.terrainData.heightmapTexture, scale, offset);
    24.         RenderTexture.ReleaseTemporary(oldHeightmap);
    25.  
    26.         RenderTexture.active = oldRT;
    27.  
    28.         m_Terrain.terrainData.DirtyHeightmapRegion(new RectInt(0, 0, m_Terrain.terrainData.heightmapTexture.width, m_Terrain.terrainData.heightmapTexture.height), TerrainHeightmapSyncControl.HeightAndLod);
    29.  
    30.     }
     
    Koesys likes this.
  4. yota_battlecry

    yota_battlecry

    Joined:
    Sep 7, 2018
    Posts:
    1
    I make a little modification from the code from StaggartCreations's post, I tested, it works.


    Code (CSharp):
    1.         static void ResizeHeightmap(int newResolution, Terrain m_Terrain)
    2.         {
    3.             RenderTexture oldRT = RenderTexture.active;
    4.  
    5.             RenderTexture oldHeightmap = RenderTexture.GetTemporary(m_Terrain.terrainData.heightmapTexture.descriptor);
    6.             Graphics.Blit(m_Terrain.terrainData.heightmapTexture, oldHeightmap);
    7.  
    8.             // TODO: Can this be optimized if there is no hole?
    9.             RenderTexture oldHoles = RenderTexture.GetTemporary(((RenderTexture)m_Terrain.terrainData.holesTexture).descriptor);
    10.             Graphics.Blit(((RenderTexture)m_Terrain.terrainData.holesTexture), oldHoles);
    11.  
    12.             Undo.RegisterCompleteObjectUndo(m_Terrain.terrainData, "Resize Heightmap");
    13.  
    14.             int dWidth = m_Terrain.terrainData.heightmapResolution;
    15.             int sWidth = newResolution;
    16.  
    17.             Vector3 oldSize = m_Terrain.terrainData.size;
    18.             m_Terrain.terrainData.heightmapResolution = newResolution;
    19.             m_Terrain.terrainData.size = oldSize;
    20.  
    21.             oldHeightmap.filterMode = FilterMode.Bilinear;
    22.  
    23.             // Make sure textures are offset correctly when resampling
    24.             // tsuv = (suv * swidth - 0.5) / (swidth - 1)
    25.             // duv = (tsuv(dwidth - 1) + 0.5) / dwidth
    26.             // duv = (((suv * swidth - 0.5) / (swidth - 1)) * (dwidth - 1) + 0.5) / dwidth
    27.             // k = (dwidth - 1) / (swidth - 1) / dwidth
    28.             // duv = suv * (swidth * k)     + 0.5 / dwidth - 0.5 * k
    29.  
    30.             float k = (dWidth - 1.0f) / (sWidth - 1.0f) / dWidth;
    31.             float scaleX = (sWidth * k);
    32.             float offsetX = (float)(0.5 / dWidth - 0.5 * k);
    33.             Vector2 scale = new Vector2(scaleX, scaleX);
    34.             Vector2 offset = new Vector2(offsetX, offsetX);
    35.  
    36.             Graphics.Blit(oldHeightmap, m_Terrain.terrainData.heightmapTexture, scale, offset);
    37.             RenderTexture.ReleaseTemporary(oldHeightmap);
    38.  
    39.             oldHoles.filterMode = FilterMode.Point;
    40.             Graphics.Blit(oldHoles, (RenderTexture)m_Terrain.terrainData.holesTexture);
    41.             RenderTexture.ReleaseTemporary(oldHoles);
    42.  
    43.             RenderTexture.active = oldRT;
    44.  
    45.             m_Terrain.terrainData.DirtyHeightmapRegion(new RectInt(0, 0, m_Terrain.terrainData.heightmapTexture.width, m_Terrain.terrainData.heightmapTexture.height), TerrainHeightmapSyncControl.HeightAndLod);
    46.             m_Terrain.terrainData.DirtyTextureRegion(TerrainData.HolesTextureName, new RectInt(0, 0, m_Terrain.terrainData.holesTexture.width, m_Terrain.terrainData.holesTexture.height), false);
    47.  
    48.         }
     
    MichaelEGA likes this.