yoppa.org


ニュートン力学による動き – Processingで動きを極める!

p5example161102

これまでやってきたように、Processingで位置と運動のベクトル(PVector)を操作することで、アニメーションを作成することができました。単純な動きの場合はこうした座標の足し算引き算で対応可能です。しかし、ここからさらに発展させて、よりリアルな動きを実現しようとすると、限界があります。今回は、単なる座標操作ではなく、運動の背後にある物理的な原理を理解して、その本質に迫ります。

アイザック・ニュートンは、運動の法則を基礎として構築した、力学体系を構築しました(ニュートン力学)。物体の運動や力、質量といったものは、ニュートンの運動の法則によって説明できます。

  • 第1法則(慣性の法則): 質点は、力が作用しない限り、静止または等速直線運動する
  • 第2法則(ニュートンの運動方程式): 質点の加速度は、そのとき質点に作用する力に比例し、質点の質量に反比例する
  • 第3法則(作用・反作用の法則): 二つの質点の間に相互に力が働くとき、質点 2 から質点 1 に作用する力と、質点 1 から質点 2 に作用する力は、大きさが等しく、逆向きである

今回は、このニュートンの運動の法則を援用しながら、様々な動きや力についてProcessingで実装しながら、動きに関する理解を深めていきます。

スライド資料

サンプルコード

位置と速度による運動 – 基本

ParticleVec2 particle;

void setup() {
  //初期設定
  size(800, 600);
  frameRate(60);
  noStroke();
  fill(255);
  //パーティクルをインスタンス化
  particle = new ParticleVec2();
  //初期位置を設定
  particle.location.set(10, 10);
  //速度を設定
  particle.velocity.set(3, 2);
}

void draw() {
  background(0);
  //パーティクルの位置を更新
  particle.update();
  //パーティクルを描画
  particle.draw();
}

//パーティクルクラス
class ParticleVec2 {
  PVector location;       //位置
  PVector velocity;       //速度
  float radius;           //パーティクルの半径

  //コンストラクター
  ParticleVec2() {
    //初期パラメーターを設定
    radius = 4.0;
    location = new PVector(width/2.0, height/2.0);
    velocity = new PVector(0, 0);
  }

  //座標の更新
  void update() {
    location.add(velocity);     //速度から位置を算出
  }

  //パーティクルを描画
  void draw() {
    ellipse(location.x, location.y, radius * 2, radius * 2);
  }
}

速度と加速度

ParticleVec2 particle;

void setup() {
  //初期設定
  size(800, 600);
  frameRate(60);
  noStroke();
  fill(255);
  //パーティクルをインスタンス化
  particle = new ParticleVec2();
  //初期位置を設定
  particle.location.set(10, 10);
  //力を加える
  particle.acceleration.set(3, 2);
}

void draw() {
  background(0);
  //パーティクルの位置を更新
  particle.update();
  //パーティクルを描画
  particle.draw();
}

//パーティクルクラス
class ParticleVec2 {
  PVector location;       //位置
  PVector velocity;       //速度
  PVector acceleration;   //加速度
  float radius;           //パーティクルの半径

  //コンストラクター
  ParticleVec2() {
    //初期パラメーターを設定
    radius = 4.0;
    location = new PVector(width/2.0, height/2.0);
    velocity = new PVector(0, 0);
    acceleration = new PVector(0, 0);
  }

  //座標の更新
  void update() {
    velocity.add(acceleration); //速度に加速度を加算
    location.add(velocity);     //速度から位置を算出
    acceleration.set(0, 0);     //加速度をリセット
  }

  //パーティクルを描画
  void draw() {
    ellipse(location.x, location.y, radius * 2, radius * 2);
  }
}

摩擦力を加える

ParticleVec2 particle;

void setup() {
  //初期設定
  size(800, 600);
  frameRate(60);
  noStroke();
  fill(255);
  //パーティクルをインスタンス化
  particle = new ParticleVec2();
  //初期位置を設定
  particle.location.set(10, 10);
  //摩擦力を設定
  particle.friction = 0.01;
  //力を加える
  particle.acceleration.set(3, 2);
}

void draw() {
  background(0);
  //パーティクルの位置を更新
  particle.update();
  //パーティクルを描画
  particle.draw();
}

//パーティクルクラス
class ParticleVec2 {
  PVector location;       //位置
  PVector velocity;       //速度
  PVector acceleration;   //加速度
  float radius;           //パーティクルの半径
  float friction;         //摩擦
  
  //コンストラクター
  ParticleVec2() {
    //初期パラメーターを設定
    radius = 4.0;
    location = new PVector(width/2.0, height/2.0);
    velocity = new PVector(0, 0);
    acceleration = new PVector(0, 0);
    friction = 0.0;
  }

  //座標の更新
  void update() {
    velocity.add(acceleration);     //速度に加速度を加算
    velocity.mult(1.0 - friction);  //摩擦力を加味した速度を計算
    location.add(velocity);         //速度から位置を算出
    acceleration.set(0, 0);         //加速度をリセット
  }

  //パーティクルを描画
  void draw() {
    ellipse(location.x, location.y, radius * 2, radius * 2);
  }
}

重力を加える

ParticleVec2 particle;

