创建岛屿地图蒙版的简单方法


21


我正在寻找一种使用C#为岛地图生成蒙版的好方法。


基本上,我使用的是由Perlin噪声生成的随机高度图,其中地形没有被水包围。

在此处输入图片说明

下一步将是生成遮罩,以确保拐角和边界都是水。

在此处输入图片说明

然后,我可以从Perlin噪声图像中减去蒙版以获得一个孤岛。

在此处输入图片说明

并在对比中玩。

在此处输入图片说明

和渐变曲线,就可以得到一个孤岛高度图。

在此处输入图片说明

(这些只是示例)

如您所见,岛上的“边缘”刚刚被切除,如果颜色值不是太白,这并不是一个大问题,因为我将灰度分为4层(水,沙子,草皮和岩)。

我的问题是,如何像第二幅图像那样生成一个好看的蒙版?


更新

我已经找到了这项技术,这对我来说似乎是一个不错的起点,但是我不确定我可以如何正确地实现它以获取所需的输出。 http://mrl.nyu.edu/~perlin/experiments/puff/


更新2

这是我的最终解决方案。

我已经makeMask()在规范化循环中实现了该功能,如下所示:

        //normalisation
        for( int i = 0; i < width; i++ ) {
            for( int j = 0; j < height; j++ ) {
                perlinNoise[ i ][ j ] /= totalAmplitude;
                perlinNoise[ i ][ j ] = makeMask( width, height, i, j, perlinNoise[ i ][ j ] );
            }
        }

这是最终功能:

    public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
        int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
        int maxVal = ( ( ( height + width ) / 2 ) / 100 * 10 );
        if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
            return 0;
        } else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
            return oldValue;
        } else {
            float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
            return oldValue * factor;
        }
    }

    private static float getFactor( int val, int min, int max ) {
        int full = max - min;
        int part = val - min;
        float factor = (float)part / (float)full;
        return factor;
    }

    public static int getDistanceToEdge( int x, int y, int width, int height ) {
        int[] distances = new int[]{ y, x, ( width - x ), ( height - y ) };
        int min = distances[ 0 ];
        foreach( var val in distances ) {
            if( val < min ) {
                min = val;
            }
        }
        return min;
    }

这将产生如图3所示的输出。

在代码中稍作更改,就可以得到最初想要的输出,如图2所示->

    public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
        int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
        int maxVal = ( ( ( height + width ) / 2 ) / 100 * 20 );
        if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
            return 0;
        } else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
            return 1;
        } else {
            float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
            return ( oldValue + oldValue ) * factor;
        }
    }

有机会您可以链接到您的完整资料吗?
stoic

Answers:


12

产生规律性的噪声,偏向中心的值越高。如果您想要示例中显示的方形岛状形状,我将使用到最近边缘的距离作为因子。

在此处输入图片说明

由于这个因素,您可以在生成掩码噪声时使用类似以下的内容:

maxDVal = (minIslandSolid - maxIslandSolid)

getMaskValueAt(x, y)
    d = distanceToNearestEdge(x,y)
    if(d < maxIslandSolid)
        return isSolid(NonSolidValue) //always non-solid for outside edges
    else if(d < minIslandSolid)
        //noisy edges
        return isSolid((1 - (d/maxDVal)) * noiseAt(x,y))
    else
        return isSolid(SolidValue) //always return solid for center of island

Where distanceToNearestEdge返回从该位置到地图最近边缘的距离。并isSolid确定0到1之间的值是否可靠(无论您的截止值是什么)。这是一个非常简单的函数,可能看起来像这样:

isSolid(浮点值)返回值<solidCutOffValue

solidCutOffValue您用来确定是否可靠的任何值在哪里。它可以是.5均匀的,也可以是.75更多或.25更少的固体。

最后,这一点(1 - (d/maxDVal)) * noiseAt(x,y)。首先,我们得到一个介于0和1之间的因子:

(1 - (d/maxDVal))

在此处输入图片说明

0外部边缘和1内部边缘上的位置。这意味着我们的噪声更有可能在内部是固体,而在外部是非固体。这就是我们将噪声应用于的因素noiseAt(x,y)

这是值的更直观表示,因为名称可能会误导实际值:

在此处输入图片说明


对于快速答案,我将尝试实现此技术。希望以此获得期望的输出。
2013年

可能需要一些调整才能以您想要的方式获得嘈杂的边缘。但这应该是您的坚实基础。祝好运!
MichaelHouse

到目前为止,我已经可以使用基本实现了,您能给我一个IsSolid函数的示例吗?我不知道如何根据距边缘的最小和最大距离获得0到1之间的值。到目前为止,请参阅我的代码更新。
2013年

