ProcessingJoy ——爱如烟尘【JAVA】

342 阅读1分钟

这是我参与8月更文挑战的第8天,活动详情查看: 8月更文挑战

效果图

该效果其实核心思想是将多“层”的心形烟进行组合,首先我们来定义其中一 “层” 的心形烟的实现代码。

Heart.pde

class Heart{
  float seed = random(10,1000);
  float offset = random(TWO_PI);
  
  float ff = random(0.5,2.5);
  float sw = random(0.8,1.8);
  
  float part = 0.1+0.5*pow(random(1),2.0);
  float rf = random(0.5,1.15);
  
  float d = random(10,120);
  int m = 500; ///< 500 个点
  OpenSimplexNoise noise = new OpenSimplexNoise(); ///< 噪声,用于扰动心形
  
  float R = 150;
  float xh(float p){
    return R/15.0*16*pow(sin(p),3);
  }
  float yh(float p){
    return  R/15.0*(-13*cos(p) +5*cos(2*p) + 2*cos(3*p) + cos(4*p));
  }

  void show(float q){
    for(int i=0;i<m;i++){
      
      float p = 1.0*i/m;
      float theta = offset + part*TWO_PI*i/m;
      
      float rad = 1.3f;
      int per = 2;
      
      float xx = rf*xh(theta) + pow(p,3.0f)*d*(float)noise.eval(seed + rad*cos(TWO_PI*(per*p-q)),rad*sin(TWO_PI*(per*p-q)));
      float yy = rf*yh(theta) + pow(p,3.0f)*d*(float)noise.eval(2*seed + rad*cos(TWO_PI*(per*p-q)),rad*sin(TWO_PI*(per*p-q)));
      
      strokeWeight(sw);
      stroke(255,ff*18*sin(PI*p));
      
      point(xx,yy);
    }
  }
}

 其中关键的代码(如下所示)代表的公式可以绘制出心形(将 360° 分成了 m 份,然后每个位置放置一个点,只要划分的足够细,就可以看起来像是一个完整的 ♥)

  float R = 150;
  float xh(float p){
    return R/15.0*16*pow(sin(p),3);
  }
  float yh(float p){
    return  R/15.0*(-13*cos(p) +5*cos(2*p) + 2*cos(3*p) + cos(4*p));
  }

 简化后对应函数示意图如下所示

然后通过叠加 Simplex 噪声的随机扰动,我们就可以模拟出烟尘的效果。

HeartSmoke.pde(主入口)

 自此基础上,接着我们构造多层(本例为 250 层)的心形烟尘,并将它们叠加起来一层一层绘制

float t, c;

int samplesPerFrame = 5;
int numFrames = 100;        
float shutterAngle = .6;

int n = 250; ///< 250 层 ♥

Heart[] array = new Heart[n];

void setup(){
  size(600,600,P3D);

  for(int i=0;i<n;i++){
    array[i] = new Heart();
  }
}

void show(){
  background(0);
  push();
  translate(width/2,height/2);
  
  for(int i=0;i<n;i++){
    array[i].show(t);
  }
  
  pop();
}

void draw() {

    t = mouseX*1.0/width;
    c = mouseY*1.0/height;
    if (mousePressed)
      println(c);
      
    frameCount = 0;
    show();

    /// @note 保存序列 gif,后期可用 gifsicle 合成真正的 gif
    // saveFrame("fr###.gif");
}

void keyPressed(){
  if(key == 'r' || key == 'R'){
    recording = !recording;
    println("recording:" + recording);
  }
}

最后附上 OpenSimplexNoise.pde(由于代码过长,为了不影响阅读,所以上传到百度网盘),关于噪声的详解可以看我之前写的一篇ShaderJoy —— 噪声之美,大家一起 “噪” 起来 【GLSL】

链接:pan.baidu.com/s/1uKqedakx… 
提取码:u8ru 

如何合成 GIF 

gifsicle 下载链接:www.lcdf.org/gifsicle/ 

下载安装完成之后,打开命令行工具,执行以下命令(默认各帧的命名模式为 fr*.gif) ,最终生成 anim.gif,各帧的延迟为 10 / 100 s

gifsicle --delay=10 --loop fr*.gif > anim.gif

gifsicle 支持的更加复杂的命令行请参见其官方手册