ssaw08 1125
TRANSCRIPT
SSAW08 後期第10回
作品制作のヒント高度なモーション:バネと引力2008年11月25日
今日の内容
• 高度なアニメーション表現を身につける• 物理法則を用いたアニメーションの表現
• ばね• 万有引力
「ばね」を定義する
• 「ばね」を数学的に定義する• フックの法則
• バネの伸びが x のとき、それによって生じる力を F とすると• F = -kx • ( k:ばね定数)
振動を繰り返す(単振動)
0
x
F = -kxma = -kx
x = -sinmk t
x = Csin(!t + ")
「ばね」を定義する
• フックの定理は以下のように書き直すことができる• springForce = -stiffness * stretch
- バネの力 = -バネの硬さ x バネの伸び
• さらに「バネの伸び」の部分を解釈する• springForce = -stiffness * (position - restPosition)
- バネの力 = -バネの硬さ x (現在位置 - ばねの静止位置)
「ばね」を定義する
• 式を整理• springForce = stiffness * (restPositon - position)
- バネの力 = バネの硬さ x (静止位置 - 現在位置)
• この時、ばねの移動速度は下記のように表現される• velocity = friction * (velocity + springFroce)
- 速度 = 摩擦 * (速度 + バネの力)
• この式を利用して、簡単なアニメーションをProcessingで作成してみる
ばね1:シンプルなばねを定義
float stiffness = 0.1; //ばねの強度float damping = 0.9; //摩擦float velocity = 0.0; //速度float targetY; //目標位置float y; //現在位置
void setup() { size(400, 400); noStroke();}
void draw() { fill(0, 12); rect(0, 0, width, height); fill(255); float force = stiffness * (targetY - y); //フックの法則 f = -kx velocity = damping * (velocity + force); //速度を計算 y += velocity; //速度分を座標に rect(10, y, width - 20, 10); //四角を描画 targetY = mouseY; //マウスのY座標を目標位置に}
ばね1:シンプルなばねを定義
ばね2:重さ(mass)を付加
• もうひとつの要素を付加する• バネに付いている物体の重さ(mass)
• 重さと力の関係はニュートンの運動の第二法則から• F = ma
• F:力、m:質量、a:加速度
• 式を変換• a = F / m
• これを「ばね」にあてはめると、下記のように解釈できる• acceleration = springForce / mass
• 加速 = バネの力 / 重さ
• 先程のプログラムに重さの要素を付加する
ばね2:重さ(mass)を付加
//2つの重さの異なる物体で、ばね運動を作成float y1, y2; //現在位置float velocity1, velocity2; //速度float mass1 = 1.0; //物体1の重さfloat mass2 = 6.0; //物体2の重さfloat stiffness = 0.1; //バネの剛性float damping = 0.9; //摩擦係数
void setup() { size(400, 400); noStroke();}
void draw() { fill(0, 12); rect(0, 0, width, height); fill(255); float targetY = mouseY; //目標位置 //物体1 float forceA = stiffness * (targetY - y1); //フックの法則 float accelerationY1 = forceA / mass1; //重さから加速度を算出 velocity1 = damping * (velocity1 + accelerationY1); //加速度を加味して速度を計算
ばね2:重さ(mass)を付加
y1 += velocity1; //位置を算出 rect(0, y1, width/2, 15); //物体2 float forceB = stiffness * (targetY - y2); //フックの法則 float accelerationY2 = forceB / mass2; //重さから加速度を算出 velocity2 = damping * (velocity2 + accelerationY2); //加速度を加味して速度を計算 y2 += velocity2; //位置を算出 rect(width/2, y2, width/2, 15);}
ばね2:重さ(mass)を付加
ばね3:2次元座標に展開、重力を付加
• Y軸方向だけでなくX軸方向にも動けるように• 重力(gravity)の要素も付加
• 常に一定の力をY軸方向に付加する
ばね3:2次元座標に展開、重力を付加
float stiffness = 0.05;float damping = 0.9;float mass = 3.0;float gravity = 0.0;float velocityX = 0.0, velocityY = 0.0;float targetX, targetY;float x, y;
void setup() { size(600, 600); smooth();}
void draw() { background(0); //X軸方向 float forceX = stiffness * (targetX - x); float accelerationX = forceX / mass; velocityX = damping * (velocityX + accelerationX); x += velocityX; //Y軸方向 float forceY = stiffness * (targetY - y); forceY += gravity; float accelerationY = forceY / mass; velocityY = damping * (velocityY + accelerationY); y += velocityY;
ばね3:2次元座標に展開、重力を付加
//物体を円で表現 noStroke(); fill(255); ellipse(x, y, 40, 40); //ばねの接続に線を引く stroke(127); noFill(); line(mouseX, mouseY, x, y); //マウス座標を目標にする targetX = mouseX; targetY = mouseY;}
ばね3:2次元座標に展開、重力を付加
ばね4:「ばね」をクラスとして定義する
• ばねの動きを汎用的に使えるようにクラスとして定義する• Spring2Dクラス
ばね4:「ばね」をクラスとして定義する
//Spring2Dクラスを利用して、ばねの動きを生成Spring2D s1, s2;float gravity = 5.0;float mass = 2.0;
void setup() { size(400, 400); smooth(); fill(0); //入力:x座標, y座標, 摩擦, 重力 s1 = new Spring2D(0.0, width / 2, mass, gravity); s2 = new Spring2D(0.0, width / 2, mass, gravity);}
void draw() { background(204); s1.update(mouseX, mouseY); s1.display(mouseX, mouseY); s2.update(s1.x, s1.y); s2.display(s1.x, s1.y);}
ばね4:「ばね」をクラスとして定義する
// class:Spring2D//「ばね」の動きをシミュレートするclass Spring2D { float vx, vy; float x, y; float gravity; float mass; float radius = 10; float stiffness = 0.2; float damping = 0.7; //コンストラクタ Spring2D(float xpos, float ypos, float m, float g) { x = xpos; y = ypos; mass = m; gravity = g; } //位置を計算 void update(float targetX, float targetY) { float forceX = (targetX - x) * stiffness; float ax = forceX / mass; vx = damping * (vx + ax); x += vx; float forceY = (targetY - y) * stiffness;
ばね4:「ばね」をクラスとして定義する
forceY += gravity; float ay = forceY / mass; vy = damping * (vy + ay); y += vy; } //表示 void display(float nx, float ny) { noStroke(); ellipse(x, y, radius*2, radius*2); stroke(255); line(x, y, nx, ny); }}
ばね4:「ばね」をクラスとして定義する
ばね5:ばねを数珠繋ぎにしてみる
• Spring2Dクラスで定義した「ばね」を直列に繋いでいく• Spring2Dクラスを配列として定義する
ばね5:ばねを数珠繋ぎにしてみる
//ばねを数珠繋ぎにint numSprings = 10; //ばねの数Spring2D[] s = new Spring2D[numSprings];float gravity = 5.0;float mass = 5.0;float stiffness = 0.2;float damping = 0.8;void setup() { size(600, 600); smooth(); fill(0); for (int i = 0; i < numSprings; i++) { s[i] = new Spring2D(width / 2, i*(height / numSprings), mass, gravity, stiffness, damping); }}void draw() { background(204); s[0].update(mouseX, mouseY); s[0].display(mouseX, mouseY); for (int i = 1; i < numSprings; i++) { s[i].update(s[i-1].x, s[i-1].y); s[i].display(s[i-1].x, s[i-1].y); }}
ばね5:ばねを数珠繋ぎにしてみる
Physicsライブラリを利用する
• 「ばね」や「引力」などを、一からプログラミングするのは大変• 既存のライブラリを活用することで、物理演算を簡単に行うことができる• Processingで利用できる物理演算ライブラリ
• Physics• JBox2D
• 今回はPhysicsを利用してみる
Physicsライブラリを利用する
• Physics Webページ• http://www.cs.princeton.edu/~traer/physics/• ここからダウンロードして、Sketchフォルダのlibrariyにインストール
Physicsライブラリを利用する
• Physicsの構成• 4つのパート
• ParticleSystem:物理演算のための環境を生成• Particles:空間を動きまわる粒(パーティクル)• Springs:2つのパーティクルを接続する「ばね」• Attractions:2つのパーティクルの間にはたらく引力
Physicsで「ばね」を表現1
• Physicsでばねの数珠繋ぎを再現する• Particleの配列を用意• Springの配列を用意• ParticleSystemを新規に生成• 指定した数だけParticleを生成し、配列に追加• Particle間をSpringで接続し、配列に追加• Particleの位置に円を描く• Springを線で描く
Physicsで「ばね」を表現1
//ばねの数珠繋ぎをPhysicsで再現import traer.physics.*;ParticleSystem physics;Particle mouse;Particle[] p;Spring[] s;
void setup(){ size( 400, 400 ); smooth(); //Physicsの環境を生成 physics = new ParticleSystem( 1.0, 0.05 ); p = new Particle[10]; s = new Spring[10]; //指定した数だけバネを数珠繋ぎにする for(int i = 0; i < p.length; i++){ p[i] = physics.makeParticle( 1.0, width/2, 20 * i, 0); if(i > 0){ s[i] = physics.makeSpring( p[i], p[i-1], 1.0, 0.1, 20); } } //先頭のパーティクルを固定 p[0].makeFixed();}
Physicsで「ばね」を表現1
void draw(){ background(0); physics.tick(); //先頭のパーティクル位置をマウスの位置に p[0].moveTo(mouseX, mouseY, 0); //パーティクルの位置に円を配置、バネを線で描画 for(int i = 0; i < p.length; i++){ noStroke(); fill(128); ellipse( p[i].position().x(), p[i].position().y(), 10, 10 ); if(i > 0){ stroke(255); line(p[i].position().x(), p[i].position().y(), p[i-1].position().x(), p[i-1].position().y()); } }}
Physicsで「ばね」を表現1
ノードガーデン1
• Jared TarbelのNodeGardenをPhysicsで再現• http://levitated.net/bones/nodeGarden/index.html
• Particleを多数生成し、画面上にランダムにちりばめる• それぞれのParticle同士を全てSpringで接続• Particle間の距離に応じて、Spring接続を表現する線の濃度を変化させる
ノードガーデン1
• N個のParticle同士を全てをSpringで結ぶには?• p[0]からは
• → p[1], → p[2], → p[3]... p[n]• p[1]からは
• → p[2], → p[3], → p[4]... p[n]• p[n]までこれを繰り返し
• この法則は、以下のfor文で実現可能
• for(int i = 0; i < n; i++){• for(j = i + 1; j < n; j++){
• 処理内容• }
• }
p[0] p[1]
p[2]
p[3]
...
p[n]
p[1] p[2]
p[3]
p[4]
...
p[n]
p[2] p[3]
p[4]
p[5]
...
p[n]
...
p[n]
ノードガーデン1
import processing.opengl.*;import traer.physics.*;ParticleSystem physics;Particle[] particles;int num = 80;
void setup(){ size(800, 800, OPENGL ); fill(255); smooth(); rectMode(CENTER); physics = new ParticleSystem(0, 0.0); particles = new Particle[num]; for (int i = 0; i < num; i++){ particles[i] = physics.makeParticle(0.2, random(width), random(height), 0); for (int j = i + 1; j < num; j++){ particles[j] = physics.makeParticle(0.2, random(width), random(height), 0); particles[j].setMass(0.5); physics.makeSpring(particles[i], particles[j], 0.1, 0.0, width/2); } }}
ノードガーデン1
void draw(){ physics.tick(0.01); background(0); for (int i = 0; i < num; i++){ rect(particles[i].position().x(), particles[i].position().y(), 8, 8); }}
ノードガーデン1
ノードガーデン2
• 全てのSpring接続を線で描画する• 先程の2重のfor文で、生成可能
ノードガーデン2
import processing.opengl.*;import traer.physics.*;ParticleSystem physics;Particle[] particles;int num = 80;
void setup(){ size(800, 800, OPENGL ); fill(255); smooth(); rectMode(CENTER); physics = new ParticleSystem(0, 0.0); particles = new Particle[num]; for (int i = 0; i < num; i++){ particles[i] = physics.makeParticle(0.2, random(width), random(height), 0); for (int j = i + 1; j < num; j++){ particles[j] = physics.makeParticle(0.2, random(width), random(height), 0); particles[j].setMass(0.5); physics.makeSpring(particles[i], particles[j], 0.1, 0.0, width/2); } }}
ノードガーデン2
void draw(){ physics.tick(0.01); background(0); for (int i = 0; i < num; i++){ fill(255); noStroke(); rect(particles[i].position().x(), particles[i].position().y(), 8, 8); stroke(127,50); //Spring間をlineで結ぶ for (int j = i + 1; j < num; j++){ line(particles[j].position().x(), particles[j].position().y(), particles[i].position().x(), particles[i].position().y()); } }}
ノードガーデン2
ノードガーデン3
• 全てのSpring接続を線の濃度を距離によって変化させる• Spring間の距離はdist関数で得ることができる
• dist(x1, y1, x2, y2);• 距離が近いほど線のアルファ値を濃くする
ノードガーデン3
import processing.opengl.*;import traer.physics.*;ParticleSystem physics;Particle[] particles;int num = 80;
void setup(){ size(800, 800, OPENGL ); fill(255); smooth(); rectMode(CENTER); physics = new ParticleSystem(0, 0.0); particles = new Particle[num]; for (int i = 0; i < num; i++){ particles[i] = physics.makeParticle(0.2, random(width), random(height), 0); for (int j = i + 1; j < num; j++){ particles[j] = physics.makeParticle(0.2, random(width), random(height), 0); particles[j].setMass(0.5); physics.makeSpring(particles[i], particles[j], 0.1, 0.0, width/2); } }}
ノードガーデン3
void draw(){ physics.tick(0.01); background(0); for (int i = 0; i < num; i++){ rect(particles[i].position().x(), particles[i].position().y(), 4, 4); for (int j = i + 1; j < num; j++){ float l = dist(particles[j].position().x(), particles[j].position().y(), particles[i].position().x(), particles[i].position().y()); stroke(255, 100 - l); line(particles[j].position().x(), particles[j].position().y(), particles[i].position().x(), particles[i].position().y()); } }}
ノードガーデン3
ノードガーデン4
• Particle間の接続を、SpringではなくAttraction (引力) におきかえる
ノードガーデン4
import processing.opengl.*;import traer.physics.*;ParticleSystem physics;Particle[] particles;int num = 80;
void setup(){ size(800, 800, OPENGL ); smooth(); fill(255); rectMode(CENTER); physics = new ParticleSystem(0, 0.0); particles = new Particle[num]; for (int i = 0; i < num; i++){ particles[i] = physics.makeParticle(0.2, random(width), random(height), 0); for (int j = i + 1; j < num; j++){ particles[j] = physics.makeParticle(0.2, random(width), random(height), 0); particles[j].setMass(0.5); physics.makeAttraction(particles[i], particles[j], 1000, width); } }}
ノードガーデン4
void draw(){ physics.tick(1); background(0); for (int i = 0; i < num; i++) { rect(particles[i].position().x(), particles[i].position().y(), 3, 3); for (int j = i + 1; j < num; j++) { float l = dist(particles[j].position().x(), particles[j].position().y(), particles[i].position().x(), particles[i].position().y()); stroke(255, 100 - l); line(particles[j].position().x(), particles[j].position().y(), particles[i].position().x(), particles[i].position().y()); } }}
ノードガーデン4
アトラクター
• 大量のパーティクルを引力で吸い上げる• キー入力で、「引き込み」と「反発」を切り替えられるように
アトラクター
import processing.opengl.*;import traer.physics.*;int NUM = 10000;Particle mouse, b, c;Particle[] others;ParticleSystem physics;Attraction[] attr;float strength = 0.0;float vect = 1.0;void setup(){ size( 1024, 768, OPENGL); frameRate( 60 ); smooth(); physics = new ParticleSystem( 0, 0.1 ); mouse = physics.makeParticle(); mouse.makeFixed(); others = new Particle[NUM]; attr = new Attraction[NUM]; for ( int i = 0; i < others.length; i++ ){ others[i] = physics.makeParticle( 1.0, random( 0, width ), random( 0, height ), 0 ); attr[i] = physics.makeAttraction( mouse, others[i], strength, 50); } noStroke(); background(0);}
アトラクター
void draw(){ mouse.moveTo( mouseX, mouseY, 0 ); physics.tick(); background( 0 ); fill(255); for ( int i = 0; i < others.length; i++ ){ Particle p = others[i]; attr[i].setStrength(strength * vect); rect(p.position().x(), p.position().y(), 1, 1); }
if(mousePressed){ strength += 100; } else { strength = 0; }}
void keyPressed() { vect *= -1;}
アトラクター