地图生成器梯度圈 [英] Gradient Circles for Map Generator

查看:136
本文介绍了地图生成器梯度圈的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我正在制作一个制作随机岛屿的地图生成器。它使用发生器中心的Perlin Noise,然后使用渐变圆圈的方法来形成岛屿。



圆形方法创建了一些圆圈,该图从渐变颜色从64开始下降到0.问题是这种方法是创建一个非天生的地图的部分与圆形边缘。当为一个像素生成perlin噪声时,它会在渐变图上得到该像素,然后将其转换成蓝色值。



所以如果perlin噪声给出一个在像素1,5上,并且梯度图上的蓝色值为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)
{
图形g = Graphics.FromImage(imgGrad);
GraphicsPath path = new GraphicsPath();
path.AddEllipse(X,Y,R,R);

PathGradientBrush pathGrBrush = new PathGradientBrush(path);
pathGrBrush.CenterColor = C1;

颜色[] colors = {C2};
pathGrBrush.SurroundColors = colors;
g.FillEllipse(pathGrBrush,X,Y,R,R);
//g.FillEllipse(Brushes.Red,X,Y,R,R);
g.Flush();
}

int amount = rnd.Next(25,30); (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;
}
如果(X <30)
{
X = 30;
}
如果(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噪音的岛屿?任何建议将不胜感激。

解决方案

正如我在评论中提到的,钻石和广场更容易,效果更好。所以算法:


  1. 配置生成属性



    在这里,您需要有一些参数,如最小,最大海拔,海平面,植被的高程范围,沙/岩石/污垢等等。斜率参数等。


  2. 创建地形高度图我称之为 zed [] []



    为此,您需要稍微修改



    [注释]



    这种方式通常只生产岛上唯一的大山丘。 (内陆生成OK)如果您想要更多的地图,您可以创建更多的地形图,并将它们平均在一起。



    我做了以下代码:将中点设置为最大高度和忽略第一个钻石通行证。在第一个平方通过后,我将中点设置回一些随机值。这增加了更多中央山丘的可能性,然后只有一个。使用这种方法和添加照明(环境+正常阴影)预览和稍微调整像素大小( 35m )我得到这个结果:





    在罕见的情况下,这可以生成内陆的地图(如果中心区域太小,要处理你可以扫描角落,如果有土地再次生成,或者在第一次通过时为中心点随机添加一些偏见。



    你可以玩代码,例如添加河流:


    1. 查找最上面的小丘

    2. 获取随机位置关闭/周围

    3. 将其设置为河流类型

    4. 找到没有设置为河流类型的最小高度邻居像素

    5. 如果它是地图的边缘或设置为海/水的类型停止否则循环#3



      如果你想要更多的话,那么一条河然后不要忘记使用一些温度pe已经完成了河流,所以算法可以正常工作。您还可以从距离起步增加河流量...这是结果:





      此后,您还应平衡形成的湖泊水位。



    So I'm making a map generator that makes random islands. It uses Perlin Noise at the heart of the generator and then a method using circles with gradients to make the islands.

    The circle method creates a number of circles in the centerish of the map with a gradient from a colour starting at 64 down to 0. The issue is that this method is creating a un-natrual look at parts of the map with circular edges. When the perlin noise is generated for a pixel it will get that pixel on the gradient map and then mutliply it by the blue value.

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

    Here is what I am getting:

    There is 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 was just wondering if anyone else knows any other methods in C# that could create an island using perlin noise? Any advice would be greatly appreciated.

    解决方案

    As I mentioned in the comment diamond and square is much easier with good enough results. So the algorithm:

    1. configure generation properties

      Here you need to have set of parameters like min,max elevation, sea level, elevation ranges for vegetation, sand/rock/dirt, etc, slope parameters etc.

    2. create terrain height map I call it zed[][]

      For this you need slightly modified Diamond&Square algorithm. The problem is this algorithm produces "inland" like terrain.

      To adjust it so it produces island like terrains you need to initialize it with lowest possible elevation in corners. Also you need to ignore the first diamond step and initialize the mid point with some random value instead (not average of corners). And last after each square iteration correct the border points to the minimal (underwater) elevation (or some random value near it).

      To achieve the good output I use approximately range <-2^15 , 2^16> while generation. After this I find min and max elevation in the generated terrain and rescale to configured elevation ranges.

      Do not forget that Diamond and square need map of resolution (2^n)+1 !!!

    3. create surface map I call it typ[][]

      When terrain map is finished you can add elevation based features like these in ascending order:

      • watter,sand,vegetation type,mountine rocks,snow

      Then add parameters based on slope of terrain

      • rocks

      Then you can add additional things like (based on some rules):

      • rivers,streams,watter-falls,building,roads,...

    I do it in C++ like this:

    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 current settings is like this:

    [Notes]

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

    I do following instead: I set the middle point to max height and ignore first diamond pass. After the first square pass I set the middle point back to some random value. This adds the possibility of more central hills then just one. Using this approach and adding lighting (ambient + normal shading) to preview and slight tweaking of pixel size (35m) I got this result:

    On rare occasion this can generate inland like map (if the central area is too small. To handle it you can scan corners for watter. if there is land generate again or add some bias for central points randomness in first pass.

    You can play with the code for example add rivers:

    1. find topest hill
    2. get random location close/around it
    3. set it to river type
    4. find smallest height neighbor pixel not set to river type
    5. if it is on edge of map or set to sea/water type stop otherwise loop #3

      If you want more then one rivers then do not forget to use some temp type for already done rivers so the algorithm can work properly. You can also increase the river volume with distance from start... Here is the result:

      After this you should also equalize the formed lakes water level.

    这篇关于地图生成器梯度圈的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