スマートフォンブラウザでAR（前半）

#はじめに
Nest+Visualのある案件で、スマートフォンのカメラでARを行うことになりました。
スマートフォンのカメラを特定の物（マーカー）にかざすと、スマートフォンの画面に3Dモデルが表示される仕掛けです。

ただしアプリケーションを配布するとした場合、

1. 配布手段
1. ユーザーにインストールして頂く手間を掛けることになる

という問題があります。

1について、ストアを通して配布する場合、審査を受ける手間や期間が問題になります。
Androidではapkファイルを自前で配布するという手段もありますが、デフォルトの設定では行えませんし、セキュリティ的に問題になり得ます。
iOSではそもそも野良アプリを配布する手段がありません。
2についてですが、突然ご自身のスマホにアプリのインストールを促された場合、応じる方は少ないと思われます。

そこで、スマートフォンのブラウザでARを行うことにしました。

#システム概要
ここでスマートフォンブラウザ（AndroidではChrome、iOSではSafari）において、
次のことを行います。

1.スマホのカメラを使ってマーカーを読みたい
2.マーカーを読み込まれたら、3Dオブジェクトを表示したい
3.ある特定のマーカーが読み込まれたら、演出を行いたい

おそらく**A-Frame**を用いるのが簡単で手っ取り早いです。
[A-Frame](https://aframe.io/)

ただし今回はカメラの動作を多少変更したい、マテリアルを直接弄ったりしたいなどあり、~~よく分かんなくなってきたので~~
3Dの描画に敢えて**Three.js**を直接使うことにしました。（A-FrameはThree.jsを内包しています。）
[Three.js](https://threejs.org/)
[AR.js](https://github.com/AR-js-org/AR.js)
また、カメラは**WebRTC**のAPIを呼び出して起動させています。

#作り始め
まず、ブラウザでローカルファイルにアクセスするため、Chromeのショートカットを新しく作り、以下の起動オプションを追加します。
` --args -allow-file-access-from-files `
※セキュリティが弱くなるので、このまま外部サイトにアクセスしないように！
※ApatchやIISなどで内部にサーバーを立てて動作確認する場合は不要です。
![Chrome_Setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626158/47ee9349-06b2-c244-3443-cf87be79df98.png)

ここでは実験用に、blenderで作った（最初からシーンに置かれている）単位キューブを使用します。
Blenderを開き、cube.glbという名前でエクスポートします。
（Blender2.8.2だと何もプラグインを追加しなくてもgltf 2.0で出力出来て便利です）

次に同じフォルダに新しいhtmlファイルを作り、以下のコードを入力します。

```ruby:sample.html
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<title>モデル表示サンプル</title>
</head>

<body style="margin : 0px; overflow: hidden;">
	<canvas id="myCanvas"></canvas>
	<!-- Three.js -->
	<script src="https://unpkg.com/three@0.110.0/build/three.min.js"></script>
	<script src="https://unpkg.com/three@0.110.0/examples/js/controls/OrbitControls.js"></script>
	<script src="https://unpkg.com/three@0.110.0/examples/js/loaders/GLTFLoader.js"></script>
	<script>
		//////////////////////////////////////////////////////////////////////////////////
		//		Init
		//////////////////////////////////////////////////////////////////////////////////
		// キャンバスの作成
		var width = 640;
		var height = 640;
		var canvasSize = new THREE.Vector2(width, height);
		
		// renderer
		var renderer	= new THREE.WebGLRenderer({
			canvas: document.querySelector('#myCanvas'),
			antialias: true,
		});
		renderer.setClearColor(0x000033, 1.0);
		renderer.setPixelRatio(window.devicePixelRatio);
		renderer.setSize( width, height );
		
		//////////////////////////////////////////////////////////////////////////////////
		//		シーンの初期化
		//////////////////////////////////////////////////////////////////////////////////
		var scene = new THREE.Scene();
		
		//////////////////////////////////////////////////////////////////////////////////
		//		カメラの作成
		//////////////////////////////////////////////////////////////////////////////////
		const camera = new THREE.PerspectiveCamera(60, width / height, 0.01, 100);
		camera.position.set(0, 0.0, 5.0);
		
		//////////////////////////////////////////////////////////////////////////////////
		//		マウス操作（OrbitControls）
		//////////////////////////////////////////////////////////////////////////////////
		controls = new THREE.OrbitControls( camera, renderer.domElement );
		controls.target.set( 0, 0, 0 );
		controls.update();
		
		//////////////////////////////////////////////////////////////////////////////////
		//		オブジェクトのロード
		//////////////////////////////////////////////////////////////////////////////////
		var obj;
		var loader = new THREE.GLTFLoader();
		var url = "cube.glb";
		loader.load( url, function(data) {
			obj = data.scene;
			scene.add( obj );
		}, undefined, function ( error ) {
			console.error( error );
		} );
		
		//////////////////////////////////////////////////////////////////////////////////
		//		ライティング
		//////////////////////////////////////////////////////////////////////////////////
		// 平行光源を作成
		const directionalLight = new THREE.DirectionalLight(0xFFFFFF);
		directionalLight.position.set(1, 1, 5);
		scene.add(directionalLight);
		// 環境光を追加
		const light = new THREE.HemisphereLight(0xFFFFFF, 0x333333, 2.0);
		light.position.set(1, 5, 1);
		scene.add(light);
		
		//////////////////////////////////////////////////////////////////////////////////
		//		毎フレームの描画処理
		//////////////////////////////////////////////////////////////////////////////////
		drawFrame();
		function drawFrame() {
			renderer.render(scene, camera);
			requestAnimationFrame(drawFrame);
		}
	</script>
</body>

</html>
```

このhtmlファイルをChromeで開くと、このようになります。
ブラウザ上で3Dオブジェクトが表示され、マウスドラッグで視点が回転することを確認しました。
![ar_cube.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626158/412a7821-996e-f228-3e37-4cf837f3485f.png)

次回はカメラ機能、AR機能を追加していきます。
後半：https://qiita.com/NestVisual/items/7d494acf6cdcbd02d45e

参考
https://qiita.com/ta-ke-no-bu/items/b7cd4b4719249a9c365e