融入动画技术的粒子效果文字动画交互应用

写在前面

本次实现的交互系统是基于粒子系统的粒子文本效果。本次课程设计主要参考代码本色一书中的内容,系统应用中运用了 《代码本色》 第一章 向量、第二章 、第四章 粒子系统等章节的动画技术,实现的一个文本粒子交互系统。该系统分为白天黑夜两种模式,通过键盘的M 、N键可进行模式的切换,读入本地文本数据,将文本数据用不同类型的粒子实现可视化效果。

接下来进入正题

大家先看看下面这张动图:
它是AE插件实现的文字粒子动画效果,在很多电影电视剧片片头字幕中,都会出现有文字散成飞沙粉尘,这种效果会要用到Particular粒子插件——一款AE粒子插件,它的粒子效果比较酷炫,视觉感官上比较引人注意。

在这里插入图片描述

那么,能不能编程来实现一个粒子效果的文字动画特效应用呢

于是就有了本次文字动画特效系统的实现。 此次课程设计的系统基于以上效果,用processing来实现一个效果更丰富,视觉上冲击更强的系统。

要介绍本系统,就不得不介绍一个重要概念——粒子系统 在讲解课程设计过程和最终呈现效果之前,我们先来了解一下 粒子系统 到底是个啥

一、简介

1.1 粒子系统定义

什么是粒子系统?

1982年,卢卡斯影业的研究员William. T. Reeves正致力于电影《星际迷航2:可汗之怒》的制作。整部电影都围绕着创世武器展开,它是一种鱼雷,能让荒芜死寂的星球发生物质重组,最后创造出适合人类居住的环境。电影中有这样一幕,某个星球在被“改造”的过程中,表面蔓延着一道火墙。粒子系统这个术语,就是在这个特效的制造过程中出现的,后来它成为计算机图形学中最常用的技术之一。“粒子系统是由许多粒子组成的用于代表模糊对象的集合。在一段特定时间内,粒子在系统中生成、移动、转化,最后消亡。”
在这里插入图片描述
在现在,粒子系统通常表示三维计算机图形学中模拟一些特定的模糊现象的技术,而这些现象用其它传统的渲染技术难以实现的真实感的游戏图形。经常使用粒子系统模拟的现象有火、爆炸、烟、水流、火花、落叶、云、雾、雪、尘、流星尾迹或者象发光轨迹这样的抽象视觉效果等等。

这样说可能比较抽象,下面我们通过几张动图来了解和认识它。

① 光束

在这里插入图片描述

② 火焰

在这里插入图片描述

③ 几何体

在这里插入图片描述

通过以上几张动图,我们可以发现,粒子系统一般是由一些基本的元素或者粒子在一定的时间内经过不同的变换而形成的动态效果。通俗的说:粒子系统是一系列独立对象的集合,这些对象通常用简单的图形或者点来表示。

1.2 粒子系统介绍

粒子系统的每个颗粒具有预定的寿命,通常为几秒,在此期间它可以经历各种变化。它在粒子系统生成或发射时开始生命。系统在空间区域内的随机位置发射粒子,形状像球体,半球,圆锥体,盒子或任意网格。显示粒子直到其时间结束,此时它将从系统中移除。系统的排放率虽然确切的发射时间略有随机化,但大致表示每秒发射的粒子数量。排放率和平均颗粒寿命的选择决定了“稳定”状态下的颗粒数量(即,排放和颗粒死亡以相同的速率发生)以及系统达到该状态所需的时间。

发射和寿命设置会影响系统的整体行为,但单个粒子也会随时间而变化。每个都有一个速度向量,确定粒子随每次帧更新移动的方向和距离。速度可以通过系统本身施加的力和重力来改变,或者当粒子被风区吹动时,可以改变速度上的地形。每个粒子的颜色,大小和旋转也可以在其寿命期间或与其当前的移动速度成比例地改变。颜色包括α(透明度)成分,因此可以使粒子逐渐消失,而不是简单地突然出现和消失。

1.3 粒子系统的应用

粒子系统在许多平台上都有广泛的应用,比如unity、Adobe After Effect(AE) 3DMAX、Processing等等,记得去年曾用c语言写过基于OPENGL雨雪的粒子系统,代码结构还是比较复杂的,而在Processing中,由于有自带的粒子系统库,所以粒子系统实现起来相对来说会简单很多。

2 交互系统应用

2.1 整体构思

对于文本粒子交互系统,在AE中有许多插件可以实现,但是在Processing平台应用还不是很广泛,该系统力争实现AE插件中的粒子效果。

基于该交互系统,可延伸扩展为相应的插件或者代码库,便于用户更加方便快捷的实现文本的粒子效果。用户可在该系统上进行扩展,添加额外的功能或者在此基础上加入用户自身的创意和想法。

本次实现的交互系统是基于粒子系统的粒子文本效果。本次课程设计主要参考代码本色一书中的内容,系统应用中运用了《代码本色》第一章 向量、第二章 、第四章 粒子系统等章节的动画技术,实现的一个文本粒子交互系统。该系统分为白天黑夜两种模式,通过键盘的M 、N键可进行模式的切换,读入本地文本数据,将文本数据用不同类型的粒子实现可视化效果。

本系统实现效果
(1)开始界面(鼠标在屏幕上任意点击即可进入交互界面)

在这里插入图片描述
黑夜模式(屏幕上有对应按钮,这里录制动图时没有录进去):
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
白天模式:
在这里插入图片描述
在这里插入图片描述

2.2 系统框架及代码实现

在这里插入图片描述
1、粒子系统类:Particle
先定义基本的粒子属性:粒子位置,速度,加速度等。然后是粒子系统内的函数实现

(1)move函数:用于检查粒子是否足够接近其目标以减慢速度

void move() {
    // Check if particle is close enough to its target to slow down
    float proximityMult = 1.0;
    float distance = dist(this.pos.x, this.pos.y, this.target.x, this.target.y);
    if (distance < this.closeEnoughTarget) {
      proximityMult = distance/this.closeEnoughTarget;
    }

    // Add force towards target
    PVector towardsTarget = new PVector(this.target.x, this.target.y);
    towardsTarget.sub(this.pos);
    towardsTarget.normalize();
    towardsTarget.mult(this.maxSpeed*proximityMult);

    PVector steer = new PVector(towardsTarget.x, towardsTarget.y);
    steer.sub(this.vel);
    steer.normalize();
    steer.mult(this.maxForce);
    this.acc.add(steer);

    // Move particle
    this.vel.add(this.acc);
    this.pos.add(this.vel);
    this.acc.mult(0);
  }

(2)draw函数:用于粒子的绘制,有多种不同类型的粒子以供用户选择。用户可以通过点击屏幕上的按钮或者点击键盘上对应的字母来更改粒子类型。

void draw(){
    
    color currentColor = lerpColor(this.startColor, this.targetColor, this.colorWeight);
    
    switch(choice){
       case 'A': 
         stroke(currentColor);
         point(this.pos.x, this.pos.y);
         break;
         
       case 'B': 
         noStroke();
         fill(currentColor);
         ellipse(this.pos.x, this.pos.y, this.particleSize+10, this.particleSize);
         ellipse(this.pos.x, this.pos.y, this.particleSize, this.particleSize+10);
         break;
         
       case 'C': 
         noStroke();
         fill(currentColor);
         ellipse(this.pos.x, this.pos.y, this.particleSize, this.particleSize);
         break;
         
       case 'D': 
         noStroke();
         fill(currentColor);
         triangle(this.pos.x, this.pos.y,this.pos.x+8, this.pos.y,this.pos.x+4, this.pos.y+4);
         break;
         
       default:             // Default executes if the case labels
         noStroke();
         fill(currentColor);
         ellipse(this.pos.x, this.pos.y, this.particleSize, this.particleSize);
         break;
    }
      
       if (this.colorWeight < 1.0) {
        this.colorWeight = min(this.colorWeight+this.colorBlendRate, 1.0);
    }
    
  }

(3)kill函数:粒子消亡的控制,如果粒子超出指定的场景范围,则将消亡值设置为true,粒子不再显示。

 void kill() {
    if (! this.isKilled) {
      // Set its target outside the scene
      PVector randomPos = generateRandomPos(width/2, height/2, (width+height)/2);
      this.target.x = randomPos.x;
      this.target.y = randomPos.y;

      // Begin blending its color to black
      this.startColor = lerpColor(this.startColor, this.targetColor, this.colorWeight);
      this.targetColor = color(0);
      this.colorWeight = 0;

      this.isKilled = true;
    }
  }

2、粒子文字效果实现
(1)generateRandomPos函数:从点的半径中挑选一个随机位置

