Diamond-Square算法的平滑问题

时间:2011-09-26 01:36:49

标签: algorithm map height procedural-programming procedural-generation

我使用菱形平方算法生成随机地形。 它工作得很好,除了我将这些大锥形状伸出或伸入地形。 问题似乎是每时不时地设置一个点太高或太低。

这是问题的图片
screenshot

当我将光滑度设置得很高时,可以更好地看到它 screenshot closeup

这是我的代码 -

private void CreateHeights()
    {
        if (cbUseLand.Checked == false) 
            return;
        int
            Size = Convert.ToInt32(System.Math.Pow(2, int.Parse(tbDetail.Text)) + 1),
            SideLength = Size - 1,
            d = 1025 / (Size - 1),
            HalfSide;
        Heights = new Point3D[Size, Size];
        float
            r = float.Parse(tbHeight.Text),
            Roughness = float.Parse(RoughnessBox.Text);

        //seeding all the points
        for (int x = 0; x < Size; x++)
            for (int y = 0; y < Size; y++)
                Heights[x, y] = Make3DPoint(x * d, 740, y * d);

        while (SideLength >= 2)
        {
            HalfSide = SideLength / 2;

            for (int x = 0; x < Size - 1; x = x + SideLength)
            {
                for (int y = 0; y < Size - 1; y = y + SideLength)
                {
                    Heights[x + HalfSide, y + HalfSide].y =
                      (Heights[x, y].y +
                      Heights[x + SideLength, y].y +
                      Heights[x, y + SideLength].y +
                      Heights[x + SideLength, y + SideLength].y) / 4 - r + ((float)(random.NextDouble() * r) * 2);
                }
            }

            for (int x = 0; x < Size - 1; x = x + SideLength)
            {
                for (int y = 0; y < Size - 1; y = y + SideLength)
                {
                    if (y != 0)
                        Heights[x + HalfSide, y].y = (Heights[x, y].y + Heights[x + SideLength, y].y + Heights[x + HalfSide, y + HalfSide].y + Heights[x + HalfSide, y - HalfSide].y) / 4 - r + ((float)(random.NextDouble() * r) * 2); 
                    if (x != 0)
                        Heights[x, y + HalfSide].y = (Heights[x, y].y + Heights[x, y + SideLength].y + Heights[x + HalfSide, y + HalfSide].y + Heights[x - HalfSide, y + HalfSide].y) / 4 - r + ((float)(random.NextDouble() * r) * 2);
                }
            }
            SideLength = SideLength / 2;
            r = r / Roughness;
        }
    }

4 个答案:

答案 0 :(得分:6)

Gavin S. P. Miller给出了SIGGRAPH '86关于Fournier,Fussel&amp; amp; Carpenter的原始算法存在根本缺陷。因此,您所看到的对于Diamond Square算法的任何天真实现都是正常的。您将需要一个单独的平滑方法,或者发布每个Diamond-Square复合步骤,或者作为后处理到所有菱形平方迭代(或两者)。米勒解决了这个问题。加权和盒式或高斯滤波是一种选择;将初始阵列播种到比初始4个点更大的程度(即,手动或使用一些内置智能复制菱形方块的前几个步骤的结果集,而是提供无偏值);在使用菱形方块增加细节之前,您给阵列的初始信息越多,结果就越好。

原因似乎在于如何执行Square步骤。在Diamond步骤中,我们采用了正方形四角的平均值来生成该正方形的中心。然后,在随后的Square步骤中,我们取四个正交相邻的邻居的平均值,其中一个是我们刚刚生成的正方形的中心点。你能看到问题吗?那些原始角高值对后续的菱形平方迭代贡献过多,因为它们通过它们自己的影响和通过它们创建的中点来贡献。这会导致尖顶(突出和侵入),因为局部衍生点更倾向于那些早期点...并且因为(通常3)其他点也会这样做,这会在迭代时围绕这些点产生“循环”影响钻石广场使用更高的深度。因此,只有当数组的初始状态未指定时,才会出现这些“混叠”问题;实际上,发生的伪像可以被视为仅使用4个点开始的直接几何结果。

您可以执行以下操作之一:

  • 进行本地过滤 - 通常很贵。
  • 更彻底地预先播种初始阵列 - 需要一些智慧。
  • 永远不要从给定的一组初始点平滑过多的步骤 - 即使你对初始数组进行种子处理也是如此,这只是相对深度和你自己的最大位移参数的关系。

答案 1 :(得分:0)

我相信每次迭代中位移r的大小应该与当前矩形的大小成比例。这背后的逻辑是分形表面是尺度不变的,因此任何矩形的高度变化应该与该矩形的大小成比例。

在您的代码中,高度的变化与r成正比,因此您应该将其与当前网格大小的大小成比例。换句话说:将r乘以循环前的粗糙度,并在每次迭代中将r除以2。

所以,而不是

r = r / Roughness;

你应该写

r = r / 2;

答案 2 :(得分:0)

上述算法的实际缺陷是概念化和实现中的错误。作为算法的菱形方块具有一些伪像,但这是基于范围的伪像。因此,某些像素的技术最大值高于其他像素。一些像素通过随机性直接给出值,而其他像素通过菱形和平方中点插值过程获得它们的值。

这里的错误是你从零开始。并重复将值添加到当前值。这导致菱形的平方范围从零开始并向上延伸。它实际上必须从零开始,并根据随机性上下移动。所以最重要的事情并不重要。但是,如果你没有意识到这一点并且天真地实现了添加到值的所有内容,而不是从零开始并从那里开始波动,那么你将暴露隐藏的工件。

米勒的笔记是正确的,但缺陷通常隐藏在噪音中。这个实现显示了那些问题。那是 NOT 正常。并且可以通过几种不同的方式进行修复。这是我扩展此算法以删除所有内存限制和大小限制并使其无限且确定性1之后的原因之一,然后我仍然从这里的核心思想转移(将问题扩展到3d)并优化GPU也发挥了作用。2

diamond squared artifacts

答案 3 :(得分:0)

您可以使用2-D median filter来消除极端情况,而不仅仅是使用平均值进行平滑处理。它实现起来很简单,通常会产生很多噪音所需的效果。