ビジュアル・ハーモニー
ジョン・ウィットニー(John Whitney)は、コンピュータアニメーションの先駆者で、コンピュータが演算処理で作り出す幾何学的なグラフィックスを使った実験的なアニメーション作品が知られています。モーショングラフィックが作り出す反復と律動の運動パターンが、視覚的に心地よいハーモニーを生み出すことを示す作品です( Matrix(1971)、 Matrix III(1972)、 Arabesque(1975))。 パターンが流体のように変化し、美しいビジュアル・ハーモニーが生まれます。Whitneyはその著書*で、グラフィックのパターンとそれが生むハーモニーについて詳しく説明しています。
*ディジタル・ハーモニー―音楽とビジュアル・アートの新しい融合を求めて、ジョン・ウィットニー、 産業図書 (1984),原本はDigital Harmony: On the Complementarity of Music and Visual Art McGraw-Hill Inc.,US(1981)
ここでは、Matrix IIIで使われた円の運動パターンを再現してみましょう。
ひとつの円が円周上を運動
ひとつの円が、円周上を運動するところから始めます。円を描くにはx、y座標の値が必要で、そのためには円周上の位置を計算する必要があります。円周上の位置は、円の中心から見た角度(時計の3時の位置が0度)と、 三角関数を使って計算できます。【リスト 三角関数:円周上を移動】を参照してください。
クリックで開始⇔停止
【リスト7-1】 float x, y; float r = 100;//移動する円周の半径 float a = 0; //3時の位置からの角度(度単位) float speed = 2.0; void setup() { size(250,250); noFill(); strokeWeight(2); } void draw() { background(255); translate(width/2, height/2); x = r * cos(radians(a)); //角度aの位置の座標を計算 y = r * sin(radians(a)); ellipse(x, y, 10, 10); a = a + speed; //角度を変化させる }
複数の円が円周上を移動
NUM個(例えば12個)の円が、円周上を移動することを考えます。円周上のどこにNUM個の円を置くかがポイントです。円周上のa度の範囲の中に、NUM個の円を均等に置き、このa度が少しずつ増加するようにします。動きがはっきりするよう、画面サイズと円周の半径をリスト7-1より大きくしました。NUM個の円の位置を保存する配列を用意します。
float x[] = new float[NUM]; float y[] = new float[NUM];draw()関数の中で、for文を使って、NUM個の円の座標を計算、描画します。円が散らばる範囲の角度aを、円の個数NUMで割った値a/NUM度ずつずらして、円周上に配置します。最初aはゼロなので、全部の円が同じ位置(3時の位置)に重なりますが、aの値が増加するにつれて、円周全体に散らばります。円の位置にパターンが生まれることに注目してください。
クリックで開始⇔停止
【リスト7-2】 int NUM = 12; //円の個数 float r = 100; //円周の半径 float x[] = new float[NUM]; float y[] = new float[NUM]; float a = 0; //角度(度単位) float speed = 2.0; void setup() { size(300,300); noFill(); strokeWeight(2); } void draw() { background(255); translate(width/2, height/2); for(int i=0; i<NUM; i++){ //aの角度の中にNUM個の円が均等に入る x[i] = r * cos(radians(a/NUM*(i+1))); // a/NUMずつ角度が増える y[i] = r * sin(radians(a/NUM*(i+1))); ellipse(x[i], y[i], 10, 10); } a = a + speed ; //散らばる範囲を変化 }
複数の円の大きさを変える
リスト7-2に対して、NUM個の円の大きさに変化をつけてみます。円の数は任意ですが、ここではNUM=80としました。
クリックで開始⇔停止
そして、リスト6-2のdraw()関数内のfor文の3行目を、次に変更します。 円の大きさは1、2、3、、、と1ずつ増加します。
ellipse(x[i], y[i], i, i);円の数を増やし、円の大きさを変えるこの行を変えただけで、アニメーションにドラスティックな変化が現れます。これはMatrix IIIに現れるパターンのひとつです。
【リスト7-3】 int NUM = 80; ........ void draw() { ......... for(int i=0; i<NUM; i++){ ........ ........ ellipse(x[i], y[i], i, i); } ............... }
らせん上に配置
次に、Whitneyが著書Digital Harmonyの中でアイディアを語っているMusic Boxのビジュアル部分を再現してみましょう。複数の円がらせん上を移動し、弦に模した線に円が触れると音を奏で、視覚と音とのハーモニーを楽しむというものです。動きはhttp://whitneymusicbox.org/を参照してください。リスト7-2は、円を半径100の円周上に配置し、配置される角度を徐々に広げました。これを変更し、らせん上に配置されるようにしてみます。 問題2-3で 円がらせん上を移動し、軌跡としてらせんを描くプログラムを作りました。らせん上に円を置くために、は問題2-2と同様、中心からの距離を変えることで、らせん上の位置を計算します。
円の数NUMを80、らせん外周の半径rを150とし、リスト7-2のdraw()関数内のfor文の1,2行目を、次に変更します。中心からの距離をr/NUMずつ増やします。
x[i] = r/NUM*(i+1) * cos(radians(a)/NUM*(i+1)); y[i] = r/NUM*(i+1) * sin(radians(a)/NUM*(i+1));ダイナミックに変化するパターンを楽しんでください。
クリックで開始⇔停止
【リスト7-4】 int NUM = 80; //円の個数 float r = 150; //らせん外周の半径 ........ void draw() { ......... for(int i=0; i<NUM; i++){ x[i] = r/NUM*(i+1) * cos(radians(a)/NUM*(i+1)); y[i] = r/NUM*(i+1) * sin(radians(a)/NUM*(i+1)); ellipse(x[i], y[i], 10, 10); } ............... }
らせん上に配置、内側の動きを大きく
リスト7-4と、実装されているWhitney Music Boxでは、動きが違うことに気づくでしょう。リスト7-4は外側の円の方が早く動きます。これを内側の円の方を早く動かし、円の大きさを10-25の間で変化させ、また円の色を全色相の間で変化させます。リスト7-4では、配列の先頭の方(iが小さい方)の円を、広がるらせん上の角度の中央の方(中心からの距離が短い方)に配置しています。これとは反対に、 配列の先頭の方(iが小さい方)の円を、中心から離れたところに置きます。for文内のx,y座標の計算部分を、次のようにします。r/NUM*(NUM-i)はiが小さい時、大きな値になります(iがゼロの時はr)。
x[i] = r/NUM*(NUM-i) * cos(radians(a/NUM*(i+1))); y[i] = r/NUM*(NUM-i) * sin(radians(a/NUM*(i+1)));次に色ですが、カラーモードをHSBにし(色相、彩度、明度で指定する方法)、色相をiの値で変化させます。次の関数は、カラーモードをHSBにし、値の指定を0-100の間で行うことを指示します。
colorMode(HSB, 100);そして、for文の中で、fill()関数で塗りの色を指定します。この時、iの値が0であれば色相を0に、NUM-1であれば色相を100にするように、map関数を使って指定しています。彩度と明度は100にします(一番鮮やかで、明るい色)。
fill(map(i, 0, NUM-1, 0, 100), 100, 100);描く円の直径dは、10から25の間で、iの値に応じて変化するよう、map関数で換算して決めます。らせんの内側に小さい円を配置するように、iが一番大きいときに10、iが0の時に25となるように換算します。
d = map(i, NUM-1, 0, 10, 25); ellipse(x[i], y[i], d, d);
クリックで開始⇔停止
【リスト7-5】 int NUM = 60; float x[] = new float[NUM]; float y[] = new float[NUM]; float r = 150; float a = 0; //角度(度単位) float speed = 2.0; float d; void setup() { size(300,300); noStroke(); colorMode(HSB, 100); } void draw() { background(100); //グレースケール0(黒)-100(白) translate(width/2, height/2); for(int i=0; i<NUM; i++){ //aの角度の中にNUM個の円が均等に入る x[i] = r/NUM*(NUM-i) * cos(radians(a/NUM*(i+1))); y[i] = r/NUM*(NUM-i) * sin(radians(a/NUM*(i+1))); fill(map(i, 0, NUM-1, 0, 100), 100, 100); //塗りの色を指定 d = map(i, NUM-1, 0, 10, 25); //円の直径を計算 ellipse(x[i], y[i], d, d); } a = a + speed ; //角度を変化させる }