首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >一种创建岛屿地图掩码的简单方法

一种创建岛屿地图掩码的简单方法
EN

Game Development用户
提问于 2013-04-22 14:04:08
回答 4查看 12.8K关注 0票数 21

我正在寻找一种很好很简单的方法,用C#生成一个岛屿地图的掩码。

基本上,我用的是一个随机的高度图,它是由perlin噪声产生的,那里的地形不被水包围。

下一步将是制作一个面罩,以确保角落和边界只是水。

然后我就可以从perlin噪音图像中减去面具,得到一个岛。

玩弄这种对比..。

梯度曲线,我可以得到一个岛的高度图,就像我想要的那样。

(当然,这些只是例子)

所以正如你所看到的,这个岛的“边缘”被切断了,如果颜色值不是太白的话,这不是什么大问题,因为我只会把灰度划分为4层(水、沙子、草和岩石)。

我的问题是,我怎样才能像在第二张图像中那样产生一个好看的面具呢?

更新

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

更新2

这是我的最后解决办法。

我在我的规范化循环中实现了makeMask()函数,如下所示:

代码语言:javascript
复制
        //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 ] );
            }
        }

这是最后一个功能:

代码语言:javascript
复制
    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中的->

代码语言:javascript
复制
    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;
        }
    }
EN

回答 4

Game Development用户

回答已采纳

发布于 2013-04-22 17:01:12

产生有规律的噪声,对中心有较高的偏差。如果你想要像你在你的例子中显示的方形岛屿形状,我会用距离最近的边缘作为你的因素。

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

代码语言:javascript
复制
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

其中,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)获得的噪声的因素。

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

票数 12
EN

Game Development用户

发布于 2013-04-22 18:46:21

如果您愿意为此节省一些计算能力,那么您可以使用与这个博客的作者类似的技术。(注:如果你想直接复制他的代码,那是在ActionScript中)。基本上,他生成准随机点(即看起来比较均匀),然后使用这些点来创建Voronoi多边形

然后,他将外部多边形设置为水,并遍历其余的多边形,如果相邻多边形中有一定比例的多边形是水,则使其成为水。然后留下一个多边形掩码,大致代表一个岛屿。

由此,你可以将噪音应用到边缘,从而产生类似于此的东西(颜色来自另一个无关的步骤):

然后你留下一个(相当)真实的岛状面具,这将服务于你的目的。您可以选择使用它作为您的Perlin噪声的掩码,或者您可以根据到海的距离生成高度值并添加噪声(尽管这似乎是不必要的)。

票数 14
EN

Game Development用户

发布于 2013-04-22 20:00:16

一个非常简单的方法是创建中心位于宽度/2和高度/2的径向或球面反梯度,为了掩蔽,你需要从噪声中减去梯度,而不是乘以它。这给你更现实的看海岸与缺点,岛屿不一定是相连的。

你可以在这里看到用梯度减去和乘以噪声的区别:http://www.vfxpedia.com/index.php?title=Tips_和_技术/自然_现象/烟雾

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

代码语言:javascript
复制
    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;
    }

不要忘记你的坡度与你的高度图一样高,你仍然需要在你的水系中考虑一些因素。

这个方法的问题是,你的高度场将围绕着地图的中心。然而,这种方法应该让您开始添加功能,并使您的景观更加多样化,因为您可以使用添加功能添加到您的高度地图。

票数 1
EN
页面原文内容由Game Development提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://gamedev.stackexchange.com/questions/54276

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档