实验要求:
主题:创作一组编程习作,体现随机行为及牛顿运动学
要求:
编程语言与工具:编程工具不限;
作品:参考《代码本色》的第0~4章内容及其实例程序(自行在processing内下载),针对这5章分别编写1个习作(一共5个),每个习作都有不少于2个案例参考,且必须有一定的拓展;
报告:写一篇文章(也可以多篇文章,但最好有一个总的导航文章),发表为博文/推文等形式,描述运用的规律,若用到了数学/物理/化学等学科中的知识,要用平实易懂的语言介绍原理,尝试运用凝练的数学语言表达(公式、方程、推导等),特别要描述出这些原理如何在作品中呈现的。
第0章 引言
根据书中引用的例子和方法,添加噪声,通过改变不同的参数来实现不同的效果:
例如:
float z=0;
void setup()
{
size(640, 360);
}
void draw()
{
background(0);
z++; loadPixels();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
float bright = noise(x*0.05, y*0.06)*255;
pixels[x+y*width] = color(bright);
}
}
updatePixels();
}
将参数xy改为0.15和0.16可以发现图片的噪声横纵坐标都变得更加紧密:
为其添加第三维坐标:
原代码中修改为:
float bright = noise(x0.15, y0.16,z*0.05)*255;
就可以实现以下效果:有点类似于飞鸟的翅膀律动。。。
第1章 向量
该章节的主要内容是向量,最基础的效果是,运用好向量可以是代码大大简化,(虽然刚开始只运用向量可能不太明显)
向量的运动:包括速度,加速度,角度等;
主要对象:拥有速度和变化加速度的mover;
(通过类名直接调用静态函数)
这里还主要讲解了鼠标与实现的该模型的交互,通过测量鼠标所在位置来使用函数决定几个物体的加速度,
当前位置加速度向量就是新的位置
当前速度加加速度向量就是新的速度;
//在前面恒定加速的基础上实现,变化的加速度(使用函数控制)
class Mover {
PVector position;
PVector velocity;
PVector acceleration;
// The Mover's maximum speed
float topspeed;
Mover() {
// Start in the center
position = new PVector(random(width),random(height));
velocity = new PVector(0,0);
topspeed = 5;
}
void update() {
// Compute a vector that points from position to mouse
PVector mouse = new PVector(mouseX,mouseY);
acceleration = PVector.sub(mouse,position);
// Set magnitude of acceleration
//acceleration.setMag(0.2);
acceleration.normalize();
acceleration.mult(0.2);
// Velocity changes according to acceleration
velocity.add(acceleration);
// Limit the velocity by topspeed
velocity.limit(topspeed);
// position changes by velocity
position.add(velocity);
}
void display() {
stroke(0);
strokeWeight(2);
fill(int(random(200)),int(random(240)),int(random(255)));
ellipse(position.x,position.y,int(random(50)),50);
}
}
Mover[] movers = new Mover[10];
void setup() {
size(640,360);
for (int i = 0; i < movers.length; i++) {
movers[i] = new Mover();
}
}
void draw() {
background(255);
for (int i = 0; i < movers.length; i++) {
movers[i].update();
movers[i].display();
}
}
这里是实现的实例:其效果为:
第2章 力
在本章中依然使用mover来封装物体的受力状况
mover类和之前一样,这里省略。
该实例显示物体之间的引力,使用该公式可以计算两个物体之间的引力,为了使之更加形象具体,我们将这个力放大来看。
效果::
创建Attractor类:
class Attractor {
float mass; // Mass, tied to size
float G; // Gravitational Constant
PVector position; // position
boolean dragging = false; // Is the object being dragged?
boolean rollover = false; // Is the mouse over the ellipse?
PVector dragOffset; // holds the offset for when object is clicked on
Attractor() {
position = new PVector(width/2,height/2);
mass = 20;
G = 1;
dragOffset = new PVector(0.0,0.0);
}
PVector attract(Mover m) {
PVector force = PVector.sub(position,m.position); // Calculate direction of force
float d = force.mag(); // Distance between objects
d = constrain(d,5.0,25.0); // Limiting the distance to eliminate "extreme" results for very close or very far objects
force.normalize(); // Normalize vector (distance doesn't matter here, we just want this vector for direction)
float strength = (G * mass * m.mass) / (d * d); // Calculate gravitional force magnitude
force.mult(strength); // Get force vector --> magnitude * direction
return force;
}
// Method to display
void display() {
ellipseMode(CENTER);
strokeWeight(4);
stroke(0);
if (dragging) fill (50);
else if (rollover) fill(100);
else fill(175,200);
ellipse(position.x,position.y,mass*2,mass*2);
}
// The methods below are for mouse interaction
void clicked(int mx, int my) {
float d = dist(mx,my,position.x,position.y);
if (d < mass) {
dragging = true;
dragOffset.x = position.x-mx;
dragOffset.y = position.y-my;
}
}
void hover(int mx, int my) {
float d = dist(mx,my,position.x,position.y);
if (d < mass) {
rollover = true;
}
else {
rollover = false;
}
}
void stopDragging() {
dragging = false;
}
void drag() {
if (dragging) {
position.x = mouseX + dragOffset.x;
position.y = mouseY + dragOffset.y;
}
}
}
主函数:
Mover[] movers = new Mover[10];
Attractor a;
void setup() {
size(640, 360);
for (int i = 0; i < movers.length; i++) {
movers[i] = new Mover(random(0.1, 2), random(width), random(height));
}
a = new Attractor();
}
void draw() {
background(255);
a.display();
a.drag();
a.hover(mouseX, mouseY);
for (int i = 0; i < movers.length; i++) {
PVector force = a.attract(movers[i]);
movers[i].applyForce(force);
movers[i].update();
movers[i].display();
}
}
void mousePressed() {
a.clicked(mouseX, mouseY);
}
void mouseReleased() {
a.stopDragging();
}
这样子就是多个物体之间万有引力的模拟
效果如下:
接下来进行拓展:
通过改变上述函数的mass可以改变其质量,但是两个物体之间的有引力产生的加速度是与其本身质量无关的,而之间的力却与本身质量有关。所以讲mass改为随机产生的random函数其效果是一样的。
我们可以讲G改为9.8来接近模拟一下重力的场景:
Mover[] movers = new Mover[5];
void setup() {
size(640, 360);
for (int i = 0; i < movers.length; i++) {
movers[i] = new Mover(random(2, 6), 0, 0);
}
}
void draw() {
background(255);
for (int i = 0; i < movers.length; i++) {
PVector wind = new PVector(0.01, 0);
PVector gravity = new PVector(0, 0.1*movers[i].mass);
movers[i].applyForce(wind);
movers[i].applyForce(gravity);
movers[i].update();
movers[i].display();
movers[i].checkEdges();
}
}
第3章 振荡
用曲线来画振荡波形:
float angle = 0;
float angleVel = 0.1;
size(640,360);
background(255);
stroke(0);
strokeWeight(2);
noFill();
beginShape();
for (int x = 0; x <= width; x += 5) {
float y = map(sin(angle),-1,1,0,height);
vertex(x,y);
angle +=angleVel;
}
endShape();
由公式:
延伸为:
但是更加简单的是:角度是一个标量,它没有方向。
简谐运动:
简谐运动是最基本也最简单的机械振动。当某物体进行简谐运动时,物体所受的力跟位移成正比,并且总是指向平衡位置。它是一种由自身系统性质决定的周期性运动。(如单摆运动和弹簧振子运动)实际上简谐振动就是正弦振动。故此在无线电学中简谐信号实际上就是正弦信号。
如果质点的位移与时间的关系遵从正弦函数的规律,即它的振动图像(x-t图像)是一条正弦曲线,这样的振动叫做简谐运动。
三角函数就是一个振荡方程:
弹簧在理想情况下也可以看作是再平衡点的简谐运动,
mover类:
class Mover {
PVector position;
PVector velocity;
PVector acceleration;
float mass = 20;
// Arbitrary damping to simulate friction / drag
float damping = 1;
// For mouse interaction
PVector dragOffset;
boolean dragging = false;
// Constructor
Mover(float x, float y) {
position = new PVector(x,y);
velocity = new PVector();
acceleration = new PVector();
dragOffset = new PVector();
}
// Standard Euler integration
void update() {
velocity.add(acceleration);
velocity.mult(damping);
position.add(velocity);
acceleration.mult(0);
}
// Newton's law: F = M * A
void applyForce(PVector force) {
PVector f = force.get();
f.div(mass);
acceleration.add(f);
}
// Draw the mover
void display() {
stroke(0);
strokeWeight(2);
fill(175);
if (dragging) {
fill(50);
}
ellipse(position.x,position.y,mass*2,mass*2);
}
// The methods below are for mouse interaction
// This checks to see if we clicked on the mover
void clicked(int mx, int my) {
float d = dist(mx,my,position.x,position.y);
if (d < mass) {
dragging = true;
dragOffset.x = position.x-mx;
dragOffset.y = position.y-my;
}
}
void stopDragging() {
dragging = false;
}
void drag(int mx, int my) {
if (dragging) {
position.x = mx + dragOffset.x;
position.y = my + dragOffset.y;
}
}
}
其他控制函数:
void setup() {
size(640,360);
// Create objects at starting position
// Note third argument in Spring constructor is "rest length"
spring = new Spring(width/2,10,100);
mover = new Mover(width/2,100);
}
void draw() {
background(255);
// Apply a gravity force to the mover
PVector gravity = new PVector(0,2);
mover.applyForce(gravity);
// Connect the mover to the spring (this calculates the force)
spring.connect(mover);
// Constrain spring distance between min and max
spring.constrainLength(mover,30,200);
// Update mover
mover.update();
// If it's being dragged
mover.drag(mouseX,mouseY);
// Draw everything
spring.displayLine(mover); // Draw a line between spring and mover
mover.display();
spring.display();
fill(0);
}
主函数:
class Spring {
// position
PVector anchor;
// Rest length and spring constant
float len;
float k = 0.2;
// Constructor
Spring(float x, float y, int l) {
anchor = new PVector(x, y);
len = l;
}
// Calculate spring force
void connect(Mover b) {
// Vector pointing from anchor to mover position
PVector force = PVector.sub(b.position, anchor);
// What is distance
float d = force.mag();
// Stretch is difference between current distance and rest length
float stretch = d - len;
// Calculate force according to Hooke's Law
// F = k * stretch
force.normalize();
force.mult(-1 * k * stretch);
b.applyForce(force);
}
// Constrain the distance between mover and anchor between min and max
void constrainLength(Mover b, float minlen, float maxlen) {
PVector dir = PVector.sub(b.position, anchor);
float d = dir.mag();
// Is it too short?
if (d < minlen) {
dir.normalize();
dir.mult(minlen);
// Reset position and stop from moving (not realistic physics)
b.position = PVector.add(anchor, dir);
b.velocity.mult(0);
// Is it too long?
}
else if (d > maxlen) {
dir.normalize();
dir.mult(maxlen);
// Reset position and stop from moving (not realistic physics)
b.position = PVector.add(anchor, dir);
b.velocity.mult(0);
}
}
void display() {
stroke(0);
fill(175);
strokeWeight(2);
rectMode(CENTER);
rect(anchor.x, anchor.y, 10, 10);
}
void displayLine(Mover b) {
strokeWeight(2);
stroke(0);
line(b.position.x, b.position.y, anchor.x, anchor.y);
}
}
效果如下:
第4章 粒子系统
粒子系统表示三维计算机图形学中模拟一些特定的模糊现象的技术,而这些现象用其它传统的渲染技术难以实现真实感的物理运动规律。经常使用粒子系统模拟的现象有火、爆炸、烟、水流、火花、落叶、云、雾、雪、尘、流星尾迹或者象发光轨迹这样的抽象视觉效果等等。
而在processing中粒子就是在屏幕中移动的对象,它有位置、速度和加速度变量,有构造函数用于内部变量的初始化,有display()函数用于绘制自身,还有update()函数用于更新位置。
受重力和阻力共同影响的粒子系统:
拓展:
模拟粒子系统
float R = random(255);
float G = random(255);
float B = random(255);
Particle(){
location = new PVector(random(width), random(height));
velocity = new PVector(random(-1, 1), random(-2, 0));
acceleration = new PVector(0, 0);
mass = 1;
lifespan = 255;
}
Particle(PVector l){
location = l.get();
acceleration = new PVector(0, 0);
velocity = new PVector(random(-1, 1),random(-2, 0));
mass = 1;
lifespan = 255;
}
void applyForce(PVector force){
acceleration.add(PVector.div(force, mass));
}
void update(){
velocity.add(acceleration);
location.add(velocity);
acceleration.mult(0);
lifespan -= 1.0;
}
void display(){
stroke(R,G,B,lifespan);
fill(R,G,B,lifespan);
ellipse(location.x, location.y, 8, 8);
}
boolean isDead(){
if(lifespan < 0.0){
return true;
}else{
return false;
}
}
void run(){
update();
display();
}
}
class Confetti extends Particle{
float R = random(255);
float G = random(255);
float B = random(255);
Confetti(PVector l){
super(l);
}
void display(){
stroke(R, G, B, lifespan);
fill(R,G,B,lifespan);
//ellipse(location.x, location.y, 8, 8);
rect(location.x, location.y, 8, 8);
}
}
class Repeller{
PVector location;
float r = 10;
float G = 100;
Repeller(float x, float y){
location = new PVector(x, y);
}
void display(){
stroke(0);
fill(0,0);
ellipse(location.x, location.y, r*2, r*2);
}
PVector repel(Particle p){
PVector dir = PVector.sub(location, p.location);
float d = dir.mag();
d = constrain(d, 5, 100);
dir.normalize();
float force = -1*G/(d*d);
dir.mult(force);
return dir;
}
}
主函数:(运行)
import java.util.*;
ArrayList<ParticleSystem> systems;
PVector gravity;
Repeller repeller;
void mousePressed(){
systems.add(new ParticleSystem(new PVector(mouseX, mouseY)));
//gravity = new PVector(0, 0.1);
}
void setup(){
size(640, 240);
systems = new ArrayList<ParticleSystem>();
repeller = new Repeller(width/2-20, height/2);
}
void draw(){
background(255);
Iterator<ParticleSystem> it = systems.iterator();
while(it.hasNext()){
ParticleSystem p = it.next();
gravity = new PVector(random(-0.1, 0.1), random(-0.1, 0.2));
p.applyRepeller(repeller);
p.applyForce(gravity);
p.run();
p.addParticle();
if(p.isDead()){
it.remove();
}
}
repeller.display();
}
结果:
(这里的GIF截图软件有点垃圾,只要碰撞中心圆就会不知怎么回事绿屏,老师见谅一下。)
这里前五章都有一个创建模拟自然系统的练习,在有了粒子系统的基础后,就可以实现一部分了。
(在例子中学习了一下)
根据教程https://blog.csdn.net/qq_38694695/article/details/89677186
成功实现了该系统,运用了较多的后面几章的知识
效果图: