【初学者向け】Three.jsで3D空間に光源を追加する

はじめに

この記事の概要

こんにちは、株式会社TOKOSのスギタです!
今回はThree.js 前回3Dオブジェクトを表示していきました、今回は表示した3Dオブジェクトに光源を追加していきたい思います。
Three.jsを触ったことない方に向けて解説しますので、最後まで見ていただけると幸いです!

この記事では、以下の内容について詳しく解説します:

  • Three.jsの光源について

前回の記事はこちら:

対象読者

  • Three.jsを使用したことのない方
  • JavaScriptを用いたアニメーションに興味のある方

この記事で扱う内容、扱わない内容

この記事で扱う内容:

  • 光源に解説
  • 3Dオブジェクト(Geometry・Material・Mesh)の解説

この記事で扱わない内容:

  • JavaScriptの基本的な仕様方法

今回の成果物

今回は前回表示した3Dオブジェクトに対して様々な「光源」を追加していきます。
「光源」で印象が大きく左右されます。
今回は様々な「光源」を紹介します。

前回までのコード

コードの紹介

JavaScript
let scene, camera, renderer;

window.addEventListener("load", init);

function init() {
  // シーン
  scene = new THREE.Scene();

  // カメラ
  camera = new THREE.PerspectiveCamera(
    50, //視野角
    window.innerWidth / window.innerHeight, //アスペクト比
    0.1, //開始地点
    1000 //終了地点
  );
  
  //レンダラー
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);
  renderer.setPixelRatio(devicePixelRatio); //デバイスの解像度に最適化する
  renderer.setClearColor(0x000000, 1); //背景色を黒に設定(第1引数:色、第2引数:透明度)
  
   //ジオメトリ
  let ballGeometry = new THREE.SphereGeometry(100, 64, 32); //球体のジオメトリの設定 
  
  //マテリアル
  let ballMaterial = new THREE.MeshPhysicalMaterial({
    color: 0x66ffff, // 青色を追加
    metalness: 0.4, // 金属度を低く設定
    roughness: 0.5, // 粗さを中程度に設定
  });
  
  //マテリアル
  let texture = new THREE.TextureLoader().load("./textures/Water.jpg"); //画像の読み込み
  let ballMaterial = new THREE.MeshPhysicalMaterial({
    map: texture, // 画像の指定
    color: 0x66ffff, // 青色を追加
    metalness: 0.4, // 金属度を低く設定
    roughness: 0.5, // 粗さを中程度に設定
  });
}
  

function animate() {
  // フレーム単位でアニメーションを読み込む
  requestAnimationFrame(animate);
  //レンダリングの記述
  renderer.render(scene, camera);
}

光源の概要

光源を追加することで3Dオブジェクトの表示をすることができるようになります。
Three.jsでは様々な光源の種類があり印象が変わります。光源の概要を紹介していきます。

Helper関数について

Three.jsでは光源のHelper関数があります。
Helper関数を使うことにより光源の位置を可視化できより効率的に開発を進めることができます。

環境光源(AmbientLight)の概要

前回章の最後に光源を追加しました。
追加した光源はSceneに対して全体を明るくするAmbientLight になります。
AmbientLight は方向性を持たない光源で、シーン全体を均一に照らします。
影を作らず、全てのオブジェクトに均一な明るさを与えてくれます!

JavaScript
//環境光の追加
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);//環境光源の呼び出し、第一引数:光源の色、第二引数:光の強さ
scene.add(ambientLight);//Sceneに追加

平行光源(DirectionalLight)の概要

太陽光のように、一方向から平行に光を照射します。
光源の位置よりも方向が重要になります!

JavaScript
//平行光源の追加
const directionalLight = new THREE.DirectionalLight(0xffffff, 3);//平行光源の呼び出し、第一引数:光源の色、第二引数:光の強さ
directionalLight.position.set(5, 10, 7.5);//光源の位置の指定、第一引数:X軸、第二引数:Y軸、第三引数:Z軸
scene.add(directionalLight);//Sceneに追加

点光源(PointLight)の概要

点光源は空間内の1点から全方向に光を放射します。電球のように、光源からの距離に応じて光の強さが減衰します!

