Three.jsを使ってみる(カメラ制御編)

# はじめに
おはようございます。こんにちは。こんばんわ。
Watatakuです。
[Three.jsを使ってみる(入門編)](https://qiita.com/watataku8911/items/8cdb5ab91dc2f75b7b73)の最後に地球儀を作成したのですが今回はその地球儀を使ってカメラの制御等を行なっていきます。
では、早速行なっていきましょう。
# 地球を中心としてカメラを回転させる
地球を中心としてカメラが円周上を自動的に移動します。以下のコードを``tick()``に追記してください。

```js
// ラジアンに変換する
const radian = rot * Math.PI / 180;
// 角度に応じてカメラの位置を設定
camera.position.x = 1000 * Math.sin(radian);
camera.position.z = 1000 * Math.cos(radian);
```
カメラの位置の設定は``cameraオブジェクト``の``positionプロパティ``に数値を代入します。

さらに今回は、カメラは常に中央を見るようにしておきたいので、``cameraオブジェクト``の``lookAt()``メソッドを使って原点座標(0, 0, 0)を指定しています。

**lookAt()メソッドはどの位置からでも指定した座標に強制的に向かせることができる命令です。**

[lookAt()](https://threejs.org/docs/#api/en/core/Object3D.lookAt)
```js
// 原点方向を見つめる
camera.lookAt(new THREE.Vector3(0, 0, 0));
```
[サンプルコード](https://github.com/watataku8911/ThreeJS-earth/blob/rot-earth/earth.html)
# マウスの座標に応じて回転させる
地球を中心として、マウスの横の移動に対してカメラが移動します。
マウスの位置に応じてカメラの位置を制御するスクリプトは次となります（一部抜粋）。

```js
let rot = 0; // 角度
let mouseX = 0; // マウス座標

// マウス座標はマウスが動いた時のみ取得できる
document.addEventListener("mousemove", (event) => {
  mouseX = event.pageX;
});

tick();

// 毎フレーム時に実行されるループイベントです
function tick() {
  // マウスの位置に応じて角度を設定
  // マウスのX座標がステージの幅の何%の位置にあるか調べてそれを360度で乗算する
  const targetRot = (mouseX / window.innerWidth) * 360;
  // イージングの公式を用いて滑らかにする
  // 値 += (目標値 - 現在の値) * 減速値
  rot += (targetRot - rot) * 0.02;

  // ラジアンに変換する
  const radian = rot * Math.PI / 180;
  // 角度に応じてカメラの位置を設定
  camera.position.x = 1000 * Math.sin(radian);
  camera.position.z = 1000 * Math.cos(radian);
  // 原点方向を見つめる
  camera.lookAt(new THREE.Vector3(0, 0, 0));

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

  requestAnimationFrame(tick);
}
```
ポイントとしては、角度の算出方法をステージの幅の何％の位置にマウスがあるかを計算で求め、それを角度に反映しているところです。
```js
// マウスの位置に応じて角度を設定
// マウスのX座標がステージの幅の何%の位置にあるか調べてそれを360度で乗算する
const targetRot = (mouseX / window.innerWidth) * 360;
```
なお途中にイージングの公式というのがでてきていますが、ウェブコンテンツの開発では一般的によく使われる公式なので、知らない方はぜひ覚えておきましょう。
```js
// イージングの公式を用いて滑らかにする
// 値 += (目標値 - 現在の値) * 減速値
rot += (targetRot - rot) * 0.02;
```
[サンプルコード](https://github.com/watataku8911/ThreeJS-earth/blob/mouse-earth/earth.html)
# カメラの動きを自動的に制御する
``THREE.OrbitControls クラス``を使用します。

[THREE.OrbitControls](https://threejs.org/docs/#examples/en/controls/OrbitControls)
```
new THREE.OrbitControls(カメラ, DOM要素)
```

次の用途で役立つ機能です。

- 周回軌道を描くように、カメラを配置する
- ポインター操作でカメラの配置やアングルを変更する

<i>OrbitControls.jsは、Three.jsライブラリの本体に含まれていないので注意が必要です。</i>CDNで利用するときは、以下のscript要素で読み込みます
```html
<script src="https://unpkg.com/three@0.137.4/examples/js/controls/OrbitControls.js"></script>
```
地球儀をマウス操作で次のようにカメラを制御できます。

- オービット（周回軌道）: 左ボタンでドラッグ
- ズーム: マウスホイール
- パン: 右ボタンでドラッグ

```js
// カメラを作成
const camera = new THREE.PerspectiveCamera(/*省略*/);
// カメラの初期座標を設定
camera.position.set(0, 0, 1000);

// カメラコントローラーを作成
const controls = new THREE.OrbitControls(camera, canvasElement);

// 滑らかにカメラコントローラーを制御する
controls.enableDamping = true;
controls.dampingFactor = 0.2;

tick();

// 毎フレーム時に実行されるループイベントです
function tick() {
  // カメラコントローラーを更新
  controls.update();

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

  requestAnimationFrame(tick);
}
```
``THREE.OrbitControlsインスタンス``の``enableDampingやdampingFactorプロパティ``を設定すると、ドラッグ時にカメラが滑らかに動くようになります。デフォルトだと機械的な動きになってしまいますが、これらのプロパティーを設定するだけで心地良い操作感になります。

``enableDampingやdampingFactorプロパティ``を使う場合は、``requestAnimationFrame``内で``THREE.OrbitControlsインスンタンス``の``updateメソッド``を呼び出す必要があります。
- [サンプルコード](https://github.com/watataku8911/ThreeJS-earth/blob/controls-earth/earth.html)
# 最後に
以上がThree.jsの基本的な使い方でした。
個人的に、これからもどんどんThree.jsを勉強していきたいと思います。また、その都度学習して身についた内容を記事にして共有していきたいです。

- [Three.jsのカメラの制御(座標制御)](https://ics.media/tutorial-three/camera_position/)
- [Three.jsのカメラの制御(OrbitControls)](https://ics.media/tutorial-three/camera_orbitcontrols/)