Processing|Processing 案例 | 日本先生Atsushi Tanaka的3D世界


文章目录

  • 引言
  • 代码
    • Bubbles 02
      • 主要流程
      • class ParticleSystem
      • class Particle
    • Space Running
      • 每次添加的粒子数量
      • 粒子的初始位置、速度
      • 粒子的渲染方式
      • 粒子是否需要删除的依据
  • 小结

引言 一花一世界,一叶一菩提。每个人眼中的花是不一样的,每个人眼中的世界也是不一样的 。昔时佛祖拈花,惟迦叶微笑,既而步往极乐。在菩提树下,从一朵花中便能悟出整个世界,最终得升。
今天就来给大家介绍日本先生Atsushi Tanaka的眼中的两个世界。
第一个是泡泡世界——Bubbles 02,效果如下:

第二个是方块世界——Space Running,效果如下:

大家肯定会觉得很奇怪,为什么这次一次讲两个作品呢?是因为这个作品背后的原理十分的相似:Bubbles 02里面是一些球体粒子向上移动,Space Running里面是一些立方体粒子向前移动。只不过是这些球体粒子和这些立方体粒子的初始位置不同罢了。
这里得说明一下:这两个作品的交互方式略有不同,为了更加关注两个作品粒子运动方式的异同,下面的代码都是按照Bubbles 02的交互方式来的实现的。所以下面代码的效果和原作品的效果会有很小的差异,不过大家不用太在意,我们应该更多地关注代码背后的原理。
同时这两个作品作者都是P5.js写的,我还是给大家介绍Processing版本的代码。
??
代码 【Processing|Processing 案例 | 日本先生Atsushi Tanaka的3D世界】在大概了解了一下Processing有关绘制3D图形的知识后我们就可以开始看代码了。那么首先我们下来看Bubbles 02。
??
Bubbles 02 主要流程
因为我们之前已经很详细地讲解过粒子系统了,所以大家对这段代码一定不陌生。
ParticleSystem ps; void setup() { //窗口大小设置为全屏,并且选择3D渲染器 fullScreen(P3D); colorMode (RGB, 256); ps = new ParticleSystem(); }void draw() { background(0); ps.run(); }

??
class ParticleSystem
下面我们来看看ParticleSystem这个类,这个类也没有太多的需要说明,主要用来管理所有的粒子。对每一个粒子进行添加、删除、更新状态(位置、生命长度等)、绘制等。
class ParticleSystem { final int MAX_CNT = 200; //系统中最大的粒子数量 ArrayList particles; float thetaX, thetaY; //分别绕x、y方向的旋转角度 ParticleSystem() { particles = new ArrayList(); }void run() { //向粒子系统中添加新的粒子 addParticle(); update(); display(); }void addParticle() { // 防止系统的粒子数量过多 if (particles.size() >= MAX_CNT) return; particles.add(new Particle()); }void update() { for (int i = particles.size() - 1; i >= 0; i--) { Particle p = particles.get(i); //删除不用的粒子 if (p.dead()) { particles.remove(p); continue; } p.update(); } //根据鼠标位置确定绕着x、y方向旋转的角度 thetaX += (mouseX - thetaX) * 0.04; thetaY += (mouseY - thetaY) * 0.04; }void display() { // 设置坐标轴的原点为屏幕的中心 translate(width / 2, height / 2); //设置不同的光 ambientLight (30, 20, 80); pointLight (255, 0, 0, 200.0, 0.0, 200.0); pointLight (255, 0, 0, -200.0, 0.0, -200.0); pointLight (0, 255, 0, 0.0, 200.0, 200.0); pointLight (0, 255, 0, 0.0, -200.0, -200.0); rotateX(thetaY * 0.01); rotateY(thetaX * 0.01); for (Particle p : particles) { p.display(); } } }

??
class Particle
现在我们来看Particle这个类。观察作品作品的每一个粒子是一个球体——泡泡。而这些球体是从屏幕下方向上移动的,所以在设置球体的初始位置的时候,我们将其y方向的位置设置的比屏幕大小的高度高一点。这里得注意目前我们坐标轴的原点是屏幕的中心,也就是(width/2, height/2, 0)的位置。
同时给每个粒子一个竖直方向向上的速度,然后我们这个速度来设置粒子的生命衰减速度。
现在大家理解下面的代码应该就没有问题了。
class Particle { PVector velocity, acceleration, location; float lifespan, lifeRate; float size; Particle() { // 设置位置、加速度、速度 float x = random(-width/2, width/2); float y = height / 2 + 40; float z = random (-1000.0, 1000.0); location = new PVector(x, y, z); acceleration = new PVector(); velocity = new PVector(0, random(-20, -1), 0); // 设置球体的半径、生命衰减速率、生命长度 size = random(120); lifeRate = -velocity.y; lifespan = y; }boolean dead() { // 如果生命长度为0,那么这个粒子就不需要了 if (lifespan < 0)return true; return false; }void update() { // 更新粒子的生命长度、速度、位置 lifespan -= lifeRate; velocity.add(acceleration); location.add(velocity); }void display() { pushMatrix(); noStroke (); translate(location.x, location.y, location.z); sphere(size); popMatrix(); } }

??
Space Running 在讲解完Bubbles 02,现在我们来讲解Space Running。因为两个作品背后的原理非常的相似,所以我只给大家指出两者不同的地方。
??
每次添加的粒子数量
不同于Bubbles 02每次只添加一个粒子,Space Running每次添加3个粒子。
void addParticle() { if (particles.size() >= MAX_CNT) return; for (int i = 0; i < 3; i++) particles.add(new Particle()); }

??
粒子的初始位置、速度
在这个作品中粒子初始位置不同,是在z轴负方向很远的位置。同时每一个粒子的速度不是竖直向上的,是向z轴正方向的。
// 初始位置不同 float x = random (-600, 600); float y = random (-600, 600); float z = -MAX_DEPTH; //初始速度不同 location = new PVector(x, y, z); acceleration = new PVector(); velocity = new PVector(0, 0, 100);

其中MAX_DEPTH是粒子新增的一个成员变量。
final float MAX_DEPTH = 2000;

??
粒子的渲染方式
在Bubbles 02中每一个粒子是一个球体,没有颜色,而在Space Running中每一个粒子是一个立方体,并且有颜色。所以首先必须给每一个粒子添加一些成员变量,并且对它们进行初始化。
新增成员变量如下。
float sizeX, sizeY, sizeZ; // 长、宽、高 color c; //颜色

初始化方法如下。
c = color (random (256), random (256), random (256)); sizeX = random(100); sizeY = random(100); sizeZ = random(100);

绘制方法如下。
void display() {pushMatrix(); translate(location.x, location.y, location.z); // 根据离屏幕的距离设置颜色的透明度 float aphla = map(abs(location.z), MAX_DEPTH, 0, 0, 255); fill(c, aphla); stroke(255); // 根据离屏幕的距离设置线条的粗细 float weight = map(abs(location.z), MAX_DEPTH, 0, 0, 1); strokeWeight(weight); // 绘制一个立方体 box(sizeX, sizeY, sizeZ); popMatrix(); }

??
粒子是否需要删除的依据
在第一个作品中我们是根据粒子生命长度是否大于零来判断粒子是否需要删除的,而在这个作品中我们是根据粒子到屏幕的距离来判断的。
具体方法如下。
boolean dead() { if (abs(location.z) > MAX_DEPTH)return true; return false; }

所以对于该作品的每一个粒子来说,lifespan和lifeRate这两个成员变量已经没有什么存在的必要了,所以将它们删除。同时修改一下Particle的update函数。
void update() { velocity.add(acceleration); location.add(velocity); }

??
小结 和普通的粒子系统一样,3D的粒子系统也有很多可以拓展的空间。可以改变粒子不同的运动模式,DIY属于你自己眼中的世界。
而在这里我创造一个名叫FishRunning的世界:一些红色的立方体小鱼在一些泡泡里面穿梭。背后的原理很简单,其实就是把上述两个作品简单地结合了一下。效果如下。

感兴趣的同学可以去我的OpenProcessing看在线效果,同时我也把这三个作品的代码都放在了我的github上。

    推荐阅读