目录

  • 1. 第0章 引言
  • 2. 第1章 向量
  • 3. 第2章 力
  • 4. 第3章 振荡
  • 5. 第4章 粒子系统
1. 第0章 引言

《代码本色》在这一章节的主要内容是模拟随机游走。
讲了利用random()函数生成随机数,利用概率和非均匀分布来模拟具有一定运动趋势的的运动的相关知识。
其中我觉得收获比较大的就是对高斯分布和Perlin噪声的学习。

高斯分布,其实就是正态分布。在日常生活中,人们的身高和体重一般都聚集在平均值附近,不会出现差值过大的数,这样的频数分布就呈现正态分布。
正态分布的曲线如下,其在平均值处的聚集程度由标准差决定。

Perlin噪声是一个非常强大算法,可用于生成各种自然特效, 包括云层、地形和大理石的纹理。因为它能生成符合自然排序(“平滑”)的伪随机数序列,所以Perlin噪声算法表现出了一定的自然性。
在这一章节中,我参考了书上的案例0-4、0-5和0-6,利用Perlin噪声更改像素的颜色做出了五颜六色的格子衫的效果,感觉在下次选购格子衫的时候可以作为颜色参考,哈哈。
大致效果如图:


代码如下:

  loadPixels();float xoff=0;for(int x=0;x<width;x++){float yoff=1000;for(int y=0;y<height;y++){float r=map(noise(xoff),0,1,0,255);//利用Perlin噪声生成好看的颜色float g=map(noise(yoff),0,1,0,255);float b=map(noise((xoff+yoff)/2),0,1,0,255);pixels[int(x+y*width)]=color(int(r),int(g),int(b));yoff+=0.01;}xoff+=0.01;}updatePixels();

在此基础上,我利用正态分布在画布的中心位置以60的标准差画同心圆,效果如图:



代码如下:

  float cx=width/2;//平均位置为画布中央float cy=height/2;float x=randomGaussian()*60+cx;//正态分布 标准差为60float y=randomGaussian()*60+cy;colorcircle(x,y);void colorcircle(float posx,float posy){translate(posx,posy); for (int i = 0; i < 90; i++) {rotate(TWO_PI/19);noStroke();fill(int(random(255)), int(random(255)), int(random(255)));float r = abs(randomGaussian()*50);ellipse(0, 0, r,r);}
}

正态分布是基于平均值和标准差的随机分布,而Perlin噪声中同样既包含有随机的成分,但是相互之间又有关联,像素点之间是平滑的进行变化,而不是像白噪声那么尖锐。
通过对正态分布和Perlin噪声的学习,让我明白了若要模拟自然运动,完全随机是不可能达到效果的,我们需要让每一次的运动都随机却又有关联才行。

2. 第1章 向量

这一章主要讲了和向量相关的知识,如何用向量来表示物体的位置和运动方向、运动速度等,其中最令我印象深刻的其一是加速度的使用,比起简单的匀速运动,添加了加速度的变速运动的运行效果更加有趣。
回顾我们以前学过的物理,加速度的定义是:速度的变化率,而速度的定义则是:位置的变化率。从本质上说,这是一种“涓滴”效应,加速度影响速度,继而影响位置。
其二是关于静态函数和非静态函数的区别,通过类名直接调用的函数称为静态函数,而通过对象实例调用的则是非静态函数。
下面是一个静态的add()函数:

和非静态函数相比,静态函数有以下特点:
1.函数被声明为static;
2.函数的返回类型不是void,而是一个PVector对象;
3.函数中会创建一个新的PVector对象(v3),它作为v1向量和v2向量相加的结果被返回。

在这一章节中,参考书上的示例1-2、1-6和1-11来模拟了池塘里游动的小鱼被鱼饵吸引的场景。

其中主要用到了向量的减法来得到方向向量,向量的加减其实也就是将向量的分量分别相加/相减

PVector mouse = new PVector(mouseX,mouseY);
acceleration = PVector.sub(mouse,position);//用鼠标位置减去物体位置得到由物体指向鼠标的方向向量

向量的加法来使物体向某个方向移动

velocity.add(acceleration);
position.add(velocity);//利用向量加法,用加速度影响速度,速度影响位置

大致效果如图,鱼饵位置为鼠标所在的位置:


主要代码如下:

  void display() {PVector mouse = new PVector(mouseX,mouseY);PVector dir=PVector.sub(position,mouse);//计算方向向量fish(position.x,position.y,dir);}void fish(float posx,float posy,PVector dir){//绘制小鱼stroke(0);strokeWeight(2);fill(255,48,48);ellipse(posx,posy,50,30);dir.normalize();ellipse(posx+32*dir.x,posy+32*dir.y,20,10);ellipseMode(CENTER);fill(0);ellipse(posx-10*dir.x,posy,8,8);}
3. 第2章 力

这一章主要学习了力的概念以及力和加速度的关系,说明了力与牛顿运动定律并且模拟了几种常见的力,包括摩擦力、空气和流体阻力以及引力。
其中我觉得很有趣的是流体阻力和引力的模拟。在示例程序里,流体阻力的效果就像物体被放进了水里一样,很有意思。而引力则有趣在被引力影响的物体的运动会互相影响。
这一章节是在第1章 向量的基础上进行的,由向量来确定力的方向向量,通过物理公式来计算力的大小,再将二者相结合来模拟现实中的力的效果。
因为觉得流体阻力和引力很有意思,在这一章节中,我参考书上的示例2-5、2-7和2-8来模拟了森林里的狼与小白兔。

流体阻力的公式如图:

让我们试着把这个公式简化,以便于用Processing模拟。
观察公式可以发现其中的变量有
1.ρ —— 它代表流体的密度,如果要模拟密度不同的流体阻力,那么可以通过该变量实现,但我目前只需要一个,所以没有使用该值,默认它为1
2.前面的v —— 它代表物体的移动速率,也就是速度向量的大小velocity.magnitude()
3.后面的v —— 它代表速度的单位向量,也就是velocity.normalize(),和摩擦力一样,阻力的方向也和物体的运动方向相反

float dragMagnitude = c *speed * speed;//计算阻力大小
PVector dragForce = m.velocity.get();//得到物体的运动方向
dragForce.mult(-1);//阻力的方向和物体的运动方向相反
dragForce.normalize();//将方向向量单位化
dragForce.mult(dragMagnitude);//将方向向量和阻力大小相乘得到流体阻力

引力的公式如图:
观察公式可以发现其中的变量主要是
1.分母的r —— 代表两个物体间的距离
2.分子的r —— 代码物体所受到的引力的方向

PVector force = PVector.sub(position,w.position); //计算方向向量
float d = force.mag(); //得到两个物体间的距离
force.normalize(); //将方向向量单位化
float strength = (G * mass * w.mass) / (d * d);//计算引力大小
force.mult(strength); //将方向向量和引力大小相乘得到引力

效果图如下,图中的灰色小球代表狼,白色小球代表小白兔。森林里的狼会被小白兔所吸引,然后去追小白兔;但当它们之间的距离小于一定值时,小白兔会有所察觉,开始逃跑。同时,我用流体阻力来模拟了因为小白兔体型比较小,在树林里(图中的绿色部分)会比较好躲藏,狼追逐的速度会变慢的情况。


主要代码如下:

forest类:主要用于生成流体阻力PVector drag(wolf m) {float speed = m.velocity.mag();float dragMagnitude = c * speed * speed;//流体阻力公式的实现:阻力的大小等于阻力系数乘以对象速度的平方PVector dragForce = m.velocity.get();dragForce.mult(-1);dragForce.normalize();dragForce.mult(dragMagnitude);return dragForce;}
wolf类:主要是实现狼受到力的作用速度发生变化void applyForce(PVector force) {PVector f = PVector.div(force,mass);acceleration.add(f);}
rabbit类:实现对狼的吸引,且要对自身跟狼的距离进行判断,若小于一定值,就要开始逃跑PVector attract(wolf w) {PVector force = PVector.sub(position,w.position);   float d = force.mag();     if(d<50)//距离小于50{PVector f = PVector.div(force,mass*70);acceleration.add(f);//开始逃跑}d = constrain(d,5.0,25.0);                           force.normalize();                                   float strength = (G * mass * w.mass) / (d * d);     force.mult(strength);     return force;}
4. 第3章 振荡

这一章主要讲了角度相关的问题,包括三角函数、指向运动的方向、极坐标系和笛卡尔坐标系相关的内容。
其中在3-4 指向运动的方向这一节中,回顾了让Mover对象朝着鼠标所在的方向加速的示例,让我想起了我在第二章 “向量”中对于池塘中游动的小鱼的模拟。
因为每一条小鱼都是由鱼身和鱼尾两个椭圆组成的,所以如何让鱼尾和鱼身一起运动呢?
在第二章中,我使用的方法是得到鱼身与鼠标之间的方向向量后,在绘制完鱼身的基础上,利用鱼身的位置坐标,分别在x、y轴方向上加上某个常数乘以单位化后的方向向量得到的结果来实现鱼尾与鱼身的同步运动。若鼠标是水平移动,这样的效果还不错,但如果鼠标是垂直移动的,那么就可能出现鱼身和鱼尾分离的现象。由此看来,光是利用向量来使物体运动是不够的,还需要用角度来模拟出物体在运动的过程中,自身旋转的情况。
为了让物体能够“根据当时的速度发生旋转”,我们需要用速度来算出旋转角度。
速度向量的三角函数示意图如下:

由三角函数,我们可以知道:
tan(θ)=velocity.y/velocity.x
其中的θ就是我们所需要的旋转角度,我们可以利用反正切函数atan()来求θ,但是由于正切的规律性,结果本应该有两个角度的,atan()却只返回一个值,这是因为atan的值域是从-90~90,也就是说atan()只处理一四象限,所以我们应该选用atan2()函数。
atan2(double y,double x) 其中y代表已知点的y坐标,同理x代表已知点的x坐标,返回值是此点与原点连线与x轴正方向的夹角,这样它就可以处理四个象限的任意情况了,它的值域相应的也就是-180~180了。

在得到了旋转角度之后,我们就可以利用pushMatrix()函数和popMatrix()函数来将所需要旋转的物体隔离开(这一步主要是防止物体的旋转影响到其他的物体),然后利用translate()函数将画布中心移动到绘制物体的位置,再利用rotate()函数来实现物体的旋转。

在解决了小鱼的运动时自身旋转的问题之后,看到了书中利用胡克定律来模拟弹力,觉得很有意思,所以在这一章节,我参考了书上的示例3-3、3-10和3-11来模拟了池塘中游动的小鱼和池塘旁的一条垂柳。

其中垂柳的模拟利用到了胡克定律来模拟弹簧,胡克定律如图:

公式中的各个量及其作用:
1.k —— 物体的劲度系数,是一个常量,它会影响弹力的大小
2.x —— 代表弹簧的形变,也就是当前长度和静止长度的差

PVector force = PVector.sub(b.position, anchor);//获取由枢轴点指向摆锤的向量
float d = force.mag();//计算枢轴点到摆锤的距离
float stretch = d - len;//计算弹簧的形变量
force.normalize();//将方向向量单位化
force.mult(-1 * k * stretch);//根据胡克定律求弹力大小,合并大小和方向

效果图如下:


主要代码如下:

fish类:主要用于实现小鱼向着鼠标方向的运动和运动时的自身旋转float angle = atan2(velocity.y,velocity.x)+PI;//计算旋转角度pushMatrix();translate(position.x,position.y);//移动画布rotate(angle);//旋转drawfish(0,0);//绘制小鱼popMatrix();
Tree类:主要通过钟摆来模拟树叶,可以用鼠标拉动最下端的树叶,从而触发它的运动void clicked(int mx, int my) {float d = dist(mx,my,position.x,position.y);//计算鼠标和树叶之间的距离if (d < 30) {//当距离小于一定值时dragging = true;//此处的dragging代表正在拖拽dragOffset.x = position.x-mx;//计算鼠标与树叶位置在x方向上的差值dragOffset.y = position.y-my;}}void drag(int mx, int my) {if (dragging) {//当dragging为真时position.x = mx + dragOffset.x;//更新树叶的位置坐标position.y = my + dragOffset.y;}}void update() { velocity.add(acceleration);velocity.mult(damping);//通过一个小于1的常量来实现速度的衰减position.add(velocity);acceleration.mult(0);}
Spring类:实现弹簧的连接void connect(Tree b) {//计算弹力,实现胡克定律PVector force = PVector.sub(b.position, anchor);//获取由枢轴点指向摆锤的向量float d = force.mag();//计算枢轴点到摆锤的距离float stretch = d - len;//计算弹簧的形变量force.normalize();//将方向向量单位化force.mult(-1 * k * stretch);//根据胡克定律求弹力大小,合并大小和方向}
5. 第4章 粒子系统

这一章节的内容主要是粒子系统。粒子系统就是一系列独立对象的集合,这些对象通常用简单的图形或者点来表示。
这一章节中我获益最多的就是对于ArrayList的使用和继承与多态的学习。
相比于数组,ArrayList是一种更高级的对象列表管理方法。我们可以利用它的动态长度特性来在粒子系统中创建、添加粒子对象和移除粒子对象。
而继承与多态则是在Java学习中所接触过的内容,继承让子类可以使用父类的属性和方法,同时也可以定义自己独有的属性和方法,提高代码可重用性、可扩展性。而多态则是指父类引用指向子类对象,从而形成多种形态。

在这一章节中,我参考了书上的示例4-3、4-5、4-7和4-8,模拟了森林中的狼与小白兔,基本想法与第二章“力”中的实验相同,森林里的狼会被小白兔所吸引,然后去追小白兔;但当它们之间的距离小于一定值时,小白兔会有所察觉,开始逃跑。但在这一章节中的狼与小白兔都是继承了Animal类的粒子,它们都有一定的生命期,用于管理粒子的“生存”时长 ,且它们的数目会根据粒子的运动行为来发生变化,狼在追捕到小白兔后,小白兔会消失(被狼吃掉了)。
另外我在这一次实验中,使用了图片来模拟狼和小白兔,而不是通过绘制不同颜色的圆。

效果图如下:


主要代码如下:

Animal类:主要用于实现狼和小白兔两类共同的功能
class Animal{float lifespan;//生存期Animal(){lifespan = 1000;}boolean isDead() {if (lifespan < 0.0) {//若生存期小于0,则为死亡return true;} else {return false;}}
}
Rabbit类:主要实现兔子对狼的吸引和兔子的逃跑PVector attract(Wolf w) {PVector force = PVector.sub(position, w.position); float d = force.mag();   //计算狼和兔子之间的距离   if (d<70)//若距离小于70,兔子就开始逃跑{PVector f = PVector.div(force, mass*100);acceleration.add(f);}d = constrain(d, 5.0, 25.0); //通过约束距离来约束引力的大小                          force.normalize();                                   float strength = (G * mass * w.mass) / (d * d);  //计算引力大小   force.mult(strength); //将引力的大小和方向合并    return force;}
ParticleSystem类:主要用于进行Rabbit类和Wolf类的粒子的生成、添加与移除操作ParticleSystem(int num,PImage rabImg_,PImage wolImg_) {rabImg=rabImg_;wolImg=wolImg_;rabbits = new ArrayList();wolves = new ArrayList();for (int i = 0; i < num; i++) {wolves.add(new Wolf(random(width), random(height),wolImg));//生成Wolf类的粒子}for(int i=0;i<2*num;i++){rabbits.add(new Rabbit(random(width), random(height),rabImg));//生成Rabbit类的粒子,数量是Wolf类粒子的两倍}}void addParticle() {if (frameCount%80==0) {rabbits.add(new Rabbit(random(width), random(height),rabImg));//每80帧生成一个新的Rabbit类粒子}if (frameCount%200==0) {wolves.add(new Wolf(random(width), random(height),wolImg));//每200帧生成一个新的Wolf类粒子}}void run() {for (int i = rabbits.size()-1; i >= 0; i--) {//遍历整个Rabbit类的ArrayList,若有死亡的粒子,则将它移除Rabbit p = rabbits.get(i);p.run();if (p.isDead()) {rabbits.remove(i);}}for (int i = wolves.size()-1; i >= 0; i--) {//遍历整个Wolf类的ArrayList,若有死亡的粒子,则将它移除Wolf p = wolves.get(i);p.run();if (p.isDead()) {wolves.remove(i);}}for (int i=0; i<rabbits.size(); i++) {Rabbit r=rabbits.get(i);for (int j=0; j<wolves.size(); j++) {Wolf f=wolves.get(j);float dis=PVector.sub(r.position, f.position).mag();if (dis<20)//若某个Rabbit类的粒子与某个Wolf类的粒子间的距离小于20,则视为该Rabbit类的粒子被吃掉了,将它移除并显示“The Rabbit was eatten!”{textSize(40);fill(0);text("The Rabbit was eatten!", r.position.x, r.position.y);rabbits.remove(i);} else {//若不小于20,则Wolf类粒子被Rabbit类粒子吸引,开始追捕PVector force = r.attract(f);f.applyForce(force);f.run();}}}}

========================================================
以上就是我对于《代码本色》这本书第0~4章内容的学习与拓展,在这本书中,我不仅学习了processing代码相关的知识,还学习和回顾了许多物理上的知识,之前使用processing来模拟物体间的力,我都是之前使用的常数或是一些简单的运算,而在学习的过程中,我发现使用物理公式来模拟是十分方便而且效果很好的。
同时,如何使用processing来模拟自然系统也是我在这次的学习过程中重点思考的一个问题。在学习过程中,我学会了正态分布和Perlin噪声这类的更加自然的随机生成方法,还有粒子系统这种更加系统性的管理方法,使用它们来模拟自然系统会更加方便快捷,这么看来,这次也算是收获不少了。之后的学习也要更加加油!

基于《代码本色》的processing学习与拓展相关推荐

  1. 代码本色——雪梨的Processing探索·Chapter 0:随机游走

    概述 Chapter 0为我们介绍了随机数.概率和噪声在运动当中起到的变化作用,下面就让我们来好好的了解下这些数学名词,究竟可以起到怎样的公用,最后再让我们来发挥想象进行这些知识的运用创作. 原理介绍 ...

  2. 《代码本色》作者Daniel Shiffman:艺术家也编程

    非商业转载请注明作译者.出处,并保留本文的原始链接:http://www.ituring.com.cn/article/179855 Daniel Shiffman是纽约大学Tisch艺术学院助理艺术 ...

  3. 基于少量样本的快速学习Few-shot learning

    基于少量样本的快速学习Few-shot learning 背景 人工智能 神经网络的三次浪潮 深度学习 人工智能困境 人工智能 → 人类智能 定义及数值原理 机器学习定义 数值原理 数据增强 数据预处 ...

  4. 基于飞桨的小样本学习工具包助你举一反三

    王雅晴,PaddleFSL负责人.飞桨高级开发者技术专家(高级PPDE).2019年博士毕业于香港科技大学计算机科学及工程学系.通过百度公司AIDU计划加入百度研究院商业智能实验室,现任资深研发工程师 ...

  5. CORL: 基于变量序和强化学习的因果发现算法

    深度强化学习实验室 官网:http://www.neurondance.com/ 论坛:http://deeprl.neurondance.com/ 来源:诺亚实验室 华为诺亚方舟实验室.西安交通大学 ...

  6. 基于NVIDIA GPUs的深度学习训练新优化

    基于NVIDIA GPUs的深度学习训练新优化 New Optimizations To Accelerate Deep Learning Training on NVIDIA GPUs 不同行业采用 ...

  7. 值得收藏!基于激光雷达数据的深度学习目标检测方法大合集(下)

    作者 | 黄浴 来源 | 转载自知乎专栏自动驾驶的挑战和发展 [导读]在近日发布的<值得收藏!基于激光雷达数据的深度学习目标检测方法大合集(上)>一文中,作者介绍了一部分各大公司和机构基于 ...

  8. 基于三维数据的深度学习综述

    众所周知,计算机视觉的目标是对图像进行理解.我们从图像中获取视觉特征,从视觉特征中对图像.场景等进行认知,最终达到理解.感知.交互.目前,比较主流的计算机视觉基本是基于二维数据进行的,但是回顾计算机视 ...

  9. 基于Solr的空间搜索学习笔记

    基于Solr的空间搜索学习笔记 在Solr中基于空间地址查询主要围绕2个概念实现: (1) Cartesian Tiers 笛卡尔层 Cartesian Tiers是通过将一个平面地图的根据设定的层次 ...

最新文章

  1. 事务处理操作(COMMIT,ROLLBACK)。复制表。更新操作UPDATE实际工作中一般都会有WHERE子句,否则更新全表会影响系统性能引发死机。...
  2. oracle,build path,linux
  3. STM32学习笔记(七) ADC模数转换测电平(普通和DMA模式)
  4. (转) Android生成签名文件并用其对apk文件进行签名
  5. 强化学习《基于价值 - MC方法和TD方法》
  6. 一款可定制的外国jQuery图表插件jqplot
  7. Sharepoint学习笔记—Ribbon系列-- 2. 在Ribbon中添加新Tab
  8. java button 点击事件_android_button按钮onclick点击事件的几种写法
  9. mysql找出最大的天数_mysql 计算连续登录最大天数
  10. 怎么制作铁闸门_咖啡师养成记 | 教你做一杯合格的拿铁咖啡
  11. 常用响应式web UI框架搜集整理
  12. 恢复触摸板功能的方法
  13. 《软件创富----共享软件创业之道》读后感
  14. iOS 统计代码行数
  15. 经典逻辑题笔试题和答案(不断更新)
  16. 哈工大计算机网络Mooc 第十一章笔记(局域网)
  17. 网友们碰到过的最难调试的 Bug
  18. 游戏界面设计艺术性的思考
  19. Renderbus瑞云渲染正式支持UE云渲染!离线渲染+实时渲染=渲染起飞!
  20. uniapp自定义导航栏,高度,自定义组件

热门文章

  1. .htaccess基本设置
  2. 车主不怕,出车祸了怎么办
  3. python ElementTree解析xml
  4. 粤语开放词典app开发
  5. macbook安装根证书
  6. BUUCTF刷题记录(2)
  7. Mysql中索引失效得原因和解决
  8. nginx反向代理配置去除前缀问题
  9. 所谓的微商,为什么要把你拉黑 ?
  10. PyCharm设置文件标头,默认模板