Three.jsこと始め

Three.jsは、JavaScriptを使った3Dグラフィックスライブラリです
WebGLを使ってブラウザ上で3Dグラフィックスを実現することができます
Three.jsは専門家向けのライブラリであり、高度な3Dグラフィックスアプリケーションの作成を目的としています

セットアップ

npmからインストール

npm install three
import * as THREE from 'three';
const scene = new THREE.Scene();

//または
import { Scene } from 'three';
const scene = new Scene();

CDNを使用する
モジュール版が推奨されている(モジュール未使用版もある)
importmapを使えばNode.jsと同じようにパッケージ名を書くだけにできます
*importmapはまだすべてのブラウザでサポートされているわけではないため、es-module-shims.js(ポリフィル)が必要

<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/three@0.121.1/build/three.module.js"
    }
  }
</script>
<script type="module">
  import * as THREE from 'three';
  const scene = new THREE.Scene();

</script>

OrbitControlsをCDNから利用する場合
*OrbitControlsを使うとカメラの動きをマウスでコントロールできます

<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>

<script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/three@0.121.1/build/three.module.js",
      "three/addons/": "https://unpkg.com/three@0.121.1/examples/jsm/"
    }
  }
</script>

<script type="module">
 import * as THREE from 'three';
 import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
</script>

OrbitControlsはnpmではインストールは不要、個別にインポートする必要があります

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls( camera, renderer.domElement );

ブラウザーがWebGLのサポートを確認して、サポートされていない場合はユーザーにメッセージを表示

if ( WebGL.isWebGLAvailable() ) {

	// 

} else {
	alert("WebGLにブラウザーまたはマシンが対応していない可能性があります。");
  return;
}

3Dシーンの構築

3Dの世界を描画するためには、Scene(シーン)、Mesh(メッシュ)、Camera(カメラ)、Renderer(レンダラー)といった要素が必要。

  • Scene(シーン)
    3Dオブジェクト、光源などが配置される「容器」。
  • Mesh(メッシュ)
    3Dオブジェクトを作成するためには、Geometry(形状)とMaterial(材質)が必要。
    例えば、形状にはBoxGeometry(箱)、材質にはMeshBasicMaterial(基本的な材質)が用いられています。
  • Camera(カメラ)
    シーンがどのように見えるかを決定。
    例えば、PerspectiveCamera(透視投影カメラ)を使用する場合にはfield of view(視野角)とaspect ratio(アスペクト比)が重要。
  • Renderer(レンダラー)
    シーンとカメラを元に画面に描画する。
    Three.jsのWebGLRendererクラスを用いてレンダラーを作成。

描画してみる

シーンとカメラをrenderメソッドに渡して、3Dオブジェクトを描画

renderer(レンダラー)・scene(シーン)・camera(カメラ)のセットアップ
*rendererはcameraに基づいてsceneがどのように見えるかを計算します

//シーン
const scene = new THREE.Scene();
//レンダラー
/*antialias:輪郭がギザギザになることを抑える処理
有効にすると計算量が増え動作が重くなる場合があります*/
const renderer = new THREE.WebGLRenderer({antialias: true});
document.body.appendChild( renderer.domElement ); //ページに追加する
/* HTMLに配置したcanvas要素を指定する場合
const renderer = new THREE.WebGLRenderer({
 canvas: document.querySelector('#myCanvas')
});
*/
/*デバイスのピクセル比を設定
renderer.setPixelRatio(window.devicePixelRatio);
デバイスのピクセル比を考慮してキャンバスのサイズを変更
renderer.setSize( window.innerWidth, window.innerHeight );
*/
//カメラ
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.z = 5;

備考 THREE.Object3Dについて

THREE.Object3DはThree.jsの基本クラスです。
シーンやカメラもこのクラスを基にしています。
例えば、カメラは自分のプロパティとメソッド以外にもObject3DとCameraのプロパティとメソッドが使えます

Three.js内でのオブジェクトはTHREE.Object3Dを基に統一されています。

Object3Dクラスの主なプロパティやメソッド

  • position:オブジェクトの位置を示すVector3オブジェクトです
  • rotation:オブジェクトの回転
  • scale:オブジェクトの拡大縮小
  • children:子オブジェクトの配列
  • add(child):子オブジェクトを追加
  • remove(child):子オブジェクトを削除
  • traverse(callback):すべての子オブジェクトを再帰的にトラバースし、コールバック関数を呼び出します
  • clone(recursive):オブジェクトを複製

