Christmas Tree Illusion
Motivation
河原先生がツイットされてたのを見てウズウズ。
元動画は削除されてもう見られないようなのですが、回転するクリスマスツリー模様が、意図はしていないのでしょうが回転方向が、時計回り、反時計回り、両方の3パターンに切り替わって見える錯視が起こっていました。
MATLABで作ろう!
これはMATLABで作ってみるしかない!
関数としては単なるスパイラルなので、高さ方向に半径を変えながら円を3Dプロットして行けば良さそうだということがすぐ分かります。
回転は、開始角を変えていけば良いですね。
「すぐできそう!」と思ったのですが、「それっぽく」見える調整には試行錯誤が必要でした。(^-^;
シミュレーション結果
早速結果を。
上から見るとこのようにずっと時計回りに回しているだけなのですが、YouTubeのヤツと同じ錯視が起こります。
コツとしては、目線を上下すると反転しやすく、裏で回ってるように見える☆を注視するとそれが前に出てきて両回転入り乱れるようです。
MATLABコード 1
we = true; % file write enable
N = 4;
filename = ['illusionTree' num2str(N) '.gif']
close all
f = figure(1);
f.Color = [1 1 1];
th0 = 0:pi/20:2*pi*1.5;
h = th0;
maxh = max(h);
for off = 0:pi/100:2*pi-pi/100
th = th0 - off;
x = h .* sin(-th);
y = h .* cos(-th);
plot3(x,y,-h,'b-p','MarkerIndices',1:4:length(h),'MarkerSize',10);
xlim([-1.8*maxh 1.8*maxh])
ylim([-1.8*maxh 1.8*maxh])
zlim([-maxh 0.5])
view(0,0)
axis off
hold on
plot3(x,y,-h,'b*','MarkerIndices',3:4:length(h),'MarkerSize',8);
for k = 2:N
th = th + 2*pi/N;
x = h .* sin(-th);
y = h .* cos(-th);
plot3(x,y,-h,'b-p','MarkerIndices',1:4:length(h),'MarkerSize',10);
plot3(x,y,-h,'b*','MarkerIndices',3:4:length(h),'MarkerSize',8);
end
hold off
drawnow
if we
frame = getframe(gca); % take image only
gr = frame2im(frame);
[bA,map] = rgb2ind(gr,256);
if (off==0)
imwrite(bA,map,filename,'gif','LoopCount',Inf,'DelayTime',1/20);
else
imwrite(bA,map,filename,'gif','WriteMode','append','DelayTime',1/20);
end
end
end
描画点はある程度多くないと当然滑らかな円になりませんが、その全てにマーカーを表示してしまうと密になりすぎてしまいます。そのような場合、'MarkerIndices' オプションでマーカーの間隔を開けることができます。
plot3(x,y,-h,'b-p','MarkerIndices',1:4:length(h),'MarkerSize',10);
plot3(x,y,-h,'b*','MarkerIndices',3:4:length(h),'MarkerSize',8);
4点おきに☆、その間に*を表示させています。
ライン数を変えてみる
コードにあるとおり、変数 N でラインの本数も変えられます。
最初は2ラインバージョンが一番錯視が起きやすかったのですが、慣れるとどれでもいけますね。(-_☆)
高速化検討
マーカーを入れるととても遅くなるようで、リアルタイム動作はしなくなります。
高速化のため、あらかじめ計算結果を配列に入れてみましょう。
オーナメントライン一本ずつのグラフィックハンドルを保存して、2回目以降は plot3() を使わず描画済み座標データである .XData/.YDataプロパティを直接更新します。z は変わらないので更新不要です。
グラフィックハンドル配列は zeros/onesではなく、gobjects() で初期化する必要があります。
ついでに回転は、表示のみの時は 2π、書き込み時はファイルサイズ削減のため 2π/N 分だけ書くようにしています。2π/N で、また全く同じ座標データに戻るからです。
MATLABコード 2
we = true; % file write enable
M = true; % draw maker
N = 4;
filename = ['illusionTree2_' num2str(N) '.gif']
close all
f = figure(1);
f.Color = [1 1 1];
th0 = 0:pi/20:2*pi*1.5;
h = th0;
maxh = max(h);
dAz = pi/100;
maxAz = 2*pi;
if we; maxAz = maxAz/N; end
n = ceil(maxAz / dAz);
maxAz = maxAz - dAz;
x = zeros(length(h),n,N);
y = zeros(length(h),n,N);
for off = 1:n
az = off * dAz - dAz;
th = th0 - az;
for k = 1:N
x(:,k,off) = h .* sin(-th);
y(:,k,off) = h .* cos(-th);
th = th + 2*pi/N;
end
end
H = gobjects(N,2);
for off = 1:n
for k = 1:N
if (off == 1)
if M
H(k,1) = plot3(x(:,k,off),y(:,k,off),-h,'b-p','MarkerIndices',1:4:length(h),'MarkerSize',10);
else
H(k,1) = plot3(x(:,k,off),y(:,k,off),-h,'b-');
end
if (k == 1)
xlim([-1.8*maxh 1.8*maxh])
ylim([-1.8*maxh 1.8*maxh])
zlim([-maxh 0.5])
view(0,0)
axis off
hold on
end
if M
H(k,2) = plot3(x(:,k,off),y(:,k,off),-h,'b*','MarkerIndices',3:4:length(h),'MarkerSize',8);
end
else
H(k,1).XData = x(:,k,off);
H(k,1).YData = y(:,k,off);
if M
H(k,2).XData = x(:,k,off);
H(k,2).YData = y(:,k,off);
end
end
end
if (off == 1); hold off; end
drawnow
if we
frame = getframe(gca); % take image only
gr = frame2im(frame);
[bA,map] = rgb2ind(gr,256);
if (off==1)
imwrite(bA,map,filename,'gif','LoopCount',Inf,'DelayTime',1/20);
else
imwrite(bA,map,filename,'gif','WriteMode','append','DelayTime',1/20);
end
end
end
Consideration
結果は・・、微妙にしか速くならない・・。
どうやらやはり、マーカーの描画がネックのようですね。(;¬_¬)
ライブスクリプトであればあとから実行結果を再生スピードを変えて再生・巻き戻しもできるので、ライブスクリプトで試すのが良いかと思います。ライブスクリプトファイルはMATLABでないと読めないので公開時は通常のスクリプトにしていますが、実際にはライブスクリプトで作ることが多いです。
ライブスクリプトにするには、新規ライブスクリプトを作って、上記コードを貼り付け、最初の設定辺りを「コントロール」で置き換えてください。
ライブスクリプトは結果をビデオやアニメーションGIFにも保存できますがFPSが指定できないので、スクリプト中でFPSを指定してファイル出力をしています。
回転錯視は日本人Webデザイナーの方が作られた「シルエット錯視」が有名ですが、こんな簡単なものでもできるのが意外でした。
ほんと認識の仕組みは不思議で面白いですね。(u_u)
それでは、少し早いですが、良いクリスマスをお過ごしください。🎄
タイトル画像モデル:高杉レナ(クレアトゥール)
この記事が気に入ったらサポートをしてみませんか?