yoppa.org


多摩美 – バイオメディアアート・ワークショップ 2013年度

ガイダンス – Bio Media Art Workshopについて、Processingの復習

このワークショップの目標

Bio Media Art Workshop (略称 BMAW) では、Processing および Arduino とそれらを組み合せて、センサーで環境の情報を計測したり、音や光、動きで 環境に働きかける方法を学び、植物と電子工作、プログラミングやネットワークを組み合せたバイオ・ガジェットを制作します。

作成したバイオ・ガジェットはTwitterやFacebookなどのSNSや、xivelyなどの計測データの共有サービスを利用して、積極的に世界中とシェアすることを目指します。

このワークショップでは、以下のツールやネットサービスを使用して作品を制作していきます。

Arduino

センサーで計測した周囲の環境に関するデータを取得してMacなどのPCに送出します。デジタルとアナログの入出力ポートを備えたマイコンを搭載した専用の基板と、専用の開発環境から構成されています。

Processing

メディアアートやソフトウェアアート、ビジュアルデザインのためのプログラミング言語であり、統合開発環境。 MITメディアラボのAesthetics and Computation Group(美学と計算グループ?)に在籍していた、Casey ReasとBenjamin Fryにより2001開発されました。

Xively (旧Patchube, cosm)

センサーなどで計測したデータを、インターネットで共有するためのWebサービス。

ワークショップ全体の構成

image

月曜日:生体情報を美しくみせる(田所)

  • Processing
  • データの可視化、可聴化
  • 生成的(Generative)表現
  • ソフトウェア・アート
  • ネットワークとの連携、Xively、SNS (Twitter, Facebook)

火曜日: 生物と人間のインタラクション(矢坂)

  • Arduino
  • センサーの技術
  • Touché
  • Botanicus Interacticus (植物とのインタラクション)

ワークショップで作成するシステムのイメージ

ワークショップでは、センサー、Arduino、Processingを組合せて作品を制作していきます。それぞれの役割とその接続イメージは下記の図のようになります。

image

制作する作品の例

センサーと植物をくみあわせた作品例です。Disney Researchに所属するIvan Poupyrev氏は、タッチセンサーに関する様々な研究を行っており、そこで開発された「Touché」というセンサーを用いて様々な興味深い作品を生みだしています。

Botanicus Interacticus – SIGGRAPH 2012 Exhibition, Disney Research by Ivan Poupyrev in collaboration with Philipp Schoessler, Jonas Loh/Studio NAND, and Munehiko Sato.

ここで用いられているオリジナルに開発されたタッチセンサー「Touché」についての解説動画です。

Processingのスピード復習

このワークショップでは、センサーで計測した情報の視覚化にはProcessingを使用します。そこで、まずワークショップ初回では、Processingでのプログラミングの概要について、一気におさらいしていきます。

Processing参考書

Processingはとても素晴しいチュートリアルの書籍が多数出版されています。代表的な書籍をいくつか紹介します。

The Nature of Code

image

Daniel Shiffman氏の”The Nature of Code”は、Webサイトで全文公開されています(!!)、またビデオチュートリアルも公開されていてとてもわかりやすく、かつ高度なチュートリアルとなっています。基本からきちんとProcessingを学んでみたい方にお勧めです。

Intro I.0 (The Nature of Code) from shiffman on Vimeo.

Processing復習1: 座標、点を描く

コンピュータ画面上に図形を描いたりアニメーションをするためには、どこに図形を描くのか、つまり「場所」を指定する必要があります。プログラミングで場所を指定するには「座標」を使用します。2次元の平面では、原点からみた横の距離と縦の距離を記述することで、1点が定まります。Processingでは、ウィンドウの左上を原点(0,0)として、右方向の距離を「x座標」、下方向の位置を「y座標」と呼び、単位はピクセル(Pixel)で記述します。例えば(300, 200)という点は、画面表示領域の左上から300ピクセル右に、200ピクセル下に移動した場所になります。

image

では、Processingで(300, 200)の場所に点を描いてみましょう。

size(640,480); //640x480pixelの画面を生成
point(300,200); //300x200の場所に点を描く