void setup() {
  //初期設定
  size(800, 600);
  frameRate(60);
  noStroke();
  fill(255);
  //パーティクルをインスタンス化
  particle = new ParticleVec2();
  particle.location.set(10, height/2); //初期位置を設定
  particle.gravity.set(0, 1);          //重力を設定
  particle.friction = 0.01;            //摩擦力を設定
  particle.mass = 1.0;                 //質量を設定
  //力を加える
  particle.addForce(new PVector(20, -20));
}

void draw() {
  background(0);
  particle.update();  //パーティクルの位置を更新
  particle.draw();    //パーティクルを描画
}

//パーティクルクラス
class ParticleVec2 {
  PVector location;       //位置
  PVector velocity;       //速度
  PVector acceleration;   //加速度
  PVector gravity;        //重力
  float radius;           //パーティクルの半径
  float friction;         //摩擦
  float mass;             //質量

  //コンストラクター
  ParticleVec2() {
    //初期パラメーターを設定
    radius = 4.0;
    location = new PVector(width/2.0, height/2.0);
    velocity = new PVector(0, 0);
    acceleration = new PVector(0, 0);
    gravity = new PVector(0, 0);
    friction = 0.0;
    mass = 1.0;
  }

  //座標の更新
  void update() {
    velocity.add(acceleration);     //速度に加速度を加算
    velocity.add(gravity);          //重力を加える
    velocity.mult(1.0 - friction);  //摩擦力を加味した速度を計算
    location.add(velocity);         //速度から位置を算出
    acceleration.set(0, 0);         //加速度をリセット
  }

  //力を加える関数
  void addForce(PVector force) {
    //質量から加速度を計算 (a = f/m);
    force.div(mass);
    acceleration.add(force);
  }

  //パーティクルを描画
  void draw() {
    ellipse(location.x, location.y, radius * 2, radius * 2);
  }
}

壁でバウンドさせる

ParticleVec2 particle;

void setup() {
  //初期設定
  size(800, 600);
  frameRate(60);
  noStroke();
  fill(255);
  //パーティクルをインスタンス化
  particle = new ParticleVec2();
  particle.location.set(10, height/2); //初期位置を設定
  particle.gravity.set(0, 1);          //重力を設定
  particle.friction = 0.01;            //摩擦力を設定
  particle.mass = 1.0;                 //質量を設定
  //力を加える
  particle.addForce(new PVector(20, -20));
}

void draw() {
  background(0);
  //パーティクルの位置を更新
  particle.update();
  //壁でバウンドさせる
  particle.bounceOffWalls();
  //パーティクルを描画
  particle.draw();
}

//パーティクルクラス
class ParticleVec2 {
  PVector location;       //位置
  PVector velocity;       //速度
  PVector acceleration;   //加速度
  PVector gravity;        //重力
  float radius;           //パーティクルの半径
  float friction;         //摩擦
  float mass;             //質量
  
  //コンストラクター
  ParticleVec2() {
    //初期パラメーターを設定
    radius = 4.0;
    location = new PVector(width/2.0, height/2.0);
    velocity = new PVector(0, 0);
    acceleration = new PVector(0, 0);
    gravity = new PVector(0, 0);
    friction = 0.0;
    mass = 1.0;
  }
  
  //座標の更新
  void update() {
    velocity.add(acceleration);     //速度に加速度を加算
    velocity.add(gravity);          //重力を加える
    velocity.mult(1.0 - friction);  //摩擦力を加味した速度を計算
    location.add(velocity);         //速度から位置を算出
    acceleration.set(0, 0);         //加速度をリセット
  }
  
  //力を加える関数
  void addForce(PVector force) {
    //質量から加速度を計算 (a = f/m);
    force.div(mass);
    acceleration.add(force);
  }
  
  //壁でバウンドさせる
  void bounceOffWalls() {
    if (location.x < 0 || location.x > width) {
      velocity.x *= -1;
    }
    if (location.y < 0 || location.y > height) {
      velocity.y *= -1;
    }
    //画面からはみ出さないように画面内に限定
    location.x = constrain(location.x, 0, width);
    location.y = constrain(location.y, 0, height);
  }
  
  //パーティクルを描画
  void draw() {
    ellipse(location.x, location.y, radius * 2, radius * 2);
  }
}

大量の物体を動かす

int NUM = 1000;
ParticleVec2[] particles = new ParticleVec2[NUM];

void setup() {
  //初期設定
  size(800, 600, P3D);
  frameRate(60);
  noStroke();
  fill(255);
  for (int i = 0; i < NUM; i++) {
    //パーティクルをインスタンス化
    particles[i] = new ParticleVec2();
    //初期位置を設定
    particles[i].position.set(width/2, height/4);
    particles[i].gravity.set(0, 0.2);
    //摩擦力を設定
    particles[i].friction = 0.005;
    //力を加える
    particles[i].mass = 1.0;
    particles[i].addForce(new PVector(random(-10, 10), random(-10, 10)));
  }
}

void draw() {
  background(0);
  for (int i = 0; i < NUM; i++) {
    //パーティクルの位置を更新
    particles[i].update();
    //壁でバウンドさせる
    particles[i].bounceOffWalls();
    //パーティクルを描画
    particles[i].draw();
  }
}

