Gradient circles for map generator

So, I am creating a map generator that creates random islands. It uses the Perlin Noise at the base of the generator, and then the method using circles with gradients to create islands.

The circle method creates several circles in the center of the map with a gradient from a color starting from 64 to 0. The problem is that this method creates a non-natural look at parts of the map with circular edges. When perlin noise is generated for a pixel, it will receive that pixel on the gradient map, and then gradually increase it to a blue value.

So, if the perlin noise gives one per pixel 1, 5, and the blue value on the gradient map is 54, it will give a noise value of 54. If the perlin noise on pixel 130, 560 is 0.5, and the color of the gradient is 64, then the value noise 32.

Here is what I get:

Picture of what I am getting

There are two key points to the code: the perlin bit:

noise = NoiseGenerator.Noise(x, y); double gradColour = getGradColour(x, y).B; double addedNoise = noise * gradColour; double gradNoise = addedNoise;// - gradColour; 

And then the gradient map generator:

  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))); } 

I'm just wondering if anyone knows of other C # methods that could create an island using perlin noise? Any advice would be greatly appreciated.

+1
source share
1 answer

As I mentioned in the commentary, diamond and square are much lighter with good results. Thus, the algorithm:

  • set generation properties

    Here you need to have a set of parameters, such as min, max elevation, sea level, altitude ranges for vegetation, sand / stone / dirt, etc., slope parameters, etc.

  • create a height map of the terrain I call it zed[][]

    To do this, you need to slightly modify the Diamond & Square algorithm. The problem is that this algorithm creates a "local" landscape.

    To set it up so that it creates island landscapes, you need to initialize it with a minimum elevation in the corners. You also need to ignore the first step of the diamond and initialize the midpoint with some random value (not average in the corners). And the last time after each square iteration, the correct border indicates the minimum (underwater) height (or some random value next to it).

    To achieve a good result, I use approximately the range <-2^15 , 2^16> when generating. After that, I find the minimum and maximum elevation in the generated landscape and scale to the specified height ranges.

    Do not forget that a card with a diamond and a square with a resolution of (2^n)+1 !!!

  • create a surface map I call it typ[][]

    When the terrain map is finished, you can add features such as ascending order:

    • water, sand, type of vegetation, rocks, snow.

    Then add options based on the slope of the terrain.

    • rocks

    Then you can add additional things, for example (based on some rules):

    • rivers, streams, waterfall-waterfall, construction, roads, ...

I am doing this in C ++ as follows:

 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; } 

The output with the current settings looks like this:

example

[Note]

This approach usually creates only one large hill on the island. (Inland is generated OK) If you want more of them, you can create more terrain maps and average them together.

Instead, I do the following: I set the midpoint to maximum height and ignore the first diamond run. After the first square pass, I returned the midpoint to some random value. This adds the possibility of more central hills, and then only one. Using this approach and adding lighting (ambient + normal shading) for a preview and a little pixel size adjustment ( 35m ), I got this result:

example

In rare cases, this can generate an internal map (if the central area is too small). To deal with it, you can scan the corners for a watter. If there is ground generating again or adding some offset for randomness at central points in the first pass.

You can play with the code, for example, add rivers:

  • find the highest hill
  • get a random location near / around it
  • set it to river type
  • find the smallest neighboring height pixel not set for river type
  • if it is on the edge of the map or set to sea / water stop mode, otherwise cycle number 3

    If you want more than one river, then do not forget to use some kind of temporary type for already made rivers so that the algorithm can work correctly. You can also increase the volume of the river from a distance from the beginning ... Here is the result:

    river

    After that, you should also equalize the water level in the lake.

+2
source

Source: https://habr.com/ru/post/1245857/


All Articles