JavaScript
const pointLight = new THREE.PointLight(0xffffff, 5, 1000, 1.0);//点光源の呼び出し、第一引数:光源の色、第二引数:光の強さ、第三引数:距離、第三引数:最大距離距離
pointLight.position.set(0, 0, 500);//光源の位置の指定、第一引数:X軸、第二引数:Y軸、第三引数:Z軸
scene.add(pointLight);//Sceneに追加

スポット光源(SpotLight)の概要

点から円錐状に光を照射する光源で、舞台照明のような効果を作り出せます。

JavaScript
const spotLight = new THREE.SpotLight(//スポット光源の呼び出し
  0xffffff, // 第一引数:光源の色、
  1, // 第二引数:光の強さ
  500, // 第三引数:最大距離距離
  Math.PI / 10, // 第四引数:角度
  0.5, // 第五引数:エッジのぼかし具合
  0 // 第六引数:減衰率
);
spotLight.position.set(100, 100, 300);//光源の位置の指定、第一引数:X軸、第二引数:Y軸、第三引数:Z軸
scene.add(spotLight);//Sceneに追加

スポット光源についてはイメージしづらいと思うのでHelper関数で光源の状況を可視化してみたいと思います。

JavaScript
const spotLightHelper = new THREE.SpotLightHelper(spotLight);//スポット光源のHelper関数の呼び出し
scene.add(spotLightHelper);//Sceneに追加

円錐になっているものがHelper関数で光源を可視化しています。
点から円錐状に広がっていることがわかります!

半球光源(HemisphereLight)の概要

上方向と下方向で異なる色を指定することができ色の境界はグラデーションのようにすることができます!
ます!

JavaScript
const hemisphereLight = new THREE.HemisphereLight(0x0000ff, 0x00ff00, 1);//点光源の呼び出し、第一引数:上方向の光源の色、第二引数:下方向の光源の色、第三引数:光の強さ
hemisphereLight.position.set(0, 10, 0);//光源の位置の指定、第一引数:X軸、第二引数:Y軸、第三引数:Z軸
scene.add(hemisphereLight);//Sceneに追加

矩形光源(RectAreaLight)の概要

矩形光源は平面の光源で、窓からの光や蛍光灯のような柔らかい光を表現できます!

JavaScript
const rectLight = new THREE.RectAreaLight(//矩形光源の呼び出し
  0xffffff, // 第一引数:光源の色
  2, // 第二引数:光の強さ
  100, // 第三引数:光源幅
  100 // 第四引数:光源高さ
);
rectLight.position.set(0, -100, 100); //光源の位置の指定、第一引数:X軸、第二引数:Y軸、第三引数:Z軸今回は下から照らしています
rectLight.rotation.x = Math.PI * 0.5; // 光源の角度
scene.add(rectLight); // 光源をシーンに追加

様々な光源を追加する

ここからは様々な光源を追加していきます。
演出したい内容によって仕様する光源が変わります!
今回は下記を使用していきます。

  • 環境光源(AmbientLight)
    • ベースとなる光源
  • 平行光源(DirectionalLight)
    • メインの光源、太陽光のようなイメージ
  • 半球光源(HemisphereLight)
    • 影の部分を柔らかく照らす
  • 点光源(PointLight)
    • アクセント用

このように何種類かの光源を追加することにより見栄えが大きく左右されます!

JavaScript
// 環境光源
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambientLight);

// 平行光源
const mainLight = new THREE.DirectionalLight(0xffffff, 1);
mainLight.position.set(5, 10, 7.5);
scene.add(mainLight);

// 半球光源
const hemisphereLight = new THREE.HemisphereLight(0x8888ff, 0x444422, 0.5);
scene.add(hemisphereLight);

// ポイントライト光源
const pointLight = new THREE.PointLight(0xffffff, 5, 2000, 0.5);
pointLight.position.set(200, 200, 200);
scene.add(pointLight);

点光源(PointLight)にアニメーションを加える

ここから点光源にアニメーションを追加していきます。
アニメーションの内容は点光源が3Dオブジェクト中心として周りを回るようにします。
光源の位置(Position)を毎フレームごと更新する必要が有るため、animate()に追記していきます。

JavaScript
let scene, camera, renderer, controls, pointLight;//pointLightをグローバルに扱うようにする