//パーティクルクラス
class ParticleVec2 {
  PVector position;       //位置
  PVector velocity;       //速度
  PVector acceleration;   //加速度
  PVector gravity;        //重力
  float radius;           //パーティクルの半径
  float friction;         //摩擦
  float mass;             //質量
  
  //コンストラクター
  ParticleVec2() {
    //初期パラメーターを設定
    radius = 4.0;
    position = new PVector(width/2.0, height/2.0);
    velocity = new PVector(0, 0);
    acceleration = new PVector(0, 0);
    gravity = new PVector(0, 0);
    friction = 0.0;
    mass = 1.0;
  }
  
  //座標の更新
  void update() {
    velocity.add(acceleration);     //速度に加速度を加算
    velocity.add(gravity);          //重力を加える
    velocity.mult(1.0 - friction);  //摩擦力を加味した速度を計算
    position.add(velocity);         //速度から位置を算出
    acceleration.set(0, 0);         //加速度をリセット
  }
  
  //力を加える関数
  void addForce(PVector force) {
    //質量から加速度を計算 (a = f/m);
    force.div(mass);
    acceleration.add(force);
  }
  
  //壁でバウンドさせる
  void bounceOffWalls() {
    if (position.x < 0 || position.x > width) {
      velocity.x *= -1;
    }
    if (position.y < 0 || position.y > height) {
      velocity.y *= -1;
    }
    //画面からはみ出さないように画面内に限定
    position.x = constrain(position.x, 0, width);
    position.y = constrain(position.y, 0, height);
  }
  
  //パーティクルを描画
  void draw() {
    ellipse(position.x, position.y, radius * 2, radius * 2);
  }
}

円形にランダムに拡がるように / マウスクリックでリセット

int NUM = 5000;
ParticleVec2[] particles = new ParticleVec2[NUM];

void setup() {
  //初期設定
  size(800, 600, P3D);
  frameRate(60);
  noStroke();
  fill(255);
  for (int i = 0; i < NUM; i++) {
    //パーティクルをインスタンス化
    particles[i] = new ParticleVec2();
    //初期位置を設定
    particles[i].position.set(width/2, height/4);
    //重力を設定
    particles[i].gravity.set(0, 0.1);
    //摩擦力を設定
    particles[i].friction = 0.002;
    //質量を設定
    particles[i].mass = 1.0;
    //半径を設定
    particles[i].radius = 1.5;
    //円形にランダムになるよう力を加える
    float length = random(10);
    float angle = random(2*PI);
    particles[i].addForce(new PVector(length*cos(angle), length*sin(angle)));
  }
  background(0);
}

void draw() {
  //背景をフェード
  fill(0,31);
  rect(0, 0, width, height);
  
  //パーティクルを描画
  fill(255);
  for (int i = 0; i < NUM; i++) {
    //パーティクルの位置を更新
    particles[i].update();
    //壁でバウンドさせる
    particles[i].bounceOffWalls();
    //パーティクルを描画
    particles[i].draw();
  }
}

void mouseReleased() {
  for (int i = 0; i < NUM; i++) {
    //パーティクルをインスタンス化
    particles[i] = new ParticleVec2();
    //初期位置を設定
    particles[i].position.set(mouseX, mouseY);
    //重力を設定
    particles[i].gravity.set(0, 0.1);
    //摩擦力を設定
    particles[i].friction = 0.002;
    //質量を設定
    particles[i].mass = 1.0;
    //半径を設定
    particles[i].radius = 1.5;
    //円形にランダムになるよう力を加える
    float length = random(10);
    float angle = random(2*PI);
    particles[i].addForce(new PVector(length*cos(angle), length*sin(angle)));
  }
}

//パーティクルクラス
class ParticleVec2 {
  PVector position;       //位置
  PVector velocity;       //速度
  PVector acceleration;   //加速度
  PVector gravity;        //重力
  float radius;           //パーティクルの半径
  float friction;         //摩擦
  float mass;             //質量

  //コンストラクター
  ParticleVec2() {
    //初期パラメーターを設定
    radius = 2.0;
    position = new PVector(width/2.0, height/2.0);
    velocity = new PVector(0, 0);
    acceleration = new PVector(0, 0);
    gravity = new PVector(0, 0);
    friction = 0.0;
    mass = 1.0;
  }

  //座標の更新
  void update() {
    velocity.add(acceleration);     //速度に加速度を加算
    velocity.add(gravity);          //重力を加える
    velocity.mult(1.0 - friction);  //摩擦力を加味した速度を計算
    position.add(velocity);         //速度から位置を算出
    acceleration.set(0, 0);         //加速度をリセット
  }

  //力を加える関数
  void addForce(PVector force) {
    //質量から加速度を計算 (a = f/m);
    force.div(mass);
    acceleration.add(force);
  }

  //壁でバウンドさせる
  void bounceOffWalls() {
    if (position.x < 0 || position.x > width) {
      velocity.x *= -1;
    }
    if (position.y < 0 || position.y > height) {
      velocity.y *= -1;
    }
    //画面からはみ出さないように画面内に限定
    position.x = constrain(position.x, 0, width);
    position.y = constrain(position.y, 0, height);
  }

  //パーティクルを描画
  void draw() {
    rectMode(CENTER);
    rect(position.x, position.y, radius*2, radius*2);
    rectMode(CORNER);
  }
}

