yoppa.org


第13回: 音響と映像の融合 – Processingで音を可視化する 2

今回も前回に引き続き、Processingによる音の可視化について探求していきます。前回使用したProcessingのSoundライブラリーは少し不安定でしたので、今回はMinimライブラリーに変更します。

今回は最終課題を見据えて、より実践的なプログラムをいくつか紹介していきます。

スライド資料

サンプルプログラム

本日使用したプログラムのサンプルは、以下からダウンロードしてください。

最終課題について

最終課題: Processingを用いた音響の視覚化

Sonic Piを用いて生成した音響作品をProcessingで視覚化した、30秒から1分程度のオリジナルのオーディオ・ビジュアル作品を制作してください。

提出物:(重要!!)

  1. 音を生成したSonic Piのプログラムソース
  2. ビジュアルを生成するProcessingのプログラムソース
  3. Processingプログラムのスクリーンショット (一番かっこいい瞬間を捉える!)

締切

2018年7月31日 (予定)

提出方法

3つの提出データ(Sonic Piソース、Processingソース、スクリーンショト)を圧縮せずにメールに添付して提出。その際に以下の情報もあわせて本文に入力してください。

  1. 作品タイトル
  2. 学籍番号
  3. 氏名

提出先

tadokoro+teu18@gmail.com


第12回: 音響と映像の融合 – Processingで音を可視化する 1

この講義では、前半にProcessingを使用したインタラクティブなアニメーションの基本、後半はSonic Piを使用したサウンドプログラミングを行ってきました。今回はいよいよこの両者を融合して、音と映像を同時に用いた表現に挑戦していきたいと思います。

まず始めにProcessingを用いて音を視覚化する方法を検討します。単純に感覚的手法で視覚化するのではなく、音を周波数成分に分解しその変化を視覚的に表現します。ここでは、FFT(高速フーリエ変換)という手法を用います。

次に、この視覚化の手法を用いて、Sonic Piで作成してレコーディングした音をProcessingで読み込み、視覚化する方法について解説します。サウンドの解析には、Processingのコアライブラリである、Soundライブラリーを使用します。

スライド資料

サンプルコード

サウンドファイルの再生

//Soundライブラリーの読み込み
import processing.sound.*;
//サウンドプレイヤー
SoundFile soundfile;

void setup() {
  //サウンドファイルを読み込んでプレイヤーを初期化
  soundfile = new SoundFile(this, "sound.aiff");
  //ループ再生
  soundfile.loop();
}

void draw(){
}

マウスで再生スピードと音量を変化

//Soundライブラリーの読み込み
import processing.sound.*;
//サウンドプレイヤー
SoundFile soundfile;

void setup() {
  size(800, 600);
  //サウンドファイルを読み込んでプレイヤーを初期化
  soundfile = new SoundFile(this, "sound.aiff");
  //ループ再生
  soundfile.loop();
}

void draw() {
  background(0);
  //マウスのX座標で再生スピードを設定
  float rate = map(mouseX, 0, width, 0.0, 2.0);
  //マウスのY座標で音量を設定
  float amp = map(mouseY, 0, height, 1.0, 0.0);
  //再生スピードを適用
  soundfile.rate(rate);
  //音量を適用
  soundfile.amp(amp);
}

音量を円の直径に反映

//Soundライブラリーの読み込み
import processing.sound.*;
//サウンドプレイヤー
SoundFile soundfile;
//音量解析
Amplitude rms;

void setup() {
  size(800, 600);
  fill(0, 127, 255);
  noStroke();
  //サウンドファイルを読み込んでプレイヤーを初期化
  soundfile = new SoundFile(this, "sound.aiff");
  //ループ再生
  soundfile.loop();
  //音量解析を初期化
  rms = new Amplitude(this);
  //音量解析の入力を設定
  rms.input(soundfile);
}

void draw() {
  background(0);
  //音量を解析して値を調整
  float diameter = map(rms.analyze(), 0.0, 1.0, 0.0, width);
  //取得した音量で円を描く
  ellipse(width/2, height/2, diameter, diameter);
}