ほとんどわかりませんが、点が描かれています!

image

Processing復習2: 線と基本図形

次にこのプログラムに線を追加して描いてみましょう。Processingでは、線は2つの点を結ぶ図形と考えます。

image

size(640,480); //640x480pixelの画面を生成
point(300,200); //(300,200)の場所に点を描く
line(50,100,400,300); //(50,100)の点から(400,300)の点へ線を引く

image

さらに四角形を描いてみます。Processingでは、四角形を描く際にはまず左上の角の座標を指定して、幅と高さを指定します。

image

size(640,480); //640x480pixelの画面を生成
point(300,200); //(300,200)の場所に点を描く
line(50,100,400,300); //(50,100)の点から(400,300)の点へ線を引く
rect(400,100,100,300); //(400,100)の点から、幅100、高さ300の四角形を描く

image

最後に円(もしくは楕円)を描いてみましょう。円や楕円は中心位置の座標と、幅と高さを指定します。

image

size(640,480); //640x480pixelの画面を生成
point(300,200); //(300,200)の場所に点を描く
line(50,100,400,300); //(50,100)の点から(400,300)の点へ線を引く
rect(400,100,100,300); //(400,100)の点から、幅100、高さ300の四角形を描く
ellipse(200,340,300,200); //(200,340)の点を中心に、幅300、高さ200の楕円を描く

image

Processing復習3: 色

次に色を指定してみましょう。現在の典型的なディスプレイでは、1つのピクセルあたり24ビットの色の情報が表現可能です。この24ビットの情報を三等分して、RGBそれぞれに8ビットずつ色の階調をわりあてています。ビットというのは、0か1かという2進数で表現される数値です。8ビットの場合であれば、2進数で00000000〜11111111の情報を扱えるということになります。これを私たちが普段馴染んでいる10進数に変換すると、0〜255になり、0を含めて256段階の階調を表現できるという意味になります。RGBそれぞれが256階調表現できることにより、その混色の結果表現できる色数は、16,777,216通り(256^3、または2^24)になります。

  • 赤(R):8bit – 00000000 〜 11111111(2進数)=0〜255(10進数)
  • 緑(G):8bit – 00000000 〜 11111111(2進数)=0〜255(10進数)
  • 緑(B):8bit – 00000000 〜 11111111(2進数)=0〜255(10進数)
  • 合計:24bit=2^24=256^3=16,777,216

Processingにおいても、色を表現する際は、RGBの輝度の値を0〜255の範囲で指定します。その結果、コンピュータで表現できる色を最大限に引き出すことが可能となっています。

Processingで色を指定できる対象は、全部で3種類あります。背景色、塗りの色、境界線の色です。それぞれ以下のように指定します。

背景色

  • background(Gray); //グレイスケール
  • background(R, G, B); //RGB
  • background(R, G, B, A); //RGBA (※Aは透明度)

塗りの色

  • fill(Gray);
  • fill(R, G, B);
  • fill(R, G, B, A);

境界線の色

  • stroke(Gray);
  • stroke(R, G, B);
  • stroke(R, G, B, A);

では、背景色、塗りの色、境界線の色を指定して図形を描いてみましょう。

background(15); //背景色
stroke(63,191,255); //線の色
fill(0,127,255,127); //塗りの色
size(640, 480); //640x480pixelの画面を生成
point(300, 200); //(300,200)の場所に点を描く
line(50, 100, 400, 300); //(50,100)の点から(400,300)の点へ線を引く
rect(400, 100, 100, 300); //(400,100)の点から、幅100、高さ300の四角形を描く
ellipse(200, 340, 300, 200); //(200,340)の点を中心に、幅300、高さ200の楕円を描く

image

Processing復習4: アニメーション、setup()とdraw()

Processingでアニメーションを実現するには、現在のプログラムをより構造化していく必要があります。Processingでは、setup()とupdate()という二つのパートに構造化してアニメーションを実現します。

  • setup() – 初期設定: プログラムの起動時に一度だけ実行されます。画面の基本設定やフレームレートなどを設定します。
  • draw() – 描画: 設定した速さ(フレームレート)でプログラムが終了するまでくりかえし実行されます。

