【Three.js】基礎編

はじめに

前回に引き続き、Three.jsについての記事になります。導入からごく簡単な図形を描画することはできたので、今回は新しい要素を加えていきたいと思います。

完成見本とサンプルコード

今回の完成見本はこちらになります。

以下のコードをHTMLファイルに貼り付けると、動画のように複数の板がグループとなって回転している様子が確認できると思います。

<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <script src="https://unpkg.com/three@0.131.3/build/three.min.js"></script>
    <style>
      body {
        margin: 0;
      }
    </style>
    <script>
      // ページの読み込みを待つ
      window.addEventListener('load', init);
      function init() {
        // サイズを指定
        const width = 960;
        const height = 540;
        // canvas要素の参照を取得する
        const canvas = document.querySelector('#myCanvas');
        // レンダラーを作成
        const renderer = new THREE.WebGLRenderer({
          canvas: canvas,
          alpha: true
        });
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(width, height);
        // シーンを作成
        const scene = new THREE.Scene();
        // カメラを作成
        const camera = new THREE.PerspectiveCamera(45, width / height);
        camera.position.set(0, 0, +1100);
        //オブジェクトをグループに格納し、グループをシーンに追加
        group = new THREE.Group();
        // ジオメトリーとマテリアルを定義
        const geometry = new THREE.BoxBufferGeometry(20,1,20);
        const material = new THREE.MeshStandardMaterial({
          color: 0xE1EDED,
          flatShading: true
        });
        // オブジェクトを100個生成
        for ( let i = 0; i < 200; i ++ ) {
          const mesh = new THREE.Mesh( geometry, material );
          // 座標をランダムで決定
          mesh.position.x = Math.random() * 500 - 250;
          mesh.position.y = Math.random() * 500 - 250;
          mesh.position.z = Math.random() * 500 - 250;
          // サイズをランダムで決定
          mesh.scale.setScalar( Math.random() * 2 + 1 );
          // 角度をランダムで生成
          mesh.rotation.x = Math.random() * Math.PI;
          mesh.rotation.y = Math.random() * Math.PI;
          mesh.rotation.z = Math.random() * Math.PI;
          group.add(mesh);
        }
        scene.add(group);
        //半球光源の生成
        const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
        hemiLight.position.set( 0, 1000, 0 );
        scene.add( hemiLight );
        // 平行光源の生成
        const dirLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
        dirLight.position.set( - 3000, 1000, - 1000 );
        scene.add( dirLight );
        // フォグの追加
        scene.fog = new THREE.Fog(0xffffff, 50, 2000); //(色, 開始距離, 終点距離)
        // クロック(時間)の生成
        clock = new THREE.Clock();
        // アニメーション
        tick();
        function tick() {
          requestAnimationFrame(tick);
          group.rotation.y += clock.getDelta() * 0.08;
          renderer.render(scene, camera);
        }
        // リサイズ処理
        onResize();
        window.addEventListener('resize',onResize);
        function onResize() {
          const width = window.innerWidth;
          const height = window.innerHeight;
          renderer.setPixelRatio(window.devicePixelRatio);
          renderer.setSize(width,height);
          camera.aspect = width/height;
          camera.updateProjectionMatrix();
        }
      }
    </script>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>

こちらのコードについて、工程ごとに解説していきます。

環境準備

まずは下準備をします。ページの読み込みを待ってから、canvas要素の準備・レンダラーの準備を行います。

