Three.js  で遊び倒す - 3D オブジェクトを動かそう -

Three.js day 3となりました。

本日は、[昨日](https://qiita.com/duka/items/399246c9d0bc2b5428df) の3D オブジェクトを動かしてみたいと思います。


#1. 完成版

![ダウンロード (69).gif](https://qiita-image-store.s3.amazonaws.com/0/232715/dd2357cc-ebb1-827b-a75a-0e2fd7229b5c.gif)

#2. 参考文献
[ICS MEDIA](https://ics.media/tutorial-three/index.html)
[初めてのThree.js](https://www.amazon.co.jp/%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AEThree-js-%E7%AC%AC2%E7%89%88-%E2%80%95WebGL%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AEJavaScript-3D%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA-Dirksen/dp/4873117704)
[three.jsの基礎の基礎：概要から静止オブジェクトの表示方法まで](https://qiita.com/yk-nakamura/items/b9ed8de0d1c3e1cab8f9)


#3. 分解してみる

❶. 
前回までのマークアップです。

```index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
    <style>
      body {
        margin: 0;
        padding: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <canvas class="stage"></canvas>

    <script>
      function init() {
        //レンダラーを作成
        const renderer = new THREE.WebGLRenderer({
          canvas: document.querySelector(".stage")
        });
        renderer.setSize(window.innerWidth, window.innerHeight);

        //シーンを作成
        const scene = new THREE.Scene();

        //カメラを作成
        const camera = new THREE.PerspectiveCamera(
          50,
          window.innerWidth / window.innerHeight,
          0.1,
          2000
        );
        camera.position.set(0, 0, 1000);

        //球を作成
        const geometry = new THREE.SphereGeometry(100, 32, 32);
        const material = new THREE.MeshNormalMaterial();
        const mesh = new THREE.Mesh(geometry, material);
        scene.add(mesh); 

        renderer.render(scene, camera);
      }

      window.addEventListener("load", init);
    </script>
  </body>
</html>
```

![スクリーンショット 2019-03-22 19.23.10.png](https://qiita-image-store.s3.amazonaws.com/0/232715/ff6c66b1-bb46-2308-3bd8-3a49070feeb1.png)


なんとも味気ない3D オブジェクトでした・・・。　
Material を変えて、雰囲気をよくしましょう。

##Material を変更しよう！

```
const material = new THREE.MeshNormalMaterial();
```

![スクリーンショット 2019-03-23 8.15.01.png](https://qiita-image-store.s3.amazonaws.com/0/232715/8aaa9553-e552-1806-ab12-ec8554b9ac0d.png)

いい感じになりました。
MeshNormalMaterialクラス は、xyz軸の色に合わせて、面の色が変化します。
(x=青、y=緑、z=赤)
前回のMeshNormalMaterialクラスと比べて、影ができるので、3D 感が増しますね。


他にも、様々なMaterial があるので、参考文献を随時ご参照ください。

##アニメーションをつけよう！
この3D オブジェクトを動かしましょう。どうやるのでしょうか？

CSS animation では、keyframe やtransitionプロパティを使いましたが、
Three.js では、当然別のやり方となります。 

オライリー本によると
>
シーンをアニメーションさせたいのであれば、最初にやる必要があるのは、特定の間隔でシーンを再描画する方法を見つけるということです。


Javascriptには、setInterval関数がありますが、これはブラウザがどういう状態かを一切考慮しません。
また、実行されるタイミングが画面の再描画と同期していないため、CPU利用率が高くなり、パフォーマンスが低くなります。

これらの問題を、requestAnimationFrame 関数が解決します。

###requestAnimationFrame関数とは？
requestAnimationFrame関数は、ブラウザができる限り滑らかかつ効率的に描画することを保証してくれる関数です。
使い方は、まず、レンダリングを処理する関数を作り、それをrequestAnimationFrame引数として渡します。
これだけで、毎フレーム毎にその関数を実行することができます、簡単ですね。

```
function renderScene(){
  window.requestAnimationFrame(renderScene);
}
```

上記の記法、よくわからない方もおられると思います。
これは、``再帰関数``と言われているものです。

つまり、自分自身を呼び出す関数が書かれている関数のことです。
繰り返し処理を行うことで、毎回描画し、アニメーションが実行され続けることが可能になります。

```
function renderScene(){
  window.requestAnimationFrame(renderScene);
  renderer.render(scene, camera);
}
```
これで、下準備は完成です。
<br/>
###球を動かそう！

先ほどのrenderScene() のなかに、書いていきましょう。

```
function renderScene() {
   mesh.position.x += 2;
   requestAnimationFrame(renderScene);
   renderer.render(scene, camera);
}
```

![ダウンロード (68).gif](https://qiita-image-store.s3.amazonaws.com/0/232715/1de9e0eb-fb8a-f295-c6c9-346594901cca.gif)

動きました。
Y軸に動かしてみましょう。

```
function renderScene() {
   mesh.position.x += 2;
   mesh.position.y += 1;
   requestAnimationFrame(renderScene);
   renderer.render(scene, camera);
}
```

![ダウンロード (69).gif](https://qiita-image-store.s3.amazonaws.com/0/232715/41d417b9-a573-45fd-f1f7-386f02c13c04.gif)

できました。

###最終コード
```index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
    <style>
      body {
        margin: 0;
        padding: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <canvas class="stage"></canvas>

    <script>
      function init() {
        //レンダラーを作成
        const renderer = new THREE.WebGLRenderer({
          canvas: document.querySelector(".stage")
        });
        renderer.setSize(window.innerWidth, window.innerHeight);

        //シーンを作成
        const scene = new THREE.Scene();

        //カメラを作成
        const camera = new THREE.PerspectiveCamera(
          50,
          window.innerWidth / window.innerHeight,
          0.1,
          2000
        );
        camera.position.set(0, 0, 1000);

        //球を作成
        const geometry = new THREE.SphereGeometry(100, 32, 32);
        const material = new THREE.MeshNormalMaterial();
        const mesh = new THREE.Mesh(geometry, material);
        scene.add(mesh);

        renderScene();

        //アニメーション
        function renderScene() {
          mesh.position.x += 2;
          mesh.position.y += 1;
          requestAnimationFrame(renderScene);
          renderer.render(scene, camera);
        }
      }

      window.addEventListener("load", init);
    </script>
  </body>
</html>
```

この動きならば、css animation 使えば、2,3分でできるのに・・・と
もどかしい思いをしつつも、ここからがthree.js の見せ場ですね。

それでは、また明日〜