perlin噪声_一天一点Processing|16Perlin噪声画线

58dd83f62470a9d752e8f47e303f9dc1.png

最近在网上看到一张图,据说是通过Perlin噪声实现的,今天我们就来实现这张图的效果吧。这次教程不难,大家可以先自行尝试尝试。

682c8251243d69565aac518e9c18c9b8.png

我在之前的教程中已经介绍过Perlin噪声,感兴趣的同学可以移步 一天一点Processing|04平滑的Perlin噪声 了解一下基础内容。

首先,我们需要准备一张方形的黑色画板,如果你比较有主见, 可以尝试各种颜色和尺寸的画板。

同时,我们可以准备多条线,每条线会有多个控制点来控制它的形状。并通过向量的二维数组来存储线上控制点的坐标信息。第一个[]信息为第几根线,[]表示线上的第几个控制点。

int lineNum = 64;             //设置线的总数
int controlPointNum = 32;     //设置每条线控制点总数
PVector[][] line = new PVector[lineNum+1][controlPointNum+1];   //二维数组记录线上点的参数,第几根线,第几个点(加1代表多一个点)
//直线尾会多一个点,所有要加1

void setup() {
  size(640, 640);    //设置画板尺寸
  smooth();
  initializeLine();  //初始化线
  frameRate(5);      //设置帧率方便观察
}

void draw() {
  background(0);      //黑色背景
  movePoint();        //移动线上的点
  drawLines();        //绘制线条
}

我们先搭建好大框架,然后我们需要来思考如何绘制线条,如何移动线上的点的坐标。

首先,我们需要初始化各个控制点的坐标。这里用到循环的方式给点逐个赋值。

//初始化线条
void initializeLine() {
  for (int i=0; i<lineNum+1; i++) {
    for (int j=0; j<controlPointNum+1; j++) {
      line[i][j] = new PVector(width/controlPointNum*j, height/lineNum*i);
    }
  }
}

然后根据坐标点绘制线条。仍是用循环。这次用过beginShape(); 和endShape(); 中间运用vertex();加入每条线的控制点来绘制线条。

//绘制线条
void drawLines() {
  //线条参数
  noFill();
  stroke(255);
  strokeWeight(0.7);

  //绘制线条
  for (int i=0; i<lineNum+1; i++) {
    beginShape();
    for (int j=0; j<controlPointNum+1; j++) {
      vertex(line[i][j].x, line[i][j].y);
    }
    endShape();
  }
}

e9c2d2cef60e13ca2a982474774979bf.png
单调的直线

我们现在就得到初始的线条。下一步就要让控制点进行偏移,让线条扭起来!!

我们可以用过movePoint()更改控制点的y坐标。

void movePoint() {
  for (int i=0; i<lineNum+1; i++) {
      for (int j=0; j< controlPointNum+1; j++){
      line[i][j].y = height/lineNum*i + random(-20, 20);
    }
  }
}

f083877673053624579c623d0de7c4b5.png

OMG!!!超级尖锐的线条,一定是我们连接线的方式不对。drawLines()函数中,用curveVertex()来连接控制点,就可以得到曲线。

      curveVertex(line[i][j].x, line[i][j].y);

950e657e82acdb715c4c21c3e6fc3c58.png

线条变得光滑了,但问题又产生了,为什么线条这么乱呢?有什么方法让它们变化得更顺滑呢??这里我们就要用到Perlin噪声中noise()函数来替代random()函数了。

line[i][j].y = height/lineNum*i + map(noise(0.1*i, 0.1*j), 0, 1, -80, 80);

我们会得到这张结果。乍一眼,比上面好多了,但是问题是,你为什么不动啊!!!

21c3f8ce4409a2b8fc86c94c20e05f1d.png

这其实和noise()函数有关,noise()函数在同一点的取值是固定的。在我们应用时,每次都从noise(0, 0)开始,得到的数其实是一致的,所以我们需要在每一次偏移之前让noise()稍稍偏移一些。

    float off = random(50);
line[i][j].y = height/lineNum*i + map(noise(0.1*i, 0.1*j+off), 0, 1, -120, 120);

现在图片就能够动起来了!!

但是仔细看会发现有一点点不完美的地方,有一些线会交叠在一起。所以我们可以在绘制线条之前看一看线是否会交叠,如果下一条和上一条交叠,给下一条线超出的几个控制点重新赋值。

      //通过判断点的上下,判断线条是否会交叉,如果交叉就把点降到上一条的下面
      if (i>0) {
        if (line[i][j].y <= line[i-1][j].y) {
          line[i][j].y = line[i-1][j].y + random(5);
        }
      }

8aad0a0cf78e8397659a019fbf937694.png

现在,得到的图形就不会存在线条交叉的现象了。

略有不完美就是两边出现了黑边。原因是因为通过curveVertex()绘制线条,起始点和终点只作为控制点,改进的方法就是在绘制线条时重新输入起点和终点。

  //绘制线条
  for (int i=0; i<lineNum+1; i++) {
    beginShape();
    curveVertex(line[i][0].x, line[i][0].y);
    for (int j=0; j<controlPointNum+1; j++) {
      curveVertex(line[i][j].x, line[i][j].y);
    }
    curveVertex(line[i][controlPointNum].x, line[i][controlPointNum].y);
    endShape();
  }

3c27ffd35b522e544a377a03b76ac3e9.png

2820a8296c6605b07f68d2cba1496072.png

最后附上代码供大家学习~~~~

int lineNum = 64;             //线的总数
int controlPointNum = 32;     //每条线控制点总数
PVector[][] line = new PVector[lineNum+1][controlPointNum+1];   //二维数组记录线上点的参数,第几根线,第几个点(加1代表多一个点)


void setup() {
  size(640, 640);    //设置画板尺寸
  smooth();
  initializeLine();  //初始化线条
  frameRate(5);      //设置帧率方便观察
}

void draw() {
  background(0);     //黑色背景
  movePoint();       //移动线上的点
  drawLines();       //绘制整条线
}

//初始化线条
void initializeLine() {
  for (int i=0; i<lineNum+1; i++) {
    for (int j=0; j<controlPointNum+1; j++) {
      line[i][j] = new PVector(width/controlPointNum*j, height/lineNum*i);
    }
  }
}

//绘制线条
void drawLines() {
  //线条参数
  noFill();
  stroke(255);
  strokeWeight(0.7);

  //绘制线条
  for (int i=0; i<lineNum+1; i++) {
    beginShape();
    curveVertex(line[i][0].x, line[i][0].y);
    for (int j=0; j<controlPointNum+1; j++) {
      curveVertex(line[i][j].x, line[i][j].y);
    }
    curveVertex(line[i][controlPointNum].x, line[i][controlPointNum].y);
    endShape();
  }
}


void movePoint() {
  float off = random(50);
  for (int i=0; i<lineNum+1; i++) {
    for (int j=0; j< controlPointNum+1; j++) {
      //line[i][j].y = height/lineNum*i + random(-20, 20); //随机偏移
      line[i][j].y = height/lineNum*i + map(noise(0.1*i, 0.1*j+off), 0, 1, -120, 120);

      //通过判断点的上下,判断线条是否会交叉,如果交叉就把点降到上一条的下面
      if (i>0) {
        if (line[i][j].y <= line[i-1][j].y) {
          line[i][j].y = line[i-1][j].y + random(5);
        }
      }
    }
  }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值