我在那里有些混乱的逻辑。我已将其修复以使其更有意义。并提供了一个示例isSolid
MichaelHouse

要获得介于0和1之间的值,您只需找出最大值即可,然后将当前值除以该值。然后我从1中减去,因为我希望零在外侧边缘,而1在内侧边缘。
MichaelHouse

14

如果您愿意为此保留一些计算能力,则可以使用与该博客作者相同的技术。(注意:如果您想直接复制他的代码,请使用ActionScript)。基本上,他生成准随机点(即看起来相对均匀),然后使用这些点创建Voronoi多边形

Voronoi多边形

然后,他将外部多边形设置为水,然后遍历其余多边形,如果一定比例的相邻多边形为水,则将其变为。然后,您剩下一个大致代表一个岛的多边形蒙版。

多边形地图

通过此操作,您可以将噪波应用于边缘,从而产生类似于此的效果(颜色来自另一个不相关的步骤):

带有嘈杂边缘的多边形贴图

然后,您会得到一个(非常)逼真的岛状面具,它将满足您的目的。您可以选择将其用作Perlin噪声的遮罩,也可以根据与海的距离生成高度值并添加噪声(尽管这似乎不必要)。


谢谢你的答复,但这是我从网上搜索到的第一个(几乎是唯一的)解决方案。这个解决方案似乎非常好,但是我想尝试“简单”的方法。
2013年

@Ace足够公平,无论您打算做什么,它都可能有点矫kill过正:P尽管如此,如果您需要它,仍然需要牢记。
2013年

很高兴有人链接到该页面-该页面始终在我的“关于某人如何实现某事的极好的帖子”列表中。
蒂姆·霍尔特

+1。太酷了 谢谢你,这一定会对我有帮助!
Andre

1

一种非常简单的方法是创建以宽度/ 2和高度/ 2为中心的径向或球形反梯度。对于屏蔽,您要从噪声中减去梯度而不是将其相乘。这样可以使您看起来更逼真的海岸,但缺点是不一定要连接岛屿。

您可以在此处看到将噪声与梯度相减和相乘之间的区别:http : //www.vfxpedia.com/index.php?title= Tips_and_Techniques/Natural_Phenomena/ Smoke

如果不确定如何创建径向渐变,可以将其用作起点:

    public static float[] CreateInverseRadialGradient(int size, float heightScale = 1)
    {
        float radius = size / 2;

        float[] heightMap = new float[size * size];

        for (int iy = 0; iy < size; iy++)
        {
            int stride = iy * size;
            for (int ix = 0; ix < size; ix++)
            {
                float centerToX = ix - radius;
                float centerToY = iy - radius;

                float distanceToCenter = (float)Math.Sqrt(centerToX * centerToX + centerToY * centerToY);
                heightMap[iy * size + ix] = distanceToCenter / radius * heightScale;
            }
        }

        return heightMap;
    }

不要忘记将渐变比例缩放到与高度图相同的高度,并且仍然需要以某种方式考虑水线。

这种方法的问题在于您的身高场将围绕地图中心居中。但是这种方法,应该让你开始与增加功能,使您的景观更加多样化,你可以使用除了到功能添加到您的高度图。


谢谢。我不确定我是否理解正确,但是您是否注意到遮罩完全不影响原始的高度图数据,它只是受到负面影响,因此它只是定义了显示的像素(%) 。但是我也尝试了简单的梯度,但是我对结果并不满意。
2013年

1

我第二次提出ollipekka的建议:您要做的是从高度图中减去合适的偏差函数,以确保边缘在水下。

有很多合适的偏差函数,但是一个相当简单的函数是:

f(x, y) = 1 / (x * (1-x) * y * (1-y)) - 16

其中xy是坐标值,缩放为介于0和1之间。此函数在地图的中心(x = y = 0.5)取值0,并且在边缘趋于无穷大。因此,从高度图减去它(按适当的常数因子进行缩放)可确保高度值也趋向于减去图边缘附近的无穷大。只要选择您想要的任意高度并将其称为海平面即可。

正如ollipekka所指出的,这种方法不能保证该岛是连续的。但是,以相当小的比例因子缩放偏差函数应使其在地图的中间区域大部分保持平坦(因此不会对地形造成太大影响),而明显的偏差仅出现在边缘附近。因此,这样做应该会给您一个方形的岛,大部分为连续的岛,最多可能在边缘附近有几个小岛。

当然,如果您不介意地形不连贯的可能性,则较大的缩放比例应会为您提供更多的水和更自然的岛屿形状。调整海平面和/或原始高度图的比例也可以用于更改所得岛屿的大小和形状。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.