FFTによるスペクトラム表示

//Soundライブラリーの読み込み
import processing.sound.*;
//サウンドプレイヤー
SoundFile soundfile;
//オーディオデバイス
AudioDevice device;
//FFT(高速フーリエ変換)
FFT fft;
//FFTサイズ
int bands = 1024;
//グラフの高さのスケールを設定
float scale = 20.0;

void setup() {
  size(800, 600);
  fill(0, 127, 255);
  noStroke();
  //オーディオバッファーの設定
  device = new AudioDevice(this, 44000, bands);
  //サウンドファイルを読み込んでプレイヤーを初期化
  soundfile = new SoundFile(this, "sound.aiff");
  //ループ再生
  soundfile.loop();
  //FFTの初期化
  fft = new FFT(this, bands);
  fft.input(soundfile);
}

void draw() {
  background(0);
  //FFT解析実行
  fft.analyze();
  noFill();
  stroke(255);
  //線分の描画開始
  beginShape();
  //FFTのバンドの数だけくりかえし
  for (int i = 0; i < bands; i++) {
    //FFTの解析結果を高さにグラフを描く
    vertex(i * width/float(bands), height - fft.spectrum[i] * height * scale);
  }
  //線分の描画終了
  endShape();
}

第11回:Sonic piでサウンドプログラミング3 構造化 – イテレーション・ループ・条件分岐

引き続きSonic Piを使った音楽プログラミングについて考えていきます。

今回は、Sonic Piにおけるプログラムの構造化とデータ構造に焦点を絞って解説していきます。構造化プログラミング言語における基本的なプログラム構造は「順次」「反復」「条件分岐」の3に代表されます。Sonic Piでもこの3つのプログラム構造を作成することが可能です。実際に音に出して確認しながらSonic Piにおけるプログラムの構造化を学びます。

スライド資料

サンプルコード

# 回数を指定しての反復
live_loop :loop do
  sample :bass_trance_c
  3.times do
    sample :drum_heavy_kick
    sleep 0.125
    sample :drum_cymbal_closed
    sleep 0.125
  end
  sample :drum_snare_hard
  sleep 0.25
end

#反復のネスト 1
live_loop :live do
  4.times do
    sample :drum_heavy_kick
    2.times do
      sample :elec_blip2, rate: 2
      sleep 0.25
    end
    sample :elec_snare
    4.times do
      sample :drum_tom_mid_soft
      sleep 0.125
    end
  end
end

#反復のネスト 2
live_loop :live do
  7.times do
    sample :bass_trance_c
    3.times do
      sample :drum_heavy_kick
      sleep 0.125
      2.times do
        sample :drum_cymbal_closed
        sleep 0.125 / 2.0
      end
    end
    sample :drum_snare_hard
    sleep 0.25
  end
  8.times do
    sample :drum_cymbal_closed
    sleep 0.125
  end
end

#条件分岐
i = 0
loop do
  if i % 4 == 0 then
    sample :drum_heavy_kick
  else
    sample :drum_cymbal_closed
  end
  sleep 0.125
  i = i+1
end

#コイントス
live_loop :live do
  if one_in(2)
    sample :drum_bass_hard
  end
  sleep 0.125
end

#コイントス、else文
live_loop :live do
  if one_in(4)
    sample :drum_bass_hard
  else
    sample :drum_cymbal_closed
  end
  sleep 0.125
end

#コイントスのネスト
live_loop :live do
  if one_in(4)
    sample :drum_bass_hard
  else
    if one_in(6)
      sample :drum_snare_hard
    else
      sample :drum_cymbal_closed
    end
  end
  sleep 0.125
end

#複数のループの共存
live_loop :live do
  sample :drum_heavy_kick
  4.times do
    sample :elec_blip2, rate: 2
    sleep 1.0/8.0
  end
  sample :elec_snare
  4.times do
    sample :drum_tom_mid_soft
    sleep 0.125
  end
