ようこそジャパリARへ！

![ジャパリAR.png](https://qiita-image-store.s3.amazonaws.com/0/153460/646aa9e0-aa9b-e32e-654a-287535895c34.png)

![DBX-ugfVwAIKQDe.jpg](https://qiita-image-store.s3.amazonaws.com/0/153460/ebdbecc2-c273-b41e-b5dc-46de4a846d8e.jpeg)


私は@jyuko、ちょっと前にA-Frameでジャパリパークてきなものを作ったヒトのフレンズだよ。

[ようこそジャパリVRへ！](http://qiita.com/jyuko/items/727a4f90e914a3932be2)

今回は、WebARを使ってMMDを表示させてみたよ！

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">WebARでMMDけもフレ。vmdもなんとか動いた。<a href="https://twitter.com/hashtag/webar?src=hash">#webar</a> <a href="https://twitter.com/hashtag/mmd?src=hash">#mmd</a> <a href="https://t.co/DtjH7z0yvt">pic.twitter.com/DtjH7z0yvt</a></p>&mdash; jyuko (@jyuko49) <a href="https://twitter.com/jyuko49/status/870910757256351745">2017年6月3日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

## WebAR？ 何それー！
**WebAR**ハ、**[Google Tango](https://get.google.com/tango/)**ノ機能ヲ**Webブラウザ**デ使エルモノダヨ！
マダ**実験的ナ機能**ダケド、色々ト楽シイ使イ方ガデキルヨ。
詳シイコトハ、**[GET AR](http://getar.jp/19010/)サン**ガ記事ニシテクレタカラ、ソッチヲ見ルトイイヨ！

[Tango対応スマホで実装するWebARとは？簡単なARアプリ開発をしてみた](http://getar.jp/19010/)

（その節はありがとうございました）


## 説明してやるのです
GET ARの記事にも書いているですが、メインのシーンは通常のThree.jsと同じように実装すればいいのです。サードパーティ製のライブラリだって使えるのですよ。

フレンズを召喚したいのなら、[MMDLoader.js](https://github.com/mrdoob/three.js/blob/dev/examples/js/loaders/MMDLoader.js)を使って、以下のコードを足すのです。
OutlineEffectを使うと何故かカメラ映像が表示されなくなるので、一旦無効にしておいたのです。我々は賢いので。

```index.html
<script src="../../libs/third_party/mmdparser.min.js"></script>
<script src="../../libs/third_party/ammo.js"></script>

<script src="../../libs/third_party/TGALoader.js"></script>
<script src="../../libs/third_party/MMDLoader.js"></script>
<script src="../../libs/third_party/OutlineEffect.js"></script>
<script src="../../libs/third_party/CCDIKSolver.js"></script>
<script src="../../libs/third_party/MMDPhysics.js"></script>

<script>
    var model, mesh, helper, effect, ikHelper, physicsHelper;
    var clock = new THREE.Clock();

    function init(){
        ...
        // 処理を最初の方に持ってくる
        renderer = new THREE.WebGLRenderer();
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( window.innerWidth, window.innerHeight );

        effect = new THREE.OutlineEffect( renderer );

        //var modelFile = 'アルパカ・スリ/Model/アルパカ・スリ.pmx';
        var modelFile = 'トキ/Model/トキ.pmx';
        var vpdFiles = ['普通の撮影ポーズ/ミクvpd/01.vpd'];
        var vmdFiles = ['Rick式サーバルメトロノーム/サーバルメトロノーム.vmd'];

        helper = new THREE.MMDHelper();
        var loader = new THREE.MMDLoader();

        // Poseの実装例
        loader.loadModel( modelFile, function ( object ) {
            model = new THREE.Object3D();
            mesh = object;
            mesh.position.z = -1.5; // 1.5m前方に表示
            mesh.scale.set(0.1, 0.1, 0.1);
            model.add( mesh );
            scene.add( model );

            var vpdIndex = 0;
            function loadVpd () {
                var vpdFile = vpdFiles[ vpdIndex ];
                loader.loadVpd( vpdFile, function ( vpd ) {
                    helper.poseAsVpd( mesh, vpd );
                }, onProgress, onError );
            }
            loadVpd();
        });

        /*
        // Motionの実装例
        // TODO:ブラウザが頻繁に落ちるので注意
        loader.load( modelFile, vmdFiles, function ( object ) {
            model = new THREE.Object3D();
            mesh = object;
            mesh.position.set(0, 0, -1.5);
            mesh.scale.set(0.1, 0.1, 0.1);
            model.add( mesh );
            helper.add( mesh );
            helper.setAnimation( mesh );
            scene.add( model );

            ikHelper = new THREE.CCDIKHelper( mesh );
            ikHelper.visible = false;
            scene.add( ikHelper );

            helper.setPhysics( mesh );
            physicsHelper = new THREE.MMDPhysicsHelper( mesh );
            physicsHelper.visible = false;
            scene.add( physicsHelper );
            helper.unifyAnimationDuration( { afterglow: 2.0 } );
        });
        */
        ...


        renderer.domElement.addEventListener("click", function(event) {
            pos.x = event.pageX / window.innerWidth;
            pos.y = event.pageY / window.innerHeight;
            // 位置と向きを基準に合わせる
            mesh.position.set(0, 0, 0);
　　　　　　         mesh.rotation.set(-Math.PI/2, 0, -Math.PI/2);
            picking();
        });
        ...
    }
    ...

    function updateAndRender() {
        ...
        helper.animate( clock.getDelta() );
        renderer.render( scene, cameraPersp );
        // TODO:OutlineEffectをかけると、カメラ映像が表示されない
        // effect.render( scene, cameraPersp );
        
        requestAnimationFrame( updateAndRender );
    }
</script>

```

あとは、[occlusionサンプル](https://github.com/googlevr/chromium-webar/blob/master/examples/threejs/occlusion/index.html)と大体同じなのです。どうですか？ わかったのですか？


<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">pickingもできた。満足。<a href="https://twitter.com/hashtag/webar?src=hash">#webar</a> <a href="https://twitter.com/hashtag/mmd?src=hash">#mmd</a> <a href="https://t.co/l89xJKAt6U">pic.twitter.com/l89xJKAt6U</a></p>&mdash; jyuko (@jyuko49) <a href="https://twitter.com/jyuko49/status/871290638557302785">2017年6月4日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>


ここまでできれば、WebAR対応のブラウザで表示するだけですが、MMDの多くは再配布NGなのでローカルにWebサーバを立てて、LANからアクセスしやがれなのです。
ちなみに我々は、Macでapachectlを使って、WiFi経由でLAN接続しているのです。

[apacheを使ってローカルサーバーを構築する方法](http://motw.mods.jp/Mac/local_server.html)

Macがないなら、自力でなんとかするのです。我々は忙しいので。


## 気をつけるんですのよ
three.jsはchromium-webarに同梱されていますけれど、MMDLoader.jsとバージョンが合っていないと、MMDが表示されないことがありますわ。[GitHub](https://github.com/mrdoob/three.js)からMMDLoader.jsをダウンロードする際にbuildフォルダに入っているthree.jsを使った方がいいですわよ。
そのとき、一行だけコード修正する箇所がありますの。詳しくは、chromium-webarの[README](https://github.com/googlevr/chromium-webar#using-the-webar-apis-in-threejs)に書いてありましてよ。

```
case 0x8b5e: case 36198: return setValueT1; // SAMPLER_2D  // case 36198: Added by WebAR
```

それと、MMDをLoadすると、ChromiumARブラウザが不安定になって、アプリ落ちが発生しやすくなりますの。できるだけファイルサイズの小さいモデルやモーションデータを使った方が安定しますわ。

![Screenshot_20170604-091824.png](https://qiita-image-store.s3.amazonaws.com/0/153460/17487d50-f295-7bc5-b250-a737527b00fa.png)


## あとがき
WebARで何を作ろうかと考えたとき、Tango Unity SDKの方が機能は充実しているし、パフォーマンスもよいので、[three.jsのサードパーティ製ライブラリ](https://threejs.org/examples/)を組み合わせる使い方がいいなと思いました。
いわゆる**Webのエコシステム**ってやつです。

UnityにインポートしたMMDをAndroidアプリに利用するのは結構面倒（できる人にはできるらしいけど）で、その点WebARならMMDLoader.jsを使ってそのまま読み込めるので簡単です。MMDに限らず、各種3DフォーマットのLoaderは充実しているので、様々なモデルを手軽に表示することができます。

Tango対応スマートフォンがないと動かないのと、ChromiumARブラウザが不安定で重たいモデルだと頻繁にアプリ落ちしてしまうのが難点ですが、いずれ端末も増えて、WebARもChromeにサポートされる頃には安定するでしょう。


## クレジット
今回お借りしたもののリストです。

googlevr/chromium-webar
https://github.com/googlevr/chromium-webar

three.js
https://threejs.org/

MMDLoader.js（[@takahirox](https://twitter.com/superhoge)）
https://github.com/mrdoob/three.js/blob/dev/examples/js/loaders/MMDLoader.js

536式トキ（[@琥沙郎/536](http://3d.nicovideo.jp/users/5324858)）
http://3d.nicovideo.jp/works/td29233

536式アルパカ・スリ（[@琥沙郎/536](http://3d.nicovideo.jp/users/5324858)）
http://3d.nicovideo.jp/works/td29307

普通の撮影ポーズ（[@KEITEL](http://seiga.nicovideo.jp/user/illust/6847988)）
http://seiga.nicovideo.jp/seiga/im5162984

Rick式サーバルメトロノーム（[@Rick](http://www.nicovideo.jp/user/2237540)）
http://www.nicovideo.jp/watch/sm30759263

けものフレンズ ロゴジェネレータ（@hiruberuto）
http://qiita.com/hiruberuto/items/463219b158d74668e7d9