image

この仕組みを利用して、円が斜め下に移動するプログラムを作成してみましょう。

float posX, posY; //円の中心位置を格納する変数
float speedX, speedY; //円の速度を格納する変数
void setup() {
  size(640, 480); //640x480pixelの画面を生成
  frameRate(60); //フレームレート
  stroke(63, 191, 255); //線の色
  fill(0, 127, 255, 127); //塗りの色
  posX = 40; //円の初期位置X
  posY = 40; //円の初期位置Y
  speedX = 3; //円の初期位置X
  speedY = 2; //円の初期位置Y
}

void draw() {
  background(15); //背景を描画
  ellipse(posX, posY, 20, 20); //指定した位置に円を描画
  posX = posX + speedX; //円のX座標を更新
  posY = posY + speedY; //円のY座標を更新
}

image

Processing復習5: 条件分岐

では、この動きを改良して画面の端にきたらバウンドする動きを加えてみましょう。この動きを実現するには以下の条件を設定する必要があります。

  • 画面の右端、もしくは画面の左端 → X方向のスピードを反転
  • 画面の上端、もしくは画面の下端 → Y方向のスピードを反転

プログラミング言語で、このような「もし○○だったら、□□しなさい、(そうでなければ、△△△しなさい)」というような制御構造を表現するための仕組みとして、「if文」というものがあります。if文は、処理の条件となる条件式、条件に合致する倍に処理される真文、条件に合わない場合に処理される偽文から構成されています。

Processingで条件式を記述するには下記の書式を用います。

if(《条件式》){
    《真文》
} else {
    《偽文》
}

偽文(そうでなければ、△△△しなさい)の部分が必要なければ、省略して

if(《条件式》){
    《真文》
}

と書くことも可能です。この条件分岐を適用して画面の端にきたらX方向もしくはY方向のスピードを反転させます。

float posX, posY; //円の中心位置を格納する変数
float speedX, speedY; //円の速度を格納する変数

void setup() {
  size(640, 480); //640x480pixelの画面を生成
  frameRate(60); //フレームレート
  stroke(63, 191, 255); //線の色
  fill(0, 127, 255, 127); //塗りの色
  posX = 40; //円の初期位置X
  posY = 40; //円の初期位置Y
  speedX = 3; //円の初期位置X
  speedY = 2; //円の初期位置Y
}

void draw() {
  background(15); //背景を描画
  ellipse(posX, posY, 20, 20); //指定した位置に円を描画
  posX = posX + speedX; //円のX座標を更新
  posY = posY + speedY; //円のY座標を更新
  if (posX < 0 || posX > width) { //もし画面の左端、または右端に到達したら
    speedX = speedX * -1; //X方向のスピードを反転
  }
  if (posY < 0 || posY > height) { //もし画面の下端、または上端に到達したら
    speedY = speedY * -1; //Y方向のスピードを反転
  }
}

このプログラムを実行すると、画面の端でバウンドする動きになります。

image

Processing復習6: ベクトル(向きと大きさ)で動きを再定義

ここまでの動きの定義の方法は、あまり洗練されたやり方とはいえませんでした。2次元平面での動きを定義するのに以下のような変数を使用しています。

  • 位置: posX と posY
  • 速度: speedX と speedY

今後、より高度なアニメーションを実現するために、例えば以下のような様々な変数が必要となってきます。

  • 加速度: X方向の加速度、Y方向の加速度
  • 到達地点: X座標、Y座標
  • 摩擦: X方向の摩擦、Y方向の摩擦
    …etc.

それぞれのパラメーターで、常に(x, y)という2つの変数を必要としています。これをより整理して定義できないものでしょうか。例えば、現在のコードの以下の部分。

float posX, posY;
float speedX, speedY;

これを

Vector location;
Vector speed;

のようにベクトルを使用して定義できると、プログラムはとても整理されたものとなります。

