周囲の世界が歪む錯視をCanvasで

Canvasでアニメーションによる錯視を作ってみた。20〜30秒ほど中心を見つめ、視野全体で螺旋が渦巻く感じを得た後に周囲の部屋を見渡すと、数秒間ほど柱なんかがグニャグニャと歪んで見える。人によって見える程度がかなり違う模様。


http://dl.dropbox.com/u/296/log_spiral/log_spiral.html

この錯視は20年ほど前にテレビで見たもので、ずっと名前がわからなかった。錯視のホームページを作っている立命館大学の北岡先生にメールでお伺いしたところ、どうも「運動残効」(motion aftereffect)とか「滝の錯視」(waterfall illusion)という名前で呼ばれるもので、19世紀から知られている図案だと分かった。

ともあれ、こんな単純なアニメーションを作るだけでも、いろいろと良い勉強になった。

1つ、確認できなくて気になったのは、「canvas.width=canvas.width」というイディオム。アニメーションのために描画済みのcanvasをクリアすることができる。このいかにもひどいやり方で描画済みコンテンツを消す方法が、実は遅いという話。fillRectで背景色で塗りつぶすほうが速いという。回転・縮小の座標変換のパラメータは自前でリセットする必要があるので、アニメーションによっては速くならないことはあるというのだけど、パフォーマンス差は確認できなかった。

もう1つ、setIntevalではなくて、requestAnimationFrameというAPIが実装されつつあって、タブにフォーカスがあるときだけコールバックしてくれるようなものがあるらしい。

もう1つ、回転するアニメーションなので、描画済みのものをビットマップとしてアングルを付けて描画したほうが、毎回線分として描くよりも速そうだと思って以下のように、dataURLを使って書いた。

    var img = (function() {
	draw();
	var img = new Image();
	var _imageURL = canvas.toDataURL("image/png");
	img.src = _imageURL
	return img;
    })();

    function rotate() {
    :
    :
        timerId = setInterval(function(){
            canvas.width = canvas.width;
            ctx.translate(d, d);
            ctx.rotate(Math.PI*(angle/180));
            ctx.drawImage(img, -d, -d);
    :
    :
        }, 100);
    }

ぜんぜん速くなかった。GPUで回転したら速そうだけど、実際のところ、img.srcには文字列にエンコードされた効率の悪いビットマップ画像が入っているし、ブラウザのJSエンジンのヒープメモリ上にあって、そのメモリイメージをフレームごとにGPUに回転系コマンドとともに転送している、のではないかという気がする。