LoginSignup
2
1

More than 5 years have passed since last update.

Three.js で遊び倒す - カメラを動かそう Part.2 -

Last updated at Posted at 2019-03-25

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

昨日 は、星空を作りました。
本日は、カメラを動かします。

1. 完成版

See the Pen universe by hiroya iizuka (@hiroyaiizuka) on CodePen.

2. 参考文献

ICS MEDIA
初めてのThree.js
CodeGrid

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();

        starNight();
        function starNight() {
          // 形状データを作成
          const geometry = new THREE.Geometry();

          for (let i = 0; i < 1000; i++) {
            const star = new THREE.Vector3();
            star.x = THREE.Math.randFloatSpread(3000);
            star.y = THREE.Math.randFloatSpread(3000);
            star.z = THREE.Math.randFloatSpread(3000);

            geometry.vertices.push(star);
          }

          // マテリアルを作成
          const material = new THREE.PointsMaterial({
            size: 10,
            color: 0xffffff
          });
          // 星を作成
          const star = new THREE.Points(geometry, material);
          scene.add(star);
        }

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

        //球を作成
        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.rotation.x += 2;
          requestAnimationFrame(renderScene);
          renderer.render(scene, camera);
        }
      }

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

スクリーンショット 2019-03-24 11.25.07.png

この中心の球を注視しながら、カメラが回転するようにしましょう。

数学の復習をしよう!

カメラの動きを理解するためには、数学の知識が必要です。
テンション下がりますね・・・。

三角関数 を学び直しましょう。

スクリーンショット 2019-03-25 7.03.22.png

半径がr の円における、円周上の点の座標は

X: r cosθ  Y: r sinθ

で計算されました。

しかし、three.js では、θではなく、radian を使います。
radian ってなんだったでしょうか・・・?汗

radian とは?

radian とは、円の半径に等しい長さの弧の中心に対する角度 のことです。

1200px-Radian_cropped_color.svg.png

例えば、半径が2cm の円があります。
弧の長さもちょうど2cm の時に、半径と、半径で作られる角度のことを、1 radian と言います。
弧の長さと、半径の比がradian と考えるとわかりやすいですね。

弧が長くなって、4cm になると 中心角は 2radian になります。

したがって、 4π cm (円周)になると、中心角は、2π radian になります。

つまり、下の式が成り立ちます。

360 度 = 2π (radian)
180 度 = π (radian)
1 度 = π/180 (radian)

扇型の中心角を、長さを使って表現できるのが、radian の特徴というわけですね。

半径がr の弧の場合は
孤の長さ = 2 × π × θ/360

となりますが、

なぜ、こんな面倒な radian を使うのか?
と興味を持たれましたかたは、こちら をご参照ください。
数学って難しいですね・・・

カメラの位置をradian で表そう!

本題に戻ります。
円周上の座標は

X: r cosθ  Y: r sinθ

で表されましたが、これらを、radian に変換すると

(角度 = rot) 

X: r cos(rot × π/180) 
Y: r sin(rot × π/180)

となります。
πは、Math.PI で表されるため、Three.js では、以下に変わります。

index.html
X: r * Math.cos(rot * Math.PI/180)
Y: r * Math.sin(rot * Math.PI/180)

これで、camera.position.x = ~ と、camera の座標を表せば良いです。
rot(角度) については、アニメーションの設定の中で、徐々に増えていくように設定しましょう。

index.html

 let rot = 0;

 function renderScene() {

          rot += 0.5;
          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));

          requestAnimationFrame(renderScene);
          renderer.render(scene, camera);
        }

4. 最終コード

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() {
        let rot = 0;
        //レンダラーを作成
        const renderer = new THREE.WebGLRenderer({
          canvas: document.querySelector(".stage")
        });
        renderer.setSize(window.innerWidth, window.innerHeight);

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

        starNight();
        function starNight() {
          // 形状データを作成
          const geometry = new THREE.Geometry();

          for (let i = 0; i < 1000; i++) {
            const star = new THREE.Vector3();
            star.x = THREE.Math.randFloatSpread(3000);
            star.y = THREE.Math.randFloatSpread(3000);
            star.z = THREE.Math.randFloatSpread(3000);

            geometry.vertices.push(star);
          }

          // マテリアルを作成
          const material = new THREE.PointsMaterial({
            size: 10,
            color: 0xffffff
          });
          // 物体を作成
          const star = new THREE.Points(geometry, material);
          scene.add(star);
        }

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

        //球を作成
        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() {
          //radian の設定
          rot += 0.5;
          const radian = (rot * Math.PI) / 180;

          //カメラのpositioning
          camera.position.x = 1000 * Math.sin(radian);
          camera.position.z = 1000 * Math.cos(radian);
          camera.lookAt(new THREE.Vector3(0, 0, 0));

          requestAnimationFrame(renderScene);
          renderer.render(scene, camera);
        }
      }

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

See the Pen universe by hiroya iizuka (@hiroyaiizuka) on CodePen.

できました。
綺麗な眺めです。

次回は、インタラクション操作を取り込んでいきます。
それでは、また明日〜

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1