私は今日、ランダムな地形生成がどのように行われる傾向があるかを調査するために数時間を費やし、プラズマ フラクタル (中点変位とダイヤモンド スクエア アルゴリズム) について読んだ後、実装を試してみることにしました。私の結果は実際にはひどいものではありませんでしたが、これらの恐ろしい正方形/線/グリッドタイプのアーティファクトがあり、取り除くことができないようです!
グレー スケール イメージとしてレンダリングすると、高さマップは次のようになります。
明らかに、これにはかなりの量のコードが含まれていますが、関連するものだけを投稿しようとします。たとえば、テクスチャに変換するコードを投稿していませんが、高さの配列を滑らかなグラデーションで埋めようとしただけで、テクスチャが正常に表示されることを心配する必要はありません:)
まず、マップの 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 に修正しました