end

live_loop :live2 do
  sample :drum_heavy_kick
  3.times do
    sample :elec_blip2, rate: 2
    sleep 1.0/8.0
  end
  sample :elec_snare
  4.times do
    sample :drum_tom_mid_soft
    sleep 0.125
  end
end

#ピアノフェイス基本
notes = [:E4, :Fs4, :B4, :Cs5, :D5, :Fs4, :E4, :Cs5, :B4, :Fs4, :D5, :Cs5]

live_loop :reich do
  i = 0
  12.times do
    play (notes[i]), release: 0.2
    sleep 0.15
    i = i + 1
  end
end

#ピアノフェイス、位相のずれ
notes = [:E4, :Fs4, :B4, :Cs5, :D5, :Fs4, :E4, :Cs5, :B4, :Fs4, :D5, :Cs5]

live_loop :reich1 do
  i = 0
  12.times do
    play (notes[i]), release: 0.2
    sleep 0.15
    i = i + 1
  end
end

live_loop :reich2 do
  i = 0
  12.times do
    play (notes[i]), release: 0.2
    sleep 0.151
    i = i + 1
  end
end

#ピアノフェイズ、いろいろアレンンジ
notes = [:E4, :Fs4, :B4, :Cs5, :D5, :Fs4, :E4, :Cs5, :B4, :Fs4, :D5, :Cs5]

use_synth :prophet
with_fx :reverb do
  live_loop :reich1 do
    i = 0
    12.times do
      play (notes[i]), release: 0.4, pan: 0.8, cutoff: 80
      sleep 0.15
      i = i + 1
    end
  end
  
  live_loop :reich2 do
    i = 0
    12.times do
      play (notes[i])+12, release: 0.4, pan: -0.8, cutoff: 80
      sleep 0.151
      i = i + 1
    end
  end
end

第10回:Sonic piでサウンドプログラミング2 – サンプルを使う、ランダム化

Sonic Piでプログラミングによる音楽制作を行います。今回は、前回のシンセを使用した音の再生とは別の方法、サンプルを使用した音の再生を取り上げます。サンプルは、サウンドファイルを読み込んで指定したタイミングで音を再生します。前回のシンセがシンセサイザーとすると、サンプルはサンプラーに相当します。演奏するタイミングだけでなく、音量、定位、再生スピードなど様々なパラメータを設定可能です。また、Sonic Piにあらかじめ用意されたサンプルだけでなく、ファイルの場所を指定して外部ファイルを読み込むことも可能です。

今回はさらに、Sonic Piにおけるランダム (乱数) の生成とその使用方法について解説します。Sonic Piでは、単純な乱数だけでなく、指定した範囲の乱数 (rrand, rrand_i)、リストの中からランダムに選択(choose)、サイコロをふってその結果で選択(dice) など様々なランダム化の関数が用意されています。音楽への応用を考えながら、乱数を使用していきます。

スライド資料


第9回: Sonic piでサウンドプログラミング入門

「メディア芸術の基礎」の前半では、主にProcessingを用いたビジュアルプログラミングを行ってきました。後半はまた新たな内容をとりあげていきます。

後半の講義では、音響や音楽をプログラミングを用いて処理し表現を行います。サウンドを扱うための開発環境として「Sonic Pi」を使用します。Sonic piは、教育現場でのプログラミングや音楽の授業をサポートするように設計された、ライブコーディング可能な無料のサウンドプログラミング開発環境です。「カノンからダブステップまで」というキャッチフレーズに代表されるように、古典〜現代の音楽を作曲できる、Mac OS XやWindows、さらにはRaspberry Piでも動かすことが可能で、柔軟なプログラミングが可能です。

今回は、Sonic Piの入門として、インストールから操作方法、そしてプログラミングの基本を学んでいきます。

スライド資料


第8回: クラスを継承する – Processing オブジェクト指向プログラミング入門2