PVector generateRandomPos(int x, int y, float mag) {
  PVector randomDir = new PVector(random(0, width), random(0, height));
  PVector pos = new PVector(x, y);
  pos.sub(randomDir);
  pos.normalize();
  pos.mult(mag);
  pos.add(x, y);
  return pos;
}

(2)nextWord函数:使所有粒子绘制下一个单词

void nextWord(String word) {
  // Draw word in memory
  PGraphics pg = createGraphics(width, height);
  pg.beginDraw();
  pg.fill(0);
  pg.textSize(100);
  pg.textAlign(CENTER);
  PFont font = createFont(fontName, 100);
  pg.textFont(font);
  pg.text(word, width/2, height/2);
  pg.endDraw();
  pg.loadPixels();

  // Next color for all pixels to change to
  color newColor = color(random(0.0, 255.0), random(0.0, 255.0), random(0.0, 255.0));

  int particleCount = particles.size();
  int particleIndex = 0;

  // Collect coordinates as indexes into an array
  // This is so we can randomly pick them to get a more fluid motion
  ArrayList<Integer> coordsIndexes = new ArrayList<Integer>();
  for (int i = 0; i < (width*height)-1; i+= pixelSteps) {
    coordsIndexes.add(i);
  }

  for (int i = 0; i < coordsIndexes.size (); i++) {
    // Pick a random coordinate
    int randomIndex = (int)random(0, coordsIndexes.size());
    int coordIndex = coordsIndexes.get(randomIndex);
    coordsIndexes.remove(randomIndex);
    
    // Only continue if the pixel is not blank
    if (pg.pixels[coordIndex] != 0) {
      // Convert index to its coordinates
      int x = coordIndex % width;
      int y = coordIndex / width;

      Particle newParticle;

      if (particleIndex < particleCount) {
        // Use a particle that's already on the screen 
        newParticle = particles.get(particleIndex);
        newParticle.isKilled = false;
        particleIndex += 1;
      } else {
        // Create a new particle
        newParticle = new Particle();
        
        PVector randomPos = generateRandomPos(width/2, height/2, (width+height)/2);
        newParticle.pos.x = randomPos.x;
        newParticle.pos.y = randomPos.y;
        
        newParticle.maxSpeed = random(2.0, 5.0);
        newParticle.maxForce = newParticle.maxSpeed*0.025;
        newParticle.particleSize = random(3, 6);
        newParticle.colorBlendRate = random(0.0025, 0.03);
        
        particles.add(newParticle);
      }
      
      // Blend it from its current color
      newParticle.startColor = lerpColor(newParticle.startColor, newParticle.targetColor, newParticle.colorWeight);
      newParticle.targetColor = newColor;
      newParticle.colorWeight = 0;
      
      // Assign the particle's new target to seek
      newParticle.target.x = x;
      newParticle.target.y = y;
    }
  }

  // Kill off any left over particles
  if (particleIndex < particleCount) {
    for (int i = particleIndex; i < particleCount; i++) {
      Particle particle = particles.get(i);
      particle.kill();
    }
  }
}

3、鼠标键盘交互
(1)在屏幕上点击鼠标左键,粒子绘制下一个单词。

void mousePressed() {
  if (mouseButton == LEFT) {
    wordIndex += 1;
    if (wordIndex > words.size()-1) { 
      wordIndex = 0;
    }
    nextWord(words.get(wordIndex));
  }
}

(2)在单词上点击鼠标右键,单词出现爆炸散开的效果

void mouseDragged() {
  if (mouseButton == RIGHT) {
    for (Particle particle : particles) {
      if (dist(particle.pos.x, particle.pos.y, mouseX, mouseY) < 50) {
        particle.kill();
      }
    }
  }
}

(3)键盘控制不同粒子效果和不同的模式。

void keyPressed() {
  //night
 if ((key == 'M') || (key == 'm')){
    background(random(50));      
    bgColor = color(0, 40); 
 }
   //day
 if ((key == 'N') || (key == 'n')){
    background(random(50,100));
    bgColor = color(255, 100);
 }
   //draw point
  if ((key == 'A') || (key == 'a')){
    choice = 'A';
  } 
  //draw star
 if ((key == 'B') || (key == 'b')) {
    choice = 'B';
  }
  //draw circle
   if ((key == 'C') || (key == 'c')) {
    choice = 'C';
  }
  //draw triangle
   if ((key == 'D') || (key == 'd')) {
    choice = 'D';
  }
}

(4)点击屏幕上的按钮来控制不同形状粒子的绘制。