ベクトルとは、幾何学的空間における、大きさと向きを持った量を意味します。例えば、速度や加速度、力は全てベクトルです。平面上や空間内の矢印としてイメージすると良いでしょう。

image

例えば、原点(0,0)から(2,3)の座標までのベクトルがあったとします。

image

このベクトルimageは、以下のように記述されます。

a = (2,3)

ベクトルは足し算することが可能です。例えば、以下の2つのベクトルがあったとします。

a = (-2,3)
b = (4,2)

このとき、aとbのベクトルを足し合わせると以下のようになります。

a + b = (-2+4,3+2) = (2,5)

これは、以下の図をイメージするとわかりやすいでしょう。

image

同様にベクトルの引き算も可能です。

a - b = (-2-4,3-2) = (-6,1)

image

Processingでは、このような座標での位置や速度を定義するのに適した「PVector」というクラス(属性と動作の型)が用意されています。今回のような2次元平面でのアニメーションも、PVectorクラスを用いることでとても簡単に整理されます。先程の画面の端でバウンドするアニメーションを、PVectorを使用して書き直してみましょう。

PVector location; //位置のベクトル
PVector velocity; //速度のベクトル

void setup() {
  size(640, 480); //640x480pixelの画面を生成
  frameRate(60); //フレームレート
  stroke(63, 191, 255); //線の色
  fill(0, 127, 255, 127); //塗りの色
  location = new PVector(40, 40); //位置のベクトルの初期設定
  velocity = new PVector(3, 2); //速度のベクトルの初期設定
}

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

とてもプログラムがすっきり整理されたものになりました。

Processing復習7: 繰り返しによる増殖

では、現在の動きの法則はそのままで、たくさんの図形を同時に動かしてみましょう。

まずすぐに思いつく方法として、図形の数だけ同じコードを書くというやりかたが考えられます。もちろん、この方法でも不可能ではありませんが、効率が悪すぎます。2個3個といった数ならなんとか対応可能ですが、100個1000個と数が増えていったらすぐに破綻してしまいます。もっと便利な方法を使用する必要がありそうです。

こうした際には、配列を使うと便利です。配列とは、同じ型のデータを連続的に並べたデータ形式です。各データをその配列の要素といい、それらは添字(インデックス)で識別されます。データのロッカーのようなイメージです。

例えば、dataという名前の配列に5つデータを格納する際は以下のように定義します。

float[] data = new float[5];

これによって、float[0] ,float[1], float[2], float[3], float[4] の5つのデータが格納可能となります。

image

では、この方法で位置(location)、速度(velocity)をそれぞれ配列に変更して、沢山の位置と速度を扱えるようにしてみましょう。

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

void setup() {
  size(640, 480); //640x480pixelの画面を生成
  frameRate(60); //フレームレート
  stroke(63, 191, 255); //線の色
  fill(0, 127, 255, 127); //塗りの色
  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方向のスピードを反転
    }
  }
}

image

さらにプログラムを工夫して、色と大きさもランダムに生成してみましょう。

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

void setup() {
  size(640, 480); //640x480pixelの画面を生成
  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), 127); //色の初期設定
    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方向のスピードを反転
    }
  }
}

image

ここまでで、Processingのプログラムの基本部分は一通り完了しました。次週はより応用的なプログラムを学んでいきましょう!

tado285 — 25 July 2013 18:40
ありがとうございます! 今後も引き続き有用な情報発信できるようがんばります!
芳郎 高田 — 24 July 2013 15:33
初めて、このサイト発見。小生にとっては有難いサイトのようです。ARDUINO, PROCESSINGとも初心者です。レーザープロジェクタを自作してみたくて、ARDUINOを触りはじめて、自作のガルボを駆動しようとし、投射データをPROCESSINGかHSPから、シリアルで送って・・・と、あれこれ考えております。 そのためには、このサイト大いに役に立ちそうです。また、このコラムで、ご質問し、お答えいただけるなら、ますます有難いのですが・・・。 もう小生72歳という老齢で、能力、気力とも以前より衰えていますので、 レーザープロシステムなどの構築には、時に、意気阻喪しがちです。どうか、お助けいただきますようにお願いします。