前回に引き続き、Processingにおけるオブジェクト指向プログラミング(OOP)の方法について解説していきます。今回はOOPの重要な概念の一つである「継承 (インヘリタンス) 」について考えていきます。継承とは、既存クラスの機能構造を共有する新たなクラス(サブクラス)を派生させることです。サブクラスからは親となるクラスのプロパティやメソッドをそのまま引き継ぐことが可能です。この継承の仕組みを効果的に活用することで、既存のクラスを再利用しながらそこに新たな機能を加えていくということが可能となります。

次回までにやってくること

次回からは、新たな内容に入ります。これまではProcessingを用いて主に視覚的な表現を扱ってきました。次回からは、Sonic Piという開発環境を用いて音や音楽を扱います。

次回までに以下のサイトからSonic Piの最新版を使用しているOSにあわせてダウンロードし、インストールしておいてください。

スライド資料

サンプルプログラム

上下左右に動きまわる円 (基本テンプレート)

// Spotクラスを宣言
Spot spot;

void setup() {
  size(800, 600, P2D);
  noStroke();
  frameRate(60);
  //位置ベクトルをランダムに生成
  PVector loc = new PVector(width/2.0, height/2.0);
  //速度ベクトルをランダムに生成
  PVector vec = new PVector(random(-4, 4), random(-4, 4));
  //インスタンス化して配列に格納
  spot = new Spot(loc, vec, random(5, 30));
  //背景を黒に
  background(0);
}

void draw() {
  // 画面をフェードさせる
  blendMode(BLEND);
  fill(0, 0, 0, 10);
  rect(0, 0, width, height);
  // 色を加算合成に
  blendMode(ADD);
  // 円の色を設定
  fill(31, 127, 255, 127);
  // Spotクラスのmove()メソッドを呼び出す
  spot.move();
  // Spotクラスのdraw()メソッドを呼び出す
  spot.draw();
}

// Spotクラス
class Spot {
  // プロパティ
  PVector location; //位置 (ベクトル!)
  PVector velocity;   //速度 (ベクトル!)
  float diameter;   //直径

  // コンストラクター
  Spot(PVector _location, PVector _velocity, float _diameter) {
    location = _location;
    diameter = _diameter;
    velocity = _velocity;
  }
  // 移動
  void move() {
    //位置ベクトル + 速度ベクトル = 次フレーム位置ベクトル
    location.add(velocity);
    //左右の壁でバウンドさせる
    if (location.x < diameter / 2 || location.x > width - diameter / 2) {
      location.x = constrain(location.x, diameter/2, width - diameter / 2);
      velocity.x *= -1;
    }
    //上下の壁でバウンドさせる
    if (location.y < diameter / 2 || location.y > height - diameter / 2) {
      location.y = constrain(location.y, diameter/2, height - diameter / 2);
      velocity.y *= -1;
    }
  }
  // 描画
  void draw() {
    ellipse(location.x, location.y, diameter, diameter);
  }
}

クラスの配列 – 大量の物体を同時に動かす

// 物体の数
int numSpots = 400;
// Spotクラスを配列として宣言
Spot[] spots = new Spot[numSpots];

void setup() {
  size(800, 600, P2D);
  noStroke();
  frameRate(60);
  // 配列の数だけSpotクラスをインスタンス化
  for (int i = 0; i < spots.length; i++) {
    //位置ベクトルをランダムに生成
    PVector loc = new PVector(width/2.0, height/2.0);
    //速度ベクトルをランダムに生成
    PVector vec = new PVector(random(-4, 4), random(-4, 4));
    //インスタンス化して配列に格納
    spots[i] = new Spot(loc, vec, random(5, 30));
  }
  background(0);
}

void draw() {
  // 画面をフェードさせる
  blendMode(BLEND);
  fill(0, 0, 0, 10);
  rect(0, 0, width, height);
  // 色を加算合成に
  blendMode(ADD);
  // 円の色を設定
  fill(31, 127, 255, 63);
  for (int i = 0; i < spots.length; i++) {
    // Spotクラスのmove()メソッドを呼び出す
    spots[i].move();
    // Spotクラスのdraw()メソッドを呼び出す
    spots[i].draw();
  }
}