if(mouseX>=5 && mouseX <=70 && mouseY>10 && mouseY <=32){
    choice='A';
  }
    if(mouseX>=5 && mouseX <=70 && mouseY>32 && mouseY <=77){
    choice='B';
  }
    if(mouseX>=5 && mouseX <=70 && mouseY>77 && mouseY <=122){
    choice='C';
  }
    if(mouseX>=5 && mouseX <=70 && mouseY>122 && mouseY <=167){
    choice='D';
  }
  if(mouseX>=5 && mouseX <=70 && mouseY>167 && mouseY <=212){
    choice='E';
  }
  

4、主函数

void setup() {
  size(900, 580);
  background(random(50));      
  bgColor = color(0, 40);
  String[] strs = loadStrings("str.txt");
  for(int i=0;i<strs.length;i++){
    words.add(strs[i]);
  }
nextWord(words.get(wordIndex));
}

void draw() {
  // Background & motion blur
  fill(bgColor);
  noStroke();
  rect(0, 0, width*2, height*2);

  for (int x = particles.size ()-1; x > -1; x--) {
    // Simulate and draw pixels
    Particle particle = particles.get(x);
    particle.move();
    particle.draw();
    
    // Remove any dead pixels out of bounds
    if (particle.isKilled) {
      if (particle.pos.x < 0 || particle.pos.x > width || particle.pos.y < 0 || particle.pos.y > height) {
        particles.remove(particle);
      }
    }
  }
  draw_button();
  if(begin_display)
  {
      User_Manual();
  }
}

2.2 系统的讨论

基于该系统,从以下几个方面针对该系统做一些讨论和思索。

(1)系统的创意性

该系统灵感源于AE插件,使用Processing编程平台,基于Java语言,开发一个粒子效果的动画特效系统,用粒子系统来实现文字的可视化。不同的笔画和形状组成一个个的字符,人类将不同的含义赋予这些字符,于是它们便有了属于自己的特殊含义。那么如果将这些字符进一步分解,又会是怎样的视觉效果呢,本系统将一个个文字字符分解开来,看成一颗颗微小的粒子,粒子与粒子之间存在着密切的联系,它们飞舞,跳跃,最终形成我们熟悉的文字效果。鼠标点击,粒子像烟花般绽放然后消散,最终化为虚无,回到本来的样子,世间的万事万物不都是从无到有,最后再化为虚无吗?

(2)系统的功能性

评判一个应用的好坏重要因素之一就是产品的功能和产品的最终价值,该系统功能是较为简洁,可塑性很强。在系统中,运用动画技术,交互技术,将文字动画特效用粒子效果得到很好的呈现,系统分为两种模式可供用户选择,每种模式下,又分为不同的粒子形状效果,给用户视觉效果上的呈现与冲击,带给用户不一样的粒子效果的体验。

(3)系统的应用性

为什么我们要学习粒子系统呢?毫无疑问,粒子系统可以用于模拟各种自然现象(比如爆炸)。实际上,它的作用不局限于此。如果我们要用代码对自然界的各种事物建模,要接触的系统肯定并不是由单个物体组成的,系统内部会有很多物体,而粒子系统非常适合对复数系统进行建模。比如一堆弹球的弹跳运动、鸟群的繁殖,以及生态系统的演化,这些研究对象都是由复数组成的系统。本系统实现的粒子效果文字动画特效可应用在插件扩展
动画片头、动画结尾等地方,将文字用另一种表达方式呈现出来,别具一格。

(4)系统的动画技术性

在大多数游戏中,角色动画是“静止的”。每次角色在屏幕上移动,都是被艺术家创造的一种特制运动。无论它是通过手工精细调制的,还是通过动作捕捉系统来记录的,这种动画都是预定义资源。当一个角色需要执行不同动作时,就需要不同的动画片段。随着计算机技术的发展,程序性动画也在不断地进步,程序性动画是一个角色行为的时间可以程序性的生成。生成程序性的动画最通常的技巧之一就是依赖物理模拟。这个通常取名为基于物理的动画。本系统也是一个程序性动画的简单体现。动画效果在系统呈现上占了很大的比值,不管是粒子汇聚成文字的过程,还是文字四处飞溅,散落成微小粒子的过程。都运用了动画技术。粒子的飞舞半径在程序中可调节,会呈现不同的粒子飞舞效果。

延伸

