アートを切り替えて不思議に魅せる技法について(Processing Advent Calendar 2020 Day1)

本記事は,Processing Advent Calendar 2020の1日目の記事です.10周年だそうで,おめでとうございます!

目次

2020振り返り・Processingコミュニティについて

せっかくのアドカレの記事なので,簡単な振り返り(という名の過去作の宣伝)と,コミュニティ関係の話を少しだけ書きます.

今年もアート制作という形でProcessingにたくさん触れました.今年は20個ほど作品を作っていて,今までの分と合わせてGitHubで公開しています.GitHubにはそれなりに厳選して載せているので,つぶやきProcessingなどの作品も含めるともっと作ったように思います.

github.com

また,コミュニティとの関わりという点では,PCJ ZINEのVol.0に寄稿させていただきました.Processingがオアシス的存在であることを熱弁しております.このZINEの発行について,関係者の皆様に感謝申し上げます.

pcdtokyo.booth.pm

このように,今年は無理なく作品制作もできたし,少しだけコミュニティにも関わることができたしで,非常に楽しいProcessingライフでした.(まだ12月に入ったばかりで気が早いかもしれませんが,)来年も楽しみたいですね.

はじめに

さて,本記事では,「アートを適切なタイミングで切り替えて不思議な感じにする技法」について書きます.イメージとしては,次のようなものです(両方自作です).

f:id:gotutiyan:20201119175650g:plain

f:id:gotutiyan:20201119175751g:plain

うまくやっているものほど見ていて不思議な気持ちになれるので,個人的に好きなパターンの一つです.本記事では,言語としてProcessingを用いて,実装をメインに紹介します.

雛形

全体の構造はこんな感じです.以下では,2種類のアートを90フレーム単位で切り替えます.

void setup(){}
void draw(){
  if(timeCount%180 < 90) アート1();
  else アート2();
}

timeCountはProcessingのシステム変数で,開始時点からのフレーム数を取得できます.また,アート1とアート2は周期性のあるアートを描画する関数で,両者がどこかで同じ絵柄を持つように作ります.あまり抽象化していませんが,上の例だと90フレームごとに切り替わることになるのです.

簡単な実例で!

市松模様で作るアート

では,簡単な実例で詳細を見てみましょう.以下のような,白黒タイルが回転するものを作ります.白と黒は市松模様のように配置します.

f:id:gotutiyan:20201119182038g:plain

まず,雛形を作ります.

void setup(){
  size(500,500);
}

void draw(){
  if(frameCount%180 < 90)art1();
  else art2();
}

void art1(){
  
}

void art2(){
  
}

この例では,「黒のタイルが回転するアート」と,「白のタイルが回転するアート」が切り替わることになります.この2つをそれぞれart1()art2()に書くわけです.今回は,黒タイル側をart1()に,白タイル側をart2()に書くことにします.

前準備

パラメータの準備だけサクッとしましょう.一行/一列に並べるタイルの数をNで表して,タイル一辺の長さをwidth/Nで計算します.それから,回転させる都合上,rectMode(CENTER)を設定します.

int N=10, len;
void setup(){
  size(500,500);
  rectMode(CENTER);
  len = width/N;
}

「黒タイル側」の実装

黒側は,背景を白にして,黒の四角を描画します.市松模様に並べるためには,四角を横にも縦にも一個飛ばしで並べる必要がありますが,これは変数i,jの2重ループを回すとすれば,i+jの偶奇に注目すると簡単に実装できます.黒は奇数のときに書くことにしましょう.

また,四角を回転させて描画する処理は,

  1. 座標軸の原点を,translate()で四角の中心に持ってくる

  2. 座標軸を,rotate()で回す

  3. 原点に四角をrect()で描画する

  4. 座標軸をresetMatrix()で元に戻す

という4ステップで実装できるため,これを全ての四角について行います.

回す角度については,frameCountラジアンに変換する必要があります.今回はmap()を使って,[0,89]の範囲を[0,PI/2]の範囲に変換します.[0.89]は切り替える周期を90フレームにしていることから来ていて,[0,PI/2]は今回のアートが90度回るたびに切り替わることから来ています.

void art1(){
  background(255);
  fill(0);
  float rad = map(frameCount%90, 0, 89, 0, PI/2.0);
  for(int i=0;i<N+1;i++){
   for(int j=0;j<N+1;j++){
     if((i+j)%2 == 1){
       translate(len*i, len*j);
       rotate(rad);
       rect(0, 0, len, len);
       resetMatrix();
     }
   }
  }
}

これで,黒タイル側は完了です.

「白タイル側」の実装

