旧ブログ(ISSEN)から移行しました

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

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

杉田侑祐
杉田侑祐9分で読めます

はじめに

この記事の概要

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

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

  • Three.jsの光源について

前回の記事はこちら:

【初学者向け】Three.jsの概要とscene・camera・rendererについて

この記事ではThree.jsの3Dオブジェクトを表示するための初期準備方法を説明しています。Three.jsやWebGLについて学ぶことが出来ます。

【初学者向け】Three.jsで3Dオブジェクトを表示しよう!

この記事ではThree.jsの3Dオブジェクトを表示するためのGeometry・Materialを解説しています。Three.jsやWebGLについて学ぶことが出来ます。

対象読者

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

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

この記事で扱う内容:

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

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

  • JavaScriptの基本的な使用方法

今回の成果物

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

前回までのコード

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 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では様々な光源の種類があり、印象が変わります。光源の概要を紹介していきます。

環境光源(AmbientLight)の概要

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

three.js docs

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

平行光源(DirectionalLight)の概要

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

three.js docs

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

点光源(PointLight)の概要

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

three.js docs

threejs.org
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)の概要

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

three.js docs

threejs.org
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)の概要

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

three.js docs

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

矩形光源(RectAreaLight)の概要

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

three.js docs

threejs.org
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, //終了地点
  )
  //カメラの位置の変更
  camera.position.set(0, 0, +500)
 
  //レンダラー
  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 texture = new THREE.TextureLoader().load("./textures/Water.jpg") //画像の読み込み
  let ballMaterial = new THREE.MeshPhysicalMaterial({
    map: texture, // 画像の指定
    color: 0x66ffff, // 青色を追加
    metalness: 0.4, // 金属度を低く設定
    roughness: 0.5, // 粗さを中程度に設定
  })
 
  //メッシュ = ジオメトリ + マテリアル
  let ballMesh = new THREE.Mesh(ballGeometry, ballMaterial)
  //シーンに追加
  scene.add(ballMesh)
 
  // 環境光源
  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)
 
  animate()
}
 
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の紹介は終わりになります!

この記事を書いた人

杉田侑祐
杉田侑祐

TOKOSのフロントエンドエンジニア兼UI/UXデザイナー。このブログではフロントエンドメインで投稿しています。HIPHOPとゲームが好きです✌️