Upload
atsushi-tadokoro
View
8.791
Download
3
Embed Size (px)
DESCRIPTION
Citation preview
Media Art II 2011 第5回:openFrameworksアニメーションを極める動きを生みだす様々なアルゴリズム
2011年10月4日多摩美術大学 情報デザイン学科 情報芸術コース田所 淳
今日の内容‣ 課題の簡単な講評‣ Particleを使用した表現
‣ 動きを生みだす様々なアルゴリズムを紹介‣ パーティクルによるドローイング‣ 重力、バウンド‣ 弾力、ばね、ばねの連結‣ ベクトル場 (Vector Field) - 流体、磁性体の表現‣ 群れの動き (Flocking)‣ 空間の充填
動きを生みだす様々なアルゴリズム‣ 乱数や単純な運動だけでなく、より複雑で有機的な動きを生みだすための手法を紹介
‣ 手持ちの表現の持駒を増やす‣ 今後の作品制作の表現の幅を拡げる基礎体力に
サンプルファイル‣ 紹介した全てのプログラムのソースは下記のリンクからダウンロード可能
‣ openFramewroks v.007 で動作確認済み
‣ http://goo.gl/zyfQR (Google Docsにリンク)
パーティクルによるドローイング
パーティクルによるドローイング‣ マウスを動かす勢いと方向を、パーティクルに付加‣ マウスドラッグに対応させる → パーティクルによるドローイングのような効果に
パーティクルによるドローイング‣ コードのポイント‣ マウスの勢いを、パーティクルの座標に変換している部分‣ testApp.cpp
void testApp::mouseMoved(int x, int y ){! prevMouseX = x;! prevMouseY = y;}
void testApp::mouseDragged(int x, int y, int button){! //1フレーム前の座標から、現在のマウス座標との差分を算出! float dx = x - prevMouseX;! float dy = y - prevMouseY;!! //新規にパーティクルを作成、計算したベクトルを付与! particle myParticle;! myParticle.setInitialCondition(x,y, dx*0.5,dy*0.5);! //配列に追加! particles.push_back(myParticle);!! //現在のマウス座標を記録! prevMouseX = x;! prevMouseY = y;!}
パーティクルによるドローイング‣ 実行結果:マウスの動きでドローイング
パーティクルによるドローイング‣ ofCurveVertexを、ofNoFill() から ofFill()にしてみると…
重力とバウンド
重力とバウンド‣ 画面の下方向に重力を (これは先週に解説済み)‣ さらに、画面の端に架空の「壁」をつくりたい‣ 壁にパーティクルが衝突すると、バウンドして跳ね返る
重力
‣ バウンドとはどういうことか?‣ 壁に当たった瞬間に、力の方向が反転する‣ + と - が逆転。力の方向に、-1 を掛ける
重力とバウンド
F -F
重力とバウンド‣ コードのポイント:バウンドする動きをParticleクラスに追加‣ Particle.cpp - bounceOffWalls() 関数void particle::bounceOffWalls(){! //壁の範囲を設定! float minx = 0;! float miny = 0;! float maxx = ofGetWidth();! float maxy = ofGetHeight();! //左右の壁でのバウンド! if (pos.x > maxx){! ! pos.x = maxx;! ! vel.x *= -1;! } else if (pos.x < minx){! ! pos.x = minx;! ! vel.x *= -1;! }! //上下の壁でのバウンド! if (pos.y > maxy){! ! pos.y = maxy;! ! vel.y *= -1;! } else if (pos.y < miny){! ! pos.y = miny;! ! vel.y *= -1;! }}
重力とバウンド‣ 実行結果:弾むパーティクル!!
弾力、ばね、ばねの連結
弾力、ばね、ばねの連結‣ 「ばね」を数学的に定義する‣ フックの法則 - バネの伸びが x のとき、それによって生じる力を F とすると
‣ F = -kx ‣ ( k:ばね定数)
振動を繰り返す(単振動)
0
x
F = -kxma = -kx
x = -sinmk t
x = Csin(ωt + θ)
弾力、ばね、ばねの連結‣ フックの定理は以下のように書き直すことができる
‣ springForce = -stiffness * stretch‣ バネの力 = -バネの硬さ * バネの伸び
‣ さらに「バネの伸び」の部分を座標として解釈する
‣ springForce = -stiffness * (position - restPosition)‣ バネの力 = -バネの硬さ * (現在位置 - ばねの静止位置)
弾力、ばね、ばねの連結‣ 式を整理
‣ springForce = stiffness * (restPositon - position)バネの力 = バネの硬さ * (静止位置 - 現在位置)
‣ バネの力から、ばねの移動速度が導きだせる
‣ velocity = dumping * (velocity + springFroce)速度 = 摩擦 * (速度 + バネの力)
弾力、ばね、ばねの連結‣ まずは、単体のバネを実現してみる‣ ばねの性質をクラスとしてまとめる → springクラス‣ 「ばね」は、2つのparticleの間に張られる‣ Particle A と Particle B
Particle A
Particle B
Spring
弾力、ばね、ばねの連結‣ ばねの運動は、springクラスのupdateで計算‣ spring.cpp - update()関数‣ フックの法則から、2つのParticleにかかる力をそれぞれ計算void spring::update(){! if ((particleA == NULL) || (particleB == NULL)){! ! return;! }!! ofVec2f pta = particleA->pos;! ofVec2f ptb = particleB->pos;!! float theirDistance = (pta - ptb).length();! float springForce = (springiness * (distance - theirDistance));! ofVec2f frcToAdd = (pta-ptb).normalized() * springForce;!! particleA->addForce(frcToAdd.x, frcToAdd.y);! particleB->addForce(-frcToAdd.x, -frcToAdd.y);}
弾力、ばね、ばねの連結‣ testAppからは、2つのparticleと、それに挟まれる1つのspringを指定する
‣ testApp.cpp - setup()関数void testApp::setup(){!!! ofBackground(0,0,0);!! ofSetVerticalSync(true);! ofSetFrameRate(60);
! //Particle A 初期設定! particle_a.setInitialCondition(400, 400, 0, 0);!! //Particle B 初期設定! particle_b.setInitialCondition(500, 500, 0, 0);
! //ばね(spring)を、パーティクル間に張る! mySpring.distance = 100; //ばねの長さ! mySpring.springiness = 0.1f; //ばねの硬さ! mySpring.particleA = &particle_a;! mySpring.particleB = &particle_b;}
弾力、ばね、ばねの連結‣ testAppでは、2つのparticleとspringの更新をする‣ testApp.cpp - update()関数
void testApp::update(){!! //力をリセット! particle_a.resetForce();! particle_b.resetForce();!! //バネを更新! mySpring.update();!! //パーティクルの状態を更新 (壁でバウンド)! particle_a.bounceOffWalls();! particle_a.update();!! particle_b.bounceOffWalls();! particle_b.update();}
弾力、ばね、ばねの連結‣ あとは、描画するのみ!!‣ testApp.cpp - draw()関数
void testApp::draw(){! ofSetColor(255, 255, 255);!! //ばねを描画! mySpring.draw();!! //particleを描画! particle_a.draw();! particle_b.draw();}
弾力、ばね、ばねの連結‣ 実行結果:空間を漂うバネが描画される‣ マウスドラッグで操作可能
弾力、ばね、ばねの連結‣ 次に、ばねを数珠繋ぎにしてみる‣ spring と particle をそれぞれ動的配列(vector)として作成‣ 全てのばねの伸縮と、それに伴なう全てのパーティクルにかかる力を計算する
弾力、ばね、ばねの連結‣ 数珠繋ぎのばねを生成している箇所‣ testApp - setup() 関数内void testApp::setup(){!! ofBackground(0,0,0);! ofSetVerticalSync(true);! ofSetFrameRate(60);! ofSetBackgroundAuto(false);! pimg.loadImage("particle32.png");
! //particleの配列を生成! for (int i = 0; i < 20; i++){! ! particle myParticle;! ! myParticle.setInitialCondition(ofRandom(500,550),ofRandom(500,550),0,0);! ! particles.push_back(myParticle);! }!! //全ての配列を順番にspringで接続していく! for (int i = 0; i < (particles.size()-1); i++){! ! spring mySpring;! ! mySpring.distance = 25;! ! mySpring.springiness = 0.2f;! ! mySpring.particleA = & (particles[i]);! ! mySpring.particleB = & (particles[(i+1)%particles.size()]);! ! springs.push_back(mySpring);! }}
弾力、ばね、ばねの連結‣ 数珠繋ぎになった、ばねとparticleの更新‣ testApp - update() 関数内void testApp::update(){! //全てのparticleの力をリセット! for (int i = 0; i < particles.size(); i++){! ! particles[i].resetForce();! }
! //全てのばねの伸縮を計算! for (int i = 0; i < springs.size(); i++){! ! springs[i].update();! }!! //全てのparticleの状態を更新! for (int i = 0; i < particles.size(); i++){! ! particles[i].addDampingForce();! ! particles[i].bounceOffWalls();! ! particles[i].update();! }}
弾力、ばね、ばねの連結‣ 全てのばねとpatricleを描画‣ testApp - draw() 関数内void testApp::draw(){
! //全てのばねを描画! ofSetColor(255, 255, 255, 127);! for (int i = 0; i < springs.size(); i++){! ! springs[i].draw();! }!! //全てのparticleを描画! ofSetColor(255, 255, 255);! for (int i = 0; i < particles.size(); i++){! ! particles[i].draw();! }!}
弾力、ばね、ばねの連結‣ 実行結果:数珠繋ぎになったばね
弾力、ばね、ばねの連結‣ 応用1:ばねを、円弧に数珠繋ぎにして力を加える
弾力、ばね、ばねの連結‣ 応用2:パーティクル同士をすべて接続して「筋肉」のように
ベクトル場 (Vector Field)- 流体、磁性体の表現
ベクトル場 (Vector Field) とは‣ ベクトル場 (Vector Field)‣ 空間の広がりの中でベクトル的な量の分布‣ 例えば…‣ 動いている流体の早さと向き‣ 磁力や重力などの力の強さと向きなど
‣ ベクトル場をシミュレーションできれば、流体は磁性体などの動きを再現することが可能になるはず
‣ ベクトル場 (Vector Field) のイメージ‣ 向きと方向をもった力が、グリッド上に整列している
ベクトル場 (Vector Field) とは
vectorFirldクラスの設計‣ まずは、ベクトル場を生成してみる
‣ vectorFieldクラス‣ ベクトル場を生成し、座標を指定すると、その位置に働く力の向きと方向を算出する
‣ 座標を指定して新たにベクトル場に力を加えることも可能‣ 外側に向う力 (噴出?)‣ 内側に向う力 (吸引?)‣ 渦巻のように回転する力 (時計回り、反時計回り)
‣ vectorFieldクラスのプロパティとメソッドを整理する‣ vectorFieldの実装の詳細は省略…
vectorFirldクラスの設計‣ vectorFirldクラス:プロパティ
型 変数名 内容
int fieldWidth ベクトルの数、横
int fieldHeight ベクトルの数、縦
int fieldSize ベクトルの総数
int externalWidth ベクトル場の幅
int externalHeight ベクトル場の高さ
vector field ベクトルの配列
vectorFirldクラスの設計‣ vectorFirldクラス:メソッド
メソッド名と引数・戻り値 説明
vectorField(); コンストラクタ
virtual ~vectorField(); デストラクタ
void setupField(int innerW, int innerH, int outerW, int outerH); ベクトル場の初期化
void clear(); ベクトル場を消去
void fadeField(float fadeAmount); 徐々にベクトル場の力が減衰するようにする
void randomizeField(float scale); 全てのベクトル場の力をランダムに生成
void draw(); ベクトル場の様子を描画
ofVec2f getForceFromPos(float xpos, float ypos); 指定した座標にかかる力のベクトルを算出
vectorFirldクラスの設計‣ vectorFirldクラス:メソッド (つづき)
メソッド名と引数・戻り値 説明
void addOutwardCircle(float x, float y, float radius, float strength); 外向きの力を加える (噴出)
void addInwardCircle(float x, float y, float radius, float strength); 内向きの力を加える (吸引)
void addClockwiseCircle(float x, float y, float radius, float strength); 時計回りの渦巻
void addCounterClockwiseCircle(float x, float y, float radius, float strength); 反時計回りの渦巻
void addVectorCircle(float x, float y, float vx, float vy, float radius, float strength);
円形の力を加える
ベクトル場を生成‣ testAppクラスからvectorFieldクラスをインスタンス化して、画面にベクトル場を生成し描画する
‣ キーをタイプすると、様々な方法でベクトル場に力を付加できるようにする‣ 1:外向きの力‣ 2:内向きの力‣ 3:時計回りの渦巻‣ 4:反時計回りの渦巻
‣ ベクトル場の力は、徐々に減衰していく
ベクトル場を生成‣ ベクトル場の初期化‣ testApp.cpp - setup() 関数oid testApp::setup(){!!! ofSetVerticalSync(true);! ofSetFrameRate(60);! ofBackground(0, 0, 0);!! //ベクトル場の初期化 (画面いっぱいに、横102コ、縦76コのベクトル)! VF.setupField(102, 76, ofGetWidth(), ofGetHeight());
! //初期状態は、外向きの力! addMode = 1;}
ベクトル場を生成‣ ベクトル場の描画‣ testApp.cpp - draw() 関数void testApp::draw(){
! //ベクトル場を描画! ofEnableBlendMode(OF_BLENDMODE_ADD);! ofSetColor(0,130,130, 200);! VF.draw();! ofDisableBlendMode();!}
ベクトル場を生成‣ 完成!! : 1~ 4 のキーで加える力の向きを決定し、マウスをドラッグしてベクトル場に力を加えてみる
ベクトル場にパーティクルを浮遊させる‣ 生成したベクトル場にパーティクルを配置してみる
‣ ベクトル場から、それぞれのパーティクルの座標にかかる力を算出して、空間の力を受けて浮遊するようにしてみる
‣ 座標上にあるパーティクルにかかる力は getForceFromPos() 関数で算出することが可能
ベクトル場にパーティクルを浮遊させる‣ ベクトル場に配置するパーティクルの初期化‣ testApp.cpp - setup() 関数void testApp::setup(){!!! ofSetVerticalSync(true);! ofSetFrameRate(60);! ofBackground(0, 0, 0);!! //パーティクルを10000個生成! for (int i = 0; i < 10000; i++){! ! particle myParticle;! ! myParticle.setInitialCondition! ! (ofRandom(0,ofGetWidth()),ofRandom(0,ofGetHeight()),0,0);! ! particles.push_back(myParticle);! }!! //ベクトル場の初期化 (画面いっぱいに、横102コ、縦76コのベクトル)! VF.setupField(102, 76, ofGetWidth(), ofGetHeight());!! //初期状態は、内向きの力! addMode = 2;}
ベクトル場にパーティクルを浮遊させる‣ ベクトル場からパーティクルにかかる力を算出する‣ testApp.cpp - update() 関数void testApp::update(){!! for (int i = 0; i < particles.size(); i++){! !! ! //particleの力をリセット! ! particles[i].resetForce();! !! ! //ベクトル場から、それぞれのparticleにかかる力を算出! ! ofVec2f frc;! ! frc = VF.getForceFromPos(particles[i].pos.x, particles[i].pos.y);! !! ! //Particleの状態を更新! ! particles[i].addForce(frc.x, frc.y);! ! particles[i].addDampingForce();! ! particles[i].bounceOffWalls();! ! particles[i].update();! }!! //ベクトル場の力の減衰! VF.fadeField(0.998f);}
ベクトル場にパーティクルを浮遊させる‣ あとは、全てのパーティクルを描画するのみ‣ testApp.cpp - update() 関数void testApp::draw(){!! ofEnableBlendMode(OF_BLENDMODE_ADD);!! //ベクトル場を描画! ofSetColor(0,130,130, 127);! VF.draw();!! //ベクトル場に配置されたparticleを描画! ofSetColor(0, 127, 255)! ;! for (int i = 0; i < particles.size(); i++){! ! particles[i].draw();! }!!! ofDisableBlendMode();!}
ベクトル場にパーティクルを浮遊させる‣ 完成:マウスをドラッグするとパーティクルが浮遊する
‣ 最初のサンプルで使用した、マウスの軌跡でドローインングするアルゴリズムと、ベクトル場を融合する
‣ マウスをドラッグすると、ベクトル場を自由に描くことができるように
ベクトル場 + ドローイング
ベクトル場 + ドローイング‣ マウスをドラッグすると、その方向と距離でベクトル場に力を加えるようにする
‣ testApp.cpp - mouseDragged() 関数
void testApp::mouseDragged(int x, int y, int button){!! float diffx = x - prevMouseX;! float diffy = y - prevMouseY;!! VF.addVectorCircle((float)x, (float)y, diffx*0.3, diffy*0.3, 60, 0.3f);!! prevMouseX = x;! prevMouseY = y;}
ベクトル場 + ドローイング‣ 完成:空間上の粒子を指でなぞるように動かすことが可能
ベクトル場 + ドローイング‣ 応用:iPadなどのマルチタッチのデバイスと組み合せ‣ MSAFluid:Memo Akten氏による流体のシミュレーション‣ http://www.msavisuals.com/msafluid
生物の動き鳥の群れをシミュレート (Boids)
鳥の群れをシミュレート (Boid)‣ Boids 「鳥もどき(bird-oid)」‣ 1987年にCraig Raynoldsによって発表された理論‣ 3つのルールを規定するだけで鳥の群れをシミュレーションできるというもの
‣ 単純な規則を用いて群体としての複雑な振る舞いを再現できることを示した
‣ Craig ReynoldsのBoidsの解説ページ‣ http://www.red3d.com/cwr/boids/
鳥の群れをシミュレート (Boid)‣ Boidsのルール
‣ Separation(引き離し)‣ 近くの鳥や物体に近づきすぎたらぶつからないように離れるルール
‣ Alingment(整列)‣ 近くの鳥たちと飛ぶスピードや方向を合わせようとするルール
‣ Cohesion(結合)‣ 鳥たちが多くいる方へ向かって飛ぶルール
鳥の群れをシミュレート (Boid)‣ このサンプルでは、particleクラス自身にBoidsのルールをメソッドとして追加している
‣ addForFlocking()‣ addFlockingForce()
ベクトル場にパーティクルを浮遊させる‣ Boidsの3つのルールを、particle.cppに追加void particle::addFlockingForce(){
! // seperation! if(seperation.count > 0){! ! seperation.sum /= (float)seperation.count;! ! float sepFrc ! = seperation.strength;! ! frc -= (seperation.sum.normalized() * sepFrc);! }!! // alignment! if(alignment.count > 0){! ! alignment.sum /= (float)alignment.count;! ! float alignFrc ! = alignment.strength;! ! frc += (alignment.sum!! * alignFrc);! }!! // cohesion! if(cohesion.count > 0){! ! cohesion.sum /= (float)cohesion.count;! ! cohesion.sum -= pos;! ! float cohFrc ! = cohesion.strength;! ! frc += (cohesion.sum.normalized() * cohFrc);! }!}
ベクトル場にパーティクルを浮遊させる‣ Boidsの3つのルールを、particle.cppに追加 (つづき)void particle::addForFlocking(particle &p){!! ofVec3f diff, diffNormalized;! float distance;!! diff! ! ! = p.pos - pos;! distance! ! = diff.length();! diffNormalized! = diff;! diffNormalized.normalize();
! if( distance > 0 && distance < seperation.distance ){! ! seperation.sum += diffNormalized;! ! seperation.count++;! }!! if( distance > 0 && distance < alignment.distance ){! ! alignment.sum += p.vel.getNormalized();! ! alignment.count++;! }!! if( distance > 0 && distance < cohesion.distance ){! ! cohesion.sum += p.pos;! ! cohesion.count++;! }}
鳥の群れをシミュレート (Boid)‣ 驚くほどリアルな群れの動きが再現される