0

私は今日、ランダムな地形生成がどのように行われる傾向があるかを調査するために数時間を費やし、プラズマ フラクタル (中点変位とダイヤモンド スクエア アルゴリズム) について読んだ後、実装を試してみることにしました。私の結果は実際にはひどいものではありませんでしたが、これらの恐ろしい正方形/線/グリッドタイプのアーティファクトがあり、取り除くことができないようです!

グレー スケール イメージとしてレンダリングすると、高さマップは次のようになります。

明らかに、これにはかなりの量のコードが含まれていますが、関連するものだけを投稿しようとします。たとえば、テクスチャに変換するコードを投稿していませんが、高さの配列を滑らかなグラデーションで埋めようとしただけで、テクスチャが正常に表示されることを心配する必要はありません:)

まず、マップの 4 つのコーナーを 0 から 1 の間のランダムな値に設定してから、再帰的な変位アルゴリズムを開始します。

    public void GenerateTerrainLayer()
    {  
        //set the four corners of the map to have random values
        TerrainData[0, 0] = (float)RandomGenerator.NextDouble();
        TerrainData[GenSize, 0] = (float)RandomGenerator.NextDouble();
        TerrainData[0, GenSize] = (float)RandomGenerator.NextDouble();
        TerrainData[GenSize, GenSize] = (float)RandomGenerator.NextDouble();

        //begin midpoint displacement algorithm...
        MidPointDisplace(new Vector2_I(0, 0), new Vector2_I(GenSize, 0), new Vector2_I(0, GenSize), new Vector2_I(GenSize, GenSize));
    }

TerrainData は単純に float* の 2D 配列です。Vector2_I は、私自身の整数ベクトル クラスです。最後の 4 つの関数は、再帰関数である MidPointDisplace、2 つのデータ値を平均してノイズを追加する CalculateTerrainPointData、4 つのデータ値を平均してノイズを追加し、わずかに高いスケール値を持つ CalculateTerrainPointData2 (中心点にのみ使用されます) です。 atm は単なるランダム ノイズであり、パーリンなどの実際のノイズではありません。次のようになります。

   private void MidPointDisplace(Vector2_I topleft, Vector2_I topright, Vector2_I bottomleft, Vector2_I bottomright)
    {
        //check size of square working on.. if its shorter than a certain amount stop the algo, we've done enough
        if (topright.X - topleft.X < DisplacementMaxLOD)
        {
            return;
        }


        //calculate the positions of all the middle points for the square that has been passed to the function
        Vector2_I MidLeft, MidRight, MidTop, MidBottom, Center;

        MidLeft.X = topleft.X;
        MidLeft.Y = topleft.Y + ((bottomleft.Y - topleft.Y) / 2);

        MidRight.X = topright.X;
        MidRight.Y = topright.Y + ((bottomright.Y - topright.Y) / 2);

        MidTop.X = topleft.X + ((topright.X - topleft.X) / 2);
        MidTop.Y = topleft.Y;

        MidBottom.X = bottomleft.X + ((bottomright.X - bottomleft.X) / 2);
        MidBottom.Y = bottomleft.Y;

        Center.X = MidTop.X;
        Center.Y = MidLeft.Y;

        //collect the existing data from the corners of the area passed to algo
        float TopLeftDat, TopRightDat, BottomLeftDat, BottomRightDat;

        TopLeftDat = GetTerrainData(topleft.X, topleft.Y);          
        TopRightDat = GetTerrainData(topright.X, topright.Y);          
        BottomLeftDat = GetTerrainData(bottomleft.X, bottomleft.Y);          
        BottomRightDat = GetTerrainData(bottomright.X, bottomright.Y);

        //and the center


        //adverage data and insert for midpoints..
        SetTerrainData(MidLeft.X, MidLeft.Y, CalculateTerrainPointData(TopLeftDat, BottomLeftDat, MidLeft.X, MidLeft.Y));
        SetTerrainData(MidRight.X, MidRight.Y, CalculateTerrainPointData(TopRightDat, BottomRightDat, MidRight.X, MidRight.Y));
        SetTerrainData(MidTop.X, MidTop.Y, CalculateTerrainPointData(TopLeftDat, TopRightDat, MidTop.X, MidTop.Y));
        SetTerrainData(MidBottom.X, MidBottom.Y, CalculateTerrainPointData(BottomLeftDat, BottomRightDat, MidBottom.X, MidBottom.Y));
        SetTerrainData(Center.X, Center.Y, CalculateTerrainPointData2(TopLeftDat, TopRightDat, BottomLeftDat, BottomRightDat, Center.X, Center.Y));


        debug_displacement_iterations++;

        //and recursively fire off new calls to the function to do the smaller squares
        Rectangle NewTopLeft = new Rectangle(topleft.X, topleft.Y, Center.X - topleft.X, Center.Y - topleft.Y);
        Rectangle NewTopRight = new Rectangle(Center.X, topright.Y, topright.X - Center.X, Center.Y - topright.Y);
        Rectangle NewBottomLeft = new Rectangle(bottomleft.X, Center.Y, Center.X - bottomleft.X, bottomleft.Y - Center.Y);
        Rectangle NewBottomRight = new Rectangle(Center.X , Center.Y, bottomright.X - Center.X, bottomright.Y - Center.Y);

        MidPointDisplace(new Vector2_I(NewTopLeft.Left, NewTopLeft.Top), new Vector2_I(NewTopLeft.Right, NewTopLeft.Top), new Vector2_I(NewTopLeft.Left, NewTopLeft.Bottom), new Vector2_I(NewTopLeft.Right, NewTopLeft.Bottom));
        MidPointDisplace(new Vector2_I(NewTopRight.Left, NewTopRight.Top), new Vector2_I(NewTopRight.Right, NewTopRight.Top), new Vector2_I(NewTopRight.Left, NewTopRight.Bottom), new Vector2_I(NewTopRight.Right, NewTopRight.Bottom));
        MidPointDisplace(new Vector2_I(NewBottomLeft.Left, NewBottomLeft.Top), new Vector2_I(NewBottomLeft.Right, NewBottomLeft.Top), new Vector2_I(NewBottomLeft.Left, NewBottomLeft.Bottom), new Vector2_I(NewBottomLeft.Right, NewBottomLeft.Bottom));
        MidPointDisplace(new Vector2_I(NewBottomRight.Left, NewBottomRight.Top), new Vector2_I(NewBottomRight.Right, NewBottomRight.Top), new Vector2_I(NewBottomRight.Left, NewBottomRight.Bottom), new Vector2_I(NewBottomRight.Right, NewBottomRight.Bottom));

    }

    //helper function to return a data value adveraged from two inputs, noise value added for randomness and result clamped to ensure a good value
    private float CalculateTerrainPointData(float DataA, float DataB, int NoiseX, int NoiseY)
    {
         return MathHelper.Clamp(((DataA + DataB) / 2.0f) + NoiseFunction(NoiseX, NoiseY), 0.0f, 1.0f) * 1.0f;
    }

    //helper function to return a data value adveraged from four inputs, noise value added for randomness and result clamped to ensure a good value
    private float CalculateTerrainPointData2(float DataA, float DataB, float DataC, float DataD, int NoiseX, int NoiseY)
    {
        return MathHelper.Clamp(((DataA + DataB + DataC + DataD) / 4.0f) + NoiseFunction(NoiseX, NoiseY), 0.0f, 1.0f) * 1.5f;
    }

    private float NoiseFunction(int x, int y)
    {
        return (float)(RandomGenerator.NextDouble() - 0.5) * 0.5f;
    }

