如何在多个Perlin噪声块之间平滑?

问题描述

在块之间平滑化

因此,我一直在致力于统一游戏,并希望将我的世界从150x150的地图扩展到看似无限的程序世界。我的计划是使用Perlin Noise作为基准,并使用0-1中的不同值来确定地形类型。我遇到的问题是,当我抽出块并相应地偏移时,块无法正确对齐,这打破了无限世界的幻想。

在这里看到)

broken chunks


WorldChunk.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Unity.Mathematics;

[System.Serializable]
public class WorldChunk
{
    public int2 Position;
    public int[,] Data;
    public float[,] Sample;

    public WorldChunk(int chunkSize = 16){
        Data = new int[chunkSize,chunkSize];
        Sample = new float[chunkSize,chunkSize];
    }
}

WorldGenerator.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Unity.Mathematics;

public class WorldGenerator : MonoBehavIoUr
{

    // Base World Data
    public int ChunkSize = 75;
    public string Seed = "";
    [Range(1f,40f)]
    public float PerlinScale = 10f;
    // Pseudo Random Number Generator
    private System.Random pseudoRandom;

    // Chunk Data Split into Sections (Each Chunk having Coords (x,y))
    public Dictionary<string,WorldChunk> chunks = new Dictionary<string,WorldChunk>();


    //============================================================
    // Set warm-up Data
    //============================================================
    private void Awake() {
        // Get/Create Seed
        if (Seed == ""){
            Seed = GeneraterandomSeed();
        }
        // Get Random Number Generator
        pseudoRandom = new System.Random(Seed.GetHashCode());
        // Using to Clear while Making Test Adjustments
        chunks.Clear();
        // Generate Starting Chunk
        for (int x = -1; x <= 1; x++)
        {
            for (int y = -1; y <= 1; y++)
            {
                // Draw Test Chunks
                GenerateChunk(x,y);
            }
        }
    }

    //============================================================
    // Generation Code
    //============================================================

    // ===
    //  Create New Chunks
    // ===
    public void GenerateChunk(int x,int y){
        // Set Key to use
        string key = $"{x},{y}";
        // Check if key exists if not Generate New Chunk
        if (!chunks.ContainsKey(key)){
            // Add Chunk,Set Position in chunk grid (for calling and block data later),Then Generate data
            chunks.Add(key,new WorldChunk(ChunkSize));
            chunks[key].Position = new int2(x,y);
            GenerateChunkData(chunks[key]);
        }
    }

    // ===
    //  Fill Chunks with Perlin Data
    // ===
    private void GenerateChunkData(WorldChunk chunk){
        // Set Offsets
        float xOffset = (float)chunk.Position.x * ChunkSize;
        float yOffset = (float)chunk.Position.y * ChunkSize;
        // Set Data to Chunk
        for (int x = 0; x < ChunkSize; x++)
        {
            for (int y = 0; y < ChunkSize; y++)
            {
                // Get Perlin Map
                float px = (float)(x) / ChunkSize * PerlinScale + xOffset;
                float py = (float)(y) / ChunkSize * PerlinScale + yOffset;

                // Set Temp Sample For Testing (This will change for Map Data (Hills and Water) later)
                chunk.Sample[x,y] = Mathf.PerlinNoise(px,py);
            }
        }
    }

    // ===
    //  Generate Random Seed of Length
    // ===
    private string GeneraterandomSeed(int maxCharamount = 10,int minCharamount = 10){
        //Set Characters To Pick from
        const string glyphs= "abcdefghijklmnopqrstuvwxyz0123456789";
        //Set Length from min to max
        int charamount = UnityEngine.Random.Range(minCharamount,maxCharamount);
        // Set output Variable
        string output = "";
        // Do Random Addition
        for(int i=0; i<charamount; i++)
        {
            output += glyphs[UnityEngine.Random.Range(0,glyphs.Length)];
        }
        // Output New Random String
        return output;
    }

    //============================================================
    // Draw Example
    //============================================================

