この記事では書籍「世界一美しい錯視アート」に掲載されている「太陽2011」という作品をProcessingを使って再現してみます。
太陽2011
今回作成した錯視アート「太陽2011」です。
書籍では「放射状方向にガクガクして見える。」と解説されています。自分としては、中心から何かが広がっていくような印象があります。
描き方のポイント
円形のグラデーションは一工夫
この作品「太陽2011」では、外側から中心に向かって薄い青色からピンク色に変化していく円形のグラデーションが背景として使われています。「円形のグラデーション」については別記事「円形のグラデーションを描く」で紹介しています。これを利用して描いてみると、下図のようになります。
ただ、書籍での「太陽2011」をよく見ると、背景のグラデーションはピンク色の部分がもう少し外側に広く描かれているようです。そこで、円形のグラデーションを描くときに一工夫しています。それは、円の直径を線形補間する際に、
color col = lerpColor(col_out, col_in, i/c); // 円の色
float dd = lerp(d, 0.0, i/c); // 円の直径
のように円の色と円の直径を同じ補間係数\(i/c\)で変化させるのではなく、ここでは
color col = lerpColor(col_out, col_in, i/c); // 円の色
float dd = lerp(d, 0.0, pow(i/c,2.0)); // 円の直径
として円の直径の補間係数を\((i/c)^2\)とすることで、円の色の変化に比べて円の直径の変化を遅くしてみました。 その結果が以下の図です。少しピンク色の領域が大きくなったことがわかると思います。
楕円を中心に向けて縮小させて並べる
この作品「太陽2011」では、外側から中心に向けて白色と黒色の楕円が交互に現れて縮小していく図形が描かれています。このような図形は別記事「縮小していく線分を描く」に書いたことを応用すれば実現できます。つまり、外側から見ていくと、白色と黒色の楕円の大きさが中心に向かってどんどん縮小していきますが、このような図形は、等比数列の計算を行うことで実現することができます。実際に描いてみると下図のようになります。
この楕円の列を左側の点を中心にして等間隔に40本描いていくと、「太陽2011」のような図形を描くことができます。
中心に赤色で塗りつぶされた円を描く
最後に、中心の位置に赤色で塗りつぶされた円を描きます。この円の直径はキャンバスサイズの10分の1の値に設定しました。
プログラムコード
今回作成した錯視アート「太陽2011」のプログラムコードを載せておきます。
void setup() {
size(1000, 1000);
noStroke();
translate(width/2, height/2);
background(#1190F5); // 背景は薄い青色
color col_out = color(#A6E9FC); // 円形のグラデーションの一番外側の色
color col_in = color(#F52CDE); // 円形のグラデーションの一番内側の色
grdCircle_slow(0.0, 0.0, width, col_out, col_in); // 円形のグラデーションを描く
float line_len = width/2.0 - width/40.0; // 一番外側の楕円の位置(収束点からの長さの初期値)
float segment_len_initial = width/50; // 一番外側の楕円の長径の初期値
float ratio = 1.0 - segment_len_initial / line_len; // 長径の縮小比率
int line_num = 40; // 中心から伸びていく図形の数
float th1 = radians(360.0/line_num); // 中心での角度
for(int i=0; i<line_num; i++){
rotate(th1);
drawEllipses(line_len, segment_len_initial, ratio, radians(1.0));
}
// 中心に赤色で塗りつぶされた円を描く
fill(255,0,0);
ellipse(0, 0, width/10, width/10);
save("sun2011.jpg");
}
// 円形のグラデーションを描く関数(円の直径の縮小を遅くしたもの)
void grdCircle_slow(
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, pow(i/c,2.0)); // 円の直径
noStroke();
fill(col);
ellipse(x, y, dd, dd); // 円を描く
}
}
// 楕円を放射線上に並べていく関数
void drawEllipses(
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 y1;
int i = 0;
while(i<100){
y1 = x * sin(theta); // 楕円の短径
noStroke();
if (i % 2 == 0){
fill(0,0,0); // 黒色
} else {
fill(255,255,255); // 白色
}
// 楕円を描く
ellipse(x, 0.0, 2.0*segment_len, 2.0*y1);
x = x - segment_len; // 収束点からの長さを更新
segment_len = segment_len * ratio; // 線分の長さを更新
i++;
}
}