この記事では書籍「世界一美しい錯視アート」に掲載されている「ニンジン」という作品をProcessingを使って再現してみます。
ニンジン
今回作成した錯視アート「ニンジン」です。
書籍では「ガクガクして見える。」と解説されています。自分としては、中心から何かが広がっていくようなもわもわっとした印象があるのですが、いかがでしょうか。
描き方のポイント
背景はオレンジ色のグラデーション
背景は円形にオレンジ色でグラデーションしたものになっています。円形のグラデーションを描くのは結構簡単です。詳細は別記事「円形のグラデーションを描く」を見てください。
台形を中心に向けて縮小して並べる
この作品「ニンジン」では、中心から外側に向けて白色と黒色の線分が交互に伸びていく線が描かれています。ポイントとしては、外側から見ていくと、白色と黒色の線分の長さが中心に向かってどんどん縮小していくというところです。このような、縮小していく線分を描くのは、等比数列の計算を行うことで実現することができます。詳細は別記事「縮小していく線分を描く」を見てください。
なお、ここまで線分と書きましたが、実際は中心に向かって長さだけではなく、幅も縮小していくので、「線分」というより「台形」と考えた方がよいです。実際に描いてみると下図のようになります。
プログラムコード
今回作成した錯視アート「ニンジン」のプログラムコードを載せておきます。
void setup() {
size(1000, 1000);
noStroke();
translate(width/2, height/2);
float diameter = width; // 一番外側の円の直径
color col_out = color(223,108,49); // 円形のグラデーションの一番外側の色
color col_in = color(258,208,0); // 円形のグラデーションの一番内側の色
grdCircle(0, 0, diameter, col_out, col_in); // 円形のグラデーションを描く
float line_len = width/2.0; // 線全体の長さ(収束点からの長さの初期値)
float segment_len_initial = width/30.0; // 線分の長さの初期値
float ratio = 1.0 - segment_len_initial / line_len; // 線分の縮小比率
float th1 = radians(0.3); // 収束点での角度
float line_num = 36; // 中心から伸びていく図形の数
for(int i=0; i<line_num; i++){
rotate(radians(360.0/line_num));
drawTrapeziums(line_len, segment_len_initial, ratio, th1);
}
}
void grdCircle(
float x, // 円の中心位置のx座標
float y, // 円の中心位置のy座標
float d, // 一番外側の円の直径
color col_out, // 円形のグラデーションの一番外側の色
color col_in // 円形のグラデーションの一番内側の色
) {
float c = 100;
for (int i=0; i<c; i++) {
color col = lerpColor(col_out, col_in, i/c); // 円の色
float dd = lerp(d, 0.0, i/c); // 円の直径
noStroke();
fill(col);
ellipse(x, y, dd, dd); // 円を描く
}
}
void drawTrapeziums(
float line_len, // 線全体の長さ(収束点からの長さの初期値)
float segment_len_initial, // 線分の長さの初期値
float ratio, // 線分の縮小比率
float theta // 収束点での角度
){
float x; // 収束点からの長さ
float segment_len; // 線分の長さ
x = line_len;
segment_len = segment_len_initial;
float x1, y1, x2, y2, x3, y3, x4, y4;
int i = 0;
while(i<100){
// 台形を描く
x1 = x * cos(theta);
y1 = x * sin(theta);
x2 = (x - segment_len) * cos(theta);
y2 = (x - segment_len) * sin(theta);
x3 = x2;
y3 = -y2;
x4 = x1;
y4 = -y1;
noStroke();
if (i % 2 == 0){
fill(0,0,0);
} else {
fill(255,255,255);
}
pushMatrix();
beginShape();
vertex(x1,y1);
vertex(x2,y2);
vertex(x3,y3);
vertex(x4,y4);
endShape(CLOSE);
popMatrix();
x = x - segment_len; // 収束点からの長さを更新
segment_len = segment_len * ratio; // 線分の長さを更新
i++;
}
}