// Spotクラス
class Spot {
  // プロパティ
  PVector location; //位置 (ベクトル!)
  PVector velocity;   //速度 (ベクトル!)
  float diameter;   //直径

  // コンストラクター
  Spot(PVector _location, PVector _velocity, float _diameter) {
    location = _location;
    diameter = _diameter;
    velocity = _velocity;
  }
  // 移動
  void move() {
    //位置ベクトル + 速度ベクトル = 次フレーム位置ベクトル
    location.add(velocity);
    //左右の壁でバウンドさせる
    if (location.x < diameter / 2 || location.x > width - diameter / 2) {
      location.x = constrain(location.x, diameter/2, width - diameter / 2);
      velocity.x *= -1;
    }
    //上下の壁でバウンドさせる
    if (location.y < diameter / 2 || location.y > height - diameter / 2) {
      location.y = constrain(location.y, diameter/2, height - diameter / 2);
      velocity.y *= -1;
    }
  }
  // 描画
  void draw() {
    ellipse(location.x, location.y, diameter, diameter);
  }
}

クラスの継承 – 重力を付加

// 物体の数
int numSpots = 400;
// Spotクラスを配列として宣言
GravitySpot[] spots = new GravitySpot[numSpots];

void setup() {
  size(800, 600, P2D);
  noStroke();
  frameRate(60);
  // 配列の数だけSpotクラスをインスタンス化
  for (int i = 0; i < spots.length; i++) {
    //位置ベクトルをランダムに生成
    PVector loc = new PVector(width/2.0, height/2.0);
    //速度ベクトルをランダムに生成
    PVector vec = new PVector(random(-4, 4), random(-4, 4), 1.0);
    //重力を0.1に設定
    float gravity = 0.1;
    //インスタンス化して配列に格納
    spots[i] = new GravitySpot(loc, vec, random(5, 30), gravity);
  }
  background(0);
}

void draw() {
  // 画面をフェードさせる
  blendMode(BLEND);
  fill(0, 0, 0, 10);
  rect(0, 0, width, height);
  // 色を加算合成に
  blendMode(ADD);
  // 円の色を設定
  fill(31, 127, 255, 63);
  for (int i = 0; i < spots.length; i++) {
    // Spotクラスのmove()メソッドを呼び出す
    spots[i].move();
    // Spotクラスのdraw()メソッドを呼び出す
    spots[i].draw();
  }
}

// Spotクラス
class Spot {
  // プロパティ
  PVector location; //位置 (ベクトル!)
  PVector velocity;   //速度 (ベクトル!)
  float diameter;   //直径

  // コンストラクター
  Spot(PVector _location, PVector _velocity, float _diameter) {
    location = _location;
    diameter = _diameter;
    velocity = _velocity;
  }
  // 移動
  void move() {
    //位置ベクトル + 速度ベクトル = 次フレーム位置ベクトル
    location.add(velocity);
    //左右の壁でバウンドさせる
    if (location.x < diameter / 2 || location.x > width - diameter / 2) {
      location.x = constrain(location.x, diameter/2, width - diameter / 2);
      velocity.x *= -1;
    }
    //上下の壁でバウンドさせる
    if (location.y < diameter / 2 || location.y > height - diameter / 2) {
      location.y = constrain(location.y, diameter/2, height - diameter / 2);
      velocity.y *= -1;
    }
  }
  // 描画
  void draw() {
    ellipse(location.x, location.y, diameter, diameter);
  }
}

//Spotを継承した、GravitySpot(重力を付加)
class GravitySpot extends Spot {
  float gravity; //重力

  //コンストラクター
  GravitySpot(PVector _location, PVector _velocity, float _diameter, float _gravity) {
    //Superクラス(Spotクラス)をインスタンス化
    super(_location, _velocity, _diameter);
    //重力を設定
    gravity = _gravity;
  }