openFrameworksアニメーションの基本

今回は、openFrameworksで図形を動かす「アニメーション」について、その基本を学んでいきます。まず始めに、位置と速度をベクトル(ofVec2f)で定義して、ベクトルの演算によって動きを生みだす方法について解説します。次に、そのベクトルを配列(Array)で表現することで、複数の物体を同時に動かしてみます。

物体の上限の数がプログラムを起動する段階で決まっていない場合もあります。そのような際には、配列の要素の数を動的に変更可能な可変長配列(std::vector)を使用すると便利です。可変長配列を使用してアニメーションする物体の上限数を可変にする方法を実際のサンプルを通して紹介していきます。

最後に簡単なインタラクションを実装してみます。マウスで画面上をクリックすると、クリックした場所から次々と物体が生成されアニメーションさせます。

スライド資料

サンプルコード

Example 1. アニメーションの基本

Example 2. 物体の数を増やしてみる – 配列とくりかえし

Example 3. 数の上限がわからない時には? – 可変長配列vectorを使う

Example 4. 一定の数に制限する

Example 5. マウスをドラッグした位置から物体を生成


International Conference on Live Coding 2016

2016年10月12日〜15日にかけて、カナダのトロントの近くにあるハミルトンという小さな街でInternational Conference on Live Coding 2016という国際会議にダメ元で作品を応募したら入選したので、参加してきました。まだほとんど知られていない集まりなので、自分の記録を兼ねて(もしかしたら渡航費の補助の資料にもなるかもしれないので…)、どんな様子だったかまとめてみたので、公開してみます。

ICLCはライブコーディングを専門に扱う国際会議です。そもそもライブコーディングとは何かというと、コード(プログラム)を常に動作させた状態でリアルタイムにライブでコーディングを行うことで音楽や映像のパフォーマンスを行うジャンルです。詳細は、ToplapによるLive Codingの説明などが参考になると思います。

国際会議といっても、まだ昨年始まったばかりで今回で2回目の生まれたばかりの集まりで、類似するICMCNIMEなどと比べるとまだ小規模で、参加人数はたぶん50人くらいという感じです。

Webサイトのスケジュールのメニューとわかるように、午前中から昼過ぎにかけて、論文発表やパネル討論などが行われ、夕方から夜にかけてコンサートが開催されるという、ICMCやNIMEなどと同様の構成になっています。ただし、規模が小さいので、同時並行でいろいろイベントがあるのではなく、同時刻には常に1つだけのイベントが行われているので、わかりやすいスケジュールでした。

印象に残った発表など

論文発表 / キーノートスピーチ / パネルディスカッション

Keynote by Andrew Sorensen

ImpromptuやExtemporeといったライブコーディング環境の開発者として、またライブコーダーとして知られるAndrew Sorensenによるキーノート。Extemporeの紹介や現在とりくんでいるプロジェクトの解説など興味深いものでした。

Sang Won Lee and Georg Essl: Live Coding the Audience Participation

ネットワーク参加型のライブコーディングパフォーマンスの試みについて

Charlie Roberts and Graham Wakefield: Live Coding the Digital Audio Workstation

ライブコーディングでAbleton Liveをコントロール

Timothy R Wood: Seer: Live Coding Interactive Audio Visual Worlds in Scala

Scalaを用いたライブコーディングなインタラクティブなオーディオビジュアル開発環境

Scott Fradkin: A Block Based Music Live Coding Environment for Kids

Scratch的なブロックベースのコーディング環境で、Sonic Piでライブコーディングするプログラミング開発環境

Takahiko Tsuchiya, Anna Xambó and Jason Freeman: Adapting DAW-Driven Musical Language to Live Coding: A Case Study in EarSketch

複数人で参加可能な、Webベースのライブコーディングな音楽教育プラットフォーム

Luke Church, Emma Söderberg, Gilad Bracha and Steven Tanimoto: Liveness becomes Entelechy – A scheme for L6

戦略的予測によるライブコーディング

Rodrigo Velasco: Poetics of Live Coding through s2hs2

ProcessingとTidalを連携したシステム「s2hs2」による、ポエティックなライブコーディング

パフォーマンス

Joana Chicau: WebPage Act I, II and III

Webブラウザをライブコーディングでハックしながらパフォーマンス

BEER: Dark Matter

SuperCollierによるライブコーディングセッションを3Dでビジュアライズ

Lee, de Carvalho Jr., Essl: Crowd in C[loud]

iPhoneから観客が参加して、Terry RileyのIn Cのような即興を作りだす

Ben Taylor: The Last Cloud

Webブラウザを用いたオーディオビジュアルパフォーマンス

Luis Fernando Navarro Del Angel, Karen Del Valle, Elihú Pérez, Diego Acevedo and Victor Mancilla: Corium: Live City

Susanne Palzer: On/Off

板の上に乗ったり降りたり

Algorave

最終日の夜は、夕方8:00pm 〜 翌日の2:00amまで、アルゴリズムによるRave “Algorave” が行われた。

