Three.js備忘録(2)

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

目次
  1. ローダーについて
  2. テクスチャ
    1. TextureLoader
    2. スカイボックス
    3. フィルタリング
    4. 繰り返し・オフセット・回転
  3. 3Dテキスト
  4. シーングラフ
  5. lil-guiの使い方
  6. レンダーターゲット

ローダーについて

ローダーは「テクスチャ、3Dモデル、音声ファイルなど」の外部リソースを読み込むために使用され、読み込むファイルの種類に応じて異なる種類があります

ローダー(THREE.TextureLoader、THREE.JSONLoader、THREE.GLTFLoaderなど)のloadメソッドについて

const loader = new THREE.TextureLoader();
loader.load(
  'ファイルのパス',
  //読み込みが完了したときに呼び出されるコールバック関数
  function (geometry, materials) {
    // ロード完了時の処理
  },
  // 読み込み中に呼び出されるコールバック関数
  function (xhr) {
    console.log((xhr.loaded / xhr.total * 100) + '%');
  },
  // 読み込みエラー時に呼び出されるコールバック関数
  function (error) {
    console.error(error);
  }
);

複数のファイルの読み込みを監視する場合

LoadingManagerは「テクスチャ、モデル、音声ファイル」などの読み込み中に進行状況を監視するためのオブジェクトです
リソースが完全に読み込まれたときに、その読み込みが失敗した場合に、または読み込み中にエラーが発生した場合に、適切な処理を実行できます

// LoadingManagerを作成する
const loadManager = new THREE.LoadingManager();
// すべてのリソースの読み込みが完了したときに呼び出される関数
loadManager.onLoad = function () {
    console.log('読み込みが完了しました。');
};
// 1つのリソースの読み込みが完了したときに呼び出される関数
loadManager.onProgress = function (url, itemsLoaded, itemsTotal) {
    console.log('読み込み中: ' + url + ' ' + itemsLoaded + '/' + itemsTotal);
};
// 読み込みが失敗したときに呼び出される関数
loadManager.onError = function (url) {
    console.log('読み込みエラー: ' + url);
};

テクスチャ

テクスチャとは画像やパターンなどの表面の外観や質感を表現するために使われる画像データのことで、3Dオブジェクトの表面に貼り付けるてリアルな外観や質感を与えることができます

備考
テクスチャーマップ(3Dオブジェクトの表面に貼り付けるための画像の種類)

  • 通常のマップ(map)
    3Dオブジェクトの表面に貼り付ける基本的なテクスチャで
    色や模様を表現するために使用されます
  • スペキュラマップ(specularMap)
    3Dオブジェクトの光沢感を表現するために使用されるテクスチャマップ
  • バンプマップ(bumpMap)
    3Dオブジェクトの表面に凹凸を与えるために使用されるテクスチャマップ
  • ノーマルマップ(normalMap)
    バンプマップより細かく3Dオブジェクトの表面に凹凸を与えるために使用されるテクスチャマップ
  • アルファマップ(alphaMap)
    3Dオブジェクトの透明度を設定するために使用されるテクスチャマップ
  • ライトマップ(lightMap)
    光源からの陰影を表現するために使用されるテクスチャマップ
  • 環境マップ(envMap)
    3Dオブジェクトに周囲の環境を反射させるために使用されるテクスチャマップ

テクスチャは最もメモリを使用する部分です
圧縮(ファイルサイズを小さく)してもGPUのメモリは変わらない
ファイルサイズが小さい→ダウンロード時間が速い
画像のサイズが小さい→メモリが少ない

TextureLoader

TextureLoader
シーンのbackgroundにテクスチャを使用したり
マテリアルでテクスチャを使用したりできます(mapプロパティに設定します)

const loader = new THREE.TextureLoader();
const texture = loader.load('ファイルパス');
//シーンのbackground
scene.background = texture;
//マテリアルでテクスチャを使用
const material = new THREE.MeshBasicMaterial({
  map: texture,
});