function init() {
 //省略
 
  pointLight = new THREE.PointLight(0xffffff, 5, 2000, 0.5);
  //pointLight.position.set(200, 200, 200); positionを削除
  scene.add(pointLight);
  
  //Helper関数
  //const pointLightHelper = new THREE.PointLightHelper(pointLight, 20);
  //scene.add(pointLightHelper);
  
  //省略
}


function animate() {
  //ポイント光源の座標を動かす
  pointLight.position.set(
    200 * Math.sin(Date.now() / 1000),
    200 * Math.sin(Date.now() / 1000),
    200 * Math.cos(Date.now() / 1000)
  );
  
  //省略
}

下記が完成状態になります!
わかりやすいようにHelper関数を追加しています。
斜め45度で点光源が周回していることがわかると思います

計算式の解説は下記になります。(計算式を知りたい型は生成AIに投げてみてください!)

  • 計算式について
    • Date.now() / 1000
      • 現在時刻をミリ秒単位で取得し、1000で割ることで秒単位に変換時間の
      • 経過とともに連続的に変化する値を生成
    • 座標の計算
      • X軸:200 * Math.sin(時間)
      • Y軸:200 * Math.sin(時間)
      • Z軸:200 * Math.cos(時間)
  • 解説
    • なぜ45度になるか
      • x = y となるため、XY平面上では y = x という直線上を移動することになり45度になります!
    • 空間での軌道
      • XY平面での直線運動とZ軸方向の周期運動が組み合わさることによって滑らかで周期的なアニメーションが実現されます

最終的なコード

コードの紹介

JavaScript
let scene, camera, renderer, pointLight;

window.addEventListener("load", init);

function init() {
  // シーン
  scene = new THREE.Scene();

  // カメラ
  camera = new THREE.PerspectiveCamera(
    50, //視野角
    window.innerWidth / window.innerHeight, //アスペクト比
    0.1, //開始地点
    1000 //終了地点
  );
  
  //レンダラー
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);
  renderer.setPixelRatio(devicePixelRatio); //デバイスの解像度に最適化する
  renderer.setClearColor(0x000000, 1); //背景色を黒に設定(第1引数:色、第2引数:透明度)
  
   //ジオメトリ
  let ballGeometry = new THREE.SphereGeometry(100, 64, 32); //球体のジオメトリの設定 
  
  //マテリアル
  let ballMaterial = new THREE.MeshPhysicalMaterial({
    color: 0x66ffff, // 青色を追加
    metalness: 0.4, // 金属度を低く設定
    roughness: 0.5, // 粗さを中程度に設定
  });
  
  //マテリアル
  let texture = new THREE.TextureLoader().load("./textures/Water.jpg"); //画像の読み込み
  let ballMaterial = new THREE.MeshPhysicalMaterial({
    map: texture, // 画像の指定
    color: 0x66ffff, // 青色を追加
    metalness: 0.4, // 金属度を低く設定
    roughness: 0.5, // 粗さを中程度に設定
  });
  // 環境光源
  const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
  scene.add(ambientLight);
  
  // 平行光源
  const mainLight = new THREE.DirectionalLight(0xffffff, 1);
  mainLight.position.set(5, 10, 7.5);
  scene.add(mainLight);
  
  // 半球光源
  const hemisphereLight = new THREE.HemisphereLight(0x8888ff, 0x444422, 0.5);
  scene.add(hemisphereLight);
  
  // ポイントライト光源
  pointLight = new THREE.PointLight(0xffffff, 5, 2000, 0.5);
  pointLight.position.set(200, 200, 200);
  scene.add(pointLight);
}
  

function animate() {
  //ポイント光源の座標を動かす
  pointLight.position.set(
    200 * Math.sin(Date.now() / 1000),
    200 * Math.sin(Date.now() / 1000),
    200 * Math.cos(Date.now() / 1000)
  );

  // フレーム単位でアニメーションを読み込む
  requestAnimationFrame(animate);
  //レンダリングの記述
  renderer.render(scene, camera);
}

さいごに

今回は光源について紹介しました!
光源で見栄えが変わるため、複数の選択肢が有るということを知っておくのはとても重要だと感じました!
また光源の追加・使用方法は簡単だったでないでしょうか!
これで一通りのThree.jsの紹介は終わりになります!