1. ホーム
  2. 錯視アート
  3. ピンクチューブ

この記事では書籍「世界一美しい錯視アート」に掲載されている「ピンクチューブ」という作品をProcessingを使って再現してみます。

ピンクチューブ

今回作成した錯視アート「ピンクチューブ」です。

ピンクチューブ

書籍では「ピンクのチューブの中を勢いよく何かが流れていくように見える。」と解説されています。何か白い光るものが走っているように見えますが、いかがでしょうか。

描き方のポイント

螺旋を描く

「ピンクチューブ」では螺旋を用いています。特に外側に行くにつれて拡がりの幅が大きくなっていますので、対数螺旋になります。また、螺旋の回転方向は黒い螺旋は中心から反時計回りに広がっていく形状で、ピンクの螺旋は中心から時計回りに広がっていく形状をしています。あと、外側に行くにつれて螺旋の線幅が大きくなっています。このような螺旋の描き方の詳細は別記事「螺旋を描く」を見てください。

時計回りに広がる対数螺旋の例

重なった部分の色をどう表現していくか

この作品「ピンクチューブ」では、白色の背景に黒色の螺旋を描き、その上にピンク色の螺旋を描いています。そのとき、黒色の螺旋とピンク色の螺旋が重なった部分は少し濃いピンク色になっています。この重なった部分の色をどう表現していくかがポイントとなります。

blendModeを使う

processingでは、色が重なる部分においてどのように色を扱うかをblendMode関数を用いて指定することができます。今回はこのblendMode関数を用いて重なった部分の色を調整しています。以下、順番に見ていきます。

①白色の背景に黒色の螺旋を描く

まず、白色の背景を準備し、その上に黒色の螺旋を描いていきます。

黒色の螺旋を描く
②blendMode(DARKEST)を指定して薄いピンク色の螺旋を描く

次に、薄いピンク色の螺旋を重ねて描いていきます。ただ、何も指定せずに薄いピンク色の螺旋を重ねると、そのまま上書きされてしまいます。

失敗例:色が上書きされてしまう

そこで、薄いピンク色の螺旋を描く前にblendMode(DARKEST)を指定します。このblendMode(DARKEST)を指定することで、色が重なった部分は暗い色の方が優先して描かれることになります。具体的には、

  • 黒色と薄いピンク色が重なった部分 → 黒色が残る
  • 白色と薄いピンク色が重なった部分 → 薄いピンク色が残る

となります。

blendMode(DARKEST)を指定して描いた場合
③blendMode(LIGHTEST)を指定して濃いピンク色の螺旋を描く

最後に、 濃いピンク色の螺旋を重ねて描いていきます。 濃いピンク色の螺旋を描く前にblendMode(LIGHTEST)を指定します。このblendMode(LIGHTEST)を指定することで、色が重なった部分は明るい色の方が優先して描かれることになります。具体的には、

  • 黒色と濃いピンク色が重なった部分 → 濃いピンク色が残る
  • 薄いピンク色と濃いピンク色が重なった部分 → 薄いピンク色が残る

となります。その結果、「ピンクチューブ」が完成します。

ピンクチューブ完成!

プログラムコード

今回作成した錯視アート「ピンクチューブ」のプログラムコードを載せておきます。

void setup() {
  size(1000, 1000);
  background(255,255,255);
  
  noStroke();
  translate(width/2, height/2);

  int black_spiral_num = 50; // 黒色の螺旋の数
  float black_spiral_width = radians(360.0/2.0/black_spiral_num); // 黒色の螺旋の幅
  float black_base = 1.5; // 黒色の螺旋の動径の大きさを定める指数関数の底
  float black_rotation_direction = -1.0; // 黒色の螺旋は反時計回り
  
  // 黒色の螺旋を描く
  fill(0,0,0);
  for(int i=0; i<black_spiral_num; i++){
    rotate(2.0*black_spiral_width);
    drawLogSpiral(black_base, black_spiral_width, width, black_rotation_direction);
  }

  int pink_spiral_num = 2; // ピンクの螺旋の数
  float pink_spiral_width = radians(360.0/2.0/pink_spiral_num); // ピンク色の螺旋の幅
  float pink_base = 1.2; // ピンク色の螺旋の動径の大きさを定める指数関数の底
  float pink_rotation_direction = 1.0; // ピンク色の螺旋は時計回り
  
  // 薄いピンク色で螺旋を描く
  fill(235,110,159); 
  blendMode(DARKEST); 
  for(int i=0; i<pink_spiral_num; i++){
    rotate(2.0*pink_spiral_width);
    drawLogSpiral(pink_base, pink_spiral_width, width, pink_rotation_direction);
  }
  
  // 濃いピンク色で螺旋を描く
  fill(228,0,127);
  blendMode(LIGHTEST);  
  for(int i=0; i<pink_spiral_num; i++){
    rotate(2.0*pink_spiral_width);
    drawLogSpiral(pink_base, pink_spiral_width, width, pink_rotation_direction);
  }
}

void drawLogSpiral(
  float base, // 動径の大きさを定める指数関数の底
  float spiral_width, // 螺旋の幅を決める角度
  float scalar, // 螺旋の最大半径
  float rotate_direction // 回転方向 時計回りの時1.0、反時計回りの時-1.0
  ){
  float STEP = 2 * PI * 0.01; // 曲線の精度
  int STEP_num = 2000;
  float theta = 0;
  pushMatrix();
  beginShape();
  for(int i=0; i<STEP_num; i++){
    vertex(scalar * pow(base, theta) * cos(rotate_direction * theta), scalar * pow(base, theta) * sin(rotate_direction * theta));
    theta -= STEP;
  }
  for(int i=0; i<STEP_num-rotate_direction * spiral_width/STEP; i++){// 終端をそろえるためにステップ数を調整
    theta += STEP;
    vertex(scalar * pow(base, theta) * cos(rotate_direction * theta + spiral_width), scalar * pow(base, theta) * sin(rotate_direction * theta + spiral_width ));
  }
  endShape();
  popMatrix();
}

コメントを残す