画像は非同期に読み込まれます
テクスチャの読み込み完了までまつ場合
loadメソッドにテクスチャの読み込みが完了したときに呼び出されるコールバックを渡します

loader.load('ファイルパス', (texture) => {
  const material = new THREE.MeshBasicMaterial({
    map: texture,
  });
  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);
});

複数のテクスチャの読み込み完了を待つ場合
LoadingManagerはローディング処理を管理するオブジェクトです
ローダーのコンストラクターにLoadingManagerオブジェクトを渡すことでローダーがLoadingManagerオブジェクトを使用できます
例:立方体の各面に異なる6つのテクスチャを貼り付ける場合はMeshを作るときに6つのマテリアルの配列を渡します

const loadManager = new THREE.LoadingManager();
const loader = new THREE.TextureLoader(loadManager); 
//6つのマテリアルの配列
const materials = [
  new THREE.MeshBasicMaterial({map: loader.load('1.jpg')}),
  new THREE.MeshBasicMaterial({map: loader.load('2.jpg')}),
  new THREE.MeshBasicMaterial({map: loader.load('3.jpg')}),
  new THREE.MeshBasicMaterial({map: loader.load('4.jpg')}),
  new THREE.MeshBasicMaterial({map: loader.load('5.jpg')}),
  new THREE.MeshBasicMaterial({map: loader.load('6.jpg')}),
]; 
loadManager.onLoad = () => {
  const cube = new THREE.Mesh(geometry, materials);
  scene.add(cube);
};

複数のマテリアルに対応しているジオメトリ

  • BoxGeometryは6つのマテリアル
  • ConeGeometryは2つのマテリアル(底面と円錐面)
  • CylinderGeometryは3つのマテリアル(底面と上面と側面)

スカイボックス

スカイボックスは3D空間に背景を描画するための技術です
6つの面から成る正立方体の形をした画像データを用いて周囲の景色を表現します
スカイボックスの作成方法は、キューブマップを用いることが一般的です

cubeTextureLoaderはキューブマップテクスチャを読み込むことができます
キューブマップテクスチャは反射や環境マッピングなどに使用されます
キューブマップ
6面のテクスチャ画像(上・下・左・右・前・後)で360度パノラマコンテンツができます

const cubeTextureLoader = new THREE.CubeTextureLoader();
scene.background = cubeTextureLoader.load([
    'ファイルパス',
    'ファイルパス',
    'ファイルパス',
    'ファイルパス',
    'ファイルパス',
    'ファイルパス'
]);

1枚のパノラマ写真の場合

パノラマ写真

THREE.WebGLCubeRenderTargetはキューブマップのターゲットを作成するためのクラスです
fromEquirectangularTextureで正距円筒図テクスチャからキューブマップテクスチャを生成します
*キューブマップは反射マッピングでよく使用される手法の一つです
反射マッピングは物体の表面に環境マップを適用することで反射の見た目を再現します

const loader = new THREE.TextureLoader();
loader.load(
   'ファイルパス',
  (texture) => {
    const rt = new THREE.WebGLCubeRenderTarget(texture.image.height);
    rt.fromEquirectangularTexture(renderer, texture);
    scene.background = rt.texture;
    //キューブ
    const geometry = new THREE.BoxGeometry(1, 1, 1);
    const material = new THREE.MeshStandardMaterial({
      roughness: 0.5,
      metalness: 0.2,
      color: 0x00ffff,  
      envMapIntensity: 1, // 環境マップの反射強度
      envMap: texture,   //反射マッピング
    });
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);
  }
});

*余談:Googleストリートビューアプリで360度のパノラマを撮影ができる

テクスチャをダウンロードできるサイト

フィルタリング

テクスチャは通常、面のピクセルと1対1に対応しないので拡大や縮小の必要があり、フィルタ設定で拡大または縮小する場合にどのようにするかを決めることができます

テクスチャが元の大きさより大きく描かれたときのフィルタ設定は
texture.magFilter属性に値を設定

  • THREE.NearestFilter
    テクスチャの最も近いピクセルを選ぶ
  • THREE.LinearFilter
    テクスチャから4ピクセルを選んで混ぜ合わせる
    *デフォルト

元の大きさよりもテクスチャが小さく描画された時のフィルタ設定は
texture.minFilter属性に値を設定

  • THREE.NearestFilter
    テクスチャの最も近いピクセルを選ぶ
  • THREE.LinearFilter
    テクスチャから4ピクセルを選んで混ぜ合わせる
  • THREE.NearestMipmapNearestFilter
    適切なMIPを選びピクセルを一つ選ぶ
  • THREE.NearestMipmapLinearFilter
    2つMIPを選びそれぞれからピクセルを選んでその2つを混ぜる。
  • THREE.LinearMipmapNearestFilter
    適切なMIPを選び4ピクセルを選んで混ぜ合わせる
  • THREE.LinearMipmapLinearFilter
    2つMIPを選びそれぞれから4ピクセルを選んで8つ全部を混ぜ合わせて1ピクセルにする
    *デフォルト

備考:MIP(ミップマップ)について

MIP(ミップマップ)は、3Dグラフィックスやテクスチャマッピングにおいて、効率的で滑らかな画像表示を実現するための技術の一つです。画像の解像度を複数のレベルに分け、異なるレベルの画像を用意することで、画像を縮小表示する場合に適切な解像度の画像を選択できます

例えば、遠くにあるオブジェクトを表示する場合に、ミップマップを使わない場合は、元の解像度の高いテクスチャ画像を縮小して表示することになりますが、画像がギザギザになったり、粗く表示されることがあります
ミップマップを使用すると、適切な解像度の画像を選択して滑らかな画像表示が可能です

ミップマップは、オリジナルの画像を2の累乗サイズに縮小した画像を複数用意し、それらをレベル0からレベルnまで順次格納します
各レベルの画像は前のレベルの画像を2倍に縮小したものです

*最適なフィルタリング方法はテクスチャの特性や使用目的に応じて異なる

繰り返し・オフセット・回転

繰り返しす場合
*デフォルトのテクスチャは繰り返しなし

  • wrapS:水平方向のラッピング属性
  • wrapT:垂直方向のラッピング属性

属性の値

  • THREE.ClampToEdgeWrapping
    最後のピクセルが永遠に繰り返される
  • THREE.RepeatWrapping
    テクスチャが繰り返される
  • THREE.MirroredRepeatWrapping
    テクスチャの鏡像が取られて繰り返される
texture.wrapS = THREE.RepeatWrapping;

繰り返しはrepeat属性で設定

texture.repeat.set(水平方向に繰り返す回数, 垂直方向に繰り返す回数);

テクスチャのオフセットはoffset属性で設定
「0 」はオフセットなし「1」 はテクスチャ全体の大きさ

texture.offset.set(xOffset, yOffset);

テクスチャの回転はrotation属性(ラジアンで指定)
center属性で回転の中心を指定(デフォルトは0,0で左下の角で回転・.5, .5でテクスチャの中心での回転)

texture.center.set(.5, .5);
//45 * Math.PI / 180
texture.rotation = THREE.MathUtils.degToRad(45);

3Dテキスト

TextGeometry(‘テキスト’, オプション)
独自でダウンロードしたフォントはFacetype.jsでjsonに変換
*ライブラリに含まれるフォント
https://github.com/mrdoob/three.js/tree/master/examples/fonts
jsonファイルを THREE.FontLoader()で読み込む必要がある

const loader = new THREE.FontLoader();
loader.load('./helvetiker_regular.typeface.json', (font) => {
  const textGeometry = new THREE.TextGeometry('three.js', {
    font: font,
    size: 0.5, 
    height:0.1
  });
  const textMaterial = new THREE.MeshBasicMaterial({color:0xffffff});
  const text = new THREE.Mesh(textGeometry, textMaterial);
  scene.add(text);
});