*ちなみに数字は相対的な値なので比率が重要

rendererのメソッドの一部抜粋

  • setSize(width, height , Boolean)
    描画バッファのサイズ(ピクセルの数)を widthとheightを基にして準備
    第三引数はキャンバス要素のスタイルを自動的に更新するかどうかを制御
    *デフォルトはtrueでレンダラーはキャンバス要素のスタイル属性を自動的に更新して、指定されたwidthとheightに合わせます
  • setPixelRatio(pixelRatio)
    キャンバスのピクセル密度を調整
    備考:そもそもキャンバスのサイズは2種類あります
    • CSSで設定されるキャンバスの表示サイズ
      canvas.clientWidth・canvas.clientHeight
    • キャンバス自体のピクセル数(ディスプレイで異なる)
      canvas.width・canvas.height
  • setClearColor ( color, alpha )
    シーンをクリアする際の背景色と透明度を設定
    color: 背景色
    alpha: 不透明度(0.0から1.0)
  • shadowMap.enabled = true;
    シーン内の影の計算を有効にします
    デフォルト: false(影の計算はデフォルトで無効)
    plane.receiveShadow = true;(影を落とされる物体)
    cube.castShadow = true;(影を落とす物体)
    *影の計算はパフォーマンスに大きな影響を及ぼす可能性あり
  • outputEncoding = THREE.sRGBEncoding;
    sRGB色空間にエンコードする
    *デフォルトはリニア色空間(ガンマ補正なし)THREE.LinearEncoding

備考
背景色を設定する
scene.background:テクスチャーやキューブマップなどのより複雑な背景を設定できます
renderer.setClearColor:単一の背景色を設定するためのシンプルな方法

scene.background = new THREE.Color( 0xffffff );

renderer.setClearColor( 0xffffff );

余談 
0xffffff(JavaScriptの16進数形式) ’#ffffff’(CSS形式の16進数の色コード)どちらの形式も受け付けます

レンダリングの背景を透明にすることができます

const renderer = new THREE.WebGLRenderer({ alpha: true });
//または
//2番目のパラメータはアルファ値で、0を指定すると透明になります。
renderer.setClearColor(0x000000, 0);

立方体はMesh(メッシュ)というオブジェクトを使用して作成します
メッシュを作るには、ジオメトリ(形状)とマテリアル(素材)が必要です
*three.jsで描画するものはシーンに追加する必要があります

const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshNormalMaterial();
// new THREE.Mesh(ジオメトリ,マテリアル)
const cube = new THREE.Mesh( geometry, material );
// シーンに追加
scene.add( cube );

シーンのレンダリング
requestAnimationFrameのタイミングにあわせて、シーンをレンダリングを行うように設定

function animate() {
	requestAnimationFrame( animate );
	cube.rotation.x += 0.01;  //cube.rotateX(0.01)でもいい
	cube.rotation.y += 0.01;  //cube.rotateY(0.01)でもいい
//レンダラーのrender関数にシーンとカメラに渡し、シーンをレンダリング
	renderer.render( scene, camera );
	};
animate();

/*またはrenderer.setAnimationLoop(callback)
requestAnimationFrameの代わりに使用できる
nullが渡されるとアニメーションが停止 
renderer.setAnimationLoop(animate)
function animate() {
//XとY回転
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
}
*/

キャンバスの解像度をデバイスのピクセル比に基づいて調整

備考:renderer.setPixelRatioについて
https://threejs.org/manual/#ja/responsive

//非推奨(デバイスのピクセル比を設定)
renderer.setPixelRatio(window.devicePixelRatio);

//setPixelRatioとsetSize を組み合わせて使用する
//高解像度のデバイスではパフォーマンスの問題が発生するので2に制限
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
//自分でキャンバスのサイズを直接指定して、デバイスピクセル比に対応した描画バッファを準備
renderer.setSize( window.innerWidth, window.innerHeight );

ウィンドウのリサイズ時にカメラのアスペクト比とレンダラーのサイズを更新