    private void OnDrawGizmos() {
        // Do this because I'm lazy and don't want to draw pixels to generated Sprites
        Awake();
        // For Each WorldChunk in the chunk Data
        foreach (WorldChunk c in chunks.Values)
        {
            // Check if it exists (Foreach is stupid sometimes... When live editing)
            if (c != null){
                // Get World Positions for Chunk (Should probably Set to a Variable in the Chunk Data)
                Vector3 ChunkPosition = new Vector3(c.Position.x * ChunkSize,c.Position.y * ChunkSize);

                // For Each X & For Each Y in the chunk
                for (int x = 0; x < ChunkSize; x++)
                {
                    for (int y = 0; y < ChunkSize; y++)
                    {
                        // Get Cell position
                        Vector3 cellPos = new Vector3((ChunkPosition.x - ChunkSize/2f) + x,(ChunkPosition.y - ChunkSize/2f) + y);
                        // Get Temp Sample and set to color
                        float samp = c.Sample[x,y];
                        Gizmos.color = new Color(samp,samp,samp);
                        // Draw Tile as Sample black or white.
                        Gizmos.DrawCube(cellPos,Vector3.one);
                    }
                }

                // Size for Cubes
                Vector3 size = new Vector3(ChunkSize,ChunkSize,1f);
                // Set Color Opaque Green
                Gizmos.color = new Color(0f,1f,0f,0.25f);
                // Draw Chunk Borders (disable to show issue)
                // Gizmos.DrawWireCube(ChunkPosition,size);
                
            }
        }
        
    }
}

我想指出何时使用:

// Get Perlin Map
float px = (float)(x + xOffset) / ChunkSize * PerlinScale;
float py = (float)(y + yOffset) / ChunkSize * PerlinScale;

代替

// Get Perlin Map
float px = (float)(x) / ChunkSize * PerlinScale + xOffset;
float py = (float)(y) / ChunkSize * PerlinScale + yOffset;

一切都正确对齐,但Perlin噪声只是重复出现。

对我来说,使这些块之间平滑以使所有内容匹配的最佳方法是什么? 有没有更好的方法可以写这个?

编辑:


感谢您的帮助Draykoon D!这是更新的信息,并在任何人需要的情况下链接到pastebin上更新的脚本!

example

以下是任何人的更新代码: ** WorldGenerator.cs **


https://pastebin.com/3BjLy5Hk

** WorldGenerator.cs **


https://pastebin.com/v3JJte3N

希望有帮助!

解决方法

您要查找的关键字是可平铺的。

但是我对您来说是个好消息,像Perlin这样的噪音功能本质上是周期性的。 因此,不要将ChunckSize * ChunkSize称为噪声函数,而应该只调用一次然后除以结果。

我建议您阅读这个出色的教程:

https://www.scratchapixel.com/lessons/procedural-generation-virtual-worlds/procedural-patterns-noise-part-1/creating-simple-1D-noise

,
  1. 请勿使用Perlin杂讯。偏向45度和90度方向。您的山丘都与这些山峰对齐,并且没有沿着更有趣的各种方向定向。您可以使用Unity.mathematics.noise.snoise(float2),但是它的重复周期很小,如果您不使用Unity Burst作业,则重复周期可能不会很快。 this is what I created/use/recommend,但肯定不是唯一的选择!请注意,所有这些噪声的范围是-1到1,而不是0到1,因此,如果重要的话,则value=value*0.5+0.5;来重新缩放它。

  2. 现在这已成为不可能,要解决您的问题,您需要将块和生成的概念分开。一般而言,这是个好主意,我一直相信尽可能在游戏中隐藏后端实现细节(例如,块)(例如,避免可见的边界)。每次生成块时,都应该在世界上找到它的开始坐标,以便坐标与其余部分无缝连续。例如,如果块为128x128,则从(0,0)开始的块应具有起始坐标(0,0),然后从(0,1)开始的块应具有起始坐标(0,128)。只有这样,才能将世界坐标乘以所需的频率。