Processing日記(4)
長嶋 洋一
(一部 SuperCollider日記かも)
2011年6月10日(金)
先週は オスロ に行ったので、その道々、 SuperCollider日記 を進めることが出来たが、Processingの方は中途半端に途切れていた。 この日は2限にゼミmeeting、そして3限の途中から「くるる」で浜松駅に行き、 「日本時間学会」大会に参加のため、日曜日まで山口に行く。 時間学会の大会は、時の記念日の付近に開催されるのである。 コマ切れの時間しかないが、山口行きの新幹線の中で作業が出来るところまで引き継げるか、 進めてみよう。前回のおさらいとしては、 udptest_01.maxpat というパッチで、以下のようにMax5側でのOSC(UDPでのsend/receive)は確認できていた。
そしてProcessing側では、 oscP5 library のサイトの「Download」のところから、 oscP5 version 0.9.6 release 05/09/2010 をダウンロードして解凍し、 この「oscP5.zip」を解凍した「oscP5」というフォルダを、Processingのプリファレンスで定義した 「Processing」フォルダの下の「libraries」フォルダの下に、以下のように置いたところまでであった。
まずは、Max5の前回のパッチ udptest_01.maxpat によって、localhost(127.0.0.1)の「ポート7400」で走らせておく。 Maxウインドウでのモニタによって、この「udpsend」からbangや数値やメッセージがbroadcastできるし、 またこのポートのudpメッセージは、「udoreceive」によって受けて、Maxウインドウで表示することでモニタ出来る。
ここで、以下のように「oscP5」ライブラリの下にある「samples」の下にある13個のサンプルを、 とりあえずは試してみよう、という事である。 ポート番号とかが違っている場合には、Max5側もいじると混乱するので、 Processingブログラムの側で変更してみることにした。 まず最初に、いちばん上にあった「oscP5broadcastClient」というサンプルから、 以下のように変更したProcessingブログラムを走らせてみた。
import oscP5.*; import netP5.*; OscP5 oscP5; NetAddress myBroadcastLocation; void setup() { size(200,200); frameRate(25); oscP5 = new OscP5(this,7400); myBroadcastLocation = new NetAddress("127.0.0.1",7400); } void draw() { background(0); } void mousePressed() { OscMessage myOscMessage = new OscMessage("/test"); myOscMessage.add(100); oscP5.send(myOscMessage, myBroadcastLocation); } void keyPressed() { OscMessage m; switch(key) { case('c'): m = new OscMessage("/server/connect",new Object[0]); oscP5.flush(m,myBroadcastLocation); break; case('d'): m = new OscMessage("/server/disconnect",new Object[0]); oscP5.flush(m,myBroadcastLocation); break; } } void oscEvent(OscMessage theOscMessage) { println("### received an osc message with addrpattern " +theOscMessage.addrPattern()+" and typetag "+theOscMessage.typetag()); theOscMessage.print(); }
するとアッサリと、Processingで開いた200*200の真っ黒なウインドウをクリックしたり、 キーボードの「c」「d」を押すと、Maxウインドウからそのメッセージ受信が表示された(^_^)。 ところが、Processingプログラムでは、udp送信だけでなくudp受信を表示する筈なのに、 自分が送ったudpメッセージも、Maxパッチから送られるudpメッセージ(これはMaxウインドウで受信確認できている)も、 ウンともスンとも言わない(^_^;)ことが判明してきた。
そこで以下のように、Max5をquitしてみると、つまりProcessingだけにしてみると、 ちゃんとudpメッセージを出したものを自分で受けて表示した。 Max5との通信は、片側通行(Processing→MaxはOK、でもMax→Processingは駄目)というだけでなく、 Processing自身が自分の送信した情報を受信しなくなる、という謎な現象である。
念のため、再度Maxパッチを起動して、今度はMax5をquitしないでudpsend/udpreceiveのパッチだけを閉じてみたところ、 以下のように、Processingのプログラムは正しく動作した。 こうなると、Maxとの共存には問題が無く、MaxのUDP関係パッチの動作との共存に何か問題がある、という事になる。 これではSuperColliderとの共存に進めない(^_^;)。
とりあえず、「oscP5」ライブラリの下にある「samples」の下のサンプルをさらに進めていろいろ実験してみたが、 続く2つは以下のようになった。
そしてこの次の「oscP5bundle」について実験していて、あぁこれも「oscP5broadcastClient」と同じ現象だ・・・と思いかけたが、 送信と受信のポートをMaxとProcessingでタスキがけになるように設定したところ、以下のように遂に成功した(^_^)。 Max側のパッチはudptest_02.maxpat である。
- oscP5broadcaster - 送信ポートと受信ポートを別個に設定。ただし、送信/受信表示が無いのでパス
- oscP5broadcastTester - 「import controlP5.*;」でエラーが出る(ライブラリが無い)のでパス
import oscP5.*; import netP5.*; OscP5 oscP5; NetAddress myRemoteLocation; void setup() { size(200,200); frameRate(25); oscP5 = new OscP5(this,9000); myRemoteLocation = new NetAddress("127.0.0.1",7400); } void draw() { background(0); } void mousePressed() { OscBundle myBundle = new OscBundle(); OscMessage myMessage = new OscMessage("/test"); myMessage.add("abc"); myBundle.add(myMessage); myMessage.clear(); myMessage.setAddrPattern("/test2"); myMessage.add("defg"); myBundle.add(myMessage); myBundle.setTimetag(myBundle.now() + 10000); oscP5.send(myBundle, myRemoteLocation); } void oscEvent(OscMessage theOscMessage) { print("### received an osc message."); print(" addrpattern: "+theOscMessage.addrPattern()); print(" typetag: "+theOscMessage.typetag()); println(" timetag: "+theOscMessage.timetag()); }
要は、送信と受信のポート番号を別個に設定して、互いに相手とやりとりしていればいいのだろう。 ちなみに、Max5のパッチ側で、「udpreceive 7400」というProcessingからの受信のためのオブジェクトに加えて、 「udpreceive 9000」というオブジェクトを追加すると、Processingからのメッセージ受信に加えて。さらに Maxから出すメッセージも受信するようになったが、その途端にProcessing側の受信が停止した(^_^;)。 つまり、「他に同じポートでメッセージを受信する子がいると、拗ねて受信しなくなる」みたいな現象である。 これは、SuperColliderとの同居を考えるとちょっと嫌だが、制御側のMaxから、それぞれ別個のポートで送れば解決しそうな気もする。
・・・上の部分まで1限の時間に研究室で進めてサーバに上げたところで10:30となり、ゼミmeetingに走った。 そしてここからは、浜松から山口に向かう新幹線(浜松→(こだま)→名古屋→(のぞみ)→新山口)の車内である。 のぞみの指定席はコンセントのある席を予約しているので、エコノミー機内と違ってバッテリの心配が無い(^_^)。 まず、とりあえずはMaxパッチを複数、走らせてみて、現象の確認を進めてみることにした。
用意してみたMax側のパッチはudptest_03.maxpatとudptest_04.maxpat である。これは以下のように、「ポート7000の受信メッセージを表示し、データ/メッセージをポート8000で送る」udptest_03.maxpatと「ポート8000の受信メッセージを表示し、データ/メッセージをポート7000で送る」udptest_04.maxpatである。 互いにたすきがけの関係にあり、相互にデータを送り合うが、表示するMaxウインドウは共通なので、 「Object ID」を添えることでどちらか判るようにした。
この道具だてが出来たところで、いったん「ポート8000の受信メッセージを表示し、データ/メッセージをポート7000で送る」udptest_04.maxpatを閉じておく。 その上で、「ポート8000の受信メッセージを表示し、データ/メッセージをポート7000で送る」Processingプログラムを走らせれば、おそらく通信できる筈であるが、そこに再度これを走らせたらどうなるか、という実験である。
ここは内容的には目新しいものは無いが、Processingのプログラムも以下のようにスッキリとさせて、 確かにProcessingとMaxとはOSCを経由して「対話」できる。
import oscP5.*; import netP5.*; OscP5 oscP5; NetAddress myBroadcastLocation; void setup() { size(200,100); frameRate(25); oscP5 = new OscP5(this,8000); myBroadcastLocation = new NetAddress("127.0.0.1",7000); } void draw() { background(255); } void mousePressed() { OscMessage myOscMessage = new OscMessage("/test"); myOscMessage.add(100); oscP5.send(myOscMessage, myBroadcastLocation); } void oscEvent(OscMessage theOscMessage) { print(" addrpattern: "+theOscMessage.addrPattern()); print(" typetag: "+theOscMessage.typetag()); println(" timetag: "+theOscMessage.timetag()); }
そして、ここにさらに以下のようにudptest_04.maxpatを開いてみると、 やはり2つのMaxパッチ同士ではOSC経由でやりとり しているが、Processingのメッセージ受信は停止した。 ただし、Processingのクリックボタンに対しては、ちゃんと受信できている。 つまり、Processingのプログラムでは、メッセージ送信は出来ているが、 他に同じポートをモニタしているMaxバッチがいると、拗ねて受信をやめてしまうのである(^_^;)。
・・・ここで名古屋で「のぞみ」に乗り換えた。新山口まで2時間半である。 MaxパッチとProcessingプログラム(正確には「スケッチ」と呼ぶべきか?)とは、とりあえず1ペアでいくとして、 もう少し、実用的な情報交換の実験を進めることにしよう。 OSCではデータであっても文字列の「メッセージ」として交換するので、 Maxウインドウの表示と同様に、Processing画面のメッセージエリアでなく、 スケッチ画面内にテキストを表示してみよう。 すっかり忘れているので、ここで「Processing日記」を最初から読み直すことになった(^_^;)。 スクリーン上にテキストを表示する、というのは「Processing日記(2)」にあったが、 これは表示座標を与えるので、自動的にスクロールしてくれるわけではなかった。 だいぶ昔にそういうプログラミングをした気がするが、ここはとりあえず思い出しである。
・・・ここで「のぞみ」は岡山から広島のあたりである。 さすがProcessingはJava出身なので、なかなか高飛車であり、 柔軟には言うことを聞いてくれない。 ようやく途中経過として出来たのは、以下のように、Processingから複数の要素の メッセージを送ってMaxで受けるのと、Maxから5個の整数をパックして送って Processingのメッセージ領域に表示するものである。 ただし、表示する文字列とか表示場所の座標を変数として定義できているので、 次のステップに進めるところが大きいので、ここで保存しておこう。 Max側のパッチはudptest_05.maxpat である。
import oscP5.*; import netP5.*; OscP5 oscP5; NetAddress myBroadcastLocation; PFont f; int y_position = 0; String message; void setup() { size(200,500); oscP5 = new OscP5(this,8000); myBroadcastLocation = new NetAddress("127.0.0.1",7000); f = createFont("Osaka", 18, true); } void draw() { background(255); fill(0); text("Hello Strings!",10,30+y_position*20); } void mousePressed() { OscMessage myMessage = new OscMessage("Processing"); myMessage.add(y_position); myMessage.add(12.34); myMessage.add("Hello! Max5"); oscP5.send(myMessage, myBroadcastLocation); } void oscEvent(OscMessage theOscMessage) { message = ""; int val1 = theOscMessage.get(0).intValue(); int val2 = theOscMessage.get(1).intValue(); int val3 = theOscMessage.get(2).intValue(); int val4 = theOscMessage.get(3).intValue(); int val5 = theOscMessage.get(4).intValue(); message = "Received: "+val1+", "+val2+", "+val3+", "+val4+", "+val5; println(message); }
・・・そして。広島を過ぎてあと15分ほどで新山口、というところで、 以下のようなものが出来た。 まったく美しくない、Javaらしからぬ「ごりごりC」になりかけているものの、 Processingプログラム内に、MaxからOSCメッセージで5個の整数データが届くと、 数値を文字列として移動させて表示している。 ここまで出来れば、ProcessingのグラフィクスをMaxでライブ制御、 というのは出来たも同然である。(^_^)
import oscP5.*; import netP5.*; OscP5 oscP5; NetAddress myBroadcastLocation; PFont f; int y_position=0, flag=1; String message1, message2; void setup() { size(250,500); oscP5 = new OscP5(this,8000); myBroadcastLocation = new NetAddress("127.0.0.1",7000); f = createFont("Osaka", 14, true); message1 = "Hello Processing!"; background(255); } void draw() { if(flag!=0){ fill(0); text(message1,10,30+y_position*20); flag=0; } } void mousePressed() { OscMessage myMessage = new OscMessage("Processing"); myMessage.add(y_position); myMessage.add(12.34); myMessage.add("Hello! Max5"); oscP5.send(myMessage, myBroadcastLocation); } void oscEvent(OscMessage theOscMessage) { message2 = ""; int val1 = theOscMessage.get(0).intValue(); int val2 = theOscMessage.get(1).intValue(); int val3 = theOscMessage.get(2).intValue(); int val4 = theOscMessage.get(3).intValue(); int val5 = theOscMessage.get(4).intValue(); message2 = "Received: "+val1+", "+val2+", "+val3+", "+val4+", "+val5; println(message2); y_position++; flag++; message1 = message2; }
今日はここまでである。 これから湯田温泉で明日の学会の前泊であるが、 ちょっと美味しいお酒がいただけそうである。(^_^)
2011年6月12日(日)
いまは新山口から名古屋に向かう「のぞみ」車内である。 6/11-12の「時間学会」は、内職の暇も与えないほど内容が充実していた。 自分の研究ネタもたくさん仕入れたし、学生にやらせてみたいネタもいくつも発掘した。 懇親会も総会もサボッて遊んだ部分は割り引いても、わざわざ山口まで駆けつけた甲斐があった。 さらに偶然とはいえ、湯田温泉駅から新山口駅までの列車が、たまたま駅に着いたら次の列車が、 なんと週末だけに運転される「SLやまぐち号」だった。 中学生の時にも撮っていた「C571」に久しぶりに会った(^_^)。そして新幹線待合室のビール、その後、新幹線車内で「ふぐ煎餅」とともにワインをいただき、 その後でちょっとだけ、広島から新神戸あたりの区間で、Processingを楽しんだ。 山口への往路で「OSCでMax5とProcessingとの連携」が確立していたので、 これをProcessingの別のプログラムに適用しよう、という事である。 作業はかなりアッサリと完了した。
以下のプログラムでは、 Max側のパッチはudptest_06.maxpat である。 パッチ内に400*400のjitterスクリーン画面を開いて、その中でボタンの有無と関係なく、 マウスカーソルの座標をリアルタイムに取得している。 そしてこのx,y座標をpackして、OSCで送っている。 Processingの方は、スクリーン内のマウスカーソルを追いかけるCG(Processing日記(2)の最後の例)を使って、 そのマウス座標を単にOSCから取得した2つの整数と置換しているだけである。 その結果、「Max5の画面内のマウスカーソル座標」を、「ProcessingのリアルタイムCGが追いかける」という、 2つの独立アプリケーションの連携が実現できた。
import oscP5.*; import netP5.*; OscP5 oscP5; NetAddress myBroadcastLocation; Mover[] movers = new Mover[20]; int x_position = 200; int y_position = 200; void setup() { size(400,400); smooth(); background(255); oscP5 = new OscP5(this,8000); myBroadcastLocation = new NetAddress("127.0.0.1",7000); for (int i = 0; i < movers.length; i++) { movers[i] = new Mover(); } } void draw() { noStroke(); fill(255,10); rect(0,0,width,height); for (int i = 0; i < movers.length; i++) { movers[i].update(); movers[i].checkEdges(); movers[i].display(); } } void oscEvent(OscMessage theOscMessage) { x_position = theOscMessage.get(0).intValue(); y_position = theOscMessage.get(1).intValue(); } class Mover { PVector location; PVector velocity; PVector acceleration; float topspeed; Mover() { location = new PVector(random(width),random(height)); velocity = new PVector(0,0); topspeed = 4; } void update() { PVector mouse = new PVector(x_position,y_position); PVector dir = PVector.sub(mouse,location); dir.normalize(); // Normalize dir.mult(0.5); // Scale acceleration = dir; // Set to acceleration velocity.add(acceleration); velocity.limit(topspeed); location.add(velocity); } void display() { stroke(0); fill(175); ellipse(location.x,location.y,16,16); } void checkEdges() { if (location.x > width) { location.x = 0; } else if (location.x < 0) { location.x = width; } if (location.y > height) { location.y = 0; } else if (location.y < 0) { location.y = height; } } }
今日はここまでである。 明日からまた日常が再開であるが、時間学会で得たネタを掘り下げるのと、 「音楽情報科学」の講義でこのサンプルを3回生に紹介するのが楽しみである。(^_^)
2011年7月28日(木)
前回のProcessing日記が6月12日、山口での 時間学会 の合間に進めたのが最後で、そこから1ヶ月半ほど、進展が無いまま放置してしまった。 そして明後日には、 ICMC2011 での発表のためにイギリスに向かう。 海外出張とかで日々の些事から途絶しないと、なかなかこの手の勉強は進まないものだ。ただし、その期間に、毎週火曜日の放課後には 野口佳恵・個展 のいろいろな準備があり、 M2の見崎さんの修了制作に向けての製作や4回生の総合演習IIのインスタレーションのための製作が、 ★ ★ ★ ★ ★ ★ ★ ★ ★ と続き、 野口佳恵「けだもの展」 を無事に開催し、 直前追い込み の末に 「メディア造形総合演習II」最終合評 を無事に終えたので、サボッていたということでもない。 学生たちの作品は、改訂した ここ に記載した。
今回の ICMC2011 では、コンサートでなくポスターセッションでの発表であり、既にポスターは 採択されたペーパー を4枚のA3に奇麗にプリントしたので、これを持っていき、パソコンで ロシアでの公演映像 でも見せればOK・・・とかなり気楽に構えている(^_^;)。 セントレアから成田に飛び、そこからロンドンに飛び、さらにマンチェスターに飛び、そこから電車でHuddersfieldという街に行く、 ということで、初めての場所なのに何も準備していない。 まぁ、なんとかなるだろう。
ここでProcessingを進めてもいいのだが、同時に進めているSuperColliderの方が放置期間が長く(^_^;)、 またProcessingの方はOSCまで到達しているので、とりあえずはSuperColliderからやってみるつもりである。 OSCについては、 野口佳恵「けだもの展」 で展示したインスタレーション作品「はやくスシになりたい」を、3台のコンピュータがOSCで連携する、 という新しいバージョンに進化させた。ばっちりと動き、オープンキャンパスでは「おはなしパネル」も3台のコンピュータ版になる。 気が向いたらProcessingに来るかもしれない。(^_^;)
2011年8月1日(月)
英国Huddersfield大学のカフェである。 SuperCollider日記 に書いたように、無事にここまでやってきて、ICMC2011の会場である。 午前のセッションが終わってランチタイム、午後イチのコンサートまでの時間にメイルチェックなどしている。今回の海外出張期間中に、担当している「サウンドデザイン」と「音楽情報科学」の最終課題の提出期限がある。 たいていの学生は自力でなんとかするが、「音楽情報科学」の2つの課題のうちの一つ、「Processingでオリジナルスケッチの作品」というのが、 なかなか学生を悩ませているらしい。 「質問はメイルで受ける」と言っていたが、イギリスまで学生のメイルがやってきた。 せっかくなので、この学生(仮にYさんとしよう)のネタを、教材として取り上げてみよう。 まず、Yさんからのメイルは以下であった。
どこかのサンプルで見たような気がするが(^_^;)、 これをとりあえずアプレットとして書き出してみると、このようになった。○○です、こんばんは。 今ずっとチカチカする状態なのですが、 オンマウスすると波紋みたいに広がる感じにしたいのですが、 やりかたがわかりません。 以下プログラム ----------------------------------- void setup() { size(800, 800); colorMode(RGB); background(20,1,55); noStroke(); smooth(); frameRate(16); } void draw() { int box_height =50; int box_width = 50; int base_color = 50; int y_color = 100; for(int i=0;i<30;i++){ for(int j=0;j<30;j++){ int e = int(random(5)); switch(e){ case 2: float r_color = random(8,y_color); fill(base_color + r_color,10,40); ellipse(j*box_width, i*box_height, box_width, box_height); break; } } } }
なんだか、あちこち無駄が多い気がする(^_^;)ので、まずは以下のように整理してみた。 MacBookAirの画面の関係でスクリーンサイズを700*700として、無駄のないように円を並べてみた。 アプレットとして書き出してみると、このようになった。 どうやら、スクリーンサイズは絶対的に記述しないとdefaultの100*100にされてしまうらしい(^_^;)。
int screen_size = 700; void setup() { size(screen_size, screen_size); colorMode(RGB); background(20,1,55); noStroke(); smooth(); frameRate(16); } void draw() { int box_size; int total_mesh = 20; int base_color = 50; int y_color = 100; box_size = screen_size / total_mesh; for(int i=0; i < total_mesh; i++){ for(int j=0; j < total_mesh; j++){ int e = int(random(5)); switch(e){ case 2: float r_color = random(8,y_color); fill(base_color + r_color,10,40); ellipse((j+0.5)*box_size, (i+0.5)*box_size, box_size, box_size); break; } } } }
これを元に、以下のように、マウスカーソルの情報である「mouseX」「mouseY」を組み合わせて、 マウスの位置に追従して白くする、というのを作ってみた。 アプレットとして書き出してみると、このようになった。
void setup() { size(700, 700); colorMode(RGB); background(20,1,55); noStroke(); smooth(); frameRate(16); } void draw() { int box_size; int total_mesh = 20; int base_color = 50; int y_color = 100; box_size = 700 / total_mesh; for(int i=0; i < total_mesh; i++){ for(int j=0; j < total_mesh; j++){ float xx = (j+0.5)*box_size; float yy = (i+0.5)*box_size; int e = int(random(5)); switch(e){ case 2: float r_color = random(8,y_color); fill(base_color + r_color,10,40); ellipse(xx, yy, box_size, box_size); break; } if ( (xx-20 < mouseX) && (xx+20 > mouseX)){ if ( (yy-20 < mouseY) && (yy+20 > mouseY)){ fill(255,255,255); ellipse(xx, yy, box_size, box_size); } } } } }
・・・というあたりで、ぼちぼちコンサートの時間が近づいてきた。 今日はこのあたりかな。
2011年8月3日(水)
英国Huddersfield大学のゲストハウスの部屋である。 SuperCollider日記 の方はそこそこ進んでいるが、Processingはボチボチである。 パソコンの時計(日本時間)は8月3日の朝9時前であるが、現地時間は8月3日になったばかりの深夜1時前である。SuperCollider日記 の8月2日のところは、午後のコンサートを楽しんで、昼食に出てから午後のpaperセッションが始まったところまでであった。 その後、Creative Arts Centerのホワイエに移動してposterを準備し、無事に1時間半ほどのposter sessionを終えた。 多数のお客さんが来てくれたので、ずっと立って対応して、だいぶ疲れてきたために、ここは前日と同様に晩のコンサートは無理、 と判断してケンタのセットを仕入れて宿に戻り、ワインで無事発表の祝杯を上げて、まだ明るいのに爆睡した。 そして今、まだ夜中なのに目が覚めて、フト起き出したところである。 まだ身体の奥に日本時間が残っているので、これからの時間帯はもう深くは眠れない。(^_^;)
ということで、途中までであったProcessingのスケッチをちょっと進めてみることにした。 まずは前回のスケッチから、ProcessingのサイトのReferenceを参照して、以下のようにいくつか無駄を省いてみた。 見た目はほぼ同等の動作だが、自分としてはだいぶ全体像が見えてきた。 やはり、他人のプログラムはそのままでは使いにくいものだ。
このスケッチでは、「draw()」の中で、iとjとの2次元スキャンループで、 スクリーン内をmeshに分けた「mesh画素」ごとに、void setup() { size(600, 600); colorMode(RGB); background(20,1,55); noStroke(); smooth(); } void draw() { int i, j, e, box_size; int total_mesh = 20; box_size = 600 / total_mesh; for(i=0; i < total_mesh; i++){ for(j=0; j < total_mesh; j++){ float xx = (j+0.5) * box_size; float yy = (i+0.5) * box_size; e = int(random(5)); switch(e){ case 2: float r_color = random(8,100); fill(r_color+50,10,40); ellipse(xx, yy, box_size, box_size); break; } if ( (xx-20 < mouseX) && (xx+20 > mouseX)){ if ( (yy-20 < mouseY) && (yy+20 > mouseY)){ fill(255,255,255); ellipse(xx, yy, box_size, box_size); } } } } } という処理を繰り返している。
- 乱数「e」と「switch(e)」とにより、たまに「mesh画素」の色を変化させる(チカチカ)
- マウス位置に近い「mesh画素」の色を明るくする
ここに、マウスカーソルを中心として「波紋」が広がるようにするためには、「draw()」の外側に、 つまり広域変数として「時間経過」をあらわすパラメータを持てばいいような気がする。 この場合、チカチカは邪魔になるので外したいのだが、考えてみるとYさんの希望(仕様)というのは、
のどちらであるのか、ハッキリしていない、という事がはっきりした(^_^;)。 プログラミングをする場合には、まず「どのようにしたいのか」を明確にする必要がある、 という、まさに教科書的な問題点であった。
- チカチカはそういうサンプルのものであり、波紋が広がればチカチカは不要
- 波紋が広がりつつ、さらにチカチカもする
そこでまずはチカチカをさせないように、「乱数「e」と「switch(e)」とにより、たまに「mesh画素」の色を変化させる」という部分を コメントアウトしてみると、背景部分は何も描画しないので、明るいmseh画素の残像だけがどんどん塗られて行くことになった。 これは駄目だろう(^_^;)。
また、時間的変化の分解能を高めるために、frameRate(16)を外してdefaultの60fpsにした。 ところがこの状態で実行させていると、MacBookAirのファンがすぐに回り出す。 モニタで見てみると、デュアルコアCPUのusageが90%ほどになっている。最大で200%なので、これは相当な仕事なのだ。 runウインドウをstopさせればこれは冷めてくるので、「試しては閉じて」の繰り返しとなった。
ここでもう少し時間をかけて、さらに自分で理解できるようにスケッチを改変した。 ポイントは、「random(5)で2のときだけswitchというのは、要するに確率が1/5」というのと、 マウス位置とmesh画素との距離を直接に計算した事である。 見た目の動作はほぼ同じだが、さらに無駄がなくなってスッキリした。(^_^)
そこで、時間経過を表す広域変数「global_time」を導入して、これを小数点レベルのラジアン単位で増加させることにした。 その結果、以下のように、波紋ではないが、マウスカーソル付近で「白」、そしてその周囲でサイン関数で半径が伸び縮み振動する「輪」が出来た。 残像であまり奇麗ではないが、これで一歩、進展というところである。 あとは減衰する波動関数のようにしてやればいいように思う。 アプレットとして書き出してみると、このようになった。void setup() { size(600, 600); colorMode(RGB); background(20,1,55); noStroke(); smooth(); } void draw() { int i, j, box_size; int total_mesh = 20; float xx, yy, distance; box_size = 600 / total_mesh; for(i=0; i < total_mesh; i++){ for(j=0; j < total_mesh; j++){ xx = (j+0.5) * box_size; yy = (i+0.5) * box_size; if( random(20) < 1 ){ fill(random(8,100)+50,10,40); ellipse(xx, yy, box_size, box_size); } distance = sqrt( sq(xx-mouseX) + sq(yy-mouseY) ); if ( distance < box_size/1.2 ){ fill(255,255,255); ellipse(xx, yy, box_size, box_size); } } } } float global_time; void setup() { size(600, 600); colorMode(RGB); background(20,1,55); noStroke(); smooth(); global_time = 0; } void draw() { int i, j, box_size; int total_mesh = 20; float xx, yy, distance, wave; box_size = 600 / total_mesh; global_time = global_time + 0.1; // wave speed if (global_time > 3.1415926535*2){ global_time = 0; } for(i=0; i < total_mesh; i++){ for(j=0; j < total_mesh; j++){ xx = (j+0.5) * box_size; yy = (i+0.5) * box_size; if( random(20) < 1 ){ fill(random(8,100)+50,10,40); ellipse(xx, yy, box_size, box_size); } distance = sqrt( sq(xx-mouseX) + sq(yy-mouseY) ); if ( distance < box_size/2 ){ fill(255,255,255); ellipse(xx, yy, box_size, box_size); } wave = distance - 100*(sin(global_time)+1) - 40; if ( (wave > box_size/2) && (wave-15 < box_size/2) ){ fill(0,255,255); ellipse(xx, yy, box_size, box_size); } } } }
・・・というあたりで、丑三つ時を過ぎてしまった。ますます頭が冴えて眠れないが、再びベッドに戻って明日に備えることにしよう。
2011年8月6日(土)
今回は、Processingよりも、むしろ SuperCollider日記 の方が相当に進んだ。 ついに「SuperColliderとOSC」というところに到達しかけたところで、 Processingと並んだ(^_^)。・・・ということで、ここはロンドンから成田に向かう機内である。 いつもの爆睡から、窮屈な姿勢による全身の痛みで目覚めた、 冷たいオレンジジュースをもらって、30分ほどの入念なストレッチで、ようやく「起きた」ところである。 目覚めた瞬間の超不快感から、この爽快感までの急激な変化が、とても快感である(^_^;)。 出発時は現地時間で8月5日の晩だったが、既に日付けは8月6日に入り、日本時間ではもう午前10を過ぎているので、 オーブンキャンパスも始まっているだろう。 ここはシベリア上空、Time to Destinationは4時間半である。 いれたてのブラックコーヒーが沁みる(^_^)。
SuperCollider日記 の8月5日の最後の部分にちょっとだけ追加。 過去のNIME/ICMCでは、Proceedingsが「冊子」となっていたこともあり、 常にノートを持参して、気付いたこと、新しいアイテア等を記録していたが、 最近ではProceedingsがpdfで配布されるので、それを読むためにパソコン持参が必須である。 すると、合間にメイルをチェック出来たり内職がバッチリ出来るので、むしろメモは減っている。 これは自分の責任であるが、まぁBad Piontかもしれない。
さて、ネットと切れている機内なのでアプレットの書き出しは省略するが、 途中まで進めていたProcessingをぼちぼち進めてみよう。 ソースさえあればコピベしてコンパイル・実行は簡単である。 ヒースローのバーでMacBookAirのバッテリを満タン充電していたので、 このプログラミングは最高の暇つぶしである。(^_^;)
前回の例では、時間的変化のために「global_time」を設けたが、ここにさらにマウスカーソルからの距離を加味すればいい。 あれこれ試行錯誤して、無事に「マウスカーソルから色の波紋が広がる」「バックはこれまで同様にチラチラ」というものが出来た。 いろいろプログラムの改良もあるが、以下である。
float global_time; int total_mesh = 20; float[][] buff = new float[total_mesh][total_mesh]; void setup() { int i, j; size(600, 600); colorMode(RGB); background(20,1,55); noStroke(); smooth(); for(i=0; i < total_mesh; i++){ for(j=0; j < total_mesh; j++){ buff[i][j] = 50; } } } void draw() { int i, j, box_size; float xx, yy, distance, wave, sin_color; box_size = 600 / total_mesh; global_time = global_time + 0.1; // wave speed if (global_time > 3.1415926535*2){ global_time = 0; } for(i=0; i < total_mesh; i++){ for(j=0; j < total_mesh; j++){ if( random(20) < 1 ){ buff[i][j] = random(8,100)+50; } xx = (j+0.5) * box_size; yy = (i+0.5) * box_size; distance = sqrt( sq(xx-mouseX) + sq(yy-mouseY) ); if ( distance < box_size/1.5 ){ fill(255,255,255); } else{ sin_color = sin( distance/20.0 - global_time) + 1; fill( buff[i][j], 20*sin_color+10, 40 ); } ellipse(xx, yy, box_size, box_size); } } }
フライトはあと3時間程度である。 SuperColliderの進展に加えて、Processingの方もとりあえず解決したので、気持ちよく帰国しよう。(^_^)
・・・ということで、今は成田空港の国内線乗り継ぎロビーである。 だいぶ慣れてきたので、スーツケースに入れずに機内持ち込みに下着一式と半袖シャツを入れておいてよかった。 成田に到着して機外に出た瞬間の熱気には絶句した。 いやー、イギリスは冷涼で快適だったんだ、と実感。 それもこの熱いさなか、節電のためにエアコンの効きも悪い。 全身、着替えたからいいものの、いやー、日本はもはや亜熱帯気候だなぁ。 明日だけ合流するオープンキャンパスでは脱水に気をつけないと。 前回のオスロからの帰国時と同様に、贅沢に「ハーゲンダッツ抹茶アイス」をいただくことしよう。
・・・ということで、ここは成田からセントレアへの機内である。 パソコンを叩ける時間はちょっとだけだろう(^_^;)。 しかし最後に、ショックな事に遭った。 売店で、「ハーゲンダッツ抹茶アイス」を買った・・・と思って開けてみると、なんか色が緑じゃない。 よく見てみると、なんと「メイプル+くるみ」のハーゲンダッツだった。 これは完全に僕の間違いなのだが、「メイプル+くるみ」のカップの中央には、 鮮やかな緑色の帯がぐるっと一周しているのである。 これを「抹茶」と思い込んだのが失敗だった(^_^;)。 ド甘いものが苦手なので、勿体ないが半分ほど残して捨ててしまった。 今回の海外出張もまぁ、だいたい順調に行ったが、こんなところでドジるとは、 「気をつけよう」という天からの警鐘だろう。反省反省。(^_^;)
機内で走らせたところでは、さきほどのプログラムの
の「20」というのは、どうも、もっと波紋の影響(グリーン成分をサインで振っている)fill( buff[i][j], 20*sin_color+10, 40 ); と「60」あたりに大きくした方がよく見える。 ただし、背景で赤系統がチラチラしているのは、判りにくくなる。 ここはトレードオフだろう。fill( buff[i][j], 60*sin_color+10, 40 ); ・・・ということで、こんなところでオシマイである。 次はいつになるか、まさか出張の時にしか進まないとなると、次は8月下旬の大阪出張、 9月上旬のリンツ(アルスエレクトロニカ)出張、そして9月中旬の札幌と神戸のハシゴ、 というところである。
2011年8月9日(火)
上の日記(8/6)で帰国して、翌8月7日は オーブンキャンパス2日目 に合流した。 終了後、撤収、そして打ち上げがあり、1回生と慰労カラオケを日付変更線を越えるまで堪能して帰宅、 翌8月8日は昼前まで爆睡して午後から研究室に行ったが、溜まった事務手続きをしているだけで終わった。 そして今日になって、ようやく午前中に ICMC2011フォトレポート をWebに上げた。 ここからさらに、先週金曜日が提出期限となっていた、「サウンドデザイン」「音楽情報科学」の2科目の学生の提出課題を見て、 成績(評価のメインは出席)を教務室マークシートに記入することで、ほぼ一日が終わりそうである。 M2の見崎さんは、黙々と作品のためのハンダ付けを進めている。メディア造形学科3回生向け専門科目「音楽情報科学」では、中間課題として「視覚と聴覚の錯覚をテーマとしたFLASHなど」を出し、 最終課題としては2つ、「インタラクティブなProcessingスケッチ」と「(架空の)サウンドインスタレーション作品の企画書」を出した。 せっかくなので、ここでこれらを紹介してみよう。 なお、一部の課題を未提出の学生も、出席点により個別に救済されている。
池田さん の中間課題は これ である。 BGMはシェパードトーン(無限に上昇or下降するサウンド)、映像は奥行きの凹凸が反転する錯視のキューブである。 最終課題の企画書は これ である。 夏らしいテーマで、実現性もそれほど無茶ではない企画書となっている。 ただし作品として実際に制作するとなれば、どこまで没入体験できるか、具体性が課題となるだろう。 確かに花火の音はするが、「サウンド」インスタレーション、としてはちょっと弱いかも。 最終課題のProcessingスケッチは以下であり、アプレットとして書き出してみると、 このようになった。 マウスの軌跡に描かれた円が、時間的に残像のように残りながら消えていく。
void setup() { size(500, 500); frameRate(20); background(255); } void draw() { fadeToWhite(); stroke(mouseX, 0, mouseY); ellipse(mouseX, mouseY, 10, 10); } void fadeToWhite() { noStroke(); fill(255, 10); rectMode(CORNER); rect(0, 0, width, height); }
建部さん の最終課題の企画書は これ である。 似たようなアイデアは過去にNIMEで見かけた記憶があるが、実現できたらウケるかもしれない。 最終課題のProcessingスケッチとしては、 このような ドキュメントが添えられて、2つのProcessingソースが提出されていた。 マウスで移動する「覗き穴」があり、その奥にランダム描画のアニメーションが動く、という構想で、 そのそれぞれは作れた(ほとんどサンプルまんま(^_^;))が、両者を合体するところで力尽きた、というところだろう。 これは、なんとなく合体できるような気もするので、そのうち時間があればやってみることにしよう。 とりあえずその2本を紹介すると、1本は以下であり、アプレットは このようになった。
void setup() { size(200, 200); background(255); smooth(); noStroke(); } void draw(){ for(int i = 0; i < 100; i++) if (frameCount % 5 == 0) { translate(random(100), 100); rotate(radians(frameCount * 2 % 360)); rect(0, 0, 10, 20); } fill(frameCount * 20 % 255,random(100),100); }
もう1本は以下であり、アプレットは このようになった。
PImage img; void setup() { size(323, 450); img = loadImage("love.jpg"); } void draw(){ loadPixels(); for (int x = 0; x < img.width; x++) { for (int y = 0; y < img.height; y++ ) { int loc = x + y*img.width; float r = red (img.pixels[loc]); float g = green (img.pixels[loc]); float b = blue (img.pixels[loc]); float distance = dist(x,y,mouseX,mouseY); float adjustBrightness = (50-distance)/50; r *= adjustBrightness; g *= adjustBrightness; b *= adjustBrightness; r = constrain(r,0,255); g = constrain(g,0,255); b = constrain(b,0,255); color c = color(r,g,b); pixels[loc] = c; } } updatePixels(); }
川口さん の最終課題の企画書は これ である。 仕掛けはシンプルそうだが、演出によっては何か出来そうな印象を期待させる。 最終課題のProcessingスケッチは以下であり、ペンの色を指定してお絵描きする、というものである。 アプレットとして書き出してみると、 このようになった。
int r = 5; color nowcolor; color mc[ ] = new color [5]; int mx = 750, my = 10, ms = 40; void setup(){ size(800,550); colorMode(HSB,360,100,100); background(0,0,100); mc[0] = color(50,0,100); mc[1] = color(0, 100,100); mc[2] = color(300 ,100,100); mc[3] = color(39,100,100); mc[4] = color(0,0,100); nowcolor = color(0,0,100); } void draw(){ if( isdrag == true ){ stroke( nowcolor ); strokeWeight( r ); line( bx, by, mouseX, mouseY ); bx = mouseX; by = mouseY; }else{ stroke( 0,0,100 ); for( int i = 0 ; i < 5 ; i++ ){ fill( mc[i] ); strokeWeight( 1 ); rect( mx, my+(ms+10)*i, ms, ms ); } } } boolean isdrag = false; int bx, by; void mousePressed(){ int i = 5; if( mouseX >=mx && mouseX <= mx+ms ){ for( i = 0 ; i < 5 ; i++ ){ if( mouseY >= my+(ms+10)*i && mouseY <= my+(ms+10)*(i+1) ) break; } } if( i < 5 ){ nowcolor = mc[i]; }else{ isdrag = true; bx = mouseX; by = mouseY; } } void mouseReleased(){ isdrag = false; }
斉藤さん の中間課題は これ である。 BGMはシェパードトーン風、映像は定番の錯視である。拡大してもギザらないので、こういうのを作るにはFLASHは最適である。 最終課題の企画書は これ である。 「食欲と音」というテーマでは、1期生5人が2回生の時に MAF2001 で発表した作品「Shock(食)ing」というのも同じである。 ただしこの作品では、ストレートに「美味しい音」と「美味しい画像」という組み合わせであった。 この斉藤さんのコンセプトは真逆の「おいしそうな食べ物の視覚的刺激を与える反面、 食欲が失せ るような音の刺激を与える」であり(^_^;)、そのシュールさは注目に値する。 最終課題のProcessingスケッチは以下であり、アプレットとして書き出してみると、 このようになった。
Mover[] movers = new Mover[20]; void setup() { size(300,300); smooth(); background(253); fill(0,0,0); ellipse(150,150,300,300); for (int i = 0; i < movers.length; i++) { movers[i] = new Mover(); } } void draw() { noStroke(); fill(33,10); rect(0,0,width,height); for (int i = 0; i < movers.length; i++) { movers[i].update(); movers[i].checkEdges(); movers[i].display(); } } class Mover { PVector location; PVector velocity; PVector acceleration; float topspeed; Mover() { location = new PVector(random(width),random(height)); velocity = new PVector(0,0); topspeed = 4; } void update() { PVector mouse = new PVector(mouseX,mouseY); PVector dir = PVector.sub(mouse,location); dir.normalize(); dir.mult(0.5); acceleration = dir; velocity.add(acceleration); velocity.limit(topspeed); location.add(velocity); } void display() { stroke(0); fill(120,165,25); ellipse(location.x,location.y,32,16); ellipse(location.x,location.y,60,60); fill(230,0,0); ellipse(location.x+20,location.y,16,32); ellipse(location.x-20,location.y,16,32); fill(3,3,3); ellipse(location.x+19,location.y,11,24); ellipse(location.x-19,location.y,11,24); } void checkEdges() { if (location.x > width) { location.x = 10; } else if (location.x < 0) { location.x = width; } if (location.y > height) { location.y = 10; } else if (location.y < 0) { location.y = height; } } void setup() { size(200,200); smooth(); } }
坂本さん の中間課題は これ である。 BGMはシェパードトーン風、映像は定番の錯視のムービーである。 最終課題の企画書は これ である。 「付箋音楽」というのは新しいが、ノートといっても透過しないといけないので要するに1枚の「薄い紙」である。 付箋が並んだところでこれを楽譜のようにスキャンして演奏するのか、刻々と付箋を貼ったりする際に音がでるのか(これだけと手の影にも反応するし、とりたてて付箋は必要ではない)、というあたりに詰めが必要かもしれない。 最終課題のProcessingスケッチは以下である。 講義の中ではあまり紹介できなかったOpen-GLに挑戦していて、 ゆっくり回転する3D立体をマウスでさらに回せる。 ただし、アプレットとして書き出してみると、うまく表示できなかったので、体験する場合にはProcessingで実行して欲しい。
import processing.opengl.*; float a; int NUM = 8; float offset = PI/NUM; color[] colors = new color[NUM]; void setup() { size(400, 400, P3D); noStroke(); colorMode(HSB,90,100,10,100); frameRate(30); for(int i=0; i < NUM; i++) { colors[i] = color(i*8+100,60,100,90); } } void draw() { background(0); ambientLight(63, 90, 31); directionalLight(255,5,255,-1,0,0); pointLight(63, 127, 255, mouseX, mouseY, 200); spotLight(100, 100, 100, mouseX, mouseY, 200, 0, 0, -1, PI, 2); translate(width/2, height/2, -50); rotateX(mouseX / 800.0); rotateY(mouseY / 100.0); for(int i=0; i < NUM; i++) { pushMatrix(); fill(colors[i]); rotateY(a+offset*i); rotateX(a/2+offset*i); rotateZ(a/3+offset*i); box(width/2.6); popMatrix(); } for(int i=0; i < NUM; i++) { pushMatrix(); fill(colors[i]); rotateY(a+offset*i); rotateX(a/2+offset*i); rotateZ(a/10); box(width/5); popMatrix(); } a+=0.01; }
白江さん の中間課題は これ である。 BGMはシェパードトーン風、映像は定番の錯視のムービーである。 最終課題の企画書は これ である。 なかなか良い雰囲気であるが、真っ暗な空間にいる海洋生物を「ライト」で照らしたら、 その造形がハッキリ見えてしまってネタバレしてしまう(^_^;)。 レーザのような「ライトのようなポインタ」という事になるかなぁ。 照らされて光る海洋生物の明かりが周囲の海洋生物を駆動して和音となる、 というのは面白い。実現に向けては壁もあるが、乗り越え甲斐があるテーマという印象である。 最終課題のProcessingスケッチは以下であり、アプレットとして書き出してみると、 このようになった。
void setup() { size(600, 600); fill(200,230,20); smooth(); rectMode(CENTER); } void draw() { if (mousePressed) { fill((-mouseX+765)/3, (-mouseY+765)/3, 200, 100); noStroke(); } else { stroke( 200,mouseY/3,mouseX/3, 100); noFill(); } ellipse(mouseX+50, mouseY+50, 20,20); ellipse(mouseX+50, mouseY-50, 20,20); ellipse(mouseX-50, mouseY+50, 20,20); ellipse(mouseX-50, mouseY-50, 20,20); rect(mouseX+70, mouseY+0, 20,20); rect(mouseX+0, mouseY+70, 20,20); rect(mouseX-70, mouseY+0, 20,20); rect(mouseX+0, mouseY-70, 20,20); }
曽根クン の中間課題は、既に1106のWebで紹介していた これ である。 個々の円が拡大縮小しているのに、クリックして全体を回転させるとその変化が知覚されなくなる、 という、比較的新しい錯視の例となっている。 最終課題の企画書は これ である。 これはKinnectなどの画像認識センサでも、さらに古くは超音波や赤外線などによる距離計測センサによって、 色々と似たような作品があった気がする(とりたててもの凄い、と評判になった訳ではない)。 指向性スピーカの意外感は自分が移動しないとあまり活用できない(停止していて聞こえるのは普通)。 やはり「肝」となるのは、触っていないのに「触った気がする」ようにするための部分で、 赤外線低周波振動による圧迫感/温熱感(いわゆる「気」)とか、風/霧の細いジェットとか、 ここが工夫のしどころだろう。 別の鑑賞者が見ているiPadに手の残像が出る・・・とかいうのは、かなりたくさんの作品で実際に試みられてきた。 最終課題のProcessingスケッチは以下であり、アプレットとして書き出してみると、 このようになった。
int nowMx = 0; int nowMy = 0; int prevMx = 0; int prevMy = 0; void setup() { size(800, 600); smooth(); } void draw(){ int mx = nowMx - prevMx; int my = nowMy - prevMy; float d = sqrt(mx*mx + my*my); float r1 = random(d * -1, 0); float r2 = random(d * -2, d * 1); int n = floor(random(10,50)); drawStar(nowMx,nowMy,r1,r2,n); prevMx = nowMx; prevMy = nowMy; nowMx = mouseX; nowMy = mouseY; } void clear(){ stroke(200); fill(200); rect(0,0,width,height); } void drawStar(int x , int y , float r1,float r2 , int num){ stroke(0,0,0,100); float rad = TWO_PI / num; for ( int i=0; i< num; i++){ float p1x = r1 * cos(rad * i) +x; float p1y = r1 * sin(rad * i) +y; float p2x = r2 * cos(rad * (i + 0.5)) +x; float p2y = r2 * sin(rad * (i + 0.5)) +y; float p3x = r1 * cos(rad * (i+1)) +x; float p3y = r1 * sin(rad * (i+1)) +y; line(p1x,p1y,p2x,p2y); line(p2x,p2y,p3x,p3y); } }
高橋さん の中間課題は これ である。 BGMはシェパードトーン風、映像は錯視風のFLASHである。 最終課題の企画書は これ である。 これは面白そうであるが、実現するためには、ちょっとお金がかかる(^_^;)。 最終課題のProcessingスケッチは以下であり、アプレットとして書き出してみると、 このようになった。
void setup() { size(400, 400); background(102); smooth(); } void draw() { variableEllipse(mouseX, mouseY, pmouseX, pmouseY); } void variableEllipse(int x, int y, int px, int py) { float speed = abs(x-px) + abs(y-py); stroke(speed); ellipse(x, y, speed, speed); }
田中さん の中間課題は これ である。 BGMはシェパードトーン風、なかなか飽きない錯視映像のムービーである。 最終課題の企画書は これ である。 これはまるでKinnectのデモ(^_^;)なので、作品としてのテイストが勝負となるが、 Wiiなどを含めて、なかなか独自性が困難かもしれない。 最終課題のProcessingスケッチは以下であり、アプレットとして書き出してみると、 このようになった。
float pd=0; void setup() { size(500, 500); background(130); colorMode(HSB, 360, 100, 100); noStroke(); smooth(); frameRate(20); } void draw() { noStroke(); fill(0, 0, 50, 50); rect(0, 0, width, height); float d = dist(mouseX, mouseY, pmouseX, pmouseY) ; fill(150 + pd, 50 + pd, 150+ pd ); beginShape(TRIANGLE_STRIP); ellipse(pmouseX+pd, pmouseY+pd, 5+pd , 5+pd); ellipse(pmouseX-pd, pmouseY-pd, 5+pd , 5+pd); ellipse(mouseX+d, mouseY+d, 5+d , 5+d); ellipse(mouseX-d, mouseY-d, 5+d , 5+d); endShape(CLOSE); pd = d; }
則兼クン の中間課題は これ である。 zipを解凍すると、以下のようにアプリケーション化された、Max/MSP/jitterのパッチが走る。 色々なシェパードトーンを選択出来る、面白いツールとなっている。
そして則兼クンの最終課題の企画書は これ である。 画像認識ものは色々と課題もあるが、まぁこんなものだろう。 最終課題のProcessingスケッチは以下であり、アプレットとして書き出してみると、 このようになった。 マウスでつまんで走らせることの出来る「orz」が可愛い。(^_^)
float a, a_abs, v, dt; float v1, v2; float fps; float pos, diff; boolean drag; void setup() { size(1250, 150); fps = 60; a_abs = 200; pos = width/2; v = v1 = v2 = 0; a = 0; drag = false; background(0); stroke(255); noFill(); frameRate(fps); } void draw() { dt = 1 / frameRate; if(drag){ v2 = v1; v1 = (mouseX - pmouseX) / dt; v = (v1 + v2)/2; pos = mouseX - diff; } if( !drag ){ if(v != 0) a = -1 * v / abs(v) * a_abs; v += a * dt; pos += v * dt; if( pos < -50 ) pos += width+50; else if( pos > width ) pos -= width+50; if( abs(v) < 10 ){ v = 0; a = 0; } } background(0); translate(pos, height/2-25); noFill(); ellipse(0, 0, 50, 50); line(25,0,100,0); line(50,0,50,50); line(100,0,100,50); line(100,50,150,50); } void mousePressed(){ if( pos-25 < mouseX && mouseX < pos+150 && height/2-50 < mouseY && mouseY < height/2+40){ drag = true; v = v1 = v2 = 0; a = 0; diff = mouseX - pos; } } void mouseReleased(){ if(drag){ drag = false; } }
平田さん の中間課題は これ である。 BGMはシェパードトーン風、映像は定番の錯視のムービーである。 最終課題の企画書は これ である。 奇麗だが、残念ながら上空からのプロジェクションでは自分の影が出来てしまうのが課題である。 最終課題のProcessingスケッチは以下であり、アプレットとして書き出してみると、 このようになった。
void setup() { size(500, 500); stroke(255); } void draw() { fill(125, mouseY / 4, mouseX / 4); ellipse(250, 250, mouseX, mouseY); } void mousePressed() { background(125, mouseY / 4, mouseX / 4); }
福田さん の中間課題は これ である。音が異常に小さい(^_^;)。 気持ちは判るが、レーザーのビームでこのように虹色の光が見えるのか、謎である。 また、正視したら健康的にはとても怖い(^_^;)。 最終課題のProcessingスケッチは2つ提出されていた。 その一つは以下であり、アプレットとして書き出してみると、 このようになった。
void setup() { size(500, 500); colorMode(RGB); background(20,0,55); noStroke(); smooth(); frameRate(24); } void draw() { int box_height =20; int box_width = 20; int base_color = 800; int y_color = 800; for(int i=0;i<50;i++) { for(int j=0;j<50;j++) { int e = int(random(5)); float r_color = random(8,y_color); float distance = dist(j * box_width, i * box_height, mouseX, mouseY); fill(200 - distance, 10, 40); ellipse(j*box_width, i*box_height, box_width, box_height); } } }
もう一つは以下であり、アプレットとして書き出してみると、 このようになった。
int box_height = 20; int box_width = 20; int base_color = 50; int y_color = 200; int row_nb, col_nb; float max_dist; void setup() { size(500, 500); colorMode(RGB); background(20, 1, 55); noStroke(); smooth(); frameRate(16); row_nb = height / box_height; col_nb = width / box_width; max_dist = sqrt(width * width + height * height); } void draw() { background(10,2,64); for (int c = 0; c < col_nb; c++) { for (int r = 0; r < row_nb; r++) { float e = random(3); float distance = dist(c * box_width, r * box_height, mouseX, mouseY) / max_dist; if (distance < 0.2 && e < 0.5) { fill(base_color + random(8, y_color / 2) + 2.5 * y_color * (0.2 - distance), 10, 40); ellipse(c * box_width, r * box_height, box_width, box_height); } } } }
三瓶さん の中間課題は5本セットである。 これ は、「円に色が付いているように見えます(るらしいです)」とのことである。 これ は、「気持ち悪い浮遊感が体感できます」とのことである。 これ は、「残像?で図形がたくさんあるように見えます」とのことである。 これ は、「見つめていると真ん中に図形らしきものが見えてきます」とのことである。 これ は、「外側の模様と内側の楕円は同じ速度で動いているのに、内側の楕円は少し遅れて動いて見えます」とのことである。 最終課題の企画書は5ページの別々のpdfに分かれて、 (1) (2) (3) (4) (5) となっている。内容は、・・・ちょっとウムム(^_^;)である。 最終課題のProcessingスケッチは以下であり、アプレットとして書き出してみると、 このようになった。
void setup() { size(400, 400); } void draw() { if (mousePressed) { fill(20,20,16,16); } else { fill(255,255,255); } size(400,400); rectMode(CENTER); rect(200,200,40,200); ellipse(200,140,120,120); ellipse(162,140,32,32); ellipse(238,140,32,32); fill(0,0,0); ellipse(162,140,20,20); fill(0,0,0); ellipse(238,140,20,20); line(180,230,120,160); line(220,230,280,160); line(180,300,160,320); line(220,300,240,320); line(184,176,216,176); }
森田さん の最終課題の企画書は これ である。 「空中にお絵描きする」というのは、インスタレーションの世界では、いわば永遠の課題である。 これをタッチパネルやタブレットに描いたのを投射したのでは面白くないのだが、 空中に自由にお絵描きして、それが飛行機雲のように残る・・・というのはなかなか厄介である。 現在までのところ、画像認識系がいくつか発表されてきているが、「これだ !」という決定版はまだ無い。 最終課題のProcessingスケッチは以下であり、アプレットとして書き出してみると、 このようになった。
void setup() { size(300,300); background(200); noStroke(); fill(0); } void draw() { rect(mouseX, mouseY,5,5); } void mousePressed() { fill(255); } void mouseReleased() { fill(0); }
・・・なんと終わってみれば、学科会議をまたいで8/10の夕方になったが、これで前期科目の成績付けも終わった。 皆んな、けっこうProcessingも使えてきているので、これを作品に反映させる学生が出て来るのが楽しみである。
2011年8月13日(土)
世間はどうやら、お盆休みである。 前回の8月9日のところで、前期課題の成績評価も終わったが、今年も 山村知世・個展 に行く話とか、 まだまだ残っていた、M2見崎さんのシステムの応援 ★ ★ とかがあった。 今日はゆっくり寝坊して昼前からボチボチ、角川から出た4期生の清水誠一郎クンのDVD 「中学星」スーパーデラックス の取り込みとか、あれこれしていた。 今もこの裏で、清水誠一郎クンが母校に凱旋して後輩たちの前で特別講義を行った、その記録ビデオを取り込んでいるところである。さて実は、宿題として、建部さんの課題で本人がやってみたかったというProcessingの懸案が残っていた。 2つの要素を合体させたいので、アプレットとしては、 これ(「A」と呼ぶ) が背景として動くところに、 これ(「B」と呼ぶ) が重なって、見えているハートの静止画の代わりに「A」のアニメーションが透けて見えたい(正確には、マウスカーソルから遠いところの画素のbrightnessを下げて黒く塗りつぶす)、という事である。
ところがちょっと実験してみたところ、どうもこれはそれほど単純簡単でもない・・・と判ってきた。 Processingでは、出力スクリーンというのは共通に1つしかないようなので、以下の問題点がある。 「A」の以下のProcessingスケッチ (これ) は、メインループの「draw()」において、「rect()」で直接にスクリーンに描画している。
ところが「B」の以下のProcessingスケッチ (これ) は、「PImage img;」を定義して、 まず最初にこのPImageに「loadImage()」でJPEG静止画を読み込み、メインループの「draw()」において、 「loadPixels()」で毎回、 このJPEG静止画を読み込んだ上で、その全画素に対して、マウス座標との距離に応じて、 「黒く塗りつぶすか」「biightnessをもって下の画素を残すか」をピクセル単位で計算処理して、 最後に「updatePixels()」で、その全画素をスクリーンに反映させている。void setup() { size(200, 200); background(255); smooth(); noStroke(); } void draw(){ for(int i = 0; i < 100; i++) if (frameCount % 5 == 0) { translate(random(100), 100); rotate(radians(frameCount * 2 % 360)); rect(0, 0, 10, 20); } fill(frameCount * 20 % 255,random(100),100); } これらを合体させるためには、PImageの中身に、 毎回のループで「A」を描画して加えていき、 それを例えば「loadPixels()」に対応した「savePixels()」というメソッドでもあれば、 「loadPixels()して描画を加えてsavePixels()した上で、 これを改めてloadPixels()で読み出してピクセルごとの画像処理を行ってupdatePixels()して表示する」 という作戦が立てられる。 ・・・ところが、「savePixels()」などというものは、Processingのリファレンスを探しても、どこにも無いのであった(^_^;)。PImage img; void setup() { size(323, 450); img = loadImage("love.jpg"); } void draw(){ loadPixels(); for (int x = 0; x < img.width; x++) { for (int y = 0; y < img.height; y++ ) { int loc = x + y*img.width; float r = red (img.pixels[loc]); float g = green (img.pixels[loc]); float b = blue (img.pixels[loc]); float distance = dist(x,y,mouseX,mouseY); float adjustBrightness = (50-distance)/50; r *= adjustBrightness; g *= adjustBrightness; b *= adjustBrightness; r = constrain(r,0,255); g = constrain(g,0,255); b = constrain(b,0,255); color c = color(r,g,b); pixels[loc] = c; } } updatePixels(); } しかし、プログラミングの世界というのは、「仕様によって出来ない」ことは無い、というのが鉄則である。 出来ないのは「プログラミングの能力が足りない」「ちゃんと理解できていないので使いこなせない」からであることがほとんどである。 そこで、 Processingのリファレンス をあれこれ探しながら、ヒントを探してみた。 まず、「B」が動いているので、以下の例のような PImage は使うことになるだろう。
「loadImage()」だと既存の静止画を読み込むだけのようだが、 以下の例のような createImage() を使えば、画素単位での描画は可能なのである。PImage b; b = loadImage("laDefense.jpg"); image(b, 0, 0); PImage img = createImage(66, 66, RGB); img.loadPixels(); for (int i = 0; i < img.pixels.length; i++) { img.pixels[i] = color(0, 90, 102); } img.updatePixels(); image(img, 17, 17);
上のモードは「RGB」で画素単位の3色を指定するだけだったが、以下のように第4パラメータでアルファ値まで記述できるようだ。
PImage img = createImage(66, 66, ARGB); img.loadPixels(); for (int i = 0; i < img.pixels.length; i++) { img.pixels[i] = color(0, 90, 102, i % img.width * 2); } img.updatePixels(); image(img, 17, 17); image(img, 34, 34);
画素単位での描画としては、以下のような pixels[] で指定できるようである。
color pink = color(255, 102, 204); loadPixels(); for (int i = 0; i < (width*height/2)-width/2; i++) { pixels[i] = pink; } updatePixels();
このあたりの道具立てを整理した上で、この「Processing日記」の最初のあたりを眺めてみると、 なんと「Processing(1)」に、以下のようなサンプルがあったのを発見した。 これは、「filter」の機能を、画素単位で処理した例として紹介されていたが、 まさにここでの使い方として活用できるサンプルなのだった。
そこでまずは実験として、背景となるアニメーション画面を簡単に作って、 これを毎回呼び出して「窓あけ画面」で塗りつぶす、という以下のProcessingスケッチを作ってみた。 あくまで第一段階であるが、アプレットとしては このようになった。PImage source; PImage destination; void setup() { size(480, 640); source = loadImage("satou.jpg"); destination = createImage(source.width, source.height, RGB); } void draw() { float threshold = 127; source.loadPixels(); destination.loadPixels(); for (int x = 0; x < source.width; x++) { for (int y = 0; y < source.height; y++ ) { int loc = x + y*source.width; if (brightness(source.pixels[loc]) > threshold) { destination.pixels[loc] = color(255); } else { destination.pixels[loc] = color(0); } } } destination.updatePixels(); image(destination,0,0); } PImage img; void setup() { size(500, 500); img = createImage(500, 500, RGB); for (int i = 0; i < img.pixels.length/2; i++) { img.pixels[i] = color(0, 150, 102); } for (int i = img.pixels.length/2; i < img.pixels.length; i++) { img.pixels[i] = color(200, 100, 32); } image(img, 0, 0); } void draw(){ loadPixels(); for (int x = 0; x < img.width; x++) { for (int y = 0; y < img.height; y++ ) { int loc = x + y*img.width; float r = red (img.pixels[loc]); float g = green (img.pixels[loc]); float b = blue (img.pixels[loc]); float distance = dist(x,y,mouseX,mouseY); float adjustBrightness = (100-distance)/100; r *= adjustBrightness; g *= adjustBrightness; b *= adjustBrightness; r = constrain(r,0,255); g = constrain(g,0,255); b = constrain(b,0,255); color c = color(r,g,b); pixels[loc] = c; } } updatePixels(); }
そして次のステップとして、メインループの「draw()」から呼ばれる関数として「updateGraphics()」というのを作って 「窓あけ」部分とは分離して、ここに、10フレームごとにランダムに色塗りする処理を入れてみた。 一部、無駄な部分も削ぎ落としてある。アプレットとしては このようになった。
PImage img; void setup() { size(500, 500); img = createImage(500, 500, RGB); for (int i = 0; i < img.pixels.length; i++) { img.pixels[i] = color(0255,255,255); } } void updateGraphics(){ if (frameCount % 10 != 0) return; int cr = int(random(255)); int cg = int(random(255)); int cb = int(random(255)); for (int i = 0; i < img.pixels.length/2; i++) { img.pixels[i] = color(cr, cg, 127); } for (int i = img.pixels.length/2; i < img.pixels.length; i++) { img.pixels[i] = color(127, cg, cb); } } void draw(){ updateGraphics(); loadPixels(); for (int x = 0; x < img.width; x++) { for (int y = 0; y < img.height; y++ ) { int loc = x + y*img.width; float r = red (img.pixels[loc]); float g = green (img.pixels[loc]); float b = blue (img.pixels[loc]); float distance = dist(x,y,mouseX,mouseY); float adjustBrightness = (100-distance)/100; r *= adjustBrightness; g *= adjustBrightness; b *= adjustBrightness; r = constrain(r,0,255); g = constrain(g,0,255); b = constrain(b,0,255); color c = color(r,g,b); pixels[loc] = c; } } updatePixels(); }
これで、あとはこの「updateGraphics()」の中に、刻々と新しい矩形をランダムに描画していくアルゴリズムを記述すればよい。 ただし、ここに最後の壁が待っている。 「A」の例では、矩形の描画において、まず「smooth()」と「noStroke()」を規定した上で、矩形そのものは単純に
rect(0, 0, 10, 20); としか呼び出していない。 ランダムな要素としては、どこに描画するかをtranslate(random(100), 100); と指定し、さらに矩形の角度をrotate(radians(frameCount * 2 % 360)); で指定している。 これはスクリーンを対象とした描画処理なので、今回は使えないのである。 ちなみに矩形の色はfill(frameCount * 20 % 255,random(100),100); と指定している。ここでは、本来であれば、正攻法として 「画面内のランダムな場所を選ぶ」 「ランダムな矩形の回転角度を選ぶ」 「画素として10*20の矩形と合同になる画素を計算する」 「その画素に色をピクセル単位で指定する」 というのを回せばいいことになる。 しかし、これはあまりに美しくない気がする(^_^;)。 もう少しで光明が見えて来るところであるが、 ここでタイムリミットとなった。 お盆なので、運転手とか、お仕事もあるのである。 続きは明日にでもやってみよう。
2011年8月14日(日)
お盆で邪魔が入らないのはいいが、暑い(^_^;)。 夏バテ気味なので、だらだらぼちぼち、進めている。 清水誠一郎クンが後輩を前にして行った特別講義の記録ムービーも無事に完成したので、 これはおいおい、学生に紹介していこう。昨日の進展を受けて、マウスに反応する「窓開け」の関数は出来たので、 あとは背景をライブ生成する「updateGraphics()」だけである。 とりあえず、メインループの「draw()」から呼び出す「窓開け」の部分を「madoake()」として切り分けた。 そして以下は、その「updateGraphics()」の中で、まずは水平な矩形をランダムな位置、ランダムな色で描画している。 建部さんの元プログラムより描画領域が大きくなり、その一方で窓が小さいので、描画を5回に1回としていた部分はコメントアウトした。 アプレットとしては このようになった。
PImage img; void setup() { size(500, 500); img = createImage(500, 500, RGB); for (int i = 0; i < img.pixels.length; i++) { img.pixels[i] = color(255,255,255); } } void draw(){ updateGraphics(); madoake(); } void updateGraphics(){ // if (frameCount % 5 != 0) return; int x_p = int(random(480)); int y_p = int(random(490)); for (int x = x_p; x < x_p+20; x++) { for (int y = y_p; y < y_p+10; y++ ) { int loc = x + y*img.width; color c = color(frameCount * 20 % 255,random(100),100); img.pixels[loc] = c; } } } void madoake(){ loadPixels(); for (int x = 0; x < img.width; x++) { for (int y = 0; y < img.height; y++ ) { int loc = x + y*img.width; float r = red (img.pixels[loc]); float g = green (img.pixels[loc]); float b = blue (img.pixels[loc]); float distance = dist(x,y,mouseX,mouseY); float adjustBrightness = (100-distance)/100; r *= adjustBrightness; g *= adjustBrightness; b *= adjustBrightness; r = constrain(r,0,255); g = constrain(g,0,255); b = constrain(b,0,255); color c = color(r,g,b); pixels[loc] = c; } } updatePixels(); }
これでも十分、マウスに反応する「窓」の中で、刻々とグラフィクスが増殖しているので、 まずまず企画の意図に近付いていると思うが、全ての矩形が水平というのもなんか癪である(^_^;)。 かといって、以下のようなアルゴリズムにすれば、簡単に確実に出来るけれども、ダサい感じがして、作りたくない。
このアルゴリズムに気が乗らない理由は、矩形が傾いた時に、画素の荒さで飛ばすのを避ける部分があまり奇麗でないからである。 画素抜けを避けるために、テストする分解能を細かくすればいいのだが、それだと最終的に何度も同じ画素に同じ色をプロットすることになるので、まぁコンピュータパワーがあるからいいとはいえ、気に入らない。 1回の矩形追加で描画する領域(直径の二乗が100+400=500、つまり直径10√5ピクセル)の円に外接する正方形ぐらいの範囲に対して、その領域の全画素について、無駄に重複させずに「1度だけ」処理するようにしたいのである。
- ランダムに配置場所(矩形の起点)を決める → P(x,y)とする
- ランダムに色を決める → color(R,G,B)とする
- ランダムに回転角度を決める → θとする
- P(x,y)からθの角度で直線を延ばし、長さが20になるまでの画素にcolor(R,G,B)をプロット
- P(x,y)から(θ+π/2)の角度で直線を延ばし、長さが10になるまでの画素を決める → Q1,・・・,Qnとする
- Q1からθの角度で直線を延ばし、長さが20になるまでの画素にcolor(R,G,B)をプロット
- Q2からθの角度で直線を延ばし、長さが20になるまでの画素にcolor(R,G,B)をプロット
- ・・・
- Qnからθの角度で直線を延ばし、長さが20になるまでの画素にcolor(R,G,B)をプロット
そこで、まずは以下のように、「draw()」の部分をコメントアウトして、つまり初期化処理の「setup()」だけで止まるようにして、 ここに新しくもう1つのPImageとして「msk」を定義し、スクリーン全体を25ピクセルの正方形領域に分割して、 その中央に「20*10ピクセル」の矩形を、少しずつ回転角度を変えて描画してみた。
PImage img; PImage msk; void setup(){ size(500, 500); background(255); smooth(); noStroke(); rectMode(CENTER); fill(0,0,0); for (int x = 13; x < 490; x += 25) { for (int y = 13; y < 490; y += 25 ) { pushMatrix(); translate(x, y); rotate(radians(((y-13)*25+x-13)/68.5)); rect(0, 0, 20, 10); popMatrix(); } } msk = createImage(500, 500, RGB); for (int x = 0; x < 500; x++) { for (int y = 0; y < 500; y++ ) { int loc = x + y*msk.width; msk.pixels[loc] = get(x, y); } } img = createImage(500, 500, RGB); for (int i = 0; i < img.pixels.length; i++) { img.pixels[i] = color(255,255,255); } } /* void draw(){ updateGraphics(); madoake(); } */ void updateGraphics(){ int x_p = int(random(480)); int y_p = int(random(490)); for (int x = x_p; x < x_p+20; x++) { for (int y = y_p; y < y_p+10; y++ ) { int loc = x + y*img.width; color c = color(frameCount * 20 % 255,random(100),100); img.pixels[loc] = c; } } } void madoake(){ loadPixels(); for (int x = 0; x < img.width; x++) { for (int y = 0; y < img.height; y++ ) { int loc = x + y*img.width; float r = red (img.pixels[loc]); float g = green (img.pixels[loc]); float b = blue (img.pixels[loc]); float distance = dist(x,y,mouseX,mouseY); float adjustBrightness = (100-distance)/100; r *= adjustBrightness; g *= adjustBrightness; b *= adjustBrightness; r = constrain(r,0,255); g = constrain(g,0,255); b = constrain(b,0,255); color c = color(r,g,b); pixels[loc] = c; } } updatePixels(); }
つまり、矩形を描画する際に、そこでいちいち回転角度のランダムを呼んでは矩形の画素を計算するのではなくて、 この20*20=400個のいずれかの回転パターンをマスクとして、その画素にだけ色を塗ろう、という作戦である。 そして完成したのが、以下である。 アプレットとしては このようになった。 建部さん、これならOKでしょう(^_^)。
PImage img, msk; void setup(){ size(500, 500); background(255); smooth(); noStroke(); rectMode(CENTER); fill(0,0,0); for (int x = 13; x < 490; x += 25) { for (int y = 13; y < 490; y += 25 ) { pushMatrix(); translate(x, y); rotate(radians(((y-13)*25+x-13)/68.5)); rect(0, 0, 20, 10); popMatrix(); } } msk = createImage(500, 500, RGB); img = createImage(500, 500, RGB); for (int x = 0; x < 500; x++) { for (int y = 0; y < 500; y++ ) { int loc = x + y*msk.width; msk.pixels[loc] = get(x, y); img.pixels[loc] = color(255,255,255); } } } void draw(){ updateGraphics(); madoake(); } void updateGraphics(){ int x_p = int(random(475)); int y_p = int(random(475)); int s_x = int(random(20)); int s_y = int(random(20)); color c = color(frameCount * 20 % 255,random(100),100); for (int x = 0; x < 25; x++) { for (int y = 0; y < 25; y++ ) { int loc = (x_p+x) + (y_p+y)*img.width; int chk = (s_x*25+x) + (s_y*25+y)*msk.width; if( red(msk.pixels[chk]) == 0){ img.pixels[loc] = c; } } } } void madoake(){ loadPixels(); for (int x = 0; x < img.width; x++) { for (int y = 0; y < img.height; y++ ) { int loc = x + y*img.width; float r = red (img.pixels[loc]); float g = green (img.pixels[loc]); float b = blue (img.pixels[loc]); float distance = dist(x,y,mouseX,mouseY); float adjustBrightness = (100-distance)/100; r *= adjustBrightness; g *= adjustBrightness; b *= adjustBrightness; r = constrain(r,0,255); g = constrain(g,0,255); b = constrain(b,0,255); color c = color(r,g,b); pixels[loc] = c; } } updatePixels(); }
区切りとして「Part4」をここまでとして、次は「Part5」として再開することにしよう。
Processing日記(5)
「日記」シリーズ の記録