  void move() {
    //速度に重力を付加
    velocity.y += gravity;
    //スーパークラスのmove()を呼び出し
    super.move();
  }

  void draw() {
    //スーパークラスのdraw()を呼び出し
    super.draw();
  }
}

クラスの継承 – 重力と摩擦力を付加

// 物体の数
int numSpots = 400;
// Spotクラスを配列として宣言
GravitySpot[] spots = new GravitySpot[numSpots];

void setup() {
  size(800, 600);
  noStroke();
  frameRate(60);
  // 配列の数だけSpotクラスをインスタンス化
  for (int i = 0; i < spots.length; i++) {
    //位置ベクトルをランダムに生成
    PVector loc = new PVector(width/2.0, height/2.0);
    //速度ベクトルをランダムに生成
    PVector vec = new PVector(random(-4, 4), random(-4, 4), 1.0);
    //円の大きさを5から30に
    float diameter = random(5, 30);
    //重力を0.1に設定
    float gravity = 0.1;
    //摩擦力を0.998に
    float friction = 0.998;
    //インスタンス化して配列に格納
    spots[i] = new GravitySpot(loc, vec, diameter, gravity, friction);
  }
  background(0);
}

void draw() {
  // 画面をフェードさせる
  blendMode(BLEND);
  fill(0, 0, 0, 10);
  rect(0, 0, width, height);
  // 色を加算合成に
  blendMode(ADD);
  // 円の色を設定
  fill(31, 127, 255, 63);
  for (int i = 0; i < spots.length; i++) {
    // Spotクラスのmove()メソッドを呼び出す
    spots[i].move();
    // Spotクラスのdraw()メソッドを呼び出す
    spots[i].draw();
  }
}

// Spotクラス
class Spot {
  // プロパティ
  PVector location; //位置 (ベクトル!)
  PVector velocity;   //速度 (ベクトル!)
  float diameter;   //直径

  // コンストラクター
  Spot(PVector _location, PVector _velocity, float _diameter) {
    location = _location;
    diameter = _diameter;
    velocity = _velocity;
  }
  // 移動
  void move() {
    //位置ベクトル + 速度ベクトル = 次フレーム位置ベクトル
    location.add(velocity);
    //左右の壁でバウンドさせる
    if (location.x < diameter / 2 || location.x > width - diameter / 2) {
      location.x = constrain(location.x, diameter/2, width - diameter / 2);
      velocity.x *= -1;
    }
    //上下の壁でバウンドさせる
    if (location.y < diameter / 2 || location.y > height - diameter / 2) {
      location.y = constrain(location.y, diameter/2, height - diameter / 2);
      velocity.y *= -1;
    }
  }
  // 描画
  void draw() {
    ellipse(location.x, location.y, diameter, diameter);
  }
}

//Spotを継承した、GravitySpot(重力と摩擦を付加)
class GravitySpot extends Spot {
  float gravity; //重力
  float friction; //摩擦力

  //コンストラクター
  GravitySpot(PVector _location,
    PVector _velocity,
    float _diameter,
    float _gravity,
    float _friction) {
    //Superクラス(Spotクラス)をインスタンス化
    super(_location, _velocity, _diameter);
    //重力を設定
    gravity = _gravity;
    //摩擦力を設定
    friction = _friction;
  }

  void move() {
    //速度に摩擦力を掛ける
    velocity.mult(friction);
    //速度に重力を付加
    velocity.y += gravity;
    //スーパークラスのmove()を呼び出し
    super.move();
  }

  void draw() {
    //スーパークラスのdraw()を呼び出し
    super.draw();
  }
}

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

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

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

スライド資料

サンプルプログラム

ランダムウォーク (クラス無し)

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() {
  //描画設定
  noStroke();
  fill(255, 31);
  //速度を設定
  velocity.x = random(-1, 1);
  velocity.y = random(-1, 1);
  location.add(velocity);
  //円を描く
  ellipse(location.x, location.y, 2, 2);
}

