Im working on a script that applies textures to terrain on realtime but its laggy as hell although i used compute shaders. Is there any optimization i can do?
The GPU → CPU conversion is the killer here, no surprise. It looks like you generate the data on the GPU, pull it back (slow) and convert it to a texture, then take the pixels from the texture and fill out the alphamap array, then send that to the terrain as a new alphamap (which means another CPU->GPU conversion).
If you can keep the work in rendertextures instead, CopyActiveRenderTextureToHeightmap()
and CopyActiveRenderTextureToTexture()
will be faster than going GPU->CPU->GPU as you’re doing now. This thread might be a good reference for using a compute shader to populate splatmaps
Thanks for the help! I altered the main function to this, but doesnt work correctly and there is still lag:
public void Paint()
{
if (terrainLayers == null || terrainData.terrainLayers.Length != 3)
InitializeTerrainLayers();
if (!texturesInitialized)
InitializeTextures();
UpdateHeightmapTexture();
UpdateTerrainLayerTiling();
// Set up the compute shader
int kernelHandle = textureApplierShader.FindKernel("CSMain");
textureApplierShader.SetTexture(kernelHandle, "Result", splatmapRT);
textureApplierShader.SetTexture(kernelHandle, "HeightMap", heightmapTexture);
textureApplierShader.SetFloat("slopeThreshold", slopeThreshold);
textureApplierShader.SetFloat("highAltitudeThreshold", 1 - highAltitudeThreshold);
textureApplierShader.SetFloat("slopeBlendRange", slopeBlendRange);
textureApplierShader.SetFloat("altitudeBlendRange", altitudeBlendRange);
textureApplierShader.SetFloat("terrainHeight", terrainData.size.y);
textureApplierShader.SetFloat("lowestPoint", Utilities.LowestPoint(Terrain.activeTerrain));
textureApplierShader.SetFloat("highestPoint", Utilities.HighestPoint(Terrain.activeTerrain));
// Dispatch the compute shader
textureApplierShader.Dispatch(kernelHandle, terrainData.alphamapWidth / 8, terrainData.alphamapHeight / 8, 1);
// Set the active render texture
RenderTexture previousActive = RenderTexture.active;
RenderTexture.active = splatmapRT;
// Apply the splatmap directly to the terrain
try
{
RectInt sourceRect = new RectInt(0, 0, splatmapRT.width, splatmapRT.height);
Vector2Int destPos = new Vector2Int(0, 0);
terrainData.CopyActiveRenderTextureToTexture(
TerrainData.AlphamapTextureName, // dest
0, // mipLevel
sourceRect, // sourceRect
destPos, // destPos
false // allowDelayedCPUSync
);
// Force the terrain to update
Terrain.activeTerrain.Flush();
}
finally
{
// Restore the previous active render texture
RenderTexture.active = previousActive;
}
}