見出し画像

[UIデザイナーが学ぶ]Three.jsのテクスチャの基本的な話

普段はメーカーでUIデザイナーとして勤務しております。

あまり業務では利用しませんが、Three.jsのテクスチャについて学び直しているので、備忘録的にまとめています。


テクスチャの読み込み方法

TextureLoaderを利用して読み込む

const textureLoader = new THREE.TextureLoader()
const colorTexture = textureLoader.load('/textures/color.jpg')

複数のテクスチャを読み込む

TextureLoaderのインスタンスは一つで複数の画像を読み込むことができます。

const textureLoader = new THREE.TextureLoader(loadingManager);
const alphaTexture = textureLoader.load("/textures/door/alpha.jpg");
const ambientOcclusionTexture = textureLoader.load("/textures/door/ambientOcclusion.jpg");
const colorTexture = textureLoader.load("/textures/door/color.jpg");
const heightTexture = textureLoader.load("/textures/door/height.jpg");
const metalnessTexture = textureLoader.load("/textures/door/metalness.jpg");
const normalTexture = textureLoader.load("/textures/door/normal.jpg");
const roughnessTexture = textureLoader.load("/textures/door/roughness.jpg");

読み込みを管理する

LoadingManagerを利用することで、複数の画像の読み込み状況を管理することができます。

const loadingManager = new THREE.LoadingManager()
loadingManager.onStart = () =>
{
    console.log('loading started')
}
loadingManager.onLoad = () =>
{
    console.log('loading finished')
}
loadingManager.onProgress = () =>
{
    console.log('loading progressing')
}
loadingManager.onError = () =>
{
    console.log('loading error')
}

// TextureLoaderの引数にLoadingManagerのインスタンスを指定する
const textureLoader = new THREE.TextureLoader(loadingManager)

読み込んだ画像の本来の色と違う場合

テクスチャのcolorSpaceをSRGBにすると解決する場合があります。

テクスチャの色空間にsRGB色空間を使用することで、テクスチャの色が正確に表示されるようになります。

sRGB色空間は、ディスプレイデバイスで一般的に使用される標準的な色空間です。テクスチャがsRGB色空間でエンコードされている場合、そのまま使用すると色が不正確に表示される可能性があります。これを防ぐために、THREE.SRGBColorSpaceを設定して、Three.jsがテクスチャの色を正しく解釈し、レンダリング時に適切にガンマ補正を行うようにします。

texture.colorSpace = THREE.SRGBColorSpace;

テクスチャの最適な画像サイズ

テクスチャに利用する画像サイズは「256 x 256、512 x 512、1024 x 1024」 2の累乗を適切になります。

理由としては、メモリ効率、ミップマッピングの効率化、ハードウェア制約、パフォーマンスの向上など、さまざまな要因によるものです。これにより、テクスチャの管理とレンダリングが最適化され、よりスムーズなグラフィックスパフォーマンスが実現されます。

テクスチャの種類

Color Map

レンガのテクスチャ

Color Map(カラーマップ)は、3Dオブジェクトの表面に色を適用するためのテクスチャです。
通常、画像ファイルとして保存され、オブジェクトの各ピクセルに対応する色情報を提供します。これにより、3Dモデルにリアルな外観を与えることができます。

const material = new THREE.MeshStandardMaterial({ map: colorTexture });


Normal Map

レンガのテクスチャ

Normal Map(ノーマルマップ)は、3Dオブジェクトの表面の詳細な凹凸をシミュレートするためのテクスチャです。
ノーマルマップは、RGB値を使用して各ピクセルの法線ベクトルをエンコードし、光の反射を変化させることで、詳細な凹凸を表現します。これにより、ポリゴン数を増やさずにリアルな表面のディテールを追加できます。

const material = new THREE.MeshStandardMaterial({
  map: colorTexture,
  normalMap: normalTexture
});

Displacement(Height) Map

レンガのテクスチャ

Displacement(Height) Map(ディスプレイスメントマップ)は、3Dオブジェクトの表面の凹凸を制御するためのテクスチャです。
ハイトマップは通常、グレースケールの画像として保存され、各ピクセルの明るさがその部分の高さを示します。白いピクセルは高い部分、黒いピクセルは低い部分を意味します。これにより、オブジェクトの表面に詳細な凹凸を追加できます。

const material = new THREE.MeshStandardMaterial({
  map: colorTexture,
  displacementMap: heightTexture,
  displacementScale: 0.1 // 凹凸の強さを調整
});