在做本次课程设计时,不禁对粒子系统有了更深一步的思考,粒子系统就是由一系列独立对象组成的集合。但粒子系统本身不也是一个对象?如果粒子系统也是个对象,我们可以让这些粒子系统对象组成一个集合,产生一个由系统组成的系统。可以尝试着去构建一个由系统组成的系统,再由这个系统组成新的系统,再由新系统组成更高一级的系通统……这样的想法并没有错,毕竟现实世界就是这么运作的,比如:器官是由细胞组成的系统,而人体则是由器官组成的系统,社区是由人组成的系统,城市是由社区组成的系统,依次类推……

我们将粒子系统放在世界中来看,你会发现其实很多事物都是由小小的元素汇聚而成,比如一滴滴水滴汇聚成了河流,一条条河流又汇聚成海洋,一颗颗泥土堆积成小土堆,在量子物理学,有很多地方开始这种讨论,宇宙中的一切都同时具有粒子和波的性质。“一切都是波,没有波动,没有距离。” 宇宙中的一切都有粒子性质。这看起来完全是疯狂的,但这是一个实验性的事实,是通过一个令人惊讶的熟悉的过程得出的。

当然,把真实的物体描述成粒子和波是有些不精确的。准确地说,量子物理学描述的对象既不是粒子也不是波,而是第三类,它们具有一些波的特性和一些粒子的特性。关于在物理导论课程中讨论光作为粒子是否合适在物理教育界引起了一些激烈的争论,不是因为光是否具有粒子性质存在任何争议,而是因为把光子称为“粒子”可能会导致一些学生的误解。我不同意这一点,因为许多把电子称为“粒子”的问题可能会引起同样的担忧。

量子物体的“第三门”性质反映在物理学家用来谈论量子现象的有时令人困惑的语言中。希格斯玻色子是在大型强子对撞机上以粒子的形式被发现的,但你也会听到物理学家们把“希格斯场”说成是一种充满了所有空间的非本地化物质。这是因为在某些情况下更方便讨论希格斯场的作用,而在其他情况下强调了粒子特性,它只是描述同一个数学对象的不同语言。

在宇宙中,我们观察到的所有事物,从物质到辐射,都可以分解为最小的成分。世界上的一切物质都是由原子组成的,原子又是由核子和电子组成的,而核子又分为夸克和胶子。例如,光也由粒子组成,即:光子;引力波。

从根本上说,宇宙究竟是由什么构成的?有没有可能存在最小的基本粒子,或者一组基本粒子,我们既可以用它来建造我们整个宇宙的所有东西,又永远不能被分割成更小的东西?这是一个科学可以解释很多的问题,但它不一定能给我们最终的答案。

附加博文推荐

1、用Processing制作一个「生态瓶」

传送门:https://zhuanlan.zhihu.com/p/64726213
在这里插入图片描述物竞天择,适者生存,不适者被淘汰,挺有趣的生态系统瓶,作者将每一种生物都可以看做是独立的粒子集合,建立单独的粒子类来表示一种生物的行为。将生态系统中的竞争和生存状态用代码展现了出来。

2、Flowing Paint ——感受抽象

传送门:https://blog.csdn.net/qq_40974751/article/details/89483672
在这里插入图片描述
作者将抽象的事物具体化,别具一格,该应用的设计以“抽象”一词出发,对热抽象的经典代表作品进行示例展示,通过对画作的颜色提取,并进行交互操作,以及对颜色的重新组合,形成文字,使我们对抽象画本身的理解加深,以及对其颜色组成有了极其深刻的印象。通过不同的交互操作以及音乐的播放,富有一定的趣味性。

3、Magic Network》:一个小孩都能玩的神经网络交互系统

传送门:https://blog.csdn.net/leonardwyh789/article/details/89600881
在这里插入图片描述
通过该神经网络可视搭建系统,用户通过该系统可以很直观地了解到神经网络是如何工作的,并且能够十分轻松地搭建出属于自己的简单神经网络。将复杂抽象的神经网络可视化,可以让更多的人了解这个领域。

参考资料:
(1)Openprocessing Creative Coding

(2)The Nature of Code: Simulating Natural Systems with Processing by Daniel Shiffman. 2012 . 周晗彬 译.

(3)粒子系统的应用
https://dev.gameres.com/program/visual/effects/lizi/

(4)AE制作粒子文字特效 Particular粒子插件
https://jingyan.baidu.com/article/5225f26bbdf505e6fb09087c.html

(5) 曹天元. 《 量子物理史话–上帝掷骰子吗》. 2008

  • 4
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值