白側は,背景を黒にして,白の四角を描画します.また,ループ変数のi+jが偶数のときに書くことにします.ロジックは黒タイル側と同じです.

void art2(){
  background(0);
  fill(255);
  float rad = map(frameCount%90, 0, 89, 0, PI/2.0);
  for(int i=0;i<N+1;i++){
   for(int j=0;j<N+1;j++){
     if((i+j)%2 == 0){
       translate(len*i, len*j);
       rotate(rad);
       rect(0, 0, len, len);
       resetMatrix();
     }
   }
  }
}

以上で,全体としては以下のようになり,完成です!いかがでしょうか.正直なところ,両方とも処理が似ているので,本来は一つの関数にして引数で調整するべきですが,今回はあえて冗長に書いています.

int N=10, len;
void setup(){
  size(500,500);
  rectMode(CENTER);
  len = width/N;
}

void draw(){
  background(0);
  if(frameCount%180 < 90)art1();
  else art2();
}

void art1(){
  background(255);
  fill(0);
  float rad = map(frameCount%90, 0, 89, 0, PI/2.0);
  for(int i=0;i<N+1;i++){
   for(int j=0;j<N+1;j++){
     if((i+j)%2 == 1){
       translate(len*i, len*j);
       rotate(rad);
       rect(0, 0, len, len);
       resetMatrix();
     }
   }
  }
}

void art2(){
  background(0);
  fill(255);
  float rad = map(frameCount%90, 0, 89, 0, PI/2.0);
  for(int i=0;i<N+1;i++){
   for(int j=0;j<N+1;j++){
     if((i+j)%2 == 0){
       translate(len*i, len*j);
       rotate(rad);
       rect(0, 0, len, len);
       resetMatrix();
     }
   }
  }
}

拡張:切り替わる速さの調節

今回の例では,90フレームごとに切り替えています.60fpsの環境なら1.5秒ごとに切り替わることになりますが,作品によっては速すぎる場合があります.この時は,切り替えるフレーム数を増やせば良いです.

一つの案としては,定数として切り替わるフレーム数を設定する方法があります.これは(適当に付けた)FRAME_UNITという変数を使って,

int FRAME_UNIT = 90;

// draw()では
if(frameCount%(2*FRAME_UNIT) < FRAME_UNIT)art1();
else art2();

//フレーム数 → 角度への変換は
float rad = map(frameCount%FRAME_UNIT, 0, FRAME_UNIT-1, 0, PI/2.0);

という感じで書けます.こうすると,FRAME_UNITを変えるだけで速さ調整ができて,便利です.(もちろん,mapメソッドのPI/2.0というところも,作品によって変わるかと思います.)

考察:この技法はどのようなアートに適用できるか?

ここではこの技法に関する個人的な考察を書きます.間違っているかもしれません.

この技法は,前面の模様と背景の模様の役割を入れ替えながら,前面の模様に対して何かしらの処理を行う技法です.例えば,市松模様の例では,前面の模様のつもりでタイルを市松模様に並べると,その背景も自然と市松模様になるので,これらを一定の周期で入れ替えることで成立しました.他にも,円を敷き詰めた場合には,その背景はアステロイド曲線で囲まれるような図形を並べた模様になるはずです.このような性質から,前面の模様と背景の模様が共に実装可能でなければいけません.例えば,リサージュ曲線で囲まれる領域を敷き詰めたような模様に対しては,その背景の模様を実装することは難しそうです.

また,色についても制約があると思っていて,切り替える瞬間の模様は2色で構成しないと成立しません.この技法による作品がなぜ不思議なのかというと,今まで図形だと思っていたところがいつの間にか背景になるからです.言い換えると,個々に独立していたはずの領域が連続的になるから,とも言えます.このような性質から,例えば,切り替える瞬間の模様に3色を使ってしまうと,ある1色が前面の役割として動いている間にも残りの2色が独立した領域を作ってしまって,不思議には見えません.ただし,3次元空間では,立方体などのように複数の面を備えたオブジェクトを扱えるので,周期の途中で一時的に他の色を出現させることは可能です.しかしこの場合でも,やはり切り替える瞬間の模様は2色だけにしないと成立しないと思います(後述の参考3のリンクを参照).

元々,僕がこの技法を知ったのは,Twitterでdave(@beesandbombs)さんの作品を見たのがきっかけでした.daveさんの作品ではこの技法がよく使われており,学ぶものが多いです.参考として,以下に3作品ほど,ツイートのリンクを貼っておきます.

  • 参考1:2次元

おわりに

今回は,アートを切り替えて不思議に魅せる技法について紹介しました.もっと良い実装方法,関連する話題などありましたら教えていただけますと幸いです.

ありがとうございました.