Hi All,
Out of curiosity I was reading the documentation about IJobParallelFor and decided to try updating the terrain realtime with a generated perlin noise texture.
I’ve tweaked the code quite a bit, and it shows good 30FPS but I feel it could be improved somehow. Any thoughts?
Below I’m sharing a video and the results of the logs in the code:
- Job elapsed time: 12
- List item
- Loop elapsed time: 17
- Time elapsed for adding up layers: 30ms (both above)
- SetHeights ellapsed time: 58
using System.Diagnostics; using Unity.Jobs; using UnityEngine; using Unity.Collections; using Debug = UnityEngine.Debug; using Unity.Mathematics; using Unbegames.Noise; using System.Collections; using Unity.Burst; using System.Threading.Tasks;
public class TerrainGenerator : MonoBehaviour
{
public float speed = 1f;
public float translationX = 0f;
public float translationY = 0f;
public float layer1HeightMultiplier = 3;
public float layer2HeightMultiplier = 3;
public float layer1ScaleMultiplier = 0.5f;
public float layer2ScaleMultiplier = 3f;
public float scale = 0.5f;
private Terrain terrain;
private Coroutine _coroutine;
// Start is called before the first frame update
void Start()
{
_coroutine = StartCoroutine(DrawTerrain());
}
private IEnumerator DrawTerrain()
{
terrain = GetComponent<Terrain>();
var detailWidth = terrain.terrainData.detailWidth;
var detailHeight = terrain.terrainData.detailHeight;
while (true)
{
var heights = GenerateTerrain(detailWidth, detailHeight);
var sw = new Stopwatch();
sw.Start();
//terrain.terrainData.SetHeightsDelayLOD(0, 0, heights);
//terrain.terrainData.SyncHeightmap();
terrain.terrainData.SetHeights(0, 0, heights);
sw.Stop();
Debug.Log($"SetHeights ellapsed time: {sw.ElapsedMilliseconds}");
translationY += Time.deltaTime * speed;
translationX += Time.deltaTime * speed;
yield return new WaitForSeconds(0.10f);
}
}
private void OnDestroy()
{
StopCoroutine(_coroutine);
}
private float[,] GenerateTerrain(int detailWidth, int detailHeight)
{
var sw = new Stopwatch();
sw.Reset();
sw.Start();
var heightsLayer1 = layer(detailWidth, detailHeight, layer1ScaleMultiplier, layer1HeightMultiplier);
sw.Stop();
Debug.Log($"Time elapsed for adding up layers: {sw.ElapsedMilliseconds}ms");
return heightsLayer1;
}
private float[,] layer(int detailWidth, int detailHeight, float scaleMultiplier, float heightMultiplier)
{
var sw = new Stopwatch();
sw.Start();
float[,] result = new float[detailWidth, detailHeight];
var heights = new NativeArray<float3>(detailHeight * detailWidth, Allocator.Persistent);
var modifiers = new NativeArray<Vector2>(1, Allocator.Persistent);
modifiers[0] = new Vector2(scale, heightMultiplier);
var dimensions = new NativeArray<Vector2>(1, Allocator.Persistent);
dimensions[0] = new Vector2(detailWidth, detailHeight);
var translation = new NativeArray<Vector2>(1, Allocator.Persistent);
translation[0] = new Vector2(translationX, translationY);
var job = new LayerJob
{
dimensions = dimensions,
heights = heights,
modifiers = modifiers,
translation = translation
};
int size = heights.Length;
JobHandle jobHandle = job.Schedule(size, 32);
jobHandle.Complete();
sw.Stop();
Debug.Log($"Job elapsed time: {sw.ElapsedMilliseconds}");
sw.Reset();
sw.Start();
for (var i = 0; i < size; i++)
{
result[(int)heights<em>.x, (int)heights_.y] = heights*.z;*_</em>
}
sw.Stop();
Debug.Log($“Loop elapsed time: {sw.ElapsedMilliseconds}”);
//foreach (var height in heights)
//{
// result[(int)height.x, (int)height.y] = height.z;
//};
heights.Dispose();
dimensions.Dispose();
translation.Dispose();
modifiers.Dispose();
return result;
}
[BurstCompile]
struct LayerJob : IJobParallelFor
{
[ReadOnly]
public NativeArray dimensions;
[ReadOnly]
public NativeArray modifiers;
[ReadOnly]
internal NativeArray translation;
public NativeArray heights;
public void Execute(int index)
{
var detailWidth = dimensions[0].x;
var detailHeight = dimensions[0].y;
var scale = modifiers[0].x;
var heightMultiplier = modifiers[0].y;
var translationX = translation[0].x;
var translationY = translation[0].y;
var x = (int)Mathf.Floor(index % detailWidth);
var y = (int)Mathf.Floor(index / detailWidth);
//var perlin = new Perlin3D();
//heights[index] = new float3(x, y, perlin.GetValue(0, new float3(x * scale + translationX, y * scale + translationY, 0)) * heightMultiplier);
heights[index] = new float3(x, y, noise.srnoise(new float2(x * scale + translationX, y * scale)) * heightMultiplier);
//heights[index] = new float3(x, y, Mathf.PerlinNoise(x * scale + translationX, y * scale + translationY) * heightMultiplier);
}
}
}