displacementMapプロパティに設定しています。
また、displacementScaleプロパティを使用して凹凸の強さを調整しています。さらに、ジオメトリには高い分割数が必要です(例:PlaneGeometry(1, 1, 100, 100))。
これにより、ハイトマップが適用され、3Dオブジェクトの表面に詳細な凹凸が追加されます。

Roughness Map

レンガのテクスチャ

Roughness Map(ラフネスマップ)は、3Dオブジェクトの表面の粗さを制御するためのテクスチャです。
ラフネスマップは通常、グレースケールの画像として保存され、各ピクセルの明るさがその部分の粗さを示します。白いピクセルは完全に粗い(光を散乱させる)、黒いピクセルは完全に滑らか(光を反射する)を意味します。

const material = new THREE.MeshStandardMaterial({
  map: colorTexture,
  roughnessMap: roughnessTexture,
  roughness: 1 // ラフネスマップを使用するために必要
});

roughnessプロパティを1に設定することで、ラフネスマップが有効になります。これにより、ラフネスマップが適用され、3Dオブジェクトの表面の粗さが制御されます。

Alpha Map


Alpha Map(アルファマップ)は、3Dオブジェクトの透明度を制御するためのテクスチャです。
通常、グレースケールの画像として保存され、各ピクセルの明るさが透明度を示します。
白いピクセルは完全に不透明、黒いピクセルは完全に透明を意味します。

const material = new THREE.MeshStandardMaterial({
  map: colorTexture,
  alphaMap: alphaTexture,
  transparent: true
});

Ambient Occlusion Map

Ambient Occlusion Map(アンビエントオクルージョンマップ)は、3Dオブジェクトの表面における環境光の遮蔽効果をシミュレートするためのテクスチャです。
これにより、オブジェクトの凹凸や隙間に影が落ち、よりリアルな陰影を表現できます。アンビエントオクルージョンマップは、通常グレースケールの画像として保存され、明るい部分が光を受ける領域、暗い部分が影になる領域を示します

const material = new THREE.MeshStandardMaterial({
  map: colorTexture,
  aoMap: ambientOcclusionTexture
});

ジオメトリにUV2属性を設定することで、アンビエントオクルージョンマップが正しく適用されるようにしています。

const geometry = new THREE.BoxGeometry();
geometry.setAttribute('uv2', new THREE.BufferAttribute(geometry.attributes.uv.array, 2));
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

Metalness Map

Metalness Map(メタルネスマップ)は、3Dオブジェクトの表面の金属性を制御するためのテクスチャです。
メタルネスマップは通常、グレースケールの画像として保存され、各ピクセルの明るさがその部分の金属性を示します。白いピクセルは完全に金属的、黒いピクセルは非金属的を意味します。

const material = new THREE.MeshStandardMaterial({
  map: colorTexture,
  metalnessMap: metalnessTexture,
  metalness: 1 // メタルネスマップを使用するために必要
});

metalnessプロパティを1に設定することで、メタルネスマップが有効になります。これにより、メタルネスマップが適用され、3Dオブジェクトの表面の金属性が制御されます。

テクスチャの小技

テクスチャにはリピートしたり、回転したりなど変換方法がいくつかあります。

テクスチャ変換前

リピート

texture.repeat.xは、Three.jsでテクスチャを繰り返す回数を制御するプロパティです。具体的には、テクスチャの水平方向(x方向)の繰り返し回数を設定します。texture.repeat.yは垂直方向(y方向)の繰り返し回数を制御します。
デフォルトでは、repeatプロパティは(1, 1)に設定されており、テクスチャは1回だけ表示されます。repeat.xやrepeat.yの値を変更することで、テクスチャを複数回繰り返して表示することができます。

const textureLoader = new THREE.TextureLoader();
const colorTexture = textureLoader.load("/textures/color.png");

// テクスチャを水平方向に2回、垂直方向に3回繰り返す
colorTexture.repeat.x = 2;
colorTexture.repeat.y = 3;

// テクスチャの繰り返しを有効にするためにラッピングモードを設定
colorTexture.wrapS = THREE.RepeatWrapping;
colorTexture.wrapT = THREE.RepeatWrapping;

また、テクスチャの繰り返しを有効にするために、wrapSとwrapTプロパティをTHREE.RepeatWrappingに設定する必要があります。

THREE.RepeatWrapping

THREE.RepeatWrapping

THREE.MirroredRepeatWrapping

テクスチャが繰り返されるたびに反転されます。これにより、テクスチャのエッジがシームレスに繋がるように見える効果が得られます。

