Three.js勉強4: アニメーションと再レンダリング

# はじめに
以前、[React Three Fiber(R3F)に関する記事](https://qiita.com/skm_bnn/items/9bdca6e2965fe8fa61e5)を投稿しました。ReactではThree.jsを容易に扱えるとの噂で、触ってみましたが、そもそもThree.jsでできることが何なの分からず、環境構築止まりになってしまいました。そこで、本記事ではThree.jsを基礎から学びつつ、備忘録として記事にまとめてみたいと思います。

- [Three.js勉強1: 環境構築](https://qiita.com/skm_bnn/private/fe4c17ac65ce5c4a89db)
- [Three.js勉強1.5: 環境構築(ローカル編)](https://qiita.com/skm_bnn/private/4a9a739ec08a41f2588e)
- [Three.js勉強2: シーンの主要要素](https://qiita.com/skm_bnn/private/32f21e39ed40668041e9)
- [Three.js勉強3: 3Dオブジェクトの描画](https://qiita.com/skm_bnn/private/e0611cfd717b96f7ff98)
- [Three.js勉強4: アニメーションと再レンダリング](https://qiita.com/skm_bnn/private/9bd86ab51a829efc43ef)

# 前回の記事

https://qiita.com/skm_bnn/items/e0611cfd717b96f7ff98

# アニメーション
アニメーションは次のコードのイメージで実装していきます。

```js: アニメーションの実装(疑似コード)
function アニメーション関数(){
  定期的に自身(アニメーション関数)を呼び出す;
  
  アニメーション処理;
}

アニメーション関数()を実行する;
```

今回は、メッシュオブジェクトを回転させるアニメーションを実装していきます。

## 定期的な関数呼び出し
[```requestAnimationFrame()```関数](https://developer.mozilla.org/ja/docs/Web/API/window/requestAnimationFrame)を利用します。この関数は、引数に渡された関数を定期的に呼び出して実行します。[```setInterval()```関数](https://developer.mozilla.org/ja/docs/Web/API/setInterval)も定期的な関数を呼び出すことは可能ですが、```requestAnimationFrame()```関数には多くの利点があるとのことです。例として、ユーザが別のブラウザタブに移動している際は関数を呼び出さないため、計算資源やバッテリーを節約できることが挙げられています([参考文献](https://threejs.org/docs/#manual/en/introduction/Creating-a-scene)より)。

## アニメーション処理(回転)
メッシュオブジェクトを回転させるには、```rotation```プロパティを変更する必要があります。このプロパティには、さらに```x```、```y```、```z```プロパティがあります。

```js: 
メッシュ.rotation.x = x軸方向の角度
メッシュ.rotation.y = y軸方向の角度
メッシュ.rotation.z = z軸方向の角度
```

## 実装
メッシュオブジェクトを回転させるアニメーションは次のようになります。
```js: アニメーションの実装
/* アニメーション関数の定義 */
function animate() {
	requestAnimationFrame( animate ); // 定期的な関数呼び出し

    /* 回転するアニメーション処理 */
	cube.rotation.x += 0.01;  // x軸方向
	cube.rotation.y += 0.01;  // y軸方向
}

animate(); // アニメーション関数の実行
```

# 再レンダリング
アニメーションの実装について示しましたが、ページの表示はプログラム開始時点(0フレーム目)のシーンが描画された静止画となっています。各フレームの描画内容をページ上に反映させるためには[再レンダリング](https://qiita.com/skm_bnn/private/e0611cfd717b96f7ff98#%E3%82%B7%E3%83%BC%E3%83%B3%E3%81%AE%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0)をする必要があります。そのため、完璧なアニメーション関数は次のようなコードになります。定期的にアニメーション関数は実行されるため、アニメーション処理を実行した後にレンダリングが行われます。すなわち、再レンダリングは描画内容を更新する処理になります。

```js: アニメーションの関数(疑似コード)
function アニメーション関数(){
  定期的に自身(アニメーション関数)を呼び出す;
  
  アニメーション処理;

  再レンダリング処理;
}
```

# ソースコード
次に今回までの内容をまとめたソースコードを示します。

```js: main.js
import * as THREE from 'three';

/* シーンの実装 */
const scene = new THREE.Scene();

/* カメラの実装 */
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);

/* レンダラーの実装 */
const renderer = new THREE.WebGLRenderer();               // インスタンスの生成
renderer.setSize(window.innerWidth, window.innerHeight);  // サイズの設定
document.body.appendChild(renderer.domElement);           // HTMLへの追加

/* 描画オブジェクトの実装 */
const geometry = new THREE.BoxGeometry(1, 1, 1);                    // ジオメトリの生成
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });  // マテリアルの生成
const cube = new THREE.Mesh(geometry, material);                    // メッシュの生成
scene.add(cube);                                                    // シーンへの追加

/* カメラの移動 */
camera.position.z = 5;

/* アニメーション関数の定義 */
function animate() {
  requestAnimationFrame(animate); // 定期的な関数呼び出し

  /* 回転するアニメーション処理 */
  cube.rotation.x += 0.01;  // x軸方向
  cube.rotation.y += 0.01;  // y軸方向

  /* 再レンダリング */
  renderer.render(scene, camera);
}

animate(); // アニメーション関数の実行
```

# 動作結果
これで、立方体を回転させたアニメーションを実装することができました。

![動作結果.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2844961/042fe169-139d-8118-9255-733f0719c9e6.gif)


# 参考
- シーンの作成

https://threejs.org/docs/#manual/en/introduction/Creating-a-scene