著名なライブコーダー達によるライブコーディングによるパフォーマンスは、やはり素晴しく、自分のパフォーマンスの今後の展開を考える上でも、とても参考になるものであった。

  • Jason Levine: Tempus Percussus
  • Ryan Kirkbride: An Algorave with FoxDot
  • Ali Khajehei and Jamie Beverley: 0b10 beats
  • yèct (Rodrigo Velasco)
  • Alexandra Cárdenas
  • Shawn Lawson and Ryan Ross Smith: Owego System Trade Routes
  • Renick Bell: Algorave Performance for Two Conductor Players
  • Charlie Roberts: Improvisation in Gibber
  • Mike Hodnick: Humanly Organized Sound
  • Norah Lorway, Nancy Lee, Kiran Bhumber and Laine Butler: Hollow Vertices: an immersive – improv live coding performance
  • Alex McLean
  • Mico Rex

一部映像があったので転載


生成的な形をつくる – Processing オブジェクト指向プログラミング入門

今回はProcessingで生成的(Generative)な形態を生みだすにはどうすればよいのか、試行錯誤しながら実験していきます。まず初めに、コードを用いた生成的な表現の実例をいくつか紹介した後、実際にProcessingでプログラミングしていきます。

まず始めに、完全にランダムな確率で動きまわる「ランダムウォーク」な動きをする点の動きをつくり、その軌跡を描いてみます。次にこのランダムな動きを増殖させていきます。増殖の際に今回は全てを一つのプログラムに書くのではなく、それぞれの点を細かなプログラムで実装し、その小さなプログラム達を組合せることで一つの機能を生みだすような設計にします。この小さなプログラムを「オブジェクト (Object)」と呼び、オブジェクトを構成単位にしてプログラムを作成していく手法を、オブジェクト指向プログラミング (OOP) と呼びます。このOOPの考え方は今後も重要な内容となってきますので、実例を通して確実に理解していきましょう。

スライド資料

サンプルコード

サンプル1: 先週の復習

PVector location;
PVector velocity;
void setup() {
  size(800, 600);
  frameRate(60);
  location = new PVector(width/2, height/2);
  velocity = new PVector(random(-10, 10), random(-10, 10));
}

void draw() {
  background(0);

  location.add(velocity);

  noStroke();
  fill(0, 127, 255);
  ellipse(location.x, location.y, 20, 20);

  if (location.x < 0 || location.x > width) {
    velocity.x = velocity.x * -1;
  }
  if (location.y < 0 || location.y > height) {
    velocity.y = velocity.y * -1;
  }
}

サンプル2: ランダムウォーク基本

PVector location;
PVector velocity;
void setup() {
  size(800, 600);
  frameRate(60);
  location = new PVector(width/2, height/2);
}

void draw() {
  background(0);
  velocity = new PVector(random(-1, 1), random(-1, 1));
  location.add(velocity);

  noStroke();
  fill(0, 127, 255);
  ellipse(location.x, location.y, 20, 20);
}

サンプル3: ランダムウォークによる軌跡

PVector location;
PVector velocity;
void setup() {
  size(800, 600);
  frameRate(60);
  location = new PVector(width/2, height/2);
  velocity = new PVector();
  background(0);
}

void draw() {
  velocity.x = random(-1, 1);
  velocity.y = random(-1, 1);
  location.add(velocity);
  noStroke();
  fill(0, 127, 255);
  ellipse(location.x, location.y, 2, 2);
}

サンプル4: ランダムウォークによる軌跡 (10倍速)

PVector location;
PVector velocity;
void setup() {
  size(800, 600);
  frameRate(60);
  location = new PVector(width/2, height/2);
  velocity = new PVector();
  background(0);
}

void draw() {
  for (int i = 0; i < 10; i++) {
    velocity.x = random(-1, 1);
    velocity.y = random(-1, 1);
    location.add(velocity);
    noStroke();
    fill(0, 127, 255, 31);
    ellipse(location.x, location.y, 2, 2);
  }
}

サンプル5: ランダムウォーク・オブジェクト

Walker walker;

void setup() {
  size(800, 600);
  frameRate(60);
  
  walker = new Walker();
  
  background(0);
}

void draw() {
  walker.draw();
}

class Walker {
  PVector location;
  PVector velocity;

  Walker() {
    location = new PVector(width/2, height/2);
    velocity = new PVector();
  }

  void draw() {
    for (int i = 0; i < 10; i++) {
      velocity.x = random(-1, 1);
      velocity.y = random(-1, 1);
      location.add(velocity);
      noStroke();
      fill(0, 127, 255, 31);
      ellipse(location.x, location.y, 2, 2);
    }
  }
}

サンプル6: ランダムウォーク・オブジェクトの配列 (10個)

int NUM = 10;
Walker[] walker = new Walker[NUM];

void setup() {
  size(800, 600);
  frameRate(60);

  for (int i = 0; i < NUM; i++) {
    walker[i] = new Walker();
  }

  background(0);
}

void draw() {
  for (int i = 0; i < NUM; i++) {
    walker[i].draw();
  }
}

class Walker {
  PVector location;
  PVector velocity;

  Walker() {
    location = new PVector(width/2, height/2);
    velocity = new PVector();
  }

  void draw() {
    for (int i = 0; i < 10; i++) {
      velocity.x = random(-1, 1);
      velocity.y = random(-1, 1);
      location.add(velocity);
      noStroke();
      fill(0, 127, 255, 15);
      ellipse(location.x, location.y, 2, 2);
    }
  }
}