ランダムウォーク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() {
  //描画設定
  noStroke();
  fill(255, 31);
  for (int i = 0; i < 10; i++) {
    //速度を設定
    velocity.x = random(-1, 1);
    velocity.y = random(-1, 1);
    location.add(velocity);
    //円を描く
    ellipse(location.x, location.y, 2, 2);
  }
}

Walkerクラスによるランダムウォーク

//Walkerクラスのオブジェクトの宣言
Walker walker;

void setup() {
  size(800, 600);
  frameRate(60);
  //クラスをインスタンス化
  walker = new Walker();
  background(0);
}

void draw() {
  //Walkerクラスのdraw()メソッドを実行
    noStroke();
    fill(255, 31);
  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);
      ellipse(location.x, location.y, 2, 2);
    }
  }
}

Walkerクラスの配列 (10個)

//Walkerクラスのオブジェクトの宣言
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() {
  //色の設定
  noStroke();
  fill(255, 31);
  //配列の数だけ描画
  for (int i = 0; i < NUM; i++) {
    walker[i].draw();
  }
}

//Walkerクラス
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);
      ellipse(location.x, location.y, 2, 2);
    }
  }
}

Walkerクラスの配列 (100個)

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

void setup() {
  size(1280, 720);
  frameRate(60);
  //Walker配列の初期化
  for (int i = 0; i < NUM; i++) {
    walker[i] = new Walker();
  }
  //背景
  background(0);
}

void draw() {
  //画面をフェードさせる
  blendMode(BLEND);
  fill(0, 5);
  rect(0, 0, width, height);
  //色の設定
  noStroke();
  blendMode(ADD);
  fill(255, 31);
  //Walker配列を描画
  for (int i = 0; i < NUM; i++) {
    walker[i].draw();
  }
}

//Walkerクラス
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);
      rect(location.x, location.y, 2, 2);
    }
  }
}

第6回: 画像の分析・再合成 (画像データ、ピクセル、ラスタ画像)

ProcessingのPImageクラスは、外部のビットマップ画像(Jpeg, GIF、PNGなど)をデータとしてプログラムに読み込むことができます。読み込んだ画像は単に画面に表示するだけでなく、色や明度やサイズを変更して表示することができます。さらには、画像に含まれる全てのピクセルの色情報を読み取り配列に格納することが可能です。そのデータをもとに別の画像を再生成することが可能となり、読み込んだ画像データの色情報をもとにした多彩な表現が可能となります。

今回はProcessingに画像を読み込んで、分析再合成することで、様々な表現の可能性について探っていきます。

スライド資料

サンプルファイル

課題: Proessingに読み込んだ画像ファイルのデータで表現する

  • PImageに読み込んだ画像ファイルの情報から、新たなイメージを生成してください
    • 読み込む画像は自由です

課題の提出方法

  • emailで提出
    • To : tadokoro+teu18@gmail.com
    • Subject: 課題1
    • 作成したプログラムの実行結果をスクリーンキャプチャーしてメールに添付
    • 本文に以下の内容を記入して提出
      • 作品のタイトル
      • 学籍番号
      • 氏名
    • 締切: 次回の講義開始時間まで

第5回: ビジュアルハーモニー (アルゴリズム、三角関数)

今回は、より複雑なアニメーションに挑戦します。たくさんの図形を、一定の手続き(= アルゴリズム)にそって動かしてみます。動きのアルゴリズムはいろいろありますが、今回はその一例として、三角関数(sin, cos , tan)を使用した動きをとりあげます。三角関数の定義は、直角三角形の角度とそれを取り囲む辺の比率で定義されます。しかし、同時に円運動を座標に変換する仕組みとしても活用可能です。この仕組みを応用してアニメーションするとどうなるか、三角関数を用いたアニメーションで美しいパターン「ビジュアルハーモニー」を生成する手法を紹介します。

スライド資料

サンプルプログラム


第4回: アニメーション基礎 (ベクトル、運動方程式)

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

スライド資料

サンプルプログム

授業内で使用したサンプルプログラムは下記からダウンロードしてください。