最近、自分の中でAR(拡張現実)が再ブレイク中です
はじめは三年前ぐらいに流行り、Unituyを使ってARを挑戦しました

デバイスごとにアプリを作り直さなければならない点がどうしても開発に煩わしさを感じてしまい、一気にブレイクが去っていきました
しかし、Web上でもARができるようになったよ!というニュースをみて改めて挑戦をしてみました
挑戦をした結果、Web上にてARでミクさんを踊らせることができたので記事にします
デモ
解説云々を入れる前に先にデモを記述します
Lat式ミクに”地球最後の告白を”を踊らせています
ARマーカーをカメラに近づけるとミクさんが踊ってくれます
著作権の問題で音楽は流れません

手持ちのスマホやパソコンでも下記のサイトにて体験ができます
下のマーカー(Hiro Marker)を認識させてください
参考:https://commons.wikimedia.org/wiki/File:Hiro_marker_ARjs.png
体験するときに、スマートフォンの場合は下記のURLをコピーしてからブラウザで起動してください
また、iPhoneの方はsafariにてアクセスをしてください
使用したライブラリ
本プログラムはThree.jsとAR.jsを用いて作成をしています
Three.jsとは
Three.jsとは、ウェブブラウザ上にて簡単にCGを描画することができるJavaScirptのライブラリです
これを使うことで簡単に3Dコンテンツを作ることができます
このライブラリの主にMMDを取り扱うための関数を使っていきます
AR.jsとは
AR.jsとは、ウェブブラウザ上で簡単にARをすることができるJavaScirptライブラリです
また、非常に高速に動作することができるだけではなく、AndroidやiOS、windowsといったあらゆるプラットフォームで動作することが可能です
プログラム
プログラムはすべてGitHubに掲載しています
プログラムにて重要なのはindex.js(staitc/js/index.js)です
コメントアウトにて解説をいれているので、本記事では重要な箇所や開発中に困ったことを記載します
Hiroマーカーの使用
本プログラムはARのサンプルでよく使われているHiroマーカーを使い、このHiroマーカーを認識したらミクさんを召喚しています
サンプルを参考にして記述をしたのですが、マーカーを認識してくれませんでした
ネットで探してみると解決策が見つかり、どうやら”HREEx.ArToolkitContext.baseURL”の場所を変えろよということでした
index.htmlにURLを変更する記述をしています
<script>THREEx.ArToolkitContext.baseURL = 'https://jeromeetienne.github.io/AR.js/three.js/'</script>
Hiroマーカーは以下からダウンロードできます
MMDのスケールの縮小
MMDをインポートしたあとそのままマーカーへ投影するとミクさんがとてつもなく大きく投影されます
そのため、ミクさんの大きさを変換してあげます
私はObject3Dを作成し、そのなかにMMDのデータを追加後、追加したObject3Dを縮小するようにしました
new THREE.MMDLoader().loadWithAnimation( modelFile, vmdFiles, function ( mmd ) {
mesh = mmd.mesh;
// 3dobject
var model = new THREE.Object3D();
// Scaleの変換
model.scale.x = 0.1;
model.scale.y = 0.1;
model.scale.z = 0.1;
model.add(mesh);
marker.add(model);
//以下略
});
一応mmd.meshにもスケールを変換するための変数(scale.set)を持っています
一度以下のように追加してプログラムを動かしてみたのですが、初期状態時にミクさんのスカートが飛び跳ねて食い込んでいたのでobject3dを使用しました
new THREE.MMDLoader().loadWithAnimation( modelFile, vmdFiles, function ( mmd ) {
mesh = mmd.mesh;
mesh.scale.set(0.1, 0.1, 0.1);
marker.add(mesh);
//以下略
});
下記のように初期状態にてスカートが跳ね返り、食い込みます
レンダリング
Web上にてレンダリングをするにはrequestAnimationFrame()を呼び出して描画をします
本プログラムにもrequestAnimationFrame()を入れているのですがコメントアウトし、setIntarvalにて描画をしています
理由として、requestAnimationFrame()にするとミクさんが汚く描画されたためです
requestAnimationFrame()について詳しく追えてはいないのですが、setIntarvalでやったほうが綺麗にミクさんが描画されたのでこちらを採用しました
//Render
// function renderScene() {
// requestAnimationFrame(renderScene);
// if(source.ready === false){ return; }
// context.update(source.domElement);
// if(ready){
// helper.update(clock.getDelta());
// }
// // renderer.render( scene, camera );
// effect.render( scene, camera );
// }
setInterval(function(){
if(source.ready === false){ return; }
if(ready){
helper.update(clock.getDelta());
}
renderer.render( scene, camera );
context.update(source.domElement);
}, 1000 / 60); //60fps
requestAnimationFrame()で動かしたい場合は、function renderScene()のコメントアウトを外し、Index.jsの冒頭付近にあるrenderScene()のコメントアウトも外すします
そのあと、setInterval()内をコメントアウトするとrequestAnimationFrame()で動作します
左がsetInterval()での描写で左がrequestAnimationFrame()での描写です
requestAnimationFrame()が髪の毛の影?が強くなっている
音楽再生
冒頭にてデモは音楽を再生することができないと記述しました
しかし、音楽データをいれてコメントアウトされている部分を外すと音楽も再生することができます
まず、“static/mmd/music/“に”地球最後の告白を”の音源をmusic.mp3にリネームして配置します
(Amazonにて販売をしています:https://amzn.to/2ODiMzm)
つぎに”staitc/js/index.js”にて,“new THREE.MMDLoader().loadWithAnimation”内に入っている音楽を再生する箇所のコメントアウトを外します
before
helper = new THREE.MMDAnimationHelper( {
afterglow: 2.0
});
new THREE.MMDLoader().loadWithAnimation( modelFile, vmdFiles, function ( mmd ) {
mesh = mmd.mesh;
// 3dobject
var model = new THREE.Object3D();
// Scaleの変換
model.scale.x = 0.1;
model.scale.y = 0.1;
model.scale.z = 0.1;
model.add(mesh);
helper.add( mesh, {
animation: mmd.animation,
physics: true,
});
//add maker
marker.add(model);
//Audio
// new THREE.AudioLoader().load(audioFile, function(buffer){
// var listener = new THREE.AudioListener();
// var audio = new THREE.Audio( listener ).setBuffer( buffer );
// listener.position.z = 1;
// helper.add( audio, audioParams );
// marker.add( listener );
// // Music Load Flag
// ready = true;
// });
ready = true;
}, onProgress, onError );
after
helper = new THREE.MMDAnimationHelper( {
afterglow: 2.0
});
new THREE.MMDLoader().loadWithAnimation( modelFile, vmdFiles, function ( mmd ) {
mesh = mmd.mesh;
// 3dobject
var model = new THREE.Object3D();
// Scaleの変換
model.scale.x = 0.1;
model.scale.y = 0.1;
model.scale.z = 0.1;
model.add(mesh);
helper.add( mesh, {
animation: mmd.animation,
physics: true,
});
//add maker
marker.add(model);
//Audio
new THREE.AudioLoader().load(audioFile, function(buffer){
var listener = new THREE.AudioListener();
var audio = new THREE.Audio( listener ).setBuffer( buffer );
listener.position.z = 1;
helper.add( audio, audioParams );
marker.add( listener );
// Music Load Flag
ready = true;
});
// ready = true; //ここのFlagをコメントアウトする
}, onProgress, onError );
その後、キャシュを削除後index.htmlを開き直すと音楽が流れます
本プログラムのライセンスについて
本プログラムに使用しているMMDやモーションは再配布が問題ないものを使用しています
製作者のLat様とよぴ様に感謝いたします
MMDのライセンス
製作者 : Lat
[email protected]
http://innoce.nobody.jp/
モーションのライセンス
製作者:よぴ
Twitter:@pyopi
Motion:https://www.nicovideo.jp/watch/sm25418510
いろんな端末でやってみた
いろんな端末で本当に動くのかやってみました
手元にあったWindows 10,iOS(iPad pro), Android(ASUS)でやってみました
Android端末だけ多少もっさり感があります
未解決
Webブラウザ上にてARマーカートラッキングしミクさんを踊らせるという目標は達成しました
しかし、最終的にやりたかった「ARマーカーを認識していなければ一時停止」というプログラムが実装できませんでした
音楽は一時停止するのですが、ミクさんは一時停止したあとに再生をすると一時停止した時間分進んだところから踊り始めます
一時停止したところからもう一度再生を始めてほしいのです
解決策をお待ちしています
まとめ
Webブラウザ上にてARマーカートラッキングしミクさんを踊らせるプログラムを作りました
様々な端末でやってみましたがどれも動作することを確認しました
一時停止機能の実装ができない状況なので解決策をお待ちしています
参考
- Qiita, Three.js + AR.js + Tween.js で遊ぶ, https://qiita.com/mkoku/items/48b39e2750bceb72fbf6, 2018/10/21
- Qiita, ようこそジャパリARへ!, https://qiita.com/jyuko/items/60555fd1c84ae46498f8, 2018/10/21
- CardboardClub, webVRでMMD【pmxのロードとか影とか, http://cardboardclub.jp/lab/mmdwebvr2/, 2018/10/21