天天看点

融入动画技术的交互应用——Particle World

融入动画技术的交互应用——Particle World
Even in the dark, you must know that the stars, the sun, and the moon have never left. We can’t see the light, but the light is always there.

背景&创意

“I want to swim in the ocean of stars!”

我喜欢星空,不记得从什么时候开始我已经看不到夜晚的星空,我只能从记忆和图片里享受这种静谧和浩瀚。遥远的星团在慢慢转动,几颗耀眼的恒星在拼命发光,无数的小星星点缀了原本沉寂的漆黑。我有个想法,如果我能触碰这些光亮,再如果我能操控这些星团描绘出我内心的星海,现在这些已经成为现实。我要用我的Particle World程序,呈现我内心的星海!

Particle World是基于Processing开发的一款简单的模拟星空程序,利用粒子模型结合引力、斥力和向量相关技术最大限度的呈现了星团、恒星、小星星等星空中的可见部分,并且添加了一些交互如吸引、扰动等影响星空动态的功能1。

功能

1.创建自定义的窗口大小;

2.随机生成不同颜色大小的粒子;

3.移动鼠标,在鼠标周围产生可变范围的斥力,影响粒子运动;

4.按住鼠标并移动,在鼠标位置处产生可变范围的吸引力,影响所有粒子的运动;

5.窗口边界检测,粒子生存周期检测;

6.随机生成星团;

程序框架

融入动画技术的交互应用——Particle World

核心技术2

完整的粒子系统的建立

在详细阅读了《代码本色》的粒子系统部分章节以后,结合目标程序实现功能的复杂性,决定先建立好基础,即将粒子系统做的尽可能完善,粒子系统的代码框架如下。

融入动画技术的交互应用——Particle World

其中边界检测函数,对粒子触碰到窗口的上、下、左、右四个边界进行检测,当粒子触碰到边界时,将会以同样大小的力向相反的方向进行反弹。

// 窗口边缘检测函数
  void checkEdges(int flag) {
    switch(flag) {
      // 触及边缘反弹
      case 1:
        if (location.x > width) {
          location.x = width;
          velocity.x = -1;
        }
        if (location.x < 0) {
          location.x = 0;
          velocity.x = 1;
        }
        if (location.y > height) {
         velocity.y *= -1;
         location.y = height;
        }
        if (location.y < 0) {
          velocity.y *= -1;
          location.y = 0;
        }
           

星团

实现思路:显示窗口内随机位置处一定范围内生成大量粒子,此范围内的所有粒子受到相同的向心力的作用,围绕星团中心做向心运动3。

1.星团圆形范围内随机生成粒子,如果用传统的random()函数,在给定范围内随机取二维坐标,随着粒子数量的增加,最终星团呈现的是是一个正方形区域,要使粒子能够在圆形的区域内随机生成,首先要编写一个circleLocation()函数,确定星团内粒子的位置。

// 圆形范围内随机取点函数
  PVector circleLocation() {
    PVector cl = new PVector();
    float currentR;
    float currentAngle;
    currentR = random(0,scope);
    currentAngle = random(1,360);
    cl.x = origin.x + sin(currentAngle * PI / 180) * currentR;
    cl.y = origin.y + cos(currentAngle * PI / 180) * currentR;
    return cl;
  }
           

2.星团内所有粒子要针对星团中心做向心运动,首先要遍历在星团范围内的所有粒子,根据公式加速度与引力的中心与粒子的距离的二次方成反比,对所有粒子的运动方式进行调整。

// 星团内粒子应用引力
void applyInward() {
  float magnetism = 100.0; // 引力强度
  float deceleration = 0.01; // 移动减速
  for (MyParticle p: starCluster) {
    // 计算星团中心和星团内粒子的距离
    float distance = dist(origin.x,origin.y,p.location.x,p.location.y);
    if (distance > 1) {
      p.acceleration.x = magnetism * (origin.x - p.location.x) / (distance * distance);
      p.acceleration.y = magnetism * (origin.y - p.location.y) / (distance * distance);
    }
    p.velocity.x += p.acceleration.x ;
    p.velocity.y += p.acceleration.y ;
    p.velocity.x = p.velocity.x * deceleration;
    p.velocity.y = p.velocity.y * deceleration;
    p.location.x += p.velocity.x;
    p.velocity.y += p.velocity.y;
  }
}
           
鼠标引力/斥力点

根据代码本色的指导,粒子类(Particle)中的加速度是一个常量,它从来不会发生变化,为了为了实现一个更好的模拟框架应该遵循牛顿力学定律,并将力的累加算法作用在粒子上,创建一个applyForce()函数;进一步优化粒子系统,在其中加入引力对象(Attractor)和斥力对象(Repeller),与applyForce()函数给粒子作用力不同,引力和斥力对于每个粒子的作用力都不同,所以需要对每个粒子进行逐个计算。在加入引力和斥力对象的过程中,不需要在粒子类(Particle)上作出任何修改,粒子不需要知道外部环境细节,它只需要管理自身的位置、速度、加速度,并接受外力的作用。

class Attractor {
  float strength = 300; // 引力强弱
  PVector location;  // 吸引对象并不移动,只要知晓位置即可
  float r = 10;
  
  Attractor(float x,float y) {
    location = new PVector(x,y);
  }
  
  void display() {
    stroke(255);
    fill(175);
    ellipse(location.x,location.y,r*2,r*2);
  }
  
  // 计算斥力(与引力方向相反)
  PVector attract(MyParticle p) {
    // 1.获取力的方向
    PVector dir = PVector.sub(location,p.location); // 力的方向
    // 2.获取距离(限制距离)
    float d = dir.mag();
    d = constrain(d,5,100);
    
    dir.normalize();
    // 3.计算力的大小
    float force = 1*strength/(d*d);
    // 4.组合方向和大小
    dir.mult(force);
    return dir;
  }
}
           
呈现效果
融入动画技术的交互应用——Particle World
总结

三体里有一句话“有时下夜班,仰望星空,觉得群星就像发光的沙漠,我自己就是一个被丢弃在沙漠.上的可怜的孩子…我有那种感觉:地球生命真的是宇宙中的偶然里的偶然,宇宙是个空荡荡的大宫殿,人类是这宫殿中唯一的一只蚂蚁。这想法让我的后半辈子有一种很矛盾的心态:有时觉得生命真珍贵,一切都重于泰山;有时觉得人是那么渺小,什么都不值得一提。反正日子就在这种奇怪的感觉中一天天过去,不知不觉人就老了”

当看向星空的时候,你明确的知道我们,我们所有的一切是一个整体,孩童会张开手想要摸摸星云,我们这些自名为成年人的无趣者不再仰望星空,事实生活里的琐事烦扰着我们,星空?不了,哦黑洞被拍到了吗?可以看看,但也没有什么改变吧。可是我们对生命的敬畏和对未知的探寻呢?也这么被搁置了吧。

“我们都是阴沟里的虫子,但总还是得有人仰望星空”。

  1. 部分功能需要进一步优化,以实际程序呈现效果为主。 ↩︎
  2. 部分功能实现参考《代码本色》。 ↩︎
  3. 可继续优化为椭圆运动轨迹。 ↩︎

继续阅读