我正在寻找一种很好很简单的方法,用C#生成一个岛屿地图的掩码。
基本上,我用的是一个随机的高度图,它是由perlin噪声产生的,那里的地形不被水包围。
下一步将是制作一个面罩,以确保角落和边界只是水。
然后我就可以从perlin噪音图像中减去面具,得到一个岛。
玩弄这种对比..。
梯度曲线,我可以得到一个岛的高度图,就像我想要的那样。
(当然,这些只是例子)
所以正如你所看到的,这个岛的“边缘”被切断了,如果颜色值不是太白的话,这不是什么大问题,因为我只会把灰度划分为4层(水、沙子、草和岩石)。
我的问题是,我怎样才能像在第二张图像中那样产生一个好看的面具呢?
我已经找到了这个技术,这对我来说似乎是一个很好的起点,但我不知道如何精确地实现它以获得所需的输出。http://mrl.nyu.edu/~perlin/experiments/puff/
这是我的最后解决办法。
我在我的规范化循环中实现了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;
}
}
发布于 2013-04-22 17:01: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
其中,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-04-22 18:46:21
如果您愿意为此节省一些计算能力,那么您可以使用与这个博客的作者类似的技术。(注:如果你想直接复制他的代码,那是在ActionScript中)。基本上,他生成准随机点(即看起来比较均匀),然后使用这些点来创建Voronoi多边形。
然后,他将外部多边形设置为水,并遍历其余的多边形,如果相邻多边形中有一定比例的多边形是水,则使其成为水。然后留下一个多边形掩码,大致代表一个岛屿。
由此,你可以将噪音应用到边缘,从而产生类似于此的东西(颜色来自另一个无关的步骤):
然后你留下一个(相当)真实的岛状面具,这将服务于你的目的。您可以选择使用它作为您的Perlin噪声的掩码,或者您可以根据到海的距离生成高度值并添加噪声(尽管这似乎是不必要的)。
发布于 2013-04-22 20:00:16
一个非常简单的方法是创建中心位于宽度/2和高度/2的径向或球面反梯度,为了掩蔽,你需要从噪声中减去梯度,而不是乘以它。这给你更现实的看海岸与缺点,岛屿不一定是相连的。
你可以在这里看到用梯度减去和乘以噪声的区别:http://www.vfxpedia.com/index.php?title=Tips_和_技术/自然_现象/烟雾
如果不确定如何创建径向梯度,可以将其用作起点:
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;
}
不要忘记你的坡度与你的高度图一样高,你仍然需要在你的水系中考虑一些因素。
这个方法的问题是,你的高度场将围绕着地图的中心。然而,这种方法应该让您开始添加功能,并使您的景观更加多样化,因为您可以使用添加功能添加到您的高度地图。
https://gamedev.stackexchange.com/questions/54276
复制相似问题