Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice

Question Problems with texture generation using perlin noise

Discussion in 'Getting Started' started by Fox671, Mar 5, 2022.

  1. Fox671

    Fox671

    Joined:
    Jan 1, 2022
    Posts:
    2
    Hello, I'm using perlin noise to generate a texture, and then use it to generate an infinite world. I think by attaching the code, everything will be clearer.
    I am using three scripts:
    Noise
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public static class Noise {
    5.  
    6.     public static float[,] GenerateNoiseMap(int mapWidth, int mapHeight, int seed, float scale, int octaves, float persistance, float lacunarity, Vector2 offset) {
    7.         float[,] noiseMap = new float[mapWidth, mapHeight];
    8.  
    9.         System.Random prng = new System.Random(seed);
    10.         Vector2[] octaveOffsets = new Vector2[octaves];
    11.         for (int i = 0; i < octaves; i++) {
    12.             float offsetX = prng.Next(-100000, 100000) + offset.x;
    13.             float offsetY = prng.Next(-100000, 100000) - offset.y;
    14.             octaveOffsets[i] = new Vector2 (offsetX, offsetY);
    15.         }
    16.  
    17.         if (scale <= 0) {
    18.             scale = 0.0001f;
    19.         }
    20.  
    21.         float maxNoiseHeight = float.MinValue;
    22.         float minNoiseHeight = float.MaxValue;
    23.  
    24.         float halfWidth = mapWidth / 2f;
    25.         float halfHeight = mapHeight / 2f;
    26.  
    27.  
    28.         for (int y = 0; y < mapHeight; y++) {
    29.             for (int x = 0; x < mapWidth; x++) {
    30.      
    31.                 float amplitude = 1;
    32.                 float frequency = 1;
    33.                 float noiseHeight = 0;
    34.  
    35.                 for (int i = 0; i < octaves; i++) {
    36.                     float sampleX = (x - halfWidth + octaveOffsets[i].x) / scale * frequency;
    37.                     float sampleY = (y - halfHeight + octaveOffsets[i].y) / scale * frequency ;
    38.  
    39.                     float perlinValue = Mathf.PerlinNoise(sampleX, sampleY) * 2 - 1;
    40.                     noiseHeight += perlinValue * amplitude;
    41.  
    42.                     amplitude *= persistance;
    43.                     frequency *= lacunarity;
    44.                 }
    45.  
    46.                 if (noiseHeight > maxNoiseHeight) {
    47.                     maxNoiseHeight = noiseHeight;
    48.                 } else if (noiseHeight < minNoiseHeight) {
    49.                     minNoiseHeight = noiseHeight;
    50.                 }
    51.                 noiseMap [x, y] = noiseHeight;
    52.             }
    53.         }
    54.  
    55.         for (int y = 0; y < mapHeight; y++) {
    56.             for (int x = 0; x < mapWidth; x++) {
    57.                 noiseMap[x, y] = Mathf.InverseLerp (minNoiseHeight, maxNoiseHeight, noiseMap[x, y]);
    58.             }
    59.         }
    60.  
    61.         return noiseMap;
    62.     }
    63. }
    MapGenerator
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class MapGenerator : MonoBehaviour {
    6.  
    7.     public const int mapChunkSize = 32;
    8.     public const int tileSize = 16;
    9.     [Header("Noise Parameters")]
    10.     public float noiseScale;
    11.     public int octaves;
    12.     [Range(0,1)]
    13.     public float persistance;
    14.     public float lacunarity;
    15.     public int seed;
    16.     public Vector2 offset;
    17.  
    18.     public TerrainType[] regions;
    19.     public Texture2D mainTexture;
    20.  
    21.     public MapData GenerateMapData(Vector2 centre) {
    22.         float[,] noiseMap = Noise.GenerateNoiseMap(mapChunkSize, mapChunkSize, seed, noiseScale, octaves, persistance, lacunarity, centre + offset);
    23.  
    24.         Texture2D texture = new Texture2D(mapChunkSize * tileSize, mapChunkSize * tileSize);
    25.         texture.filterMode = FilterMode.Point;
    26.         texture.wrapMode = TextureWrapMode.Clamp;
    27.         for (int x = 0; x < mapChunkSize; x++) {
    28.             for (int y = 0; y < mapChunkSize; y++) {
    29.                 float currentHeight = noiseMap[x, y];
    30.                 for (int i = 0; i < regions.Length; i++) {
    31.                     if (currentHeight <= regions[i].height) {
    32.                         texture.SetPixels(x * tileSize, y * tileSize, tileSize, tileSize, mainTexture.GetPixels(regions[i].coord.x, regions[i].coord.y, tileSize, tileSize));
    33.                         break;
    34.                     }
    35.                 }
    36.             }
    37.         }
    38.         texture.Apply();
    39.  
    40.         return new MapData(noiseMap, texture);
    41.     }
    42. }
    43.  
    44. [System.Serializable]
    45. public struct TerrainType {
    46.     public string name;
    47.     public float height;
    48.     public Vector2Int coord;
    49. }
    50.  
    51. public struct MapData {
    52.     public readonly float[,] heightMap;
    53.     public readonly Texture2D texture;
    54.  
    55.     public MapData (float[,] heightMap, Texture2D texture) {
    56.         this.heightMap = heightMap;
    57.         this.texture = texture;
    58.     }
    59. }
    EndlessTerrain

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class EndlessTerrain : MonoBehaviour {
    6.  
    7.     public const float maxViewDst = 64;
    8.     int mapChunkSize;
    9.     public Transform viewer;
    10.  
    11.     public static Vector2 viewerPosition;
    12.     int chunksVisibleInViewDst;
    13.  
    14.     static MapGenerator mapGenerator;
    15.  
    16.     Dictionary<Vector2, TerrainChunk> terrainChunkDictionary = new Dictionary<Vector2, TerrainChunk>();
    17.     List<TerrainChunk> terrainChunksVisibleLastUpdate = new List<TerrainChunk>();
    18.  
    19.     void Start() {
    20.         mapGenerator = FindObjectOfType<MapGenerator>();
    21.  
    22.         mapChunkSize = MapGenerator.mapChunkSize;
    23.         chunksVisibleInViewDst = Mathf.RoundToInt(maxViewDst / mapChunkSize);
    24.     }
    25.  
    26.     void Update() {
    27.         viewerPosition = new Vector2 (viewer.position.x, viewer.position.y);
    28.         UpdateVisibleChunks ();
    29.     }
    30.  
    31.     void UpdateVisibleChunks() {
    32.  
    33.         for (int i = 0; i < terrainChunksVisibleLastUpdate.Count; i++) {
    34.             terrainChunksVisibleLastUpdate.SetVisible(false);
    35.         }
    36.         terrainChunksVisibleLastUpdate.Clear();
    37.        
    38.         int currentChunkCoordX = Mathf.RoundToInt(viewerPosition.x);
    39.         int currentChunkCoordY = Mathf.RoundToInt(viewerPosition.y);
    40.  
    41.         for (int yOffset = -chunksVisibleInViewDst; yOffset <= chunksVisibleInViewDst; yOffset++) {
    42.             for (int xOffset = -chunksVisibleInViewDst; xOffset <= chunksVisibleInViewDst; xOffset++) {
    43.                 Vector2 viewedChunkCoord = new Vector2(currentChunkCoordX + xOffset, currentChunkCoordY + yOffset);
    44.  
    45.                 if (terrainChunkDictionary.ContainsKey(viewedChunkCoord)) {
    46.                     terrainChunkDictionary[viewedChunkCoord].UpdateTerrainChunk();
    47.                     if (terrainChunkDictionary[viewedChunkCoord].IsVisible()) {
    48.                         terrainChunksVisibleLastUpdate.Add(terrainChunkDictionary[viewedChunkCoord]);
    49.                     }
    50.                 } else {
    51.                     terrainChunkDictionary.Add(viewedChunkCoord, new TerrainChunk(mapChunkSize, viewedChunkCoord, transform));
    52.                 }
    53.             }
    54.         }
    55.     }
    56.  
    57.     public class TerrainChunk {
    58.  
    59.         public GameObject chunkObject;
    60.         Vector2 position;
    61.         Bounds bounds;
    62.  
    63.         SpriteRenderer spriteRenderer;
    64.         MapData mapData;
    65.  
    66.         public TerrainChunk(int size, Vector2 coord, Transform parent) {
    67.             position = coord * size;
    68.             bounds = new Bounds(coord, Vector2.one);
    69.             Vector3 positionV3 = new Vector3(coord.x, coord.y, 0);
    70.             mapData = mapGenerator.GenerateMapData(position);
    71.  
    72.             chunkObject = new GameObject("Terrain Chunk");
    73.             chunkObject.transform.position = positionV3;
    74.             spriteRenderer = chunkObject.AddComponent<SpriteRenderer>();
    75.             spriteRenderer.sprite = Sprite.Create(mapData.texture, new Rect(0, 0, mapData.texture.width, mapData.texture.height), new Vector2(0.5f, 0.5f), 16);
    76.  
    77.             chunkObject.transform.localScale = Vector3.one / size;
    78.             chunkObject.transform.parent = parent;
    79.             SetVisible(false);
    80.         }
    81.  
    82.         public void UpdateTerrainChunk() {
    83.             float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
    84.             bool visible = viewerDstFromNearestEdge <= maxViewDst;
    85.             SetVisible(visible);
    86.         }
    87.  
    88.         public void SetVisible(bool visible) {
    89.             chunkObject.SetActive(visible);
    90.         }
    91.  
    92.         public bool IsVisible() {
    93.             return chunkObject.activeSelf;
    94.         }
    95.     }
    96. }

    So what's the problem? The problem is that for each chunk I generate my own texture by changing the Vector2 offset value. And for some reason I don't understand, the offset value works strangely in Noise, because instead of a smooth transition between chunks, I get completely unrelated chunk textures. If you have any questions, ask, I will answer all.
     
    Last edited: Mar 6, 2022