Change Terrain Heightmap Resolution without Resizing Terrain

Inspector Behaviour (Desired Effect)

When I change my terrain’s heightmap resolution through the inspector via:

Terrain → Terrain Settings → Texture Resolutions (On Terrain Data) → Heightmap Resolution,

the resolution of my heightmap is increased without the terrain visibly changing.


Script Behaviour (Unexpected Effect)

However, when I try to recreate this behaviour via script like so: Terrain.activeTerrain.terrainData.heightmapResolution = 1025;

The terrain not only changes heightmap resolution but also size, with very strange effects like repeating textures etc.


How can I resize the heightmap via script WITHOUT any visible changes to the terrain, exactly as done via the inspector?


Essentially, I want to produce the exact same change produced by selecting the following, but via a script:

163119-screenshot.png

I’m having the same problem. I can generate a scripted terrain at a heightmapScale of (1, 200, 1), but with terrain at size 4096 the heightmapResolution must be 4097. Then I can manually lower the heightmapResolution in the editor to say, 257 which then works fine after applying the “resizing” button in the Unity Terrain editor. Then I see (via script) that the heightmapScale has been reset by the editor’s resizing to the value (16, 200, 16). This all makes sense since 16*256 + 1 = 4096 + 1.

However, my problem is that I have to do this manually for all of my terrain tiles. Why can’t Unity support the editor’s resizing button with a scripting function? Or simply provide a scripting function to map lower resolution heightmaps onto terrains? Is some way to do this?

Using the code kenncann linked above I hacked together a solution. But I don’t really understand it, and I had to remove a bunch of stuff from the code I also didn’t understand.

static void ResizeHeightmap(int newResolution, Terrain m_Terrain)
    {
        RenderTexture oldRT = RenderTexture.active;
 
        RenderTexture oldHeightmap = RenderTexture.GetTemporary(m_Terrain.terrainData.heightmapTexture.descriptor);
        Graphics.Blit(m_Terrain.terrainData.heightmapTexture, oldHeightmap);
 
        int dWidth = m_Terrain.terrainData.heightmapResolution;
        int sWidth = newResolution;
 
        Vector3 oldSize = m_Terrain.terrainData.size;
        m_Terrain.terrainData.heightmapResolution = newResolution;
        m_Terrain.terrainData.size = oldSize;
 
        oldHeightmap.filterMode = FilterMode.Bilinear;
 
        float k = (dWidth - 1.0f) / (sWidth - 1.0f) / dWidth;
        float scaleX = (sWidth * k);
        float offsetX = (float)(0.5 / dWidth - 0.5 * k);
        Vector2 scale = new Vector2(scaleX, scaleX);
        Vector2 offset = new Vector2(offsetX, offsetX);
 
        Graphics.Blit(oldHeightmap, m_Terrain.terrainData.heightmapTexture, scale, offset);
        RenderTexture.ReleaseTemporary(oldHeightmap);
 
        RenderTexture.active = oldRT;
 
        m_Terrain.terrainData.DirtyHeightmapRegion(new RectInt(0, 0, m_Terrain.terrainData.heightmapTexture.width, m_Terrain.terrainData.heightmapTexture.height), TerrainHeightmapSyncControl.HeightAndLod);
 
    }

@UKCEH you have to do it by this way;

terrain.terrainData.heightmapResolution = size.x + 1;
terrain.terrainData.size = size;

So you have to assign heigtmapResolution first and then size. (size is Vector3 by the way)…

The only way I was ever able to play around with heightmap was by doing it this way:

void PlayWaves()
{
	timer++;
	float min = 0.005f;
	float max = 0.08f;
	heightPower = Mathf.Clamp(heightPower, min, max);

	if (heightPower == max) { up = false; }
	if (heightPower == min) { up = true; }

	if (timer > 2)
	{
		if (up) { heightPower += min; } else { heightPower -= min; }
		material2.SetFloat("_Parallax", heightPower);
		timer = 0;
	}
}

void MoveWaves()
{
	scrollSpeed = scrollSpeed += new Vector2(-waveSpeed, waveSpeed);
	material2.SetTextureOffset("_MainTex", scrollSpeed);
	if (scrollSpeed.x < -9.99f) scrollSpeed = Vector2.zero;
}