環境マップとHDR画像
環境マップは3D空間において環境の光や影響を表現するために使用されるテクスチャです
*周囲の光や物体の反射、屈折、散乱などの影響を反映できる
リアルな表現をするために「360度のパノラマ画像」や「HDR画像」を使用することが一般的です
備考
環境マップを生成するためのマッピング方式には2種類があります
*マッピング方式を指定するにはマテリアルのenvMap.mappingプロパティを使用します
- キューブマッピング(スカイボックス)
立方体の6面に対して画像を割り当てる方法です
環境マップを生成するために最も一般的に使用される方法でTHREE.CubeTextureLoader
を使用して読み込むことができます
*Three.js備忘録(2)のスカイボックスを参照 - 球面マッピング(360度パノラマコンテンツ)
360度全方向の環境を球体の表面にマッピングした画像です
カメラが球体の中心にあるかのように、物体表面から反射される光を計算することで周囲の環境を表現でき、環境マップの反射をより正確に再現することができます
キューブマッピングに比べて計算量が多くなりますTHREE.SphericalReflectionMapping
を使用して読み込むことができます
//6枚の画像から球面マッピング
const reflectionTexture = new THREE.CubeTextureLoader()
.setPath('path/to/textures/')
.load([
'1.jpg', '2.jpg',
'3.jpg', '4.jpg',
'5.jpg', '6.jpg'
]);
material.envMap = reflectionTexture;
material.envMap.mapping = THREE.SphericalReflectionMapping;
HDRは高いダイナミックレンジを持つ画像です
ダイナミックレンジとは、画像が表現できる最も明るい部分と最も暗い部分の輝度差を表します
従来の画像は8ビット(256まで)
HDR画像は10ビット以上(数千から数万)
HDRファイルフォーマットにはEXR(.exr)やHDR(.hdr)などがあります
通常のディスプレイは、比較的狭いダイナミックレンジしか持っていないためHDR画像をそのまま使用すると明るさが失われたり色が歪んだりする可能性があり、トーンマッピング(通常のディスプレイで表示可能な範囲に変換する処理)が必要です
RendererオブジェクトのtoneMapping
プロパティを使用してトーンマッピングの方法を指定できます
Rendererオブジェクトプロパディ
toneMappingプロパティを使用して設定することができます
outputEncoding
画像の色空間を指定するプロパティ
デフォルトではsRGBEncoding(sRGBカラースペースのエンコード)が使用されますが、HDR画像の表現にはLinearEncoding(線形カラースペースのエンコード)が使用されていますtoneMapping
トーンマッピング手法を選択するプロパティ
LinearToneMapping・ReinhardToneMapping・ACESFilmicToneMappingのいずれかを選択toneMappingExposure
レンダリングされたシーンの明るさを調整するプロパティです
toneMappingを使用する場合に適用されます
値が大きいとシーンが明るくなり、小さくと暗くなります
トーンマッピング手法
LinearToneMapping
LDR画像の表示に使用されます
レンダリングされた画像の輝度値をそのまま表示する手法です
レンダリングされた画像がより現実的な表現をする場合は別の手法を使うReinhardToneMapping
HDR画像をLDR画像に変換する手法の一つです
より自然な色合で表示されることが期待されますACESFilmicToneMapping
HDR画像をLDR画像に変換する際に最も現実的な画像を生成することが期待されます
HDRファイルを読み込んでトーンマッピング
THREE.RGBELoader(HDRファイルを読み込むためのローダー)
import { RGBELoader } from "three/addons/loaders/RGBELoader.js";
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 2;
const loader = new THREE.RGBELoader();
loader.load('ファイルパス', (texture) => {
// 読み込みが完了したら、テクスチャを取得するコールバック関数を実行する
});
HDR画像をテクスチャにして環境マップを設定する
テクスチャにエンコーディングとマッピング方法の設定が必要です
const loader = new RGBELoader();
loader.load(
"ファイルパス",
(texture) => {
texture.encoding = THREE.RGBEEncoding;
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.background = texture;
/*シーンに環境マップを設定する場合
scene.environment = texture;*/
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(0.3, 50, 50),
new THREE.MeshStandardMaterial({
roughness: 0,
metalness: 0.8,
color: 0xffea00,
//オブジェクトに環境マップを設定する場合
envMap: texture,
})
);
scene.add(sphere);
}
);
マッピング方式の例
THREE.EquirectangularReflectionMapping
360度のパノラマ画像を反射マッピングに使用する場合に適したマッピング方法
*周囲を回転することができるTHREE.EquirectangularRefractionMapping
360度のパノラマ画像を屈折マッピングに使用する場合に適したマッピング方法
オブジェクトの内部での光の屈折を表現するのに適しています

余談:プログレスバーを表示する
*ローダーのハンドリングについてはThree.js備忘録(2)を参照
<progress id="progressbar" value="" max="100"></progress>
const progressBar = document.querySelector('#progressbar');
//リソースが1つの場合loadメソッドのハンドリング
//読み込み中に呼び出されるコールバック関数内で
(xhr) => {
progressBar.value = (xhr.loaded / xhr.total) * 100;
},
//複数ソースでLoadingManagerのハンドリング
// 1つのリソースの読み込みが完了したときに呼び出される関数内で
loadManager.onProgress = function (url, loaded, total) {
progressBar.value = (loaded / total) * 100;
};
//完了後
//プログレスバーを非表示にする
progressBar.style.display = "none";
3Dモデルの描画
3Dモデルのファイル形式には、多数の種類があり
three.jsで扱える代表的な3Dモデルのファイル形式
- 「OBJファイル」は3Dモデルの形状とマテリアル情報を表現するためのテキスト形式のファイルです
単一のオブジェクトを表現するためのものであり、シーングラフを表現することはできません
全て1つの大きなメッシュになります - 「GLTFファイル」はWebGLに最適化されたバイナリ形式のファイルです
シーングラフ、マテリアル、テクスチャ、アニメーションなど、多くの機能を含むことができます - その他、STL・Collada・FBXなど
とりあえず確認したいときは、ファイルをThree.js editorにアップロードすると便利です
*「add」→「DirectionalLight」を追加する

GLTFファイルの描画
3Dオブジェクトのシーングラフをコンソールで確認してみる
*テクスチャ用の画像やbinがあれば同じGLTFファイルと同じディレクトリに配置する
備考
GLTFファイルは、JSON形式で書かれていてbinファイルの参照が含まれます
バイナリデータはbinファイルに保存されています
GLTFファイルとbinファイルは一般的に一緒に使用されます
const loader = new GLTFLoader();
loader.load(
'ファイルパス',
(gltf) =>{
//gltf.sceneは、シーングラフでモデルのルートノードに相当
const root = gltf.scene
console.log(root)
scene.add(root);
});

name属性からノードを取得(getObjectByName)してマテリアルを変更してみる
const loader = new GLTFLoader();
loader.load(
'ファイルパス',
(gltf) =>{
scene.add(gltf.scene);
//getObjectByNameでノードを取得
root.getObjectByName('Cube_1_0').material.color.set(0xffff00)
}
);
GLTFファイルを読み込んで描画したオブジェクトに影をつける

シーンに環境マップを設定する場合の例
const rgbLoader = new RGBELoader();
const gltfLoader = new GLTFLoader();
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 2;
rgbLoader.load(
"hoge.hdr",
(texture) => {
texture.encoding = THREE.RGBEEncoding;
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture;
gltfLoader.load(
'hoge.gltf',
(gltf) =>{
scene.add(gltf.scene);
}
);
}
);
OBJファイルの描画
MTLファイルはOBJファイルと一緒に使用され、マテリアルの情報が含まれます
- OBJファイルにはジオメトリ情報
- MTLファイルにはマテリアルの情報
MTLファイルがある場合
MTLLoaderを使用してMTLファイルを読み込んだ後、そのマテリアルをOBJLoaderに設定setMaterials
はマテリアルをOBJLoaderに設定するためのメソッドです。
const mtlLoader = new MTLLoader();
const objLoader = new OBJLoader();
mtlLoader.load('hoge.mtl', (materials) =>{
materials.preload();
objLoader.setMaterials(materials);
objLoader.load('hoge.obj', (object) =>{
scene.add(object);
});
},);
OBJファイルにMTLファイルがない場合のマテリアル
OBJファイル単一のオブジェクトです
通常、複数のマテリアルを持つジオメトリを含んでいるため、traverse
メソッドを使用してOBJファイルから読み込まれたマテリアルごとのジオメトリを取得して処理します
const material = new THREE.MeshLambertMaterial({ color: 0x5c3a21 });
objLoader.load(
"hoge.obj",
(object) => {
object.traverse((child) => {
if (child instanceof THREE.Mesh) {
//法線の情報がない場合
child.geometry.computeFaceNormals();
child.geometry.computeVertexNormals();
//マテリアルの設定
child.material = material;
}
});
scene.add(object);
})
ジオメトリの法線の計算
computeFaceNormals
ジオメトリの面の法線は各面の3つの頂点の法線の平均値として計算されます
面が等しい重みを持つ場合に適していますcomputeVertexNormals
頂点の法線は頂点を共有する全ての面の法線の平均値として計算されます
ジオメトリがシームレスでない場合(継ぎ目が目立つ状態)に使用されます
計算量が多い- 外部ツールによる計算
ジオメトリに法線が含まれている場合は、OBJファイルから読み込むことができます
MTLファイルがない場合のOBJファイルを描画

無料でアセットが取得できるサイト
「CC0(パブリックドメイン)」を選択するとクレジット表記不要
HDRも無料でダウンロードできる