代码本色0章——Perlin噪声生成起伏地形
此博文依据代码本色第0章中的随机数与Perlin噪声运用写成,并观看了丹尼尔希夫曼关于perlin噪声的讲解,https://www.youtube.com/watch?v=D8UgRyRnvXU,大家可以看一看
参考案例
效果图
Perlin噪声
一个好的随机数生成器能产生互不关联且毫无规律的随机数。跟我们前面看到的一样,一定程度的随机性有利于有机体和生命活动的建模。然而,单独把随机性作为唯一指导原则是不够的,它并不完全符合自然界的特征,Perlin噪声将这一点考虑在内了,此算法可用于生成各种自然特效,包括云层、地形和大理石纹理。
Perlin噪声算法表现出了一定的自然性,因为它能生成符合自然排序(“平滑”)的伪随机数序列。
processing内置了Perlin噪声算法的实现:noise()函数。noise()函数可以有1~3个参数,分别代表,一维,二维,三维的随机数。
图(a)
图(b)
图(a)为noise函数,图(b)为random函数可见noise函数更为光滑,没有图(b)中小球跳跃的感觉,可见,noise函数可以是的图形更加平滑和连续。
void setup()
{
size(400, 400);
}
float t=3;
void draw()
{
background(255);
translate(100,100);
float n=map(noise(t),0,1,-100,100);
ellipse(n,180,16,16);
t+=0.1;
}
此代码看起来很简单,但是切记需要用,map()函数来约束noise的范围,关于map函数可以在官网reference找到,这里就不解释了。
地形绘制
首先观察上面的图,会发现一下规律:
1.起伏的地形由很多三角形组成,且三角形大小相同;
2.地形起伏随机;
从图上我们只能看出这么多信息。下面我们来实践
三角形区域创建
//
首先,第一步,我们先生成多个小网格,首先就会想到用rect()函数,于是生成了上面,但是我们发现,如果是这样,生成三角形就会有困难,因为效果图中绘制的三角形并不在直线上,因此我们想到processing自带的vertex()函数,于是图形又成了这样
//
int cols,rows;
int density=20;
void setup()
{
size(600, 400);
int w=600;
int h=800;
cols=w/density;
rows=h/density;
}
void draw()
{
background(0);
stroke(255);
noFill();
for(int x=0;x<cols;x++)
{
beginShape(TRIANGLE_STRIP);
for(int y=0;y<rows-1;y++)
{
vertex(y*density,x*density);
vertex(y*density,(x+1)*density);
}
endShape();
}
}
//
完成上面的步骤之后,我们会发现这还只是在二维平面上的,我们要怎么样转成三维的呢?其实只要在size()里面加入P3D这个参数即可,所以图形又变成了这样。
///
这个地方要注意的是,成为三维图形之后,需要调整角度,即用rotate()函数将图形转一转,同时还需要调整图形显示的地方才可以看见,不然就是一条线或者一片黑。同时随时改变三角形的顶点,即可生成如下效果。
/
//
为了使用方便我们新建一个二维矩阵,存储变化的点。
代码如下:
float[][] terrain;
void setup() {
size(600, 600,P3D);
cols=w/density;
rows=h/density;
terrain=new float[cols][rows];
}
void draw()
{
for(int x=0;x<cols;x++)
{
for(int y=0;y<rows;y++)
terrain[x][y]=map(noise(x,y),0,1,-50,50);
}
background(0);
stroke(255);
noFill();
translate(width/2,height/2);
rotateX(PI/4);
translate(-w/2,-h/2);
for(int x=0;x<cols;x++)
{
beginShape(TRIANGLE_STRIP);
for(int y=0;y<rows-1;y++)
{
vertex(y*density,x*density,terrain[x][y]);
vertex(y*density,(x+1)*density,terrain[x][y+1]);
}
endShape();
}
}
之后呢,我们需要让此地形起伏慢一点并且,能够运动起来,我们选择其中的因此,可知我们只需改变noise()的x,y值即可,以y为例,我们每次绘制的时候都变化0.1(加减都可);
jump-=0.1;
float yoff=0;
for(int y=0;y<rows;y++)
{
float xoff=jump;
for(int x=0;x<cols;x++)
{
terrain[x][y]=map(noise(xoff,yoff),0,1,-100,100);
xoff+=0.2;
}
yoff+=0.2;
}
上面的代码段加载draw函数中,这样才能动起来
至此,我们就完成了地形构造。改变其中的density可以改变地形的平缓度,改变下off,yoff,可以让地形沿着不同的方向生成,大家感兴趣可以自己试一试!
为了方便大家,以下贴出所有代码。
int cols,rows;
int density =20;
int w=2000;
int h=1600;
float[][] terrain;
float jump=0;
void setup()
{
size(600,600,P3D);
cols=w/density;
rows=h/density;
terrain=new float[cols][rows];
}
void draw()
{
jump-=0.1;
float yoff=jump;
for(int y=0;y<rows;y++)
{
float xoff=0;
for(int x=0;x<cols;x++)
{
terrain[x][y]=map(noise(xoff,yoff),0,1,-100,100);
xoff+=0.2;
}
yoff+=0.2;
}
background(0);
stroke(255);
noFill();
translate(width/2,height/2+50);
rotateX(PI/3);
translate(-w/2,-h/2);
for(int y=0;y<rows-1;y++)
{
beginShape(TRIANGLE_STRIP);
for(int x=0;x<cols;x++)
{
vertex(x*density,y*density,terrain[x][y]);
vertex(x*density,(y+1)*density,terrain[x][y+1]);
}
endShape();
}
}