0

今天我花了几个小时研究随机地形生成是如何完成的,在阅读了等离子分形(中点位移和菱形正方形算法)之后,我决定尝试实现一个。我的结果实际上并不可怕,但我有这些可怕的方形/线条/网格类型的人工制品,我似乎无法摆脱!

当呈现为灰度图像时,我的高度图看起来像: 高度图 http://sphotos-d.ak.fbcdn.net/hphotos-ak-ash3/535816_10151739010123327_225111175_n.jpg

显然这涉及到相当多的代码,但我会尝试发布仅相关的代码。例如,我没有发布将其转换为纹理的代码,但别担心,我已经尝试用平滑渐变填充我的高度数组,并且纹理很好:)

我首先将地图的四个角设置为 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 只是一个 2D 浮点数组*。Vector2_I 只是我自己的整数向量类。最后四个函数是 MidPointDisplace,它是递归函数,CalculateTerrainPointData 平均 2 个数据值并添加一些噪声,CalculateTerrainPointData2 平均 4 个数据值并添加一些噪声并具有稍高的比例值(仅用于中心点),最后我的噪声函数 atm 只是一些随机噪声,而不是像 perlin 等真正的噪声。它们看起来像这样:

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

好的,感谢您花时间查看-希望有人知道这种网格状图案是从哪里出现的:)

*edit - 意外写入整数,更正为浮点数

4

3 回答 3

2

我在您的代码中发现了 3 个问题。(其中2个是相关的)

您不会在每一步中缩小随机性。每一步都必须减少随机性。否则你会得到白(-ish)噪音。您选择一个因子(0.5-0.7 对我的目的来说效果很好)并在每次递归中将减少乘以 alpha 并将生成的随机数乘以该因子。

你交换了菱形和方步。首先是菱形,然后是正方形。反过来是不可能的(见下)。

Your square step uses only points in one direction. This one probably causes the rectangular structures you are talking about. The squares must average the values to all four sides. This means that the square step depends on the point generated by the diamond step. And not only the diamond step of the rectangle you are currently looking at, also of the rectangles next to it. For values outside of the map, you can either wrap, use a fixed value or only average 3 values.

于 2016-04-23T09:05:23.013 回答
1

我在您的CalculateTerrainPointData实现中看到了一个问题:您没有缩小NoiseFunction每次迭代的结果。

请参阅中点位移算法的描述

  • 从单个水平线段开始。
    • 重复足够多的次数:
      • 在场景中的每个线段上重复:
        • 找到线段的中点。
        • 将 Y 中的中点替换为随机量。
        • 缩小随机数的范围。

在代码中不做太多更改的快速方法是添加一些scale参数MidPointDisplace(默认设置为 1.0f)和CalculateTerrainPointData; 用它CalculateTerrainPointData来乘以NoiseFunction; 并通过每次递归调用来减少它MidPointDisplace(..., 0.5f * scale)

不确定这是否是您的图像看起来错误的唯一原因,或者还有其他问题。

于 2013-02-18T15:09:50.193 回答
1

根据维基百科对中点位移的总结,只有最中心点的平均值会添加噪声 - 尝试仅通过添加噪声CalculateTerrainPointData2和去除噪声CalculateTerrainPointData

于 2013-07-10T21:49:04.313 回答