// サイズを指定
const width = 960;
const height = 540;
// canvas要素の参照を取得する
const canvas = document.querySelector('#myCanvas');
// レンダラーを作成
const renderer = new THREE.WebGLRenderer({
  canvas: canvas,
  alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);

続けて、シーンとカメラも用意しましょう。

// シーンを作成
const scene = new THREE.Scene();
// カメラを作成
const camera = new THREE.PerspectiveCamera(45, width / height);
camera.position.set(0, 0, +1100);

ここまでのコードで、3Dオブジェクトを生成・描画するための空間を構築することができました。
次の項目から、新しい要素が加わってきます。

オブジェクトの生成

前回と大きく異なる要素として、「描画するオブジェクトの数」が挙げられると思います。今回は200個ほどのオブジェクトを生成し、それらの座標・サイズ・角度をランダムに設定して配置します。最終的に、「複数のオブジェクトを一つのグループとして扱い、そのグループに対して回転アニメーションを設定する」という流れになります。

//オブジェクトをグループに格納し、グループをシーンに追加
group = new THREE.Group();
// ジオメトリーとマテリアルを定義
const geometry = new THREE.BoxBufferGeometry(20,1,20);
const material = new THREE.MeshStandardMaterial({
  color: 0xE1EDED,
  flatShading: true
});
// オブジェクトを200個生成
for ( let i = 0; i < 200; i ++ ) {
  const mesh = new THREE.Mesh( geometry, material );
  // 座標をランダムで決定
  mesh.position.x = Math.random() * 500 - 250;
  mesh.position.y = Math.random() * 500 - 250;
  mesh.position.z = Math.random() * 500 - 250;
  // サイズをランダムで決定
  mesh.scale.setScalar( Math.random() * 2 + 1 );
  // 角度をランダムで生成
  mesh.rotation.x = Math.random() * Math.PI;
  mesh.rotation.y = Math.random() * Math.PI;
  mesh.rotation.z = Math.random() * Math.PI;
  group.add(mesh);
}
scene.add(group);

200個のオブジェクトはそれぞれ独立して扱うのではなく一つのグループとして取り扱いますので、「THREE.Groupクラス」を用いて変数groupを用意します。

その後は通常のオブジェクトと同様、ジオメトリ・マテリアルを定義します。「BoxBufferGeometry」で薄い直方体、「MeshStandardMaterial」で均一な塗りつぶしという風に設定しました。

for文を用いて、オブジェクトを200個作成します。先に用意したジオメトリ・マテリアルの情報でメッシュを作成したら、そのメッシュの座標・サイズ・角度をランダムに設定します。座標については、0以上1未満の数をランダムで生成してくれる「Math.random」を用いると良いでしょう。角度を定義する際には「Math.PI」で円周率を取得・使用しましょう。「add()」を用いて、生成したメッシュをグループに追加します。

最終的に、200個のメッシュが格納されたグループをシーンに追加することで、画面上に描画されるようになります。

光源の設置

前回にはなかった要素として、「光源」を追加してみたいと思います。光源を設置することで物体に光が当たり、オブジェクトの質感もわかりやすくなります。今回は、特定の方向に放射される「平行光源」と3D空間全体に光を当ててくれる「半球光源」を設置します。

//半球光源の生成 new THREE.DirectionalLight(色, 光の強さ)
const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
hemiLight.position.set( 0, 1000, 0 );
scene.add( hemiLight );
// 平行光源の生成 new THREE.HemisphereLight(空の色, 地の色, 光の強さ)
const dirLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
dirLight.position.set( - 3000, 1000, - 1000 );
scene.add( dirLight );

フォグの追加

さらに新しく「フォグ」も付け足しましょう。フォグ(=fog)とは霧・濃霧という意味で、遠くのものが霞んで見えるような効果を加えることができます。これにより、空間の奥行きを演出することができます。

// フォグの追加
scene.fog = new THREE.Fog(0xffffff, 50, 2000); //(色, 開始距離, 終点距離)

アニメーションの設定

最後にアニメーションの設定を行います。「オブジェクトのグループごと回転させる」ことは先に述べましたが、今回は「異なるブラウザのサイズでも3Dを全画面に表示する」というリサイズ処理の仕組みも実装したいと思います。

まずはアニメーションからですが、「時間経過で関数を呼び続ける」「表示結果を最新のものに更新する」という基本的な流れは前回のものと変わりません。異なるのは回転の挙動を「clock.getDelta()」で制御していることです。「* 0.1」の数値の部分を変更すると、回転のスピードを制御することができます。

// クロック(時間)の生成
clock = new THREE.Clock();
// アニメーション
tick();
function tick() {
  requestAnimationFrame(tick); //関数を呼び続ける
  group.rotation.y += clock.getDelta() * 0.1; //回転
  renderer.render(scene, camera); 表示結果を更新
}

続けてリサイズ処理についてです。リサイズ時に「renderer.setSize」でレンダラーのサイズを画面幅に合わせること、「camera.aspect」でカメラの縦横比を正しく調整することがポイントです。

// リサイズ処理
onResize();
window.addEventListener('resize',onResize); //リサイズイベント発生時に実行
function onResize() {
  // サイズ(画面の幅・高さ)を取得
  const width = window.innerWidth;
  const height = window.innerHeight;
 // レンダラーのサイズを調整する
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width,height);
  // カメラのアスペクト比を正す
  camera.aspect = width/height;
  camera.updateProjectionMatrix();
}

以上で完成です!

RECENT ENTRIES

  • 【Javascript】GSAPで高度なアニメーションをお手軽に実装しよう

    VIEW MORE
  • 【Javascript】GSAPを使ってアニメーション実装をもっと自由に!

    VIEW MORE
  • 【Jsライブラリ】vanilla-tilt.jsで目を引くホバー演出を実装しよう

    VIEW MORE

CONTACT

contact