簡體   English   中英

如何在多個柏林噪聲塊之間平滑?

[英]How to smooth between multiple perlin noise chunks?

塊之間的平滑

所以我一直在統一開發一款游戲,並希望將我的世界從 150x150 map 擴展到看似無限的程序世界。 我的計划是使用 Perlin Noise 作為基礎,並使用 0-1 的不同值來確定地形類型。 我遇到的問題是,當我抽出我的塊並相應地偏移我的塊時,我的塊沒有正確排列,這打破了無限世界的幻覺。

(在這里看到)

碎塊


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];
    }
}

世界生成器.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;

一切都正確對齊,但柏林噪音只是重復。

什么是我在塊之間平滑以使一切匹配的最佳方法? 有沒有更好的方法來寫這個?

編輯:


感謝 Draykoon D 的幫助! 如果有人需要,這里是更新的信息和指向 pastebin 上更新腳本的鏈接!

例子

這是任何想要它的人的更新代碼:** WorldGenerator.cs**


https://pastebin.com/3BjLy5Hk

** WorldGenerator.cs**


https://pastebin.com/v3JJte3N

希望有幫助!

您正在尋找的關鍵詞是可平鋪的。

但我要告訴你一個好消息,噪聲 function (例如 perlin)本質上是周期性的。 因此,不要將 ChunckSize * ChunkSize 稱為噪音 function 您應該只調用一次然后除以結果。

我會建議你閱讀這個優秀的教程:

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 作業,它可能不會很快。 這是我創建/使用/推薦的,但它肯定不是唯一的選擇,請注意所有這些噪音的范圍是 -1 到 1 而不是 0 到 1,所以如果這比value=value*0.5+0.5;重要value=value*0.5+0.5; 重新調整它。

  2. 現在已經不礙事了,要解決您的問題,您需要將塊和生成的概念分開。 總的來說,這是一個好主意,我始終相信盡可能從游戲玩法中隱藏后端實現細節(例如塊)(例如避免可見邊界)。 每次生成塊時,您應該在世界中找到它的起始坐標,以便坐標與 rest 無縫銜接。 例如,如果塊是 128x128,那么從 (0, 0) 開始的塊應該有起始坐標 (0, 0),那么從 (0, 1) 開始的塊應該有起始坐標 (0, 128)。 只有這樣,通過乘以所需的頻率將世界坐標轉換為噪聲坐標。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM