Three.jsのレンダラー・ライト・カメラの使い方と設定まとめ

前回はライトを無視する MeshBasicMaterial でキューブを表示しました。今回はちゃんとライトを当てて、影を落として、マウスでカメラを回せるようにします。地味な回に見えますが、レンダラー・ライト・カメラは何を作っても必ず触る部分なので、ここの理解が後々効いてきます。

今回のデモは右上のGUIでライトの強さと視野角をいじれるようにしました。数値を動かすと見え方がどう変わるか、実際に触ってみるのが一番早いです。

デモを別タブで開く

WebGLRendererのオプション

レンダラーを作るときのオプションでよく使うのはこのあたりです。

const renderer = new THREE.WebGLRenderer({
  antialias: true, // ギザギザ(ジャギー)を軽減する
  alpha: true,     // 背景を透過させる(HTMLと重ねるときに使う)
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true; // 影を使うなら明示的にON

antialias: true はほぼ必須です。デフォルトはoffで、offのままだとオブジェクトの輪郭がギザギザになります。

alpha: true は背景を透明にするオプションで、普段は不要ですが、Three.jsのcanvasを他のHTML要素の上に重ねる構成で必要になります。この使い方はCSS3DRendererとの共存の回で詳しくやります。

あと高解像度ディスプレイで表示がぼやける場合は renderer.setPixelRatio(window.devicePixelRatio) を入れると直ります。ただしピクセル数が増えるぶん負荷も上がるので、モバイルも考えるなら上限を2に制限するのが定石です。

ライトは2種類の組み合わせから始める

Three.jsには何種類もライトがありますが、まずはこの2つの組み合わせで十分です。

// 環境光: 方向を持たず、シーン全体を均一に照らす
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);

// 平行光源: 太陽光のように一定方向から照らす
const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(5, 8, 3);
scene.add(directionalLight);

役割分担ははっきりしていて、**環境光は「真っ黒な影を作らないための底上げ」、平行光源は「立体感を出すためのメイン光源」**です。

デモで環境光を0にしてみると、光が当たっていない面が完全な黒になって不気味になります。逆に環境光だけにすると、全部の面が同じ明るさになってのっぺりします。環境光を弱め(0.3前後)+平行光源をメインに、というバランスが基本形です。

平行光源の position は「光が来る方向」を決めるためのもので、遠さは関係ありません。(5, 8, 3) でも (500, 800, 300) でも同じ向きなら結果は同じです。

似たものに HemisphereLight(空の色と地面の色の2色で照らす)もあって、屋外シーンだとこちらの方が自然になることが多いです。環境光の代わりに使えます。

影の設定は3ヶ所

影はデフォルトでは一切出ません。「レンダラー・ライト・オブジェクト」の3ヶ所で明示的にONにする必要があります。

renderer.shadowMap.enabled = true;   // 1. レンダラー
directionalLight.castShadow = true;  // 2. ライト(影を作る)
box.castShadow = true;               // 3. 影を落とす側のオブジェクト
floor.receiveShadow = true;          //    影を受ける側のオブジェクト

どれか1つでも忘れると影が出ないので、「影が出ない!」となったらこの3ヶ所を順に確認してください。私は毎回どれかを忘れます。

PerspectiveCameraの4つの引数

const camera = new THREE.PerspectiveCamera(
  70,                                     // fov: 視野角(度)
  window.innerWidth / window.innerHeight, // aspect: アスペクト比
  0.1,                                    // near: これより近くは描画しない
  1000                                    // far: これより遠くは描画しない
);

fov(視野角)は画角の広さです。デモのGUIで動かすとわかりますが、小さくすると望遠レンズのように圧縮された画になり、大きくすると広角レンズのように遠近感が誇張されます。人の視界に近いのは60〜75くらいで、迷ったら70前後にしておけば無難です。

nearとfarは描画する距離の範囲です。この範囲外のオブジェクトは存在していても描画されません。「遠くに置いたオブジェクトが表示されない」というときは、だいたいfarが小さすぎるのが原因です。かといってnearを極端に小さく、farを極端に大きくすると、奥行きの精度が足りなくなって面がチラつくZファイティングという現象が起きるので、シーンの規模に合わせてほどほどに設定します。

OrbitControlsでカメラを操作する

マウスでぐりぐり視点を回せるようにするのがOrbitControlsです。公式アドオンなのでimportmapに three/addons/ を登録していればすぐ使えます。

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);
controls.maxPolarAngle = Math.PI / 2.01; // 地面より下に回り込めなくする
controls.maxDistance = 30;               // ズームアウトの上限

そのまま使うと、カメラが地面の下に潜れてしまって床の裏側が見えたり、無限にズームアウトできてシーンが米粒になったりします。maxPolarAnglemaxDistance の2つを設定しておくと操作感が一気にまともになります。

Math.PI / 2.01 という中途半端な値は「真横(90度)よりほんの少しだけ上まで」という意味です。ぴったり Math.PI / 2 にすると地面すれすれの視点になって床が線になるので、少しだけ余裕を持たせています。

似たコントロールに MapControls もあります。こちらは地図アプリのようにドラッグで平行移動するタイプで、俯瞰で見せたいシーンに向いています。

ライトの調整はGUIを使うと早い

ライトの強さや位置は数字をいじって保存してリロード…を繰り返すと日が暮れるので、Three.js公式に同梱されているlil-guiを使うのがおすすめです。

import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

const gui = new GUI();
gui.add(ambientLight, 'intensity', 0, 3, 0.1).name('環境光の強さ');
gui.add(directionalLight, 'intensity', 0, 5, 0.1).name('平行光源の強さ');

gui.add(オブジェクト, 'プロパティ名', 最小, 最大, 刻み) だけでスライダーが生えます。開発中だけ有効にして、ちょうどいい値が見つかったらコードに書き写す、という使い方をしています。

まとめ

  • レンダラーは antialias: true が基本、影を使うなら shadowMap.enabled
  • ライトは環境光(底上げ)+平行光源(立体感)の組み合わせから
  • 影はレンダラー・ライト・オブジェクトの3ヶ所すべてでONにする
  • カメラのnear/farはシーンの規模に合わせる、fovは70前後が無難
  • OrbitControlsは maxPolarAnglemaxDistance を設定して使う

Three.js解説シリーズ(全7回)

  1. 【Three.js入門】ビルドツール不要、CDNだけで始める基本の使い方
  2. レンダラー・ライト・カメラの使い方と設定まとめ(この記事)
  3. キーボードとNipplejsでプレイヤーを動かす
  4. GLTFLoaderで3Dモデルをインポートする
  5. ファイル分割はDIパターンがおすすめ
  6. CloudflareへのデプロイとWebSocketでの位置同期
  7. WebGLレンダラーとCSS3Dレンダラーを共存させる