colorTexture.wrapS = THREE.RepeatWrapping;
colorTexture.wrapT = THREE.RepeatWrapping;
THREE.MirroredRepeatWrapping

オフセット

texture.offsetプロパティは、Three.jsでテクスチャの表示位置を制御するために使用されます。
このプロパティは、テクスチャのUV座標をオフセットすることで、テクスチャの表示位置を調整します。offsetプロパティは、THREE.Vector2オブジェクトとして設定され、xとyの値を指定します。

const textureLoader = new THREE.TextureLoader();
const colorTexture = textureLoader.load('/textures/color.png');

// テクスチャを水平方向に0.5、垂直方向に0.25オフセット
colorTexture.offset.set(0.5, 0.25);
colorTexture.offset.set(0.5, 0.25);

回転

const textureLoader = new THREE.TextureLoader();
const colorTexture = textureLoader.load('/textures/color.png');

// 90度回転
colorTexture.rotation = Math.PI * 0.5;
colorTexture.rotation = Math.PI * 0.5;

デフォルトでは、テクスチャの回転中心はUV座標の (0, 0) に設定されています。これはテクスチャの左下隅に相当します。この状態でテクスチャを回転させると、左下隅を基準に回転するため、回転の結果が直感的でない場合があり上記のような意図しない結果になる場合があります。

const textureLoader = new THREE.TextureLoader();
const colorTexture = textureLoader.load('/textures/color.png');

// テクスチャの回転中心を中央に設定
colorTexture.center.x = 0.5;
colorTexture.center.y = 0.5;

// テクスチャを90度回転
colorTexture.rotation = Math.PI * 0.5;

テクスチャの回転中心をUV座標の (0.5, 0.5) に設定することで、テクスチャの中央を基準に回転させることができます。これにより、テクスチャがその中心を基準に回転するため、回転の結果が直感的で自然になります。

colorTexture.center.x = 0.5;
colorTexture.center.y = 0.5;

minFilter

minFilterは、Three.jsでテクスチャが縮小される際に使用されるフィルタリング方法を指定するプロパティです。
テクスチャが小さな領域にマッピングされるとき、どのようにピクセルをサンプリングするかを制御します。

プロパティは以下になります。
デフォルトは ですTHREE.LinearMipmapLinearFilterです。

指定方法は以下です。

const colorTexture = textureLoader.load("/textures/color.png");
colorTexture.minFilter = THREE.LinearMipMapLinearFilter;

THREE.NearestFilter

  • 最も近いピクセルの色を使用します。

  • 高速ですが、ジャギー(ギザギザ)が目立つことがあります。

THREE.LinearFilter

  • 周囲のピクセルの色を線形補間して使用します。

  • 滑らかな結果が得られますが、若干のぼやけが発生することがあります。

THREE.NearestMipMapNearestFilter

  • 最も近いミップマップレベルを選択し、そのレベルの最も近いピクセルの色を使用します。

  • 高速で、ミップマップを使用するためメモリ効率が良いです。

THREE.LinearMipMapNearestFilter

  • 最も近いミップマップレベルを選択し、そのレベルのピクセルを線形補間して使用します。

  • 滑らかな結果が得られます。

THREE.NearestMipMapLinearFilter

  • 2つのミップマップレベル間で最も近いピクセルの色を線形補間して使用します。

  • 高速で、滑らかな結果が得られます。

THREE.LinearMipMapLinearFilter

  • 2つのミップマップレベル間でピクセルの色を線形補間して使用します。

  • 最も滑らかな結果が得られますが、計算コストが高いです。

magFilter

magFilterは、Three.jsでテクスチャが拡大される際に使用されるフィルタリング方法を指定するプロパティです。
テクスチャが大きな領域にマッピングされるとき、どのようにピクセルをサンプリングするかを制御します。

指定方法は以下です。

const colorTexture = textureLoader.load("/textures/color.png");
colorTexture.magFilter = THREE.LinearFilter;

小さいテクスチャを設定した例です。

左 : NearestFilter   右 : LinearFilter

THREE.NearestFilter

  • 最も近いピクセルの色を使用します。

  • 高速ですが、ジャギー(ギザギザ)が目立つことがあります。

THREE.LinearFilter

  • 周囲のピクセルの色を線形補間して使用します。

  • 滑らかな結果が得られますが、若干のぼやけが発生することがあります。

THREE.NearestFilterを利用するとマインクラフトのような表現ができそうです。

まとめ

テクスチャは奥が深く、まだまだ初歩的な部分しか学べてないです。

いいなと思ったら応援しよう!