オプション

  • font:使用されるフォント
  • size :テキストのサイズ(デフォルトは 100)
  • height: テキストを押し出す太さ(デフォルトは50)
  • curveSegments : 曲線上の点の数(デフォルトは12)
  • bevelEnabled : ベベル(面取り)(デフォルトはFalse)
  • bevelThickness :テキストのベベルの深さ(デフォルトは10)
  • bevelSize : ベベルがアウトラインからどれだけ離れているか(デフォルトは8)
  • bevelOffset : 開始する アウトラインからの距離(デフォルトは0)
  • bevelSegments: ベベル セグメントの数(デフォルトは3)

シーングラフ

Sceneクラスは3Dシーンのルートノードとして機能します
他のノードを子ノードとして持つことができ、オブジェクトの階層構造を柔軟に構築することができます

THREE.GroupもしくTHREE.Object3Dはシーングラフで使用されるオブジェクトクラスです
複数の3Dオブジェクトをまとめて位置、回転、スケーリング、親子関係などの変換情報を保持できます
THREE.GroupもしくTHREE.Object3Dで「空のノード(ローカルな空間)」を作り、そこにaddメソッドで子要素となる物体を追加したりできます
*シーンの座標とは別のローカルな座標ができるイメージです

子の位置は親の位置から相対的になる
*親オブジェクトが移動すると、その子オブジェクトも自動的に移動します

//空のノード
const parent = new THREE.Group();
scene.add(parent);

//parentに物体を追加
parent.add(物体);

lil-guiの使い方

 npmでインストール

npm install lil-gui --save-dev
import GUI from 'lil-gui'

CDNを使う

import GUI from 'https://cdn.jsdelivr.net/npm/lil-gui@0.18/+esm';
<script src="https://cdn.jsdelivr.net/npm/lil-gui@0.18"></script>
<script>
  const GUI = lil.GUI
</script>

プロパティのデータ型に基づいて適切なコントローラーが選択されます

const gui = new GUI();
obj = {
	myBoolean: true,
	myNumber: 1,
	myFunction: function() { alert( 'hi' ) },
	myColor: '#ffffff',
}
//コントロールパネルに表示を追加
gui.add( obj, 'myBoolean' ); 	// チェックボックス
gui.add( obj, 'myNumber' 0, 100, 10 ); //数字 (最小値,最大値,間隔)
gui.add( obj, 'myFunction' ); 	// ボタン
gui.addColor( obj, 'myColor' ); //カラー ピッカー

addFolder()
コントローラを折りたたみ可能なグループに編成

const folder = gui.addFolder( 'Position' );
folder.add( obj, 'x' );

onChangeメソッド
コントローラーが変更されるたびに関数を呼び出す
*新しい値は変更のたびに関数に渡される

gui.add( params, 'foo' ).onChange( value => {
	console.log( value );
} );

地球とテキストを異なるスピードで回転させるために
親コンテナに「地球」と「テキストを子要素に持つオブジェクト」を子要素に追加してそれぞれを回転させる
*親は子と同じ位置でそれぞれを異なる速度で回転できる

レンダーターゲット

three.jsではデフォルトで画面(canvas要素)がレンダーターゲットに設定されていますが、これを変更することで、画面以外の場所にもレンダリングされた結果を描画することができます

メモリ上にレンダリングした結果をテクスチャとして使用します

WebGLRenderTarget
レンダーターゲットを作成
*横幅と高さが小さいと映像が粗くなります

const rtWidth = 1000;
const rtHeight = 1000;
const renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight);

オフスクリーンレンダリング用のシーンとカメラ
*カメラのアスペクトはレンダーターゲットのアスペクトに設定

const rtCamera = new THREE.PerspectiveCamera(75, rtWidth / rtHeight, 0.1, 5);
rtCamera.position.z = 2;
 
const rtScene = new THREE.Scene();
rtScene.background = new THREE.Color('red');

オフスクリーンのレンダリング

renderer.setRenderTarget(renderTarget);
renderer.render(rtScene,rtCamera);
renderer.setRenderTarget(null);

マテリアルにレンダーターゲットを適応する場合の例

const material = new THREE.MeshPhongMaterial({
  map: renderTarget.texture,
});