時間を割いて見てくれてありがとう - 誰かがこのグリッドのようなパターンがどこから現れているか知っていることを願っています:)

*編集 - 間違って int を書きました。float に修正しました

4

3 に答える 3

2

あなたのコードで 3 つの問題を特定しました。(そのうちの 2 つは関連しています)

各ステップでランダム性を縮小しません。各ステップでランダム性を減らす必要があります。そうしないと、ホワイト(っぽい)ノイズが発生します。係数を選択し(私の目的では0.5-0.7でうまくいきました)、各再帰で削減にアルファを掛け、生成された乱数をその係数でスケーリングします。

ひし形と四角いステップを入れ替えました。最初にダイヤモンド、次に正方形。逆は不可能です (次を参照)。

正方形のステップは、一方向のポイントのみを使用します。これはおそらく、あなたが話している長方形の構造を引き起こします. 正方形は、4 つの辺すべての値を平均する必要があります。これは、正方形のステップがダイヤモンドのステップによって生成されるポイントに依存することを意味します。また、現在見ている長方形のひし形のステップだけでなく、その隣の長方形も同様です。マップ外の値については、ラップするか、固定値を使用するか、平均 3 つの値のみを使用できます。

于 2016-04-23T09:05:23.013 に答える
1

あなたの実装には問題があります。各反復CalculateTerrainPointDataでの結果を縮小していません。NoiseFunction

中点変位アルゴリズムのこの説明を参照してください。

  • 単一の水平線分から始めます。
    • 十分な回数繰り返します。
      • シーン内の各線分について繰り返します。
        • 線分の中点を見つけます。
        • Y の中点をランダムな量だけ移動します。
        • 乱数の範囲を減らします。

あまり変更せずにコードでこれを行う簡単な方法は、(デフォルトは 1.0f に設定されている) および;にscaleパラメーターを追加することです。の結果を乗算するために使用します。への再帰呼び出しごとに削減します。MidPointDisplaceCalculateTerrainPointDataCalculateTerrainPointDataNoiseFunctionMidPointDisplace(..., 0.5f * scale)

それがあなたの画像が間違って見える唯一の原因なのか、それとも他の問題があるのか​​ はわかりません.

于 2013-02-18T15:09:50.193 に答える