平滑具有不同幅度的随机噪声

时间:2018-11-17 22:20:33

标签: c# convolution smoothing noise-generator

我有一个返回有限噪声的函数。例如,让我们想象出输入范围是[-1,1]。通过我的方法,我可以返回有界/范围内的噪声(取决于我们当前的生物群系)。

    /// <summary>
    /// Converts the range.
    /// </summary>
    /// <param name="originalStart">The original start.</param>
    /// <param name="originalEnd">The original end.</param>
    /// <param name="newStart">The new start.</param>
    /// <param name="newEnd">The new end.</param>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    public static float ConvertRange(
        float originalStart, float originalEnd, // original range
        float newStart, float newEnd, // desired range
        float value) // value to convert
    {
        float scale = (newEnd - newStart) / (originalEnd - originalStart);
        return (newStart + ((value - originalStart) * scale));
    }

    /// <summary>
    /// Gets the bounded noise.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <param name="meanHeight">Height of the mean.</param>
    /// <param name="amplitude">The amplitude.</param>
    /// <returns></returns>
    // [InRange(-.5f, .5f)] && [InRange(0, 1)]
    public static float GetBoundedNoise(float value, float meanHeight, float amplitude)
    {
        return Mathf.Clamp01(ConvertRange(0, 1, -amplitude, amplitude, ConvertRange(-1, 1, 0, 1, value)) + (meanHeight + .5f));
    }

选中此项以了解平均高度和幅度:https://i.gyazo.com/9dc9cbe949f82d7342d7778e904563de.mp4

注意:噪声值由FastNoise库给出。 (您可以在Github上看到它)

问题是每个区域边界上的高度都不匹配:

正常区域:

...

噪声区域:

...

黑色像素等于y = 0,白色像素等于y = 1。 (您可以忽略黄色像素)

但是正如您所看到的,有不同幅度和平均高度的不同生物群落(水,草,草,干草,沥青)。

我已经尝试过高斯卷积,但是有一个问题:CPU的迭代次数过多(最好在GPU中执行)。

为什么?好吧,我对每个区域边界像素都应用了高斯卷积(我有一种优化的方法来做到这一点)。想象一下,我们得到了810k点。并对每个像素进行一次卷积并进行81次迭代(以获取该部分的平均高度)。但这仅用于一个像素,现在我们必须对另外81个像素(9x9)或25个像素(5x5)或其他像素进行均值。

(在最佳情况下)需要进行1,640,250,000次迭代(以在每个区域边界周围获得非常小的平滑网格)。

您可以看到我的旧代码:

            // Smothing segments

            //var kernel = Kernels.GAUSSIAN_KERNEL_9;

            //int kernelSize = kernel.GetLength(0);

            //if (pol != null && !pol.Segments.IsNullOrEmpty())
            //    foreach (Segment segment in pol.Segments)
            //    {
            //        int d = segment.Distance;

            //        for (int i = 0; i <= d; ++i)
            //        {
            //            Point p = (Vector2)segment.start + segment.Normal * d;

            //            //if (d % kernelSize == 0) // I tried to get less itwrations by checking if the current d modulus from kernelSize was 0. But no luck.
            //            Filters<Color32>.ConvolutionAtPoint(mapWidth, mapHeight, p.x, p.y, target, kernel, 1, pol.Center.x, pol.Center.y, true);
            //        }
            //    }
            //else
            //{
            //    if (pol == null)
            //        ++nullPols;
            //    else if (pol != null && pol.Segments.IsNullOrEmpty())
            //        ++nullSegments;
            //}

++是调试计数器,请忽略它们。

卷积点执行以下操作:

     private static void ConvolutionAtPointFunc(int width, int height, T[] source, params object[] parameters)
    {
        float[][] kernel = (float[][])parameters[0];
        int kernelSize = kernel.Length;
        int iteration = (int)parameters[1];

        int _x = (int)parameters[2];
        int _y = (int)parameters[3];
        int xOffset = (int)parameters[4];
        int yOffset = (int)parameters[5];
        bool withGrid = (bool)parameters[6];

        for (int ite = 0; ite < iteration; ++ite)
        {
            Color c = new Color(0f, 0f, 0f, 0f);

            for (int y = 0; y < kernelSize; ++y)
            {
                int ky = y - kernelSize / 2;
                for (int x = 0; x < kernelSize; ++x)
                {
                    int kx = x - kernelSize / 2;

                    try
                    {
                        if (!withGrid)
                        {
                            c += ((Color)(dynamic)source[F.P(_x + kx + xOffset, _y + ky + yOffset, width, height)]) * kernel[x][y];
                            ++FiltersDebug.convolutionIterations;
                        }
                        else
                        {
                            for (int i = 0; i < 81; ++i)
                            {
                                int __x = i % 9,
                                    __y = i / 9;

                                c += ((Color)(dynamic)source[F.P(_x + __x + kx + xOffset, _y + __y + ky + yOffset, width, height)]) * kernel[x][y];
                                ++FiltersDebug.convolutionIterations;
                            }

                            source[F.P(_x + kx + xOffset, _y + ky + yOffset, width, height)] = (dynamic)c;
                        }
                    }
                    catch
                    {
                        ++FiltersDebug.outOfBoundsExceptionsIn;
                    }
                }
            }

            if (!withGrid)
                try
                {
                    source[F.P(_x + xOffset, _y + yOffset, width, height)] = (dynamic)c;
                }
                catch
                {
                    ++FiltersDebug.outOfBoundsExceptions;
                }
        }
    }

您会看到未优化。代码是这样的:http://wiki.unity3d.com/index.php/TextureFilter

我无法想象有其他方法可以实现这种方法。我能想到的最好的办法是绘制一条垂直线(垂直于当前线段(我有一个工具来获取多边形的边缘(线段由起点和终点组成,其中线段的起点=当前)边和线段的末端=前一条边)))(线中每个点的平均噪声)。但是还有一个问题:

...

在带有钝角突起的段之间存在间隙(标有黄色)。并且在具有清晰投影的片段上重叠了噪声梯度。

我意识到的另一种方法是从所有需要它的区域边界中获得一个梯度轮廓。

类似的东西:

...

我还看到了Cuberite的实现(http://cuberite.xoft.cz/docs/Generator.html#heightgen):

但是我不理解这部分,如果我可以从中提取一些内容:

  

如果我们以查询列为中心的9x9生物群落区域,   为其中的每个生物群落生成高度,将其求和并除   到81(生物群落总数),我们将有效地   整个地形的9个长期平均值,所有边界   突然变得流畅。下图显示了以下情况   应用平均过程后的上一段。

注意:我已经创建了一个扭曲的voronoi函数,以将当前的生物群系带到某个地形点(遵循本指南,但是我不完全了解该怎么做,因为我不了解这种方法,而且我也看不到与此文本相关的任何代码。

但是我没有起点,也没有如何使用优化算法解决问题。另外,我也不知道要研究什么。因此,由于这个原因,我很乐意提供任何帮助。

1 个答案:

答案 0 :(得分:0)

我认为这不是最好的方法。

但是,对于所有访问此问题的人,您都应该访问第2部分:Smoothing noises with different amplitudes (Part 2)

一个很好的答案。