Diamond-Square 算法的平滑问题

发布于 2024-12-06 14:17:04 字数 2410 浏览 1 评论 0 原文

我正在使用菱形方形算法来生成随机地形。 它工作得很好,除了我让这些大圆锥形状要么伸出或伸入地形。 问题似乎在于,时不时会有一个点被设置得太高或太低。

这是问题的图片
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;
        }
    }

I am using the diamond-square algorithm to generate random terrain.
It works fine except I get these large cone shapes either sticking out of or into the terrain.
The problem seems to be that every now and then a point gets set either way too high or way too low.

Here is a picture of the problem
screenshot

And it can be better seen when I set the smoothness really high
screenshot closeup

And here is my code -

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

画尸师 2024-12-13 14:17:04

Gavin SP Miller 在 SIGGRAPH '86 上发表了有关 Fournier、Fussel &卡彭特最初的算法存在根本性缺陷。因此,对于任何简单的 Diamond Square 算法实现,您所看到的都是正常的。您将需要一种单独的平滑方法,可以是后处理每个菱形方形复合步骤,也可以作为所有菱形方形迭代的后处理(或两者)。米勒对此做出了回应。加权和盒子或高斯过滤是一种选择;将初始数组播种到比初始 4 个点更大的程度(即,手动或使用某些内置智能复制 Diamond-square 前几步的结果集,但提供无偏值);在使用 Diamond-square 增加细节之前为数组提供的初始信息越多,结果就越好。

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

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

  • 进行本地过滤——通常很昂贵。
  • 更彻底地预先播种初始阵列——需要一些情报。
  • 永远不要从给定的一组初始点向下平滑太多步骤 - 即使您确实播种了初始数组,这也适用,这只是相对深度与您自己的最大位移参数结合的问题。

Gavin S. P. Miller gave a SIGGRAPH '86 talk about how Fournier, Fussel & Carpenter's original algorithm was fundamentally flawed. So what you're seeing is normal for any naive implementation of the Diamond Square algorithm. You will require a separate approach for smoothing, either post each Diamond-Square compound step, or as a post-process to all diamond-square iterations (or both). Miller addressed this. Weighting and box or gaussian filtering are one option; seeding the initial array to a greater degree than just the initial 4 points (i.e., replicating the resultsets of the first few steps of diamond-square either manually or using some built-in intelligence, but instead supplying unbiased values); the more initial information you give the array before increasing the detail using diamond-square, the better your results will be.

The reason appears to be in how the Square step is performed. In the Diamond step, we've taken the average of the four corners of a square to produce that square's centre. Then, in the subsequent Square step, we take the average of four orthogonally-adjacent neighbours, one of which is the square's centre point we just produced. Can you see the problem? Those original corner height values are contributing too much to the subsequent diamond-square iteration, because they are contributing both through their own influence AND through the midpoint that they created. This causes the spires (extrusive and intrusive), because locally-derived points tend more strongly toward those early points... and because (typically 3) other points do as well, this creates "circular" influences around those points, as you iterate to higher depths using Diamond-Square. So these kinds of "aliasing" issues only appear when the initial state of the array is underspecified; in fact, the artifacting that occurs can be seen as a direct geometric consequence of using only 4 points to start with.

You can do one of the following:

  • Do local filtering -- generally expensive.
  • Pre-seed the initial array more thoroughly -- requires some intelligence.
  • Never smooth too many steps down from a given set of initial points -- which applies even if you do seed the initial array, it's all just a matter of relative depths in conjunction with your own maximum displacement parameters.
陌上芳菲 2024-12-13 14:17:04

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

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

所以,

r = r / Roughness;

你应该写

r = r / 2;

I believe the size of the displacement r in each iteration should be proportional to the size of the current rectangle. The logic behind this is that a fractal surface is scale invariant, so the variation in height in any rectangle should be proportional to the size of that rectangle.

In your code, the variation in height is proportional to r, so you should keep it proportional to the size of your current grid size. In other words: multiply r by the roughness before the loop and divide r by 2 in each iteration.

So, instead of

r = r / Roughness;

you should write

r = r / 2;
孤单情人 2024-12-13 14:17:04

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

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

米勒的笔记是正确的,但缺陷通常隐藏在噪音中。这个实现展示了这些问题。这是正常的。并且可以通过几种不同的方式来修复。这就是为什么我扩展这个算法以消除所有内存限制和大小限制并使其无限和确定性1,然后我仍然放弃了这里的核心思想(将其扩展到 3d 并针对 GPU 进行优化的问题也发挥了作用。2

钻石平方文物

The actual flaw in the above algorithm is an error in conceptualization and implementation. Diamond square as an algorithm has some artifacting but this is range based artifacts. So the technical max for some pixels is higher than some other pixels. Some pixels are directly given values by the randomness while others acquire their values by the diamond and squared midpoint interpolation processes.

The error here is that you started from zero. And repeatedly added the value to the current value. This causes the range of diamond squared to start at zero and extend upwards. It must actually start at zero and go both up and down depending on the randomness. So the top range thing won't matter. But, if you don't realize this and naively implement everything as added to the value, rather than starting at zero and fluctuating from there, you will expose the hidden artifacts.

Miller's notes were right, but the flaw is generally hidden within the noise. This implementation is shows those problems. That is NOT normal. And can be fixed a few different ways. This was one of the reasons why after I extended this algorithm to remove all the memory restrictions and size restrictions and made it infinite and deterministic1, I then still switched away from the core idea here (the problems extending it to 3d and optimizing for GPUs also played a role.2

diamond squared artifacts

画骨成沙 2024-12-13 14:17:04

您可以使用二维中值滤波器来获取走出极端。它实现起来很简单,通常会产生所需的效果,但会产生大量噪音。

Instead of just smoothening with an average, you can use a 2-D median filter to take out extremes. It is simple to implement, and usually generates the desired effect with a lot of noise.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文