サンプル6: ランダムウォーク・オブジェクトの配列 (100個)

int NUM = 100;
Walker[] walker = new Walker[NUM];

void setup() {
  size(800, 600);
  frameRate(60);

  for (int i = 0; i < NUM; i++) {
    walker[i] = new Walker();
  }

  background(0);
  blendMode(ADD);
}

void draw() {
  for (int i = 0; i < NUM; i++) {
    walker[i].draw();
  }
}

class Walker {
  PVector location;
  PVector velocity;

  Walker() {
    location = new PVector(width/2, height/2);
    velocity = new PVector();
  }

  void draw() {
    for (int i = 0; i < 10; i++) {
      velocity.x = random(-1, 1);
      velocity.y = random(-1, 1);
      location.add(velocity);
      noStroke();
      fill(0, 127, 255, 5);
      ellipse(location.x, location.y, 2, 2);
    }
  }
}

サンプル7: ランダムウォーク・オブジェクトの配列 (2000個 + 画面効果)

int NUM = 2000;
Walker[] walker = new Walker[NUM];

void setup() {
  size(1920, 1200, P3D);
  frameRate(60);
  noCursor();

  for (int i = 0; i < NUM; i++) {
    walker[i] = new Walker();
  }

  background(0);
}

void draw() {
  blendMode(BLEND);
  fill(0, 15);
  rect(0, 0, width, height);
  for (int i = 0; i < NUM; i++) {
    walker[i].draw();
  }
}

class Walker {
  PVector location;
  PVector velocity;

  Walker() {
    location = new PVector(width/2, height/2);
    velocity = new PVector();
  }

  void draw() {
    for (int i = 0; i < 4; i++) {
      velocity.x = random(-1, 1);
      velocity.y = random(-1, 1);
      location.add(velocity);
      noStroke();
      blendMode(ADD);
      fill(15, 63, 255);
      ellipse(location.x, location.y, 2, 2);
    }
  }
}

Webサービスを利用する

今回の講義の前半は、前回の課題、オンラインポートフォリオの表紙ページの制作の講評を行います。作成したページをサーバーにアップロードして、1人ずつ制作したページをみていきます。

後半は、いろいろなWebサービスの利用方法について解説します。写真、動画、音楽などのデータはWebサーバーにアップロードして貼り付けることもできますが、Webサービスを利用するとより簡易に利用することができます。数多くあるWebサービスの中から、今回は、写真共有のためのFlickr、動画共有のためのVimeo、音楽共有のためのSoundCloudを紹介します。

スライド資料


動きを生みだす – アニメーションとベクトル

今回からいよいよ動きのある表現(= アニメーション)について扱っていきます。アニメーションを実現するには、まず時間を扱う基本構造を知る必要があります。Processingでは、setup(), draw() という2つのブロックにわけて、初期化と更新を行うことでアニメーションを実現しています。まず始めはこの基本構造について理解します。次に、これから動きを扱う際に、向きと大きさをもった「ベクトル」という概念を理解します。ベクトルを理解することで、位置や運動を整理して記述することが可能となります。最後に、この基本構造をベクトルを活用して簡単なアニメーションを作成します。

スライド資料

サンプルコード

サンプルコード 1

//初期化関数
void setup() {
  size(800, 600);
  frameRate(12);   //書き換え頻度の設定
  background(0);
}
//メインループ
void draw() {
  float diameter = random(100);
  noStroke();
  fill(random(255), random(255), random(255));
  ellipse(random(width), random(height), diameter, diameter);
}

サンプルコード 2

float locationX, locationY; //円の中心位置を格納する変数
float velocityX, velocityY; //円の速度を格納する変数
void setup() {
    size(800, 600); //800x600 pixelの画面を生成
    frameRate(60); //フレームレート
    locationX = width/2; //円の初期位置X
    locationY = height/2; //円の初期位置Y
    velocityX = 3; //円の初期位置X
    velocityY = 2; //円の初期位置Y
}

void draw() {
    background(0); //背景を描画
    locationX = locationX + velocityX; //円のX座標を更新
    locationY = locationY + velocityY; //円のY座標を更新
    noStroke(); //枠線なし
    fill(0, 127, 255); //塗りの色
    ellipse(locationX, locationY, 20, 20); //指定した位置に円を描画
}

サンプルコード 3

float locationX, locationY; //円の中心位置を格納する変数
float velocityX, velocityY; //円の速度を格納する変数

void setup() {
    size(800, 600); //800x600 pixelの画面を生成
    frameRate(60);  //フレームレート
    locationX = width/2; //円の初期位置X
    locationY = height/2; //円の初期位置Y
    velocityX = 3;  //円の初期位置X
    velocityY = 2;  //円の初期位置Y
}

void draw() {
    background(0); //背景を描画
    locationX = locationX + velocityX; //円のX座標を更新
    locationY = locationY + velocityY; //円のY座標を更新
    noStroke(); //枠線なし
    fill(0, 127, 255); //塗りの色
    ellipse(locationX, locationY, 20, 20); //指定した位置に円を描画

    if (locationX < 0 || locationX > width) { //もし画面の左端、または右端に到達したら
        velocityX = velocityX * -1; //X方向のスピードを反転
    }
    if (locationY < 0 || locationY > height) { //もし画面の下端、または上端に到達したら
        velocityY = velocityY * -1; //Y方向のスピードを反転
    }
}

