平滑不同幅度的随机噪声 [英] Smoothing random noises with different amplitudes

查看:77
本文介绍了平滑不同幅度的随机噪声的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个返回有限噪声的函数。例如,假设输出范围为[-1,1]。使用我的方法,我可以返回有界/范围内的噪声(取决于我们当前的生物群系)。

  ///< ; summary> 
///转换范围。
///< / summary>
///< param name = originalStart>原始开始。< / param>
///< param name = originalEnd>原始结尾。< / param>
///< param name = newStart>新的开始。< / param>
///< param name = newEnd>新的结尾。< / param>
///< param name = value>该值。< / param>
///< returns>< / returns>
public static float ConvertRange(
float originalStart,float originalEnd,//原始范围
float newStart,float newEnd,//所需范围
float值)//要转换的值
{
浮动比例=(newEnd-newStart)/(originalEnd-originalStart);
return(newStart +(((value-originalStart)* scale)));
}

///< summary>
///获取有界噪声。
///< / summary>
///< param name = value>该值。< / param>
///< param name = meanHeight>平均值的高度。< / param>
///< param name = amplitude>振幅。< / param>
///< returns>< / returns>
// [InRange(-。5f,.5f)]&& [InRange(0,1)]
公共静态float GetBoundedNoise(浮点值,float meanHeight,浮点幅度)
{
return Mathf.Clamp01(ConvertRange(0,1,-振幅,幅度,ConvertRange(-1,1,0,1,value))+(meanHeight + .5f));
}

选中此项以了解平均高度和振幅:



有噪点的区域:





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



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



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



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



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



您可以看到我的旧代码:

  //平滑段

// var kernel = Kernels.GAUSSIAN_KERNEL_9;

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

// if(pol!= null&&!pol.Segments.IsNullOrEmpty())
// foreach(pol.Segments中的细分市场)
// {
// int d = segment.Distance;

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

// // if(d%kernelSize == 0)//我试图通过检查kernelSize当前的d模数是否为0来减少迭代次数。但是没有运气。
//过滤器< 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高度,T []源,params object []参数)
{
float [] [] kernel =(float [] [])parameters [0];
int kernelSize = kernel.Length;
int迭代=(int)参数[1];

int _x =(int)参数[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< eration; ++ ite)
{
Color c = new Color(0f,0f,0f,0f);

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

试试
{
if(!withGrid)
{
c + =((颜色)(动态)source [FP(_x + kx + xOffset ,_y + ky + y偏移,宽度,高度)])* kernel [x] [y];
++ FiltersDebug.convolutionIterations;
}
其他
{
for(int i = 0; i <81; ++ i)
{
int __x = i%9 ,
__y = i / 9;

c + =((颜色)(动态)源[FP(_x + __x + kx + xOffset,_y + __y + ky + yOffset,宽度,高度)])* kernel [x] [y ];
++ FiltersDebug.convolutionIterations;
}

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

if(!withGrid)
try
{
source [FP(_x + xOffset, _y + yOffset,宽度,高度)] =(动态)c;
}
catch
{
++ FiltersDebug.outOfBoundsExceptions;
}
}
}

您会看到非常未优化。代码来自于此:



有片段之间的间隙(标记为黄色)呈钝角。在具有清晰投影的线段上出现重叠的噪声梯度。



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



类似的东西:





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



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


如果我们将9x9个生物群落的区域居中放置在所查询的列周围,则
会为其中每个生物群落生成高度,将它们相加并用
除以81(生物群落总数),我们将有效地在整个地形上形成
9倍的移动平均值,所有边界将突然变成
光滑。下图显示了应用平均过程后上一段
的情况。


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



但是我没有从哪里开始,也没有如何用优化算法解决问题。而且,我不知道要研究什么。因此,有一个问题,就是这个原因,欢迎您的帮助。

解决方案

我认为这不是最好的方法



但是,对于每个访问此问题的人,您都应该访问第2部分:具有不同幅度的平滑噪声(第2部分)



有一个很好的答案。


I have a function that returns a bounded noise. For example, let's imagine that out input range is [-1, 1]. With my method I can return a bounded/in range noise (depending on the biome we are currently).

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

Check this to understand what is mean height and amplitude: https://i.gyazo.com/9dc9cbe949f82d7342d7778e904563de.mp4

Note: Noise value is given by FastNoise library. (You can see it on Github)

The problem is that there is an height unmatch on each region border:

Normal region:

Noised region:

Black pixel is equal to y=0, and white pixel to y=1. (You can ignore yellow pixels)

But as you can see, there is diffent biomes with different amplitudes and mean heights (Water, Grass, Grass, DryGrass, Asphalt).

I have tried gaussian convolution, but there is a problem: Too many iterations for CPU (it would be optimal to be executed in GPU).

Why? Well, I apply a gaussian convolution for each region border pixel (I have an optimized method to get that). Imagine we get 810k points. And apply a convolution foreach pixel has 81 iterations (to get the mean of heights from that portion). But this is only for one pixel, now we have to do the mean for another 81 pixels (9x9) or 25 pixels (5x5) or whatever.

There are (in the best case) 1,640,250,000 iterations to do (to get a very small smoothed grid around each region border).

You can see my old code for this:

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

++ are debug counters, ignore them.

And Convolution at Point does the following:

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

As you can see very unoptimized. The code is from this: http://wiki.unity3d.com/index.php/TextureFilter

I can't imagine any other way to do this approach. The best I could imagine to do this, is to do draw a perpendicular line (perpendicular to the current segment (I have a util to get edges of a polygon (segment is formed by a start point and an end point where segment start = current edge and segment end = previous edge))) (with a mean noise for each point in the line). But there is also a problem:

There is a gap (marked with yellow) between segments with a obstuse projection. And an overlapped noise gradient on segments with sharp projection.

Another approach I realized is to get an gradient contour from all region borders that need it.

Something like that:

I also saw Cuberite implementation (http://cuberite.xoft.cz/docs/Generator.html#heightgen):

But I don't understand this part, and if I can extract something from this:

If we take the area of 9x9 biomes centered around the queried column, generate height for each of the biomes therein, sum them up and divide by 81 (the number of biomes summed), we will be effectively making a 9-long running average over the terrain, and all the borders will suddenly become smooth. The following image shows the situation from the previous paragraph after applying the averaging process.

Note: I already created a distorted voronoi function to get the current biome at a terrain point (following this guide, but I don't fully understand what to do, because I don't understand this approach and I neither can see any code relative to this text).

But I don't where to start and neither how to resolve problem with an optimized algorithm. Also, I don't know what to research. So I have a problen, by this reason, any help is welcome.

解决方案

I think this is not the best way to do this.

But, to everybody that visit this question, you should visit Part 2: Smoothing noises with different amplitudes (Part 2)

There is a very nice answer.

这篇关于平滑不同幅度的随机噪声的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