Processing粒子系统-烟雾画出来的世界名画

目录:

  • 效果欣赏
  • 参考示例
  • 程序思想以及代码实现
  • 作者有话说

效果欣赏

注:烟雾的的扩散方向随着鼠标位置的不同而不同,构成烟雾的基础图元可以是普通几何体,也可以是材质贴图。

鼠标位置出现的蓝色或者黄色的圆为录屏软件所添加,程序运行时并不会有显示。


参考示例

注:本程序的参考案例均来自《代码本色》一书的第四章

参考案例一:4-02

ArrayList<Particle> particles;void setup() {size(640, 360);particles = new ArrayList<Particle>();
}void draw() {background(255);particles.add(new Particle(new PVector(width/2, 50)));// Looping through backwards to deletefor (int i = particles.size()-1; i >= 0; i--) {Particle p = particles.get(i);p.run();if (p.isDead()) {particles.remove(i);}}
}
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com// Simple Particle Systemclass Particle {PVector position;PVector velocity;PVector acceleration;float lifespan;Particle(PVector l) {acceleration = new PVector(0, 0.05);velocity = new PVector(random(-1, 1), random(-2, 0));position = l.copy();lifespan = 255.0;}void run() {update();display();}// Method to update positionvoid update() {velocity.add(acceleration);position.add(velocity);lifespan -= 2.0;}// Method to displayvoid display() {stroke(0, lifespan);strokeWeight(2);fill(127, lifespan);ellipse(position.x, position.y, 12, 12);}// Is the particle still useful?boolean isDead() {if (lifespan < 0.0) {return true;} else {return false;}}
}

示例代码的运行效果如下:

示例代码的参考作用主要是熟悉绘制粒子系统的一个完整的流程(出生-更新-死亡)。在该示例中,每个粒子从出生起获得的向下加速度一定,但是初始速度不同,每一帧都在更新粒子的位置(update函数)直至粒子的生命值被消耗完,就移除粒子。

为了维持粒子系统的稳定,在移除粒子的同时不断往粒子系统里添加新的粒子,这就是一个简单粒子系统的思想。

参考示例二:4-08

// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com// Smoke Particle System// A basic smoke effect using a particle system
// Each particle is rendered as an alpha masked image/* @pjs preload="processingjs/chapter04/_4_08_ParticleSystemSmoke/data/texture.png"; */ParticleSystem ps;void setup() {size(640,360);PImage img = loadImage("texture.png");ps = new ParticleSystem(0,new PVector(width/2,height-75),img);
}void draw() {background(0);// Calculate a "wind" force based on mouse horizontal positionfloat dx = map(mouseX,0,width,-0.2,0.2);PVector wind = new PVector(dx,0);ps.applyForce(wind);ps.run();for (int i = 0; i < 2; i++) {ps.addParticle();}// Draw an arrow representing the wind forcedrawVector(wind, new PVector(width/2,50,0),500);}// Renders a vector object 'v' as an arrow and a position 'loc'
void drawVector(PVector v, PVector pos, float scayl) {pushMatrix();float arrowsize = 4;// Translate to position to render vectortranslate(pos.x,pos.y);stroke(255);// Call vector heading function to get direction (note that pointing up is a heading of 0) and rotaterotate(v.heading2D());// Calculate length of vector & scale it to be bigger or smaller if necessaryfloat len = v.mag()*scayl;// Draw three lines to make an arrow (draw pointing up since we've rotate to the proper direction)line(0,0,len,0);line(len,0,len-arrowsize,+arrowsize/2);line(len,0,len-arrowsize,-arrowsize/2);popMatrix();
}
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.comclass Particle {PVector pos;PVector vel;PVector acc;float lifespan;PImage img;Particle(PVector l,PImage img_) {acc = new PVector(0,0);float vx = randomGaussian()*0.3;float vy = randomGaussian()*0.3 - 1.0;vel = new PVector(vx,vy);pos = l.get();lifespan = 100.0;img = img_;}void run() {update();render();}// Method to apply a force vector to the Particle object// Note we are ignoring "mass" herevoid applyForce(PVector f) {acc.add(f);}  // Method to update positionvoid update() {vel.add(acc);pos.add(vel);lifespan -= 2.5;acc.mult(0); // clear Acceleration}// Method to displayvoid render() {imageMode(CENTER);tint(255,lifespan);image(img,pos.x,pos.y);// Drawing a circle instead// fill(255,lifespan);// noStroke();// ellipse(pos.x,pos.y,img.width,img.height);}// Is the particle still useful?boolean isDead() {if (lifespan <= 0.0) {return true;} else {return false;}}
}
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com// Smoke Particle System// A class to describe a group of Particles
// An ArrayList is used to manage the list of Particles class ParticleSystem {ArrayList<Particle> particles;    // An arraylist for all the particlesPVector origin;        // An origin point for where particles are birthedPImage img;ParticleSystem(int num, PVector v, PImage img_) {particles = new ArrayList<Particle>();              // Initialize the arraylistorigin = v.get();                        // Store the origin pointimg = img_;for (int i = 0; i < num; i++) {particles.add(new Particle(origin, img));    // Add "num" amount of particles to the arraylist}}void run() {for (int i = particles.size()-1; i >= 0; i--) {Particle p = particles.get(i);p.run();if (p.isDead()) {particles.remove(i);}}}// Method to add a force vector to all particles currently in the systemvoid applyForce(PVector dir) {// Enhanced loop!!!for (Particle p: particles) {p.applyForce(dir);}}  void addParticle() {particles.add(new Particle(origin, img));}}

示例的运行效果如下(鼠标位置的蓝色是录屏软件自动添加的,程序运行不带这个效果):

该示例中将将多个粒子的管理封装成类,形成粒子系统类,在用这个类可以方便地对添加粒子、更改粒子受力以及销毁粒子,本例子里面粒子的受力除了重力外还有水平方向的力,具体的受力大小和方向由鼠标的位置来决定。

最后程序的运行结果是一个被被风吹动的烟雾。

程序思想以及代码实现

在看到以上两个示例后,我灵感一现:如果把每个烟雾粒子系统当成一个小小的图元,图元的颜色来自给出的名画,烟雾的方向随着鼠标变化,那么就可以得到一幅动态的名画。

换成编程上的思想就是:根据读入的“名画”长宽,每间隔固定像素,生成一个个粒子系统,每个粒子系统的颜色来自对应位置名画的像素颜色。读取鼠标所在的位置,更改粒子系统受力的方向

参考根据上面的示例,我们需要一个粒子类,需要以下这些属性,其中颜色属性是特有的。

  PVector pos;//粒子的位置PVector vel;//速度PVector acc;//加速度float lifespan;//生命PImage img;//图片color mycolor;//颜色

在初始化粒子的时候应该赋予粒子初始位置、速度、生命以及颜色(如果使用贴图的话还应该有图片)。

 Particle(PVector l,PImage img_,color c) {acc = new PVector(0,0);float vx = randomGaussian()*0.3;      //高斯噪声产生随机数float vy = randomGaussian()*0.3 - 1.0;vel = new PVector(vx,vy);pos = l.get();                       //位置lifespan = 50.0;                    //生命周期img = img_;mycolor=c;}

在渲染粒子的时候,粒子的颜色应该随着粒子的生命衰减而变浅,这里采用的是降低透明度的方式来体现这一点。本程序中设定的图元为方形,也可以更改为圆形,三角或者用户自定义的图形或材质。

 // 展示 选择材质贴图或者直接用简单几何void render() {//imageMode(CENTER);//tint(mycolor,lifespan);//控制颜色以及透明度//image(img,pos.x,pos.y);// Drawing a circle insteadfill(mycolor,lifespan);noStroke();rect(pos.x,pos.y,5,5);}

接下来设定粒子系统,粒子系统首先得获取到颜色才能将颜色给粒子,为此有参数mycolor。

class ParticleSystem {ArrayList<Particle> particles;    // 粒子列表PVector origin;                   // 粒子的出生点PImage img;                       //材质color mycolor;ParticleSystem(int num, PVector v, PImage img_,color c) {particles = new ArrayList<Particle>();              // 初始化粒子列表origin = v.get();                                   // 保存初始点img = img_;mycolor=c;for (int i = 0; i < num; i++) {particles.add(new Particle(origin, img,mycolor));        // 为粒子系统添加添加num个粒子}}
//管理粒子的活动void run() {for (int i = particles.size()-1; i >= 0; i--) {Particle p = particles.get(i);p.run();if (p.isDead()) {particles.remove(i);//粒子死亡,移除}}}//为粒子添加力void applyForce(PVector dir) {for (Particle p: particles) {p.applyForce(dir);}}  void addParticle() {particles.add(new Particle(origin, img,mycolor));}
}

最后集合使用上述的类了。在此之前,我们先读入一张图片,作为目标“名画”,processing里面提供函数loadImage

PImage myImage;
 myImage = loadImage("6.jpg");

为了更好的视觉效果,将画布的尺寸调整为图片的大小(也可以再大一点)。

  surface.setSize(myImage.width, myImage.height);

用一个for循环遍历图片,初始化粒子系统,这里设置的像素间隔为8。

particlesS = new ArrayList<ParticleSystem>();              // 初始化粒子系统int temp=8;for (int i=0;i<myImage.width;i=i+temp){for(int j=0;j<myImage.height;j=j+temp){particlesS.add(new ParticleSystem(0,new PVector(i,j),img,myImage.get(i,j)));  }}

捕捉鼠标位置,生成相应的受力,这里将鼠标在画布上的位置映射到一定范围内,防止受力过于夸张。

  // 添加受力float dx = map(mouseX,0,width,-0.2,0.2);float dy=map(mouseY,0,height,-0.3,0.3);PVector wind = new PVector(dx,dy);

更新粒子系统:

for (int i=0;i<particlesS.size()-1;i++)
{ParticleSystem p = particlesS.get(i);p.applyForce(wind);p.run();for (int j = 0; j < 2; j++) {p.addParticle();}
}

下面给出使用粒子系统的文件的代码:

PImage myImage;
int halfImage;
ArrayList<ParticleSystem> particlesS;    // 粒子列表void setup() {size(640,360);PImage img = loadImage("2.png");//如果用材质的话,可以用这个myImage = loadImage("6.jpg");surface.setSize(myImage.width, myImage.height);//myImage.loadPixels();particlesS = new ArrayList<ParticleSystem>();              // 初始化粒子系统int temp=8;for (int i=0;i<myImage.width;i=i+temp){for(int j=0;j<myImage.height;j=j+temp){particlesS.add(new ParticleSystem(0,new PVector(i,j),img,myImage.get(i,j)));  } }
}void draw() {background(255);// 添加受力float dx = map(mouseX,0,width,-0.2,0.2);float dy=map(mouseY,0,height,-0.3,0.3);PVector wind = new PVector(dx,dy);for (int i=0;i<particlesS.size()-1;i++)
{ParticleSystem p = particlesS.get(i);p.applyForce(wind);p.run();for (int j = 0; j < 2; j++) {p.addParticle();}
}
}

到了这一步,程序还是不能运行的,因为找不到图片文件,所以我们需要在源文件pde的同级目录下新建一个data文件夹,文件夹里面装我们的图片。

作者有话说

千万!每次写代码都要注意保存,就在我写这篇博客的过程中,下课了,我去吃饭,我明明按了ctrl+S但是!它并没有保存!后来把代码又写了一遍!我。。。好难啊。

话说回来,虽然本程序并不复杂,但是本人还是十分喜欢最后的效果,而且感觉还可以做出诸多的拓展,比如说鼠标点击画面产生波纹的效果。

Processing粒子系统-用烟雾画出来的世界名画相关推荐

  1. python画自己的名字_Python+OpenCV 十几行代码模仿世界名画

    原标题:Python+OpenCV 十几行代码模仿世界名画 现在很多人都喜欢拍照(自拍).有限的滤镜和装饰玩多了也会腻,所以就有 APP 提供了模仿名画风格的功能,比如 prisma.versa 等, ...

  2. javafx粒子系统之烟雾模拟

    javafx粒子系统之烟雾模拟 功能说明: 用图片纹理模拟烟雾生成,烟雾可以随风飘散. 在线运行 源码下载 程序讲解: 1.粒子对象定义: 定义Particle对象继承Parent. 定义以下属性: ...

  3. 人脸识别撞脸名画_测测自己的自拍最像哪幅世界名画?谷歌这款App逆天了

    原标题:测测自己的自拍最像哪幅世界名画?谷歌这款App逆天了 一款匹配人脸与著名画作的谷歌APP在周末引发了一波自拍狂潮. 本周末,谷歌旗下的数字博物馆App,Google Arts and Cult ...

  4. 人脸识别撞脸名画_你可能撞脸世界名画 支付宝让你遇见名画中的自

    原标题:你可能撞脸世界名画 支付宝让你遇见名画中的自 只要上传自己的美照,不到一分钟你就看到与自己高度相似的世界经典名画中的人物. 这是支付宝今天推出的"遇见名画中的自己"趣味测试 ...

  5. 计算机画家的阅读答案,计算机眼中的世界名画

    原标题:计算机眼中的世界名画 <蒙娜丽莎>是文艺复兴时代画家列奥纳多·达·芬奇所绘的丽莎·盖拉尔迪尼的肖像画.它具有超高的艺术价值和收藏价值,所以人们对它的仿造也一直没有停止过.对于名家艺 ...

  6. 博物馆守卫问题(世界名画展览馆)

    世界名画展览馆(博物馆守卫问题) 在某博物馆中摆放了非常重要的文物,为了节省人力,该博物馆专门购买了警卫机器人来看管这些文物.该博物馆的房间排列整齐,房间的大小相同.每个警卫机器人能够巡查的范围除本身 ...

  7. 世界名画陈列馆(最少机器人问题和不重复监视问题)

    问题描述: 世界名画陈列馆问题.世界名画陈列馆由m×n个排列成矩形阵列的陈列室组成.为了防止名画被盗,需要在陈列室中设置警卫机器人哨位.每个警卫机器人除了监视它所在的陈列室外,还可以监视与它所在的陈列 ...

  8. 算法设计与分析: 5-19 世界名画陈列馆问题

    5-19 世界名画陈列馆问题 问题描述 世界名画陈列馆由 m×nm×n m \times n 个排列成矩形阵列的陈列室组成.为了防止名画被盗,需要在陈列室中设置警卫机器人哨位.每个警卫机器人除了监视它 ...

  9. 世界名画陈列馆问题(不重复监视)

    问题描述 世界名画陈列馆由m*n个排列成矩形阵列的陈列室组成.为了防止名画被盗,需要在陈列室中设置警卫机器人哨位.除了监视所在的陈列室,每个警卫机器人还可以监视与它所在的陈列室相邻的上.下.左.右4个 ...

最新文章

  1. MacOS开发必备工具brew,安装nginx反向代理,替代linux工具 apt-get和 yum...
  2. 经典面试|为何Kafka这么快?
  3. BGP小实验(一)——小实验练练手走起来
  4. 中国产业数字化发展报告:数智创新,智驱未来
  5. 第四步_安装gcc交叉编译工具
  6. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析...
  7. mysql 存储过程 长字符串_MySQL存储过程--长字符串扯分
  8. Atitit 文员招募规范 attilax总结
  9. Uniform Server
  10. arm-linux-gcc stdio.h,arm-linux-gcc stdio.h no such file or directory错误
  11. e4a 蓝牙温度app_IIOT应用之Arduino无线蓝牙温湿度和距离测量系统
  12. 2021算法竞赛入门班第一节课枚举贪心习题
  13. java 遍历二叉树_java实现二叉树遍历的三种方式
  14. 新赛季更新完服务器要维护到什么时候,王者荣耀新赛季刚更新就出乱子,维护到九点才开服,普攻都消失了...
  15. pow函数以及math.h的一些坑
  16. 阿里云轻量级GPU计算型vgn6i云服务器配置性能详解
  17. 蓝桥杯刷题日记 更新到2022/2/5
  18. 基于Python的Flask框架实现的寻宝通关游戏 课程论文+项目源码
  19. 杭电复试 —— 2015年
  20. InfoWorld的2019年度技术奖获奖者

热门文章

  1. 鼠标滑过,二级菜单显示
  2. 智多星骗子:骗钱的手段太高超
  3. 【Vue2.0学习】—数据绑定
  4. buuctf————findkey
  5. python语言关键字的是_不属于Python语言关键字的是( )
  6. MSI和MSI-X对比(五)
  7. 华为手机 从服务器获取安装包信息,华为openGauss 获取并校验安装包
  8. ArcGIS GeoEvent 使用教程(一)
  9. 尚邮——Wopus周六聚会大图分享(2009-12-28 11:50:01)
  10. 在上海,你可以直接用高德地图打一辆无人驾驶出租车去上班了