所以我正在制作一个制作随机岛屿的地图生成器。它在发生器的核心使用Perlin Noise,然后使用带渐变的圆来制造岛屿。
圆形方法在地图的中心创建了一些圆圈,其渐变颜色从64到0开始。问题是此方法使用圆形创建地图部分的非自然外观边缘。当为像素生成perlin噪声时,它将在渐变映射上获得该像素,然后将其多个蓝色值。
因此,如果perlin噪声在像素1,5上给出1,并且梯度图上的蓝色值为54,则它将输出54的噪声值。如果像素130,560上的perlin噪声为0.5且渐变色值为64则噪声值为32。
这是我得到的:
代码有两个关键点,即perlin位:
noise = NoiseGenerator.Noise(x, y);
double gradColour = getGradColour(x, y).B;
double addedNoise = noise * gradColour;
double gradNoise = addedNoise;// - gradColour;
然后是渐变映射生成器:
public static void DrawGrad(float X, float Y, float R, Color C1, Color C2)
{
Graphics g = Graphics.FromImage(imgGrad);
GraphicsPath path = new GraphicsPath();
path.AddEllipse(X, Y, R, R);
PathGradientBrush pathGrBrush = new PathGradientBrush(path);
pathGrBrush.CenterColor = C1;
Color[] colours = { C2 };
pathGrBrush.SurroundColors = colours;
g.FillEllipse(pathGrBrush, X, Y, R, R);
//g.FillEllipse(Brushes.Red, X, Y, R, R);
g.Flush();
}
int amount = rnd.Next(25, 30);
for (int i = 0; i < amount / 4; i++)
{
float X = rnd.Next(-800, 1748);
float Y = rnd.Next(-800, 1748);
float R = rnd.Next(1000, 1200);
DrawGrad(X, Y, R, Color.FromArgb(255, 0, 0, rnd.Next(15, 20)), Color.FromArgb(0, 0, 0, 0));
}
for (int i = 0; i < amount; i++)
{
double positionDiv = 1.98;
double X1 = rnd.Next(0, 450) / positionDiv;
double Y1 = rnd.Next(0, 450) / positionDiv;
double R1 = rnd.Next(300, 650) / 4;
float R = (float)R1;
float X = (float)X1;
float Y = (float)Y1;
while (X + R > 1004)
{
X = 924 - R;
}
while (Y + R > 1004)
{
Y = 924 - R;
}
if(X < 30)
{
X = 30;
}
if(Y < 30)
{
Y = 30;
}
DrawGrad(X, Y, R, Color.FromArgb(255, 0, 0, rnd.Next(40, 64)), Color.FromArgb(0, 0, 0, rnd.Next(13, 17)));
}
我只是想知道是否有其他人知道C#中可能使用perlin噪声创建岛屿的任何其他方法?任何建议将不胜感激。
答案 0 :(得分:3)
正如我在评论中提到的,钻石和方形更容易得到足够好的结果。所以算法:
配置生成属性
在这里你需要有一组参数,如最小值,最大高度,海平面,植被高度范围,沙/岩/泥等,坡度参数等。
创建地形高度图我称之为zed[][]
为此,您需要稍加修改的Diamond&Square算法。问题是这种算法像地形一样产生“内陆”。
要进行调整以使其产生类似岛屿的地形,您需要在角落中以尽可能低的高度对其进行初始化。此外,您需要忽略第一个菱形步骤并使用一些随机值初始化中点(不是角的平均值)。并且在每次方格迭代之后,最后将边界指向最小(水下)高程(或其附近的某个随机值)。
为了获得良好的输出,我在生成时使用大约范围<-2^15 , 2^16>
。在此之后,我在生成的地形中找到最小和最大高程,并重新缩放到配置的高程范围。
不要忘记Diamond和square需要分辨率(2^n)+1
!!!
创建表面地图我称之为typ[][]
完成地形图后,您可以按升序添加基于高程的功能:
然后根据地形坡度添加参数
然后您可以添加其他内容(基于某些规则):
我在C ++中这样做:
void map_random(int _xs,int _ys)
{
// config
int h0=-1000,h1=3000; // [m] terrain elevation range
int h_water= 0; // [m] sea level
int h_sand=15; // [m] sand level
int h_evergreen=1500; // [m] evergreen level
int h_snow=2000; // [m] snow level
int h_rock=1800; // [m] mountine rock level
float a_rock=60.0; // [deg] mountine rock slope
float d_pixel=15.0; // [m] pixel size
bool _island=true;
// types
enum _cover_enum
{
_cover_none=0,
_cover_water,
_cover_snow,
_covers,
_cover_shift=0,
_cover_mask=15,
};
DWORD _cover[_covers]=
{
// RRGGBB
0x00000000, // none
0x00004080, // water
0x008F8F8F, // snow
};
enum _terrain_enum
{
_terrain_enum_beg=-1,
_terrain_dirt,
_terrain_sand,
_terrain_rock,
_terrains,
_terrain_shift=4,
_terrain_mask=15,
};
DWORD _terrain[_terrains]=
{
// RRGGBB
0x00301510, // dirt
0x00EEC49A, // sand
0x00777777, // rock
};
enum _flora_enum
{
_flora_enum_beg=-1,
_flora_none,
_flora_grass,
_flora_hardwood,
_flora_evergreen,
_flora_deadwood,
_floras,
_flora_shift=8,
_flora_mask=15,
};
DWORD _flora[_floras]=
{
// RRGGBB
0x00000000, // none
0x007F7F3F, // grass
0x001FFF1F, // hardwood
0x00007F00, // evergreen
0x007F3F1F, // deadwood
};
// variables
float a,b; int c,t,f;
int x,y,z,xx,yy,mxs,mys,dx,dy,dx2,dy2,r,r2;
int **ter=NULL,**typ=NULL;
Randomize();
// align resolution to power of 2
for (mxs=1;mxs+1<_xs;mxs<<=1); if (mxs<3) mxs=3;
for (mys=1;mys+1<_ys;mys<<=1); if (mys<3) mys=3;
ter=new int*[mys+1]; for (y=0;y<=mys;y++) ter[y]=new int[mxs+1];
typ=new int*[mys+1]; for (y=0;y<=mys;y++) typ[y]=new int[mxs+1];
// [Terrain]
// diamond & square random height map -> ter[][]
dx=mxs; dx2=dx>>1; r=1<<16; // init step,half step and randomness
dy=mys; dy2=dy>>1; r2=r>>1;
// set corners values
if (_island)
{
t=-r2;
ter[ 0][ 0]=t;
ter[ 0][mxs]=t;
ter[mys][ 0]=t;
ter[mys][mxs]=t;
ter[dy2][dx2]=r2;
}
else{
ter[ 0][ 0]=Random(r);
ter[ 0][mxs]=Random(r);
ter[mys][ 0]=Random(r);
ter[mys][mxs]=Random(r);
}
for (;dx2|dy2;dx=dx2,dx2>>=1,dy=dy2,dy2>>=1) // subdivide step until full image is filled
{
if (!dx) dx=1;
if (!dy) dy=1;
// diamond (skip first one for islands)
if ((!_island)||(dx!=mxs))
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
ter[y][x]=((ter[y-dy2][x-dx2]+ter[y-dy2][x+dx2]+ter[y+dy2][x-dx2]+ter[y+dy2][x+dx2])>>2)+Random(r)-r2;
// square
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
for (x=dx ,xx=mxs-dx ;x<=xx;x+=dx)
ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])>>2)+Random(r)-r2;
for (y=dy ,yy=mys-dy ;y<=yy;y+=dy)
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])>>2)+Random(r)-r2;
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
{
y= 0; ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y+dy2][x])/3)+Random(r)-r2;
y=mys; ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x])/3)+Random(r)-r2;
}
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
{
x= 0; ter[y][x]=((ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])/3)+Random(r)-r2;
x=mxs; ter[y][x]=((ter[y][x-dx2]+ter[y-dy2][x]+ter[y+dy2][x])/3)+Random(r)-r2;
}
// adjust border
if (_island)
{
for (y=0;y<=mys;y+=dy2) { ter[y][0]=t; ter[y][mxs]=t; }
for (x=0;x<=mxs;x+=dx2) { ter[0][x]=t; ter[mys][x]=t; }
}
// adjust randomness
// r=(r*100)>>8; if (r<2) r=2; r2=r>>1;
r>>=1; if (r<2) r=2; r2=r>>1;
}
// rescale to <h0,h1>
xx=ter[0][0]; yy=xx;
for (y=0;y<mys;y++)
for (x=0;x<mxs;x++)
{
z=ter[y][x];
if (xx>z) xx=z;
if (yy<z) yy=z;
}
for (y=0;y<mys;y++)
for (x=0;x<mxs;x++)
ter[y][x]=h0+(((ter[y][x]-xx)*(h1-h0))/(yy-xx));
// [Surface]
for (y=0;y<mys;y++)
for (x=0;x<mxs;x++)
{
z=ter[y][x];
// max slope [deg]
a=atan2(ter[y][x+1]-z,d_pixel);
b=atan2(ter[y+1][x]-z,d_pixel);
if (a<b) a=b; a*=180.0/M_PI;
c=_cover_none;
if (z<=h_water) c=_cover_water;
if (z>=h_snow ) c=_cover_snow;
t=_terrain_dirt;
if (z<=h_sand) t=_terrain_sand;
if (z>=h_rock) t=_terrain_rock;
if (a>=a_rock) t=_terrain_rock;
f=_flora_none;
if (t==_terrain_dirt)
{
r=Random(100);
if (r>10) f=_flora_grass;
if (r>50)
{
if (z>h_evergreen) f=_flora_evergreen;
else{
r=Random(h_evergreen);
if (r<=z) f=_flora_evergreen;
else f=_flora_hardwood;
}
}
if (r<5) f=_flora_deadwood;
}
typ[y][x]=(c<<_cover_shift)|(t<<_terrain_shift)|(f<<_flora_shift);
}
// [copy data] rewrite this part to suite your needs it just compute color based on type of terrain and height
// ter[][] is elevation in meters
// typ[][] is surface type
/*
for (y=0;y<_ys;y++)
for (x=0;x<_xs;x++)
pic.p[y][x].dd=(((ter[y][x]-h0)*255)/(h1-h0))*0x00010101;
for (y=0;y<_ys;y++)
for (x=0;x<_xs;x++)
{
r=typ[y][x];
c=(r>> _cover_shift)& _cover_mask;
t=(r>>_terrain_shift)&_terrain_mask;
f=(r>> _flora_shift)& _flora_mask;
r=_terrain[t];
if (c) r= _cover[c];
if (c==_cover_water)
{
xx=256-((ter[y][x]<<7)/h0);
yy=int(r>>16)&255; yy=(yy*xx)>>8; r=(r&0x0000FFFF)|(yy<<16);
yy=int(r>> 8)&255; yy=(yy*xx)>>8; r=(r&0x00FF00FF)|(yy<< 8);
yy=int(r )&255; yy=(yy*xx)>>8; r=(r&0x00FFFF00)|(yy );
}
if (f){ if (c) r|=_flora[f]; else r=_flora[f]; };
pic.p[y][x+_xs].dd=r;
}
*/
// free ter[][],typ[][]
for (y=0;y<=mys;y++) delete[] ter[y]; delete[] ter; ter=NULL;
for (y=0;y<=mys;y++) delete[] typ[y]; delete[] typ; typ=NULL;
}
当前设置的输出如下:
<强> [注释] 强>
这种方法通常只在岛上生产单一的大山。 (内陆生成正常)如果您想要更多内陆地图,您可以创建更多地形图并将它们平均在一起。
我做了以下事情:我将中间点设置为最大高度并忽略第一次钻石传球。在第一个方格后,我将中间点设置回一些随机值。这增加了更多中央山丘然后只有一个的可能性。使用这种方法并添加光照(环境+正常着色)来预览和略微调整像素大小(35m
)我得到了这个结果:
在极少数情况下,这可以产生像地图一样的内陆(如果中心区域太小。要处理它,你可以扫描角落的瓦特。如果再次产生陆地或者在第一次通过时增加一些中心点随机性的偏差。< / p>
您可以使用代码来添加河流: