Processing粒子系统-用烟雾画出来的世界名画
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粒子系统-用烟雾画出来的世界名画相关推荐
- python画自己的名字_Python+OpenCV 十几行代码模仿世界名画
原标题:Python+OpenCV 十几行代码模仿世界名画 现在很多人都喜欢拍照(自拍).有限的滤镜和装饰玩多了也会腻,所以就有 APP 提供了模仿名画风格的功能,比如 prisma.versa 等, ...
- javafx粒子系统之烟雾模拟
javafx粒子系统之烟雾模拟 功能说明: 用图片纹理模拟烟雾生成,烟雾可以随风飘散. 在线运行 源码下载 程序讲解: 1.粒子对象定义: 定义Particle对象继承Parent. 定义以下属性: ...
- 人脸识别撞脸名画_测测自己的自拍最像哪幅世界名画?谷歌这款App逆天了
原标题:测测自己的自拍最像哪幅世界名画?谷歌这款App逆天了 一款匹配人脸与著名画作的谷歌APP在周末引发了一波自拍狂潮. 本周末,谷歌旗下的数字博物馆App,Google Arts and Cult ...
- 人脸识别撞脸名画_你可能撞脸世界名画 支付宝让你遇见名画中的自
原标题:你可能撞脸世界名画 支付宝让你遇见名画中的自 只要上传自己的美照,不到一分钟你就看到与自己高度相似的世界经典名画中的人物. 这是支付宝今天推出的"遇见名画中的自己"趣味测试 ...
- 计算机画家的阅读答案,计算机眼中的世界名画
原标题:计算机眼中的世界名画 <蒙娜丽莎>是文艺复兴时代画家列奥纳多·达·芬奇所绘的丽莎·盖拉尔迪尼的肖像画.它具有超高的艺术价值和收藏价值,所以人们对它的仿造也一直没有停止过.对于名家艺 ...
- 博物馆守卫问题(世界名画展览馆)
世界名画展览馆(博物馆守卫问题) 在某博物馆中摆放了非常重要的文物,为了节省人力,该博物馆专门购买了警卫机器人来看管这些文物.该博物馆的房间排列整齐,房间的大小相同.每个警卫机器人能够巡查的范围除本身 ...
- 世界名画陈列馆(最少机器人问题和不重复监视问题)
问题描述: 世界名画陈列馆问题.世界名画陈列馆由m×n个排列成矩形阵列的陈列室组成.为了防止名画被盗,需要在陈列室中设置警卫机器人哨位.每个警卫机器人除了监视它所在的陈列室外,还可以监视与它所在的陈列 ...
- 算法设计与分析: 5-19 世界名画陈列馆问题
5-19 世界名画陈列馆问题 问题描述 世界名画陈列馆由 m×nm×n m \times n 个排列成矩形阵列的陈列室组成.为了防止名画被盗,需要在陈列室中设置警卫机器人哨位.每个警卫机器人除了监视它 ...
- 世界名画陈列馆问题(不重复监视)
问题描述 世界名画陈列馆由m*n个排列成矩形阵列的陈列室组成.为了防止名画被盗,需要在陈列室中设置警卫机器人哨位.除了监视所在的陈列室,每个警卫机器人还可以监视与它所在的陈列室相邻的上.下.左.右4个 ...
最新文章
- MacOS开发必备工具brew,安装nginx反向代理,替代linux工具 apt-get和 yum...
- 经典面试|为何Kafka这么快?
- BGP小实验(一)——小实验练练手走起来
- 中国产业数字化发展报告:数智创新,智驱未来
- 第四步_安装gcc交叉编译工具
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析...
- mysql 存储过程 长字符串_MySQL存储过程--长字符串扯分
- Atitit 文员招募规范 attilax总结
- Uniform Server
- arm-linux-gcc stdio.h,arm-linux-gcc stdio.h no such file or directory错误
- e4a 蓝牙温度app_IIOT应用之Arduino无线蓝牙温湿度和距离测量系统
- 2021算法竞赛入门班第一节课枚举贪心习题
- java 遍历二叉树_java实现二叉树遍历的三种方式
- 新赛季更新完服务器要维护到什么时候,王者荣耀新赛季刚更新就出乱子,维护到九点才开服,普攻都消失了...
- pow函数以及math.h的一些坑
- 阿里云轻量级GPU计算型vgn6i云服务器配置性能详解
- 蓝桥杯刷题日记 更新到2022/2/5
- 基于Python的Flask框架实现的寻宝通关游戏 课程论文+项目源码
- 杭电复试 —— 2015年
- InfoWorld的2019年度技术奖获奖者