LoginSignup
6
1

More than 3 years have passed since last update.

Scratch 3.0 で Theta写真をぐるぐる回そう!

Posted at

オリジナルブロックを作ろう!

みんな大好きScratch。3.0では自分で拡張機能が作れると聞いていたが、実際にどうやって作るのか知らなかった。少し調べてみたところ、Scratch 3.0でオリジナルブロックをつくろうにバッチリ書いてあった。なんだ、Reactで作れるじゃないかということで、ゴールデンウィークのお楽しみとして、Theta写真のビューアー作りに挑戦しました。

ソースコードはどこにある?

Scratch 3.0の拡張機能は、現在までのところ、ソースコードを自分で編集して追加する必要があります。ここにあるレポジトリが実体ですが、scratch-vm と scratch-gui から調査を始めました。

Theta写真(360度写真)をブラウザで表示するためには様々な方法があると思います。ScratchがReactで書かれているならば、React 360aframe-react を使えば瞬殺なんじゃないかと、当初は考えていました。

しばらく(実際はかなり長いこと)ソースコードを眺めていても、一向に取りつく島が見つからず半ば諦めようかと思ったところで、scratch-renderを使えばなんとかなるかもと思い至りました。Scratch 3.0 を Hackしよう。 scratch-render.js で 何か作ってみようにあるように、このパッケージがScratchのレンダリング処理の核心だと分かります。

Three.jsのレンダリング結果を転送する

scratch-renderそのものに手を入れるのは、私には難しそうだったので、scratch-renderのインターフェイスを使ってTheta写真を描画しました。基本的なアイデアは、Three.js equirectangular panorama demoのコードでレンダリングしたデータを、ビットマップとしてscratch-renderに転送しています。

    init() {
        // https://threejs.org/examples/webgl_panorama_equirectangular.html
        this.lon = 0;
        this.lat = 0;
        this.phi = 0;
        this.theta = 0;

        this.camera = new THREE.PerspectiveCamera(75, 480 / 360, 1, 2000);
        this.camera.target = new THREE.Vector3(0, 0, 0);

        this.scene = new THREE.Scene();
        var geometry = new THREE.SphereBufferGeometry(500, 60, 40);
        // invert the geometry on the x-axis so that all of the faces point inward
        geometry.scale(-1, 1, 1);
        var texture = new THREE.TextureLoader().load(img);
        var material = new THREE.MeshBasicMaterial({ map: texture });
        this.mesh = new THREE.Mesh(geometry, material);
        this.scene.add(this.mesh);

        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize(480, 360);
        this.renderer.render(this.scene, this.camera);

        this.canvas = document.createElement("canvas");
        this.canvas.width = 480;
        this.canvas.height = 360;

        this.loadImage(this.renderer.domElement.toDataURL())
            .then(res => {
                this.ctx = this.canvas.getContext("2d");
                this.ctx.drawImage(res, 0, 0);
                this.skinId = this.runtime.renderer.createBitmapSkin(
                    this.canvas,
                    1
                );
                const drawableId = this.runtime.renderer.createDrawable(
                    this.layerGroup.testPattern
                );
                this.runtime.renderer.updateDrawableProperties(drawableId, {
                    skinId: this.skinId
                });
            })
            .catch(e => console.error(e));
    }

アニメーション処理も同様で、カメラを回転させた状態で描画したデータを、再びscratch-renderに転送しています。

    animate() {
        requestAnimationFrame(this.animate.bind(this));
        this.lon += this.interval;
        this.lat = Math.max(-85, Math.min(85, this.lat));
        this.phi = THREE.Math.degToRad(90 - this.lat);
        this.theta = THREE.Math.degToRad(this.lon);

        this.camera.target.x =
            500 * Math.sin(this.phi) * Math.cos(this.theta);
        this.camera.target.y = 500 * Math.cos(this.phi);
        this.camera.target.z =
            500 * Math.sin(this.phi) * Math.sin(this.theta);

        this.camera.lookAt(this.camera.target);
        this.renderer.render(this.scene, this.camera);

        this.loadImage(this.renderer.domElement.toDataURL())
            .then(res => {
                this.ctx.drawImage(res, 0, 0);
                this.runtime.renderer.updateBitmapSkin(
                    this.skinId,
                    this.canvas,
                    1
                );
            })
            .catch(e => console.error(e));
    }

こんな感じで動きました!

こちらで動作を試せます。右下の「拡張機能を追加」から"Scratch 360"を選んでください。

ソースコードはここにあります。

動いたものの・・

Scratchのレンダリングは、WebGLで行われていることが分かりました。本来だったら、3Dのレンダリングだってヌルヌルに動くはずですが、私の作った拡張ではそうなっていません。scratch-render は twgl で作られていて、これの使い方が分からない。仕方ないので、Three.jsでレンダリングしてビットマップを転送していますが、コレジャナイ感がすごいです。scratch-renderに手を入れれば、はるかに滑らかに描画ができると思います。

とはいえ、3Dグラフィックスが無くても Scratchは素晴らしいシステムなので、次は別のものを作ってみようかなって思いました。

参考

Ishiharaさんのコンテンツを参考にさせてもらいました。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1