init();
window.addEventListener('resize', init);
function init() {
  //サイズを取得
  const width = window.innerWidth;
  const height = window.innerHeight;
  //レンダラーの調整
  resizeRendererToDisplaySize(renderer);
  // カメラのアスペクト比
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
}
function resizeRendererToDisplaySize(renderer) {
  const canvas = renderer.domElement;
  const pixelRatio = window.devicePixelRatio;
  const width = (canvas.clientWidth * pixelRatio) | 0;
  const height = (canvas.clientHeight * pixelRatio) | 0;
  const needResize = canvas.width !== width || canvas.height !== height;
  if (needResize) {
    renderer.setSize(width, height, false);
  }
  return needResize;
}

追記:改良版
sizesオブジェクトを使用
ウィンドウのリサイズごとにサイズを更新し、レンダラーとカメラをその新しいサイズに合わせる
*デバイスのピクセル比は最大値を2に制限

const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

const renderer = new THREE.WebGLRenderer({
    canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

window.addEventListener('resize', () =>{
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

ジオメトリ

ジオメトリは3Dモデルの形状を定義するためのデータ構造です
ジオメトリは「頂点、面、法線、テクスチャ座標」などの情報を持ちます

一般的に、ジオメトリは以下の手順で作成されます

  1. 頂点の作成
    頂点(3次元空間内の位置を表す座標)を定義します
  2. 面の作成
    頂点を結んで面を作成
    面は頂点を結んでできる平面のことで多角形で表現されます
    ジオメトリの面は、通常三角形か四角形です
    *三角形が一般的に使用される理由は、WebGLが三角形を描画することに特化しているためです
  3. 法線の計算
    各面の法線ベクトルを定義
    法線は面の向きを定義するために使用されます
    法線ベクトルは、面の平面に垂直な単位ベクトルです。
  4. テクスチャ座標(UV座標)の作成
    テクスチャマッピングを行う場合はテクスチャ座標を追加する必要があります
    テクスチャ座標は、3Dモデルの表面に貼り付けられる2D画像の位置を定義します。

ジオメトリは要素を直接定義することができる他、事前に用意されているジオメトリを使用することもできます。

Three.jsに用意されているさまざまなジオメトリ(形状)の抜粋

  • 箱:BoxGeometry(width, height,奥行き)
  • 球体:SphereGeometry(半径,widthの分割数, heightの分割数 )
  • 20面体:IcosahedronGeometry(半径)
  • 12面体:DodecahedronGeometry(半径)
  • 8面体:OctahedronGeometry(半径)
  • 4面体:TetrahedronGeometry(半径)
  • 円錐:ConeGeometry( 半径,高さ,Radialの分割数 )
  • 円柱:CylinderGeometry(radiusTop,radiusBottom,高さ,Radialの分割数)
  • ドーナツ:TorusGeometry(半径、チューブ半径、Radialの分割数、tubularの分割数 )
  • 平面:PlaneGeometry( width, height )
  • 平らな円:CircleGeometry(半径,分割数 )

ShapeGeometryExtrudeGeometryで星型のオブジェクトを作る
*ExtrudeGeometryは2Dシェイプを3Dジオメトリに押し出します

const geometry = createStarGeometry();
function createStarGeometry(innerRadius = 0.4, outerRadius = 0.8, points = 5) {
  const shape = new THREE.Shape();
  const PI2 = Math.PI * 2;
  const inc = PI2 / (points * 2);
  shape.moveTo(outerRadius, 0);
  let inner = true;
  for (let i = inc; i < PI2; i += inc) {
    const radius = inner ? innerRadius : outerRadius;
    shape.lineTo(Math.cos(i) * radius, Math.sin(i) * radius);
    inner = !inner;
  }
  const extrudeSettings = {
    steps: 1,
    depth: 1,
    bevelEnabled: false,
  };
  return new THREE.ExtrudeGeometry(shape, extrudeSettings);
}

余談:度からラジアンへの変換

const deg = 45
const rad = deg * (Math.PI / 180 )

カメラ

3Dシーンにおいて、カメラの「カメラの種類」「位置」「方向」「FOV」は「どう見えるか」を決める非常に重要な要素です

カメラの種類と「位置」「方向」「FOV」

  • PerspectiveCamera : 遠くにある立方体ほど小さく描画される
    • 位置(Position)
      カメラがシーン内でどこにあるかを定義
      camera.position.set(x, y, z);で設定可能
      位置によって視点が変わり、オブジェクトの大きさや形が違って見える。
    • 方向(Direction)
      通常、lookAt()メソッドでカメラが向く方向を設定(カメラのrotationプロパティを直接変更することもできます)
      このメソッドによって、どのオブジェクトや座標にフォーカスするかが決まる。
    • FOV(Field of View)
      視野角を設定。大きいほど多くのものが見え、小さいほどズームインして見える。
  • OrthographicCamera : 立方体が同じサイズで描画される
    遠近感がなくオブジェクト同士の相対的な位置関係がそのまま保たれ、方向性や位置関係は直結しています
    • 位置(Position)
      PerspectiveCameraとは違い、位置が変わってもオブジェクトの大きさは変わらない。
    • 方向(Direction)
      PerspectiveCameraと同様、lookAt()で方向を設定。
    • FOV
      FOVが存在しない。
      代わりに、カメラの視野はleft, right, top, bottom, near, farプロパティで直接定義。

デフォルトでカメラのFOVは垂直方向に設定されています

camera.up.set(x, y, z)はThree.jsでカメラの「上方向」を設定するメソッドです
デフォルトでは、このベクトルは(0, 1, 0)に設定されており、「y軸が上」という世界観です

例えば上から見下ろす場合はZ軸の正が上だとカメラに伝える必要があります
camera.up.set(0, 0, 1)

PerspectiveCameraのプロパティ(fov, aspect, near, far)やOrthographicCameraのプロパティ(left, right, top, bottom, near, farなど)を動的に変更した後、有効にするにはupdateProjectionMatrixを呼び出す必要がある
*例えば:ウィンドウがリサイズされたときにはアスペクト比を変更することが重要です

camera.updateProjectionMatrix();

// カメラのアスペクト比
camera.aspect = width / height;
camera.updateProjectionMatrix();

PerspectiveCamera

PerspectiveCameraについて
参照:https://threejs.org/manual/#ja/fundamentals
4つの引数で錐台(先端が切り取られたピラミッドのような3D形状)を定義します
*錐台の外側は何も描画しません

const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
const perspectiveCamera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 100);

// カメラが近づく: オブジェクトが大きく見える
perspectiveCamera.position.z = 2;

// カメラが上昇: シーンの底部がより可視化される
perspectiveCamera.position.y = 2; 

// カメラが右に移動: シーンが左にスクロールするように見える
perspectiveCamera.position.x = 2; 

// 方向: カメラが前を向く 正面のオブジェクトが最も鮮明に見えます
perspectiveCamera.lookAt(0, 0, 0); 

// カメラが(2, 0, 0)座標を中心にしてシーンを観察
//シーン内でx=2, y=0, z=0の位置がカメラの中心となり
//シーン内の他のオブジェクトもこの点を中心に配置されるように見え
//座標に近いオブジェクトは大きく、遠いオブジェクトは小さく見えます
perspectiveCamera.lookAt(2, 0, 0);

CameraHelper(カメラの錐台を描画)
オブジェクトがカメラの内にあるか等の確認ができます

const cameraHelper = new THREE.CameraHelper(camera);
scene.add(cameraHelper);
  • FOV
    FOVはカメラがどれだけ「広く」見るかを決定する角度です。
    通常、この角度はビューポート(画面)の上端と下端の間で形成されます。
    パースペクティブカメラ(遠近カメラ)では、この角度は通常「度」で表されます。ラジアンではありません。
    視野角が大きいほど、視界が広くなり、遠近感も強調されます。
  • Aspect(アスペクト比)
    これはCanvas(描画領域)の幅と高さの比率です。
    カメラのアスペクト比をCanvasのアスペクト比に合わせることで、ウィンドウのサイズが変わっても形状のプロポーション(縦横比)が保たれます。
  • Near(ニア)と Far(ファー)
    これらはカメラが「見る」範囲の前後を定義します。
    “Near”はカメラからどれだけ近くを見るか、”Far”はどれだけ遠くを見るかを表します。
    この範囲の外のオブジェクトはレンダリングされません。

「Fov」と「カメラのアスペクト比」と「ビューポートのアスペクト比」の関係について
FOV:カメラが「見る」垂直方向の視野角です
カメラのアスペクト比:3Dレンダリングにおいて、カメラが「見る」画面の幅と高さの比率
ビューポートのアスペクト比:実際の画面(ウィンドウやディスプレイ)の幅と高さの比率

  • FOVは視野の「高さ」を決定します。これは垂直方向の視野角を意味し、一般的に角度で表されます。
  • カメラのアスペクト比が視野の「幅」に影響します。具体的には、FOVで設定した視野の高さにアスペクト比を掛けることで、実際にレンダリングされる視野の「幅」が決まります
  • ビューポート(実際の表示画面)のアスペクト比とカメラのアスペクト比が一致しない場合、表示されるオブジェクトが歪んで見える可能性があります。

OrthographicCamera

OrthographicCameraについて

const camera = new THREE.OrthographicCamera( left, right, top, bottom, near, far );
  • left, right: カメラが左右にどれだけ見るか。
  • top, bottom: カメラが上下にどれだけ見るか。
  • near, far: PerspectiveCameraと同様、これらはカメラがどれだけ近く、または遠くを見るかを制御します

OrthographicCameraではレンダリングエリア: 通常、left、right、top、bottomで直接指定されます。
FOVが関与しないので、アスペクト比が直接レンダリングエリアに影響を与えます

例えば、キャンバスのサイズが800×600(アスペクト比1.3333)の場合
OrthographicCameraのレンダリングエリアもこのアスペクト比に合わせて調整する必要があります。具体的には、leftとright(またはtopとbottom)の値をこのアスペクト比でスケーリングします。そうしないと、表示されるオブジェクト(例えばキューブ)が歪んで見える可能性があります。

const aspectRatio =  1.3333  //(800 / 600)
const camera = new THREE.OrthographicCamera(-1 * aspectRatio, 1 * aspectRatio, 1, -1, 0.1, 100);

OrbitControls

備考:Three.jsでは、ユーザーとのインタラクションを容易にするためにいくつかの「controls」が提供されています。これは基本的にカメラの動きを制御するためのユーティリティです
最も一般的に使用されるのは「OrbitControls」です。
「OrbitControls」使用すると、ユーザーはマウスやタッチ操作でシーンを自由に回転、ズーム、パン(移動)することができます

THREE.OrbitControlsクラス
マウス操作でカメラを制御できます
マウスホイール:ズーム
左ボタンでドラッグ:回転
右ボタンでドラッグ:パン(向きを左右に振る)
.update()はカメラの位置を手動で変更した後に呼び出す必要がある

// カメラを作成
const camera = new THREE.PerspectiveCamera(/*省略*/);
// カメラの初期座標を設定
camera.position.set(0, 0, 5);
const controls = new OrbitControls( camera, renderer.domElement );
/*原点から5単位上にターゲットを設定する場合の例
controls.target.set(0, 5, 0);
*/
/*滑らかなカメラコントローラーを制御する場合
controls.enableDamping = true; // ダンピングを有効に
controls.dampingFactor = 0.2; // ダンピング係数を0.2に設定
アニメーションループ内でupdate()を呼び出す必要がある
controls.update();
*/

*ダンピング(減衰)効果:ユーザーがマウスを動かしてカメラを回転させた後、カメラはすぐには停止せずにゆっくりと減速します

座標を表示する
X 軸(赤)・ Y 軸(緑)・Z 軸(青)

// 引数は線の長さ(デフォルトは 1)
const axes = new THREE.AxisHelper(25);
scene.add(axes);

Three.jsの座標系は

  • X軸: 左から右へ正
  • Y軸: 下から上へ正(上方向)
  • Z軸: 奥から手前へ正(スクリーンから外へ)

*DOMと異なるので注意

平面にグリッドを表示する

//sizeグリッドのサイズ/デフォルトは10 
//Divisionsグリッド全体の分割数/デフォルトは10
const gridHelper = new THREE.GridHelper( size, divisions );
scene.add(gridHelper);

マテリアルとライト

「ライトが必要なマテリアル」と「ライト不要のマテリアル」があります

色は16進数(例:0xFF0000)または
CSS文字列(例:'#F00''red''rgb(255,0,0)''hsl(0,100%,50%)')で設定できます

備考

ColorはRGB(Red、Green、Blue)値に基づく色を表現するためのクラスです
*値は0〜1

const color = new THREE.Color( r, g, b );

ColorクラスではRGB値の他にHSL(Hue色相、Saturation彩度、Lightness明度)値でも色を表現きます
ColorオブジェクトをHSL値で作成する

const color = new THREE.Color();
color.setHSL( h, s, l );

ライト

Three.jsにはさまざまなライトが用意されています

  • AmbientLight(環境光)
    特性
    シーン全体に均一な光を提供します。
    シャドウを生成しない。
    使いどころ
    ベースラインとなる照明を提供する場合。
    全体的な明るさを調整する場合。
  • DirectionalLight(指向性光)
    特性
    一定方向からの光をシミュレートします。
    シャドウを生成することができます。
    使いどころ
    屋外環境での太陽光を模倣する場合。
    シーン全体に一方向から照らす光が必要な場合。
  • HemisphereLight(半球光)
    特性
    地面と天井(または空)からの二色の光を模倣します。
    シャドウを生成しない。
    使いどころ
    アウトドアやインドアで自然な照明を模倣する場合。
    環境光をよりリアルにしたい場合。
  • PointLight(点光源)
    特性
    単一の点から全方向に光を放射します。
    シャドウを生成することができます。
    使いどころ
    ランプ、キャンドルなどの局所的な光源を模倣する場合。
    特定のオブジェクトを強調する場合。
  • SpotLight(スポットライト)
    特性
    特定の方向に光を放射する。
    シャドウを生成することができます。
    使いどころ
    ステージ照明やフラッシュライトなど、特定の方向に強い光が必要な場合。
    特定の場所やオブジェクトに焦点を当てる場合。
  • RectAreaLight(矩形エリア光)
    特性
    矩形形状の光源からの照明を模倣します。
    シャドウを生成しない。
    使いどころ
    写真撮影のセットや屋内の天井照明など、矩形の光源が必要な場合。
    ソフトで広がりのある光が必要な場合。
    *RectAreaLight はMeshStandardmaterial および MeshPhysicalmaterial でのみ機能します。

一般的な引数

  • AmbientLight(環境光)
    color: 光の色。
    intensity: 光の強度。
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
  • DirectionalLight(指向性光)
    color: 光の色。
    intensity: 光の強度。
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
  • HemisphereLight(半球光)
    skyColor: 天空の色。
    groundColor: 地面の色。
    intensity: 光の強度。
    const hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 0.5);
  • PointLight(点光源)
    color: 光の色。
    intensity: 光の強度。
    distance: 光の減衰距離。
    decay: 減衰率。
    const pointLight = new THREE.PointLight(0xffffff, 0.5, 100, 2);
  • SpotLight(スポットライト)
    color: 光の色。
    intensity: 光の強度。
    distance: 光の減衰距離。
    angle: ビームの角度。
    penumbra: ビームの周辺のぼかし。
    decay: 減衰率。
    const spotLight = new THREE.SpotLight(0xffffff, 0.5, 100, Math.PI / 4, 0.1, 2);
  • RectAreaLight(矩形エリア光)
    color: 光の色。
    intensity: 光の強度。
    width: 矩形の幅。
    height: 矩形の高さ。
    const rectAreaLight = new THREE.RectAreaLight(0xffffff, 0.5, 10, 10);

シーンにライトを追加する

const light = new THREE.AmbientLight(0xFFFFFF, 1.0);
scene.add(light);

DirectionalLight と SpotLightの光源は指向性があり、どの方向に光が放たれるかを制御するためにターゲットを使用します
*特にターゲットを設定しない限り、光は原点(0,0,0)方向に照射されます

  • DirectionalLightでは、光はターゲットの位置に向かって一定の方向で照らされます。
  • SpotLightでは、ターゲットの方向に光が照射され、その光は設定された角度(アングル)と距離で広がります。

ターゲットがシーンに追加されている必要があります

//DirectionalLightはターゲットは通常座標(x, y, z)で指定
const light = new THREE.DirectionalLight(0xFFFFFF, 1.0);
light.target.position.set(-5, 0, 0);
scene.add(light.target);

//SpotLighttは通常オブジェクトそのものを指定
const light = new THREE.SpotLight(0xffffff, 0.5, 100, Math.PI / 4, 0.1, 2);
light.target =オブジェクト;
scene.add(オブジェクト)

コストランク

  • 低: AmbientLight, HemisphereLight
  • 中: DirectionalLight, PointLight
  • 高: SpotLight, RectAreaLight

ライトのヘルパー
例:SpotLightHelper

const spotLight = new THREE.SpotLight(0xffffff);
const lightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotLight);
scene.add(lightHelper);
// ループ内でヘルパーを更新する場合
lightHelper.update();

//DirectionalLightのヘルパー
//const dLightHelper = new THREE.DirectionalLightHelper(light, 5);
  • HemisphereLightHelper
  • DirectionalLightHelper
  • PointLightHelper
  • RectAreaLightHelper
  • SpotLightHelper

*注意:RectAreaLightHelperはThree.jsのコアパッケージには含まれていません
three/examples/jsm/helpers/RectAreaLightHelper.js

マテリアル

備考:「シェーダー」は、3Dモデルの見た目(色や光など)を決めるプログラムです。
THREE.jsでは「マテリアル(内部でシェーダーを使かっています)」でリアルな表現から単純なフラットシェーディングまで、さまざまな効果を簡単に実装できます。
また、テクスチャも使えて、更にリアルな効果を出せます。
*テクスチャについてはhttps://koro-koro.com/threejs-no2/

const material = new THREE.MeshBasicMaterial({ color: 0xFF0000});

//または
const material = new THREE.MeshPhongMaterial();
material.color.set(0x00FFFF); 

//その他のプロパティの一例
//flatShading:立方体のときの面がそのまま残るような見た目
material.flatShading=true
//side:面のどちら側をレンダリングするか THREE.BackSideand(裏面) 
material.side = THREE.DoubleSide(両側)

マテリアルの種類

ライト不要(ライトに反応しない)のマテリアル

  • MeshNormalMaterial
    RGBで可視化するマテリアル
  • MeshBasicMaterial
    陰がつかないので均一な塗りつぶした状態にる

ライトが必要なマテリアル

  • MeshLambertMaterial(光沢感がない)
    頂点でのみ光を計算します
    emissive: 自己発光色
    reflectivity: 反射率
  • MeshToonMaterial(2トーンの外観ができる)
  • MeshPhongMaterial(光沢感のある)
    全てのピクセルで光を計算
    specular: スペキュラ(反射)色
    shininess: 光沢度
  • MeshStandardMaterial(光の反射や散乱など物理ベースレンダリング(PBR)を再現)
    roughness(粗さ)はshininessの反対で 0(光沢)〜1(マッド)
    metalness(金属的) 0(非金属)〜1(金属)
    *MeshPhysicalMaterial は布やガラス、コーティングされた表面などよりリアルで複雑な物理表現も可能だが負荷が高い

マテリアルの描画は多くのGPUパワーが必要なので必要ない場合は、単純な素材を使用します

  1. MeshBasicMaterial
  2. MeshLambertMaterial
  3. MeshPhongMaterial
  4. MeshStandardMaterial

MeshBasicMaterialのプロパティ

mapプロパティを使用すると、ジオメトリの表面にテクスチャを適用できます

const material = new THREE.MeshBasicMaterial({
    map: texture //ロードしたテクスチャ
});
//同じ
const material = new THREE.MeshBasicMaterial();
material.map = texture

colorプロパティを用いて、ジオメトリ全体に一様な色を適用できます

material.color = new THREE.Color('#ff0000');
//または
material.color.set('#ff0000')

wireframeプロパティをtrueにすると、ジオメトリを形成する三角形が1pxの薄い線で表示されます

material.wireframe = true;

opacityプロパティで透明度を制御できますが、これを機能させるにはtransparentプロパティをtrueに設定する必要があります。

material.transparent = true;
material.opacity = 0.5;

alphaMapプロパティを使用して、テクスチャで透明度を制御できます。

material.transparent = true;
material.alphaMap = alphaTexture; //ロードしたテクスチャ

sideプロパティで、ジオメトリのどの面(表面か裏面か、または両方)が見えるかを制御できます
デフォルトでは表面(THREE.FrontSide)が可視ですが、
裏面(THREE.BackSide)または
両方(THREE.DoubleSide)を表示させることもできます

material.side = THREE.DoubleSide;

リアルタイムで影を生成するためのステップがあります。
具体的には、各光源からシーンを「見る」ようなレンダリングが行われます
これは、光源が一種のカメラであるかのように動作します
このレンダリングの結果は、「シャドウマップ」と呼ばれるテクスチャに保存され、後で、このシャドウマップが実際のシーンに影として適用されます。

  1. レンダラーでシャドウマップを有効化
    renderer.shadowMap.enabled = true
  2. ライトの影を有効にする
    directionalLight.castShadow = true
  3. 影を生成したいオブジェクトを設定
    sphere.castShadow = true (球は影を生成)
  4. 影を受けるMeshオブジェクトを設定
    plane.receiveShadow = true (平面は影を受ける)

Three.jsで影を生成することができる光源は3種類あります

  • PointLight
    点光源はすべての方向に均等に光を放射します。
    6つの異なる方向すべてでシャドウマップが生成されるのでパフォーマンスへの影響が大きくなる可能性があります。
  • DirectionalLight
    方向光源は無限遠に位置すると考えられ、全体に平行な光線を放射します。
    OrthographicCameraが用いられtop, right, bottom, leftのプロパティで影の範囲を調整。
    これは例えば太陽光をシミュレートするのによく用います。
  • SpotLight
    スポットライトは特定の方向に円錐形の光を放射します。
    PerspectiveCameraが用いられ、fovで影の範囲を調整。

シャドウマップの最適化

  • レンダリングサイズ(Render Size)
    デフォルトでは512×512ですが、2の累乗(例: 1024×1024)でサイズを変更できます。
    directionalLight.shadow.mapSize.width = 1024;
    directionalLight.shadow.mapSize.height = 1024;
  • 近接と遠方(Near and Far)
    カメラのnearとfarプロパティを調整して、シャドウが急に切れるバグを解消できます。
    directionalLight.shadow.camera.near = 1;
    directionalLight.shadow.camera.far = 6;
  • 振幅(Amplitude)
    カメラの振幅(表示範囲)を調整できます。
    directionalLight.shadow.camera.top = 2;
    directionalLight.shadow.camera.right = 2;
    directionalLight.shadow.camera.bottom = -2;
    directionalLight.shadow.camera.left = -2;
    値を広げると、影を計算する範囲が広がるが精度が低下
    必要最小限の値に設定してパフォーマンスを向上
  • ブラー(Blur)
    radiusプロパティでシャドウのぼかしを調整できます。
    directionalLight.shadow.radius = 10;
  • シャドウマップのアルゴリズム
    複数のアルゴリズムがあり、パフォーマンスと品質のトレードオフが存在します。
    THREE.BasicShadowMap: 高速だが低品質
    THREE.PCFShadowMap: やや遅いが滑らかなエッジ
    THREE.PCFSoftShadowMap: さらに遅いが、より柔らかいエッジ
    THREE.VSMShadowMap: 遅く、制約が多いが、予期せぬ結果が出る可能性もある
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;

例:DirectionalLightの影
範囲が小さい場合、振幅の設定が必要な場合があります

light.shadow.camera.right = 100;
light.shadow.camera.left = -100;
light.shadow.camera.top = -100;
light.shadow.camera.bottom = 100;

「light.shadow.camera」 は、その光源が生成する影に用いられる仮想的な「カメラ」になり、シャドウマップ(影のテクスチャ)を生成する際の視点を決定します
カメラのヘルパーに「light.shadow.camera」 を渡すと、その仮想カメラの視野範囲、近接距離、遠方距離などが視覚的に表示され、影がおかしく見えるときに、その原因が何であるかを素早く診断できます

//カメラのヘルパーにlight.shadow.cameraを渡す
const helper = new THREE.CameraHelper( light.shadow.camera );
scene.add( helper );

*シャドウを生成することは計算負荷が高いので、できるだけ少数のライトでシャドウを有効にすることが推奨されています

*静的なオブジェクトには、3Dモデリングソフトウェアで事前にシャドウをベイク(焼き付け)し、それをテクスチャとして利用する方法があります。この手法はパフォーマンスが向上します

Fog(霧)をシーンに追加

遠くのオブジェクトを視覚的にぼかす効果であり、リアルな環境を作成するのに役立ちます
*カメラから遠いオブジェクトほど暗く(薄く)表示できます

  • new THREE.Fog(color, near, far);
    color: フォグの色を指定します。
    near: この距離からフォグが始まります。
    far: この距離でフォグが完全に色になります。
    nearからfarにかけて、オブジェクトは徐々にFogで覆われます
  • new THREE.FogExp2(color, density);
    color: フォグの色を指定します。
    density: フォグの密度を指定します。
    Fogは遠くへ行くほど急激に濃くなります

scene.fogプロパティにセットすることで、シーン全体にフォグ効果が適用されます

//far(color)からnearにcolorがフェード
scene.fog = new THREE.Fog(color, near, far);
//距離に応じて指数関数的に大きくなる
scene.fog = new THREE.FogExp2(color, density);