サンプルコード 4

PVector location; //円の中心位置を格納する変数
PVector velocity; //円の速度を格納する変数
void setup() {
    size(800, 600); //800x600 pixelの画面を生成
    frameRate(60); //フレームレート
    location = new PVector(random(width), random(height));
    velocity = new PVector(random(-10, 10), random(-10, 10));
}

void draw() {
    background(0); //背景を描画
    location.add(velocity); //位置のベクトルに速度のベクトルを加算、次の位置になる

    noStroke(); //枠線なし
    fill(0, 127, 255); //塗りの色
    ellipse(location.x, location.y, 20, 20); //指定した位置に円を描画
    if (location.x < 0 || location.x > width) { //もし画面の左端、または右端に到達したら
        velocity.x = velocity.x * -1; //X方向のスピードを反転
    }
    if (location.y < 0 || location.y > height) { //もし画面の下端、または上端に到達したら
        velocity.y = velocity.y * -1; //Y方向のスピードを反転
    }
}

サンプルコード 5

int NUM = 100; //配列の数
//位置のベクトルの配列
PVector[] location = new PVector[NUM];
//速度のベクトルの配列
PVector[] velocity = new PVector[NUM];

void setup() {
  size(800, 600); //800x600pixelの画面を生成
  frameRate(60); //フレームレート
  noStroke(); //枠線なし
  fill(0, 127, 255); //塗りの色
  for (int i = 0; i < NUM; i++) { //配列の数だけ繰り返し
    //位置のベクトルの初期設定
    location[i] = new PVector(random(width), random(height));
    //速度のベクトルの初期設定
    velocity[i] = new PVector(random(-4, 4), random(-4, 4));
  }
}

void draw() {
  background(15); //背景を描画
  for (int i = 0; i < NUM; i++) { //配列の数だけ繰り返し
    //指定した位置に円を描画
    ellipse(location[i].x, location[i].y, 20, 20);
    //位置のベクトルに速度のベクトルを加算、次の位置になる
    location[i].add(velocity[i]);
    //もし画面の左端、または右端に到達したら
    if ((location[i].x > width) || (location[i].x < 0)) {
      velocity[i].x *= -1; //X方向のスピードを反転
    }
    //もし画面の下端、または上端に到達したら
    if ((location[i].y > height) || (location[i].y < 0)) {
      velocity[i].y *= -1; //Y方向のスピードを反転
    }
  }
}

サンプルコード 6

int NUM = 500; //配列の数
//位置のベクトルの配列
PVector[] location = new PVector[NUM];
//速度のベクトルの配列
PVector[] velocity = new PVector[NUM];
//塗りの色の配列
color[] col = new color[NUM];
//円の大きさ(直径)の配列
float[] diameter = new float[NUM];

void setup() {
  size(800, 600); //800x600pixelの画面を生成
  frameRate(60); //フレームレート
  noStroke();
  for (int i = 0; i < NUM; i++) { //配列の数だけ繰り返し
    //位置のベクトルの初期設定
    location[i] = new PVector(random(width), random(height));
    //速度のベクトルの初期設定
    velocity[i] = new PVector(random(-4, 4), random(-4, 4));
    //色の初期設定
    col[i] = color(random(255), random(255), random(255), 192);
    //大きさの初期設定
    diameter[i] = random(3, 40);
  }
}

void draw() {
  background(15); //背景を描画
  //配列の数だけ繰り返し
  for (int i = 0; i < NUM; i++) {
    fill(col[i]); //色を指定
    //指定した位置に円を描画
    ellipse(location[i].x, location[i].y, diameter[i], diameter[i]);
    //位置のベクトルに速度のベクトルを加算、次の位置になる
    location[i].add(velocity[i]);
    //もし画面の左端、または右端に到達したら
    if ((location[i].x > width) || (location[i].x < 0)) {
      velocity[i].x *= -1; //X方向のスピードを反転
    }
    //もし画面の下端、または上端に到達したら
    if ((location[i].y > height) || (location[i].y < 0)) {
      velocity[i].y *= -1; //Y方向のスピードを反転
    }
  }
}

反復と乱数

プログラムを構造化していく際に、3つの重要な構成要素があります。それは、「順次 (Sequence)」、「反復 (Iteration)」、「分岐 (Selection)」 です。今回は、この3つの構成要素の中の反復 (Iteration) に注目して、Processingで形を描きながら反復について考えます。また、一意的でないランダムな値である「乱数」と反復を組合せて、どのような表現か可能となるか探求していきます。

スライド資料


Sonic Pi 応用 3 – 楽器をデザインする

Sonic Piの応用編の3回目は、オリジナルのSonic Piの楽器 (Synths) を作成します。beep、fm、prophet、tb303など、Sonic Piでは最初から様々な楽器が定義され「use_synth」をつかって読み込むことですぐに利用可能となっています。内蔵されている楽器を使うだけでも多くのことができるのですが、自分自身の楽器をオリジナルに作成できれば、可能性はさらに大きく拡がります。Sonic Piでは、SuperColliderでプログラミングすることで楽器を新規に定義して読み込むことが可能です。今回は、この方法で楽器を定義してSonic Piで演奏してみます。

