見出し画像

Christmas Tree Illusion

Motivation


河原先生がツイットされてたのを見てウズウズ。

元動画は削除されてもう見られないようなのですが、回転するクリスマスツリー模様が、意図はしていないのでしょうが回転方向が、時計回り、反時計回り、両方の3パターンに切り替わって見える錯視が起こっていました。

MATLABで作ろう!

これはMATLABで作ってみるしかない!

関数としては単なるスパイラルなので、高さ方向に半径を変えながら円を3Dプロットして行けば良さそうだということがすぐ分かります。

回転は、開始角を変えていけば良いですね。

「すぐできそう!」と思ったのですが、「それっぽく」見える調整には試行錯誤が必要でした。(^-^;

シミュレーション結果

早速結果を。

Christmas Tree Illusion
You'll see three rotation patterns: clockwise, counterclockwise, and both.
If you look up and down or look away for a moment, you get the opposite rotation, and if you look at the one that looks like it's going around backwards, you get both rotations mixed up.

上から見るとこのようにずっと時計回りに回しているだけなのですが、YouTubeのヤツと同じ錯視が起こります。

Top View
こちらは錯視が起こらない

コツとしては、目線を上下すると反転しやすく、裏で回ってるように見える☆を注視するとそれが前に出てきて両回転入り乱れるようです。

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ラインバージョン
3ラインバージョン
6ラインバージョン
8ラインバージョン

最初は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)


それでは、少し早いですが、良いクリスマスをお過ごしください。🎄


タイトル画像モデル:高杉レナ(クレアトゥール)


この記事が参加している募集

つくってみた

やってみた

この記事が気に入ったらサポートをしてみませんか?