Three.js備忘録(4)

HP制作・コーディングなどのお手伝いをいたします

目次
  1. 環境マップとHDR画像
  2. 3Dモデルの描画
    1. GLTFファイルの描画
    2. OBJファイルの描画
  3. 無料でアセットが取得できるサイト

環境マップと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」を追加する

Three.js editor

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も無料でダウンロードできる