スライド資料

サンプルプログラム

  • SuperCollider
//-----------------------------------------------------

( // 01: Sin波基本形
SynthDef("piTest", {
    //出力バスの番号指定
    arg out_bus = 0;
    //Sin波の生成
    var mix = SinOsc.ar(440).dup;
    //DACへ
    Out.ar(out_bus, mix);
}
//ファイルの出力先を指定
).writeDefFile("/Users/tado/Desktop/my-synths");
)

//-----------------------------------------------------

(// 02: Envelope追加
SynthDef("piTest", {
    arg out_bus = 0;
    var mix = SinOsc.ar(440).dup;
    //Envelope生成
    var env = EnvGen.ar(Env.perc(0.0, 1.0, 1.0), doneAction: 2);
    //Envelope適用
    mix = mix * env;
    Out.ar(out_bus, mix);
}
//ファイルの出力先を指定
).writeDefFile("/Users/tado/Desktop/my-synths");
)

//-----------------------------------------------------

(// 03: 音程と音量を指定
SynthDef("piTest", {
    //音程と音量を引数を指定
    arg out_bus = 0, note = 52, amp = 1;
    //音程(MIDI番号)から周波数へ
    var freq = note.midicps;
    //周波数をSin波に適用
    var mix = SinOsc.ar(freq).dup;
    var env = EnvGen.ar(Env.perc(0.0, 1.0, 1.0), doneAction: 2);
    mix = mix * env;
    Out.ar(out_bus, mix);
}
).writeDefFile("/Users/tado/Desktop/my-synths");
)

//-----------------------------------------------------

(// 04: Pan(定位)とエンベロープを指定
SynthDef("piTest", {
    arg out_bus = 0, note = 52, amp = 1, attack = 0, release = 1, pan=0;
    var freq = note.midicps;
    var mix = SinOsc.ar(freq);
    var env = EnvGen.ar(Env.perc(attack, release, 1.0), doneAction: 2);
    //Panを適用
    mix = Pan2.ar(mix * env, pan);
    Out.ar(out_bus, mix);
}
).writeDefFile("/Users/tado/Desktop/my-synths");
)

//-----------------------------------------------------

(// 05: Detune追加
SynthDef("piTest", {
    arg out_bus = 0, note = 52, amp = 1, attack = 0, release = 1, detune = 1.01, pan=0;
    var freq = note.midicps;
    //Detuneを追加
    var mix = (SinOsc.ar(freq) + SinOsc.ar(freq*detune))/2.0;
    var env = EnvGen.ar(Env.perc(attack, release, 1.0), doneAction: 2);
    mix = Pan2.ar(mix * env, pan);
    Out.ar(out_bus, mix);
}
).writeDefFile("/Users/tado/Desktop/my-synths");

//-----------------------------------------------------

(// 06: Moogシンセ風
SynthDef("piTest", {
    arg out_bus = 0, note = 52, amp = 1, attack = 0, release = 1, detune = 1.01, pan=0;
    var freq = note.midicps;
    var mix =  MoogFF.ar(
        Pulse.ar([freq,freq*0.5], 0.8),
        freq*2.5,
        128
    );
    var env = EnvGen.ar(Env.perc(attack, release, amp), doneAction: 2);
    mix = Pan2.ar(mix * env, pan);
    Out.ar(out_bus, mix);
}
).writeDefFile("/Users/tado/Desktop/my-synths");
)
  • Sonic Pi
load_synthdefs "/Users/tado/Desktop/my-synth"
use_synth :piTest

live_loop :live do
  use_random_seed 8
  32.times do
    3.times do
      ns = (scale :c1, :mixolydian, num_octaves: 8)
      play ns.choose, attack: 0.0, release: 2.0, amp: 1.5
    end
    sleep 0.25
  end
end

openFrameworkプログラミングはじめの一歩

ようやく統合開発環境(IDE)のセットアップが完了しました。今回から (今度こそ) openFrameworksによるプログラミングを始めていきます。

まず始めに、新規にopenFrameworksのプロジェクトを生成する方法を解説します。openFrameworksでプログラムを開始するには、まず様々なライブラリーやビルドのためのスクリプトが設定されたプロジェクトを作成する必要があります。ダウンロードしたopenFrameworksのパッケージの中に、ProjectGeneratorというアプリケーションが用意されています。ProjectGeneratorを使用することで簡単に新規のプロジェクトの作成が可能となります。

新規プロジェクトの作成がうまくいったら、さっそくコーディングしてみましょう。今回は図形を描画して、その図形を直線運動させる簡単なアニメーションの作成までを目指します。

スライド資料


かたちとコード – Processingで図形を描くには?

この講義では、Processingを使用してコードによるデザインを行います。その最初の一歩として、コードによって「かたち」を描くにはどうすれば良いのか考えていきます。

まず始めに、1950年代〜70年代の、コンピューター黎明期から発展期におけるコード(プログラム)による様々な視覚表現について紹介します。過去の作家がどのようなアイデアで、何を表現しようとしてきたのか、その歴史を辿ります。

後半は、そうした歴史を踏まえた上で、Processing実際に図形を描いていきます。まずProcessingの操作基本を解説し、簡単な図形を描きながらProcessingでのプログラミングの基本を学びます。

スライド資料