我试图在程序上产生一些河流.
我有一个平面(没有高程概念)方格作为基础,并希望在其上绘制一个分支结构,如图所示.
你能分享一下可以用来完成的步骤吗?
我不是在寻找最快的实现,因为没有实时生成,但更简单的实现将是首选. Lua是我的语言,但任何事都可以.
更多的东西:
>形状应该是算法生成的.
>形状应该是
使用种子值可控制.
解决方法
我认为生成河流是一种落后的方法,因为你需要根据它们的形状调整很多东西,这将很难.我会创建随机地形高度图并从中提取特征(如在现实世界中),这更容易且更接近现实.在最终地图中,您忽略高度并使用平面高度(如果您真的想要平面地图).以下是您可以从高度图中提取的一些内容:
>河流和湖泊
通过播种随机高海拔点并沿着它下坡到海平面或地图边缘.
>植被或地面
从坡度和高度,你可以确定地面是沙子,泥土,岩石.如果有树木,灌木丛,草地或其他什么.
在这里看看这个QA:random island generator
和一些河流概述:
种子也在为这种方法工作.
[Edit1]承诺C代码
这基本上生成随机高度图然后种子和下坡跟随河流(如果地形块下坡流动,湖泊会自动生成).地形类型也由坡度和高度确定.
//--------------------------------------------------------------------------- picture pic; //--------------------------------------------------------------------------- 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=35.0; // [m] pixel size int d_river_w=5; // [pixel] river max width int d_river_l=150; // [pixel] river base length per width increase bool _island=true; // types enum _cover_enum { _cover_none=0,_cover_water,// sea _cover_snow,_covers,_cover_shift=0,_cover_mask=15,}; DWORD _cover[_covers]= { // RRGGBB 0x00000000,// none 0x00003080,// watter (sea) 0x00EEEEEE,// snow }; enum _terrain_enum { _terrain_dirt=0,_terrain_sand,_terrain_rock,_terrain_water,// streams,rivers,lakes _terrain_temp,// temp _terrains,_terrain_shift=4,_terrain_mask=15,}; DWORD _terrain[_terrains]= { // RRGGBB 0x00301510,// dirt 0x00EEC49A,// sand 0x006F6F6F,// rock 0x00006080,// water (streams,lakes) 0x00006080,// temp }; enum _flora_enum { _flora_none=0,_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,da; int c,t,f; int x,y,z,xx,yy,mxs,mys,dx,dy,dx2,dy2,r,r2,ix,l; int xh1,yh1; // topest hill position 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] for (;;) { // diamond & square random height map -> ter[][] dx=mxs; dx2=dx>>1; r=(mxs+mys)<<1; // 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]=r+r; // top of central hill } 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; } if (_island) { // recompute middle position after first pass so there can be more central hills if (dx==mxs) ter[dy2][dx2]=Random(r2); // adjust border to underwatter 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>>=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; xh1=x; yh1=y; } } for (y=0;y<=mys;y++) for (x=0;x<=mxs;x++) ter[y][x]=h0+(((ter[y][x]-xx)*(h1-h0))/(yy-xx)); // test for correctness if (_island) { l=0; for (x=0;x<=mxs;x++) { if (ter[0][x]>h_water) l++; if (ter[mys][x]>h_water) l++; } for (y=0;y<=mys;y++) { if (ter[y][0]>h_water) l++; if (ter[y][mxs]>h_water) l++; } if (l>1+((mxs+mys)>>3)) continue; } break; } // [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); } // [Rivers] for (ix=10+Random(5),a=0.0,da=2.0*M_PI/float(ix);ix;ix--) { // random start around topest hill a+=da*(0.75+(0.50*Random())); for (l=0;l<10;l++) { b=Random(mxs>>3); x=xh1; x+=float(b*cos(a)); y=yh1; y+=float(b*sin(a)); if ((x<1)||(x>=mxs)) continue; if ((y<1)||(y>=mys)) continue; if (typ[y][x]&0x00F==_cover_water) continue; l=-1; break; } if (l>=0) continue; // safety check for (l=0,r2=0;;) { // stop on map edge if ((x<=0)||(x>=mxs-1)||(y<=0)||(y>=mys-1)) break; // decode generated surface r=typ[y][x]; c=(r>> _cover_shift)& _cover_mask; t=(r>>_terrain_shift)&_terrain_mask; f=(r>> _flora_shift)& _flora_mask; // stop if reached sea if (c==_cover_water) break; // insert river dot radius = r2 dx=x-r2; if (dx<0) dx=0; dx2=x+r2; if (dx2>=mxs) dx2=mxs-1; dy=y-r2; if (dy<0) dy=0; dy2=y+r2; if (dy2>=mys) dy2=mys-1; for (yy=dy;yy<=dy2;yy++) for (xx=dx;xx<=dx2;xx++) if (((xx-x)*(xx-x))+((yy-y)*(yy-y))<=r2*r2) if (((typ[yy][xx]>>_terrain_shift)&_terrain_mask)!=_terrain_water) typ[yy][xx]=(typ[yy][xx]&0x00F)|(_terrain_temp<<_terrain_shift); // step to smalest elevation neighbor dx=x; dy=y; z=h1; typ[y][x]=(typ[y][x]&0x00F)|(_terrain_water<<_terrain_shift); xx=x; yy=y; xx--; r=ter[yy][xx]; if ((z>=r)&&(((typ[yy][xx]>>_terrain_shift)&_terrain_mask)!=_terrain_water)) { z=r; dx=xx; dy=yy; } yy--; r=ter[yy][xx]; if ((z>=r)&&(((typ[yy][xx]>>_terrain_shift)&_terrain_mask)!=_terrain_water)) { z=r; dx=xx; dy=yy; } xx++; r=ter[yy][xx]; if ((z>=r)&&(((typ[yy][xx]>>_terrain_shift)&_terrain_mask)!=_terrain_water)) { z=r; dx=xx; dy=yy; } xx++; r=ter[yy][xx]; if ((z>=r)&&(((typ[yy][xx]>>_terrain_shift)&_terrain_mask)!=_terrain_water)) { z=r; dx=xx; dy=yy; } yy++; r=ter[yy][xx]; if ((z>=r)&&(((typ[yy][xx]>>_terrain_shift)&_terrain_mask)!=_terrain_water)) { z=r; dx=xx; dy=yy; } yy++; r=ter[yy][xx]; if ((z>=r)&&(((typ[yy][xx]>>_terrain_shift)&_terrain_mask)!=_terrain_water)) { z=r; dx=xx; dy=yy; } xx--; r=ter[yy][xx]; if ((z>=r)&&(((typ[yy][xx]>>_terrain_shift)&_terrain_mask)!=_terrain_water)) { z=r; dx=xx; dy=yy; } xx--; r=ter[yy][xx]; if ((z>=r)&&(((typ[yy][xx]>>_terrain_shift)&_terrain_mask)!=_terrain_water)) { z=r; dx=xx; dy=yy; } if ((dx==x)&&(dy==y)) { // handle invalid path or need for a lake!!! if (dx>mxs>>1) dx++; else dx--; if (dy>mys>>1) dy++; else dy--; } x=dx; y=dy; // increase river volume with length l++; if (l>d_river_l*(r2+1)) { l=0; if (r2<d_river_w) r2++; } } // make merging of rivers possible for (y=0;y<=mys;y++) for (x=0;x<=mxs;x++) if (((typ[y][x]>>_terrain_shift)&_terrain_mask)==_terrain_water) typ[y][x]=(typ[y][x]&0x00F)|(_terrain_temp<<_terrain_shift); } for (y=0;y<=mys;y++) for (x=0;x<=mxs;x++) if (((typ[y][x]>>_terrain_shift)&_terrain_mask)==_terrain_temp) typ[y][x]=(typ[y][x]&0x00F)|(_terrain_water<<_terrain_shift); // [copy data] rewrite this part to suite your needs for (y=1;y<_ys;y++) for (x=1;x<_xs;x++) { float nx,ny,nz,x0,y0,z0,x1,y1,z1; // (nx,nz) = surface normal nx=0.0; ny=0.0; nz=ter[y][x]; x0=-d_pixel; y0=0.0; z0=ter[y][x-1]; x1=0.0; y1=-d_pixel; z1=ter[y-1][x]; x0-=nx; x1-=nx; y0-=ny; y1-=ny; z0-=nz; z1-=nz; nx=(y0*z1)-(z0*y1); ny=(z0*x1)-(x0*z1); nz=(x0*y1)-(y0*x1); x0=1.0/sqrt((nx*nx)+(ny*ny)+(nz*nz)); nx*=x0; ny*=x0; nz*=x0; // z = ambient light + normal shading nz=(+0.7*nx)+(-0.7*ny)+(+0.7*nz); if (nz<0.0) nz=0.0; nz=255.0*(0.2+(0.8*nz)); z=nz; // r = base color 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 (f){ if (c) r|=_flora[f]; else r=_flora[f]; }; // sea color is depending on depth not surface normal if (c==_cover_water) z=256-((ter[y][x]<<7)/h0); // apply lighting z to color r yy=int(r>>16)&255; yy=(yy*z)>>8; if (yy>255) yy=255; r=(r&0x0000FFFF)|(yy<<16); yy=int(r>> 8)&255; yy=(yy*z)>>8; if (yy>255) yy=255; r=(r&0x00FF00FF)|(yy<< 8); yy=int(r )&255; yy=(yy*z)>>8; if (yy>255) yy=255; r=(r&0x00FFFF00)|(yy ); // set pixel to target image pic.p[y][x].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; } //---------------------------------------------------------------------------
该代码基于我所链接的答案中的代码,但具有附加功能(包括河流).我使用自己的图片类图片,所以一些成员是:
> xs,ys图像大小(以像素为单位)
> p [y] [x] .dd是(x,y)位置的像素,为32位整数类型
>清晰(颜色) – 清除整个图像
> resize(xs,ys) – 将图像调整为新分辨率
> bmp – VCL封装了带Canvas访问的GDI Bitmap
您可以调整Diamond& Square中的调整随机性以更改地形平滑度.高度限制和阈值也可以被篡改.
为了实现像河流这样的更多的增长,在群集中种植更多的起始点,以便它们能够及时合并到单个或更多的河流中.