Hello three.js World

#Hello three.js World
### ○ three.jsとは
ブラウザ上で３Dグラフィックスや２Dグラフィックスを実現するためのWebGLと言った技術(わかりやすくいえばcanvasなど)を使って、VRとかオブジェクト関係をいじるのを簡単にできるようにした商業利用可能なJavaScriptライブラリ。 
UnityやMayaなどのオブジェクト関連をいじったことがある人は結構直感的に使えるかも。 
最近は日本語の記事も増えてきたけど、アニメーションさせたりとかそれを切り替えたり発火したりetc....とかまともに組み込もうと思うとなかなか使えるサイトがなくて困ったので一回包括的に書いてみようかなと思います。 

リファレンスはここ：[three.js](https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene)
example : [three.js/example](https://threejs.org/examples/)

### ○ この記事で書くこと
 - three.jsの簡単なHello World
 - オブジェクトの読み込み、表示

three.jsのMMD関連はこちら：[three.js でMMDを使う](https://qiita.com/shoichi1023/items/6cbaefe078c33f600bfe)

###準備
まずはライブラリを準備するとこからだよね

npmの場合

```
npm install --save three
```

直ダウンロード：[three.js/download](https://github.com/mrdoob/three.js/?PHPSESSID=3d110c25f9ee23ac1642f6f238ba357e)
※three.min.jsはbuildの中

###まずは概念から
３Dを描画するときに必要なもの

 - 空間
     - シーンって言います
 - カメラ
     - 物体を映すカメラ
 - ライト
     - ５種類くらいあります。大体２個使う。環境光(Ambient Light)とプラスもう一つって感じ
     - 詳しくはここにのってる：[ライトの種類と三点照明](http://www.alchemyschool.com/mayatips/lighting01)
 - レンダー
     - 描画更新をするもの
 - 物体
   - オブジェクト
 
ざっとこんな感じ

例えば、モデルの撮影をしているとして、 

 - そのモデルの立ってるところがシーン 
 - カメラマンがカメラ 
 - 部屋の電気が環境光で、レフ板がもう一つの光 
 - 現実世界だと人は普通に動くけど、時間の概念がレンダーに近いかな？ 
 - 物体はモデルさんだね

###〇 hello world


○ ファイル構成

    - index.html
    - js
        - main.js
        - three.min.js
    - css
        - style.css


```index.html

<!DOCTYPE html>

<html>

 <head>
   <meta charset="utf-8" />

   <meta http-equiv="Content-Style-Type" content="text/css" />
   <meta http-equiv="Content-Script-Type" content="text/javascript" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">

   <title>three.js sample</title>

   <link rel="stylesheet" href="./css/style.css" />

   <script src="js/three.min.js"></script>
   <script src="./js/main.js"></script>

 </head>

 <body>
    <div id="container">
    </div>
 </body>

</html>
```

```style.css
@charset "utf-8";

*{
	margin: 0;
	padding: 0;
	box-sizing: border-box;
	font-family :
	YuGothic,    /* Mac用 */
	'Yu Gothic', /* Windows用 */
	sans-serif;
	-webkit-appearance:none;
}

#container {
    overflow: hidden;
}
```

```main.js
var camera, scene, render, mesh;

window.onload = function(){
    init();
    update();
}

function init(){

    var target = document.getElementById("container");
    var width = window.innerWidth;
    var height = window.innerHeight;
    
    //描画更新するためのもの
    render = new THREE.WebGLRenderer();
    //Retinaディスプレイに対応するためのもの
    render.setPixelRatio(window.devicePixelRatio);
    //3Dの空間のサイズを指定
    render.setSize(width,height);
    //16進数で色指定、なれないかもだけどなんか明示的に0xって書いてることが多い
    render.setClearColor(0xdddddd);
    //影をつける
    render.shadowMapEnabled = true;
    //htmlに描画
    target.appendChild(render.domElement);
    
    //空間を定義
    scene = new THREE.Scene();

    //環境光を作成して空間に追加
    var ambient = new THREE.AmbientLight(0xeeeeee);
    scene.add(ambient);

    //もう一つの光(今回はポイントライト)を作成して座標を設定して追加
    var light = new THREE.PointLight(0xffffff, 1, 50);
    light.position.set( 0, 15, -40);
    scene.add(light);

    //カメラの作成(第二引数はアスペクト比)、位置の設定
    camera = new THREE.PerspectiveCamera(40, width/height, 1, 1000);
    camera.position.set(0, 10, 60);

    //オブジェクトを作成(骨組み)
    var cube = new THREE.BoxBufferGeometry(10, 10, 10);
    //マテリアルを作成(物体の質感とか見た目を設定するやつ)
    var material = new THREE.MeshBasicMaterial( { color: 0xff8080, emissive: 0xffffff, side: THREE.DoubleSide, flatShadhing:true } );
    //２つをひっつけて１つの物体にする
    mesh = new THREE.Mesh(cube, material);
    //位置
    mesh.position.set(0, 10, 0);
    //傾き
    mesh.rotation.set(0, 0, 0);
    //大きさ(比率)
    mesh.scale.set(1, 1, 1);
    //追加
    scene.add(mesh);

    // リサイズ時に３D空間もリサイズする
    window.addEventListener('resize', onWindowResize, false);

}

function onWindowResize() {
    //アスペクト比の変更
    camera.aspect = width / height;
    //カメラの内部状況の更新(新しいアスペクト比を読み込む)
    camera.updateProjectionMatrix();
    //3D空間のサイズ変更
    renderer.setSize(width, height);
}

function update(){
    //アニメーションつけるためのものupdate関数自体を引数に渡す
    requestAnimationFrame( update );
    //まわる
    mesh.rotation.x += 0.005;
    mesh.rotation.y += 0.01;
    //今の画面を消す
    render.clear();
    //新しい画面を表示する
    render.render(scene,camera);
}
```
###〇 オブジェクトを読み込んでみる
先程はGeometryを使ってオブジェクトを生成していた部分を、.objと.mtlのファイルを読み込む形に書き換えてみる。
オブジェクトを読み込むためには、そのファイル専用のLoaderを使って読み込む。
Loaderのファイルは[three.js/example/js/loaders](https://github.com/mrdoob/three.js/tree/dev/examples/js/loaders)にある。
今回は.objと.mtlなのでOBJLoaderとMTLLoaderを使います。
テストの際にchromeのローカルファイルへのajax関連で詰まる人がいるかもしれないので対策記事を貼っておきます。
[[*その他*] ChromeにてAjaxでローカルファイルにアクセス](https://qiita.com/cigalecigales/items/33afaa42f91542ffa62e)

○ ファイル構成

    - index.html
    - js
        - main.js
        - three.min.js
        - OBJLoader.js
        - MTLLoader.js
    - css
        - style.css
    - module
        - snowman.obj
        - snowman.mtl
オブジェクトのダウンロードはここ：[GoogleDrive](https://drive.google.com/drive/folders/19ewovvH6tB9cObfWSzqMlfcrrT_CsQIt?usp=sharing)

変更前

```index.html
   <script src="js/three.min.js"></script>
   <script src="./js/main.js"></script>
```

変更後

```index.html
   <script src="js/three.min.js"></script>
   <!-- Loaderファイルが先 -->
   <script src="js/OBJLoader.js"></script>
   <script src="js/MTLLoader.js"></script>
   <script src="./js/main.js"></script>
```
変更前

```main.js
//41~54行目
    camera.position.set(0, 10, 60);
   //オブジェクトを作成(骨組み)
    var cube = new THREE.BoxBufferGeometry(10, 10, 10);
    //マテリアルを作成(物体の質感とか見た目を設定するやつ)
    var material = new THREE.MeshBasicMaterial( { color: 0xff8080, emissive: 0xffffff, side: THREE.DoubleSide, flatShadhing:true } );
    //２つをひっつけて１つの物体にする
    mesh = new THREE.Mesh(cube, material);
    //位置
    mesh.position.set(0, 10, 0);
    //傾き
    mesh.rotation.set(0, 0, 0);
    //大きさ(比率)
    mesh.scale.set(1, 1, 1);
    //追加
    scene.add(mesh);
```

```main.js
//74~76行目
    //まわる
    mesh.rotation.x += 0.005;
    mesh.rotation.y += 0.01;
```
変更後

```main.js
    camera.position.set(0, 15, 100);

    var onProgress = function ( xhr ) {
        if ( xhr.lengthComputable ) {
            var percentComplete = xhr.loaded / xhr.total * 100;
            console.log( Math.round(percentComplete, 2) + '% downloaded' );
        }
    };

    var onError = function ( xhr ) {
    };

    var mtlLoader = new THREE.MTLLoader();
    //mtlの読み込み。ここのPATHがルートからのPATHになるので注意
	mtlLoader.load( "./module/snowman.mtl", function( materials ) {

		materials.preload();
        //objのインスタンスにmtlをセット
		var objLoader = new THREE.OBJLoader();
		objLoader.setMaterials( materials );
        //objの読み込み。ここのPATHがルートからのPATHになるので注意
        objLoader.load( "./module/snowman.obj", function(obj) {
            mesh = obj;
            //位置
            mesh.position.set(0, 0, 0);
            //傾き
            mesh.rotation.set(0, -5, 0);
            //大きさ(比率)
            mesh.scale.set(10, 10, 10);
            //追加
            scene.add(mesh);
		}, onProgress, onError );
	});
```

```main.js
if(mesh != undefined){
        //まわる
        mesh.rotation.y += 0.01;
    }
```