Help us understand the problem. What is going on with this article?

A-Frameでステレオ画像をテクスチャとして使う

A-Frameでステレオ画像・動画を表示したい.

A-Frameで3Dな動画を再生したかったのですが,A-Frameには左右の目に別の画像を見せる標準的な手段が用意されてなさそう.

tex3d.png

こういうテクスチャを<a-plane>に設定すると以下のようになってしまいますが,

vrplane.png

ステレオ画像として扱って,

vrplane-stereo.png

こんな感じにしたい.

VRモード時のカメラのレイヤー

とりあえずTHREE.jsのコードを読むと左右のカメラに別のレイヤーが設定されている.ドキュメントには説明見つからなかったので将来も使えるのかはわからない.

  • 左: 0, 1
  • 右: 0, 2

デフォルトではすべてのオブジェクトはレイヤー0なので左右のカメラから見えます.

コンポーネントの実装

もっとスマートなやり方ある気がするけど,とりあえずオブジェクトをコピーしてUVを上書くコンポーネントを作る.

AFRAME.registerComponent('stereo-texture', {
    schema: {
        mode: { default: "side-by-side", oneOf: ["side-by-side", "top-and-bottom"] }
    },
    init: function () {
        this._componentChanged = this._componentChanged.bind(this);
        this._checkVrMode = this._checkVrMode.bind(this);
        this.el.addEventListener('componentchanged', this._componentChanged, false);
        this.el.sceneEl.addEventListener('enter-vr', this._checkVrMode, false);
        this.el.sceneEl.addEventListener('exit-vr', this._checkVrMode, false);
    },
    update: function () {
        this._reset();
        if (this.el.getObject3D("mesh") === null) return;
        let luv = this._makeObj(1, "stereo-left").geometry.getAttribute("uv");
        let ruv = this._makeObj(2, "stereo-right").geometry.getAttribute("uv");
        if (this.data.mode == "side-by-side") {
            luv.setArray(luv.array.map((v, i) => i % 2 == 0 ? v / 2 : v));
            ruv.setArray(ruv.array.map((v, i) => i % 2 == 0 ? v / 2 + 0.5 : v));
        } else if (this.data.mode == "top-and-bottom") {
            luv.setArray(luv.array.map((v, i) => i % 2 == 1 ? v / 2 + 0.5 : v));
            ruv.setArray(ruv.array.map((v, i) => i % 2 == 1 ? v / 2 : v));
        }
        luv.needsUpdate = true;
        ruv.needsUpdate = true;

        this.el.getObject3D("mesh").visible = false;
        this._checkVrMode();
    },
    remove: function () {
        this.el.removeEventListener('componentchanged', this._componentChanged, false);
        this.el.sceneEl.removeEventListener('enter-vr', this._checkVrMode, false);
        this.el.sceneEl.removeEventListener('exit-vr', this._checkVrMode, false);
        this._reset();
    },
    _checkVrMode: function () {
        let leftObj = this.el.getObject3D("stereo-left");
        if (leftObj != null) {
            this.el.sceneEl.is('vr-mode') ? leftObj.layers.disable(0) : leftObj.layers.enable(0);
        }
    },
    _makeObj: function (layer, name) {
        let obj = this.el.getObject3D("mesh").clone();
        obj.geometry = obj.geometry.clone();
        obj.layers.set(layer);
        this.el.setObject3D(name, obj);
        return obj;
    },
    _reset: function () {
        if (this.el.getObject3D("stereo-left") != null) {
            this.el.getObject3D("mesh").visible = true;
            this.el.removeObject3D("stereo-left");
            this.el.removeObject3D("stereo-right");
        }
    },
    _componentChanged: function (ev) {
        if (ev.detail.name === 'geometry' || ev.detail.name === 'material') {
            this.update();
        }
    }
});

使い方

a-skyやa-planeで使えます.VRモードでないときは左目用の絵が使われます.サイドバイサイドとトップアンドボトム形式が使えます.
動画も同じように動きます.

サイドバイサイド形式のステレオ画像をa-planeに設定する.

<a-plane src="images/tex3d.png" width="5" height="5" position="0 1.3 -5" stereo-texture></a-plane>

360°全天球画像をa-skyに貼る.

<a-sky src="3d360.jpg" stereo-texture="mode:top-and-bottom"></a-sky>

  • コードはMITライセンスとしますが,コピーライトとか特に表示しなくて良いです.
  • WebGL Multiview extensionとかと相性悪そうなので,シェーダで実現したほうが良さそうな気もする.
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away