Blog

three.jsのTextureLoaderで読み込んだ画像が暗い

three.jsをアップデートしたらTextureLoaderで読み込んだ画像が暗くなってしまいました。
左が暗くなった画像、右が元々の画像です。

結論

three.jsのバージョンがr155以上の場合

1、テクスチャのcolorSpaceをSRGBにする。

texture.colorSpace = THREE.SRGBColorSpace;

もしくは、レンダラーのoutputColorSpaceLinearSRGBColorSpaceにし、テクスチャのcolorSpaceはデフォルト(NoColorSpace)でも同じ結果でした。…が、ディスプレイがsRGBだとアウトプットもsRGBがよいのかなと思っています。(詳細な違いまでは検証していません)

renderer.outputColorSpace = THREE.LinearSRGBColorSpace;

2、ライトのintensityに、Math.PIをかける。

const light = new THREE.AmbientLight(new THREE.Color(0xFFFFFF), Math.PI);

three.jsのバージョンがr152〜r154の場合

上記の1番(テクスチャのcolorSpace)のみでOK。

three.jsのバージョンがr152未満の場合

カラーマネジメントもライトも仕様がかわってないはずなので、それでも暗いならレンダラーのoutputEncodingをsRGBにしてみると変わるかもしれません。

renderer.outputEncoding = THREE.sRGBEncoding;

※今回の記事対象外ですが、参考までに。

カラーマネジメントの仕様変更

概要

r152で、色と照明に影響を与える変更がありました。
物理ベースのシーンと非物理ベースのシーンの両方で画質を向上するため、リニアワークフローが導入されました。
▼参考
Updates to Color Management in three.js r152

通常、私たちが扱う画像やテクスチャは sRGB という非線形のカラースペースで保存されており、視覚に適したコントラストを持っています。
しかし、コンピュータ上での光や色の計算は リニア空間(直線的な関係) で行うのが物理的に正確です。

  1. テクスチャや色は非線形のsRGBで保存されていますが、これをリニア空間に変換して正確に光や反射を計算します。
  2. シーン内の光や影の計算は、リニアな光の動作に基づいてリニア空間で光の計算を行います。リニア空間では、光の強さの変化が物理的に正確になります。
  3. 計算が終わったら、最終的な画像を再びsRGBに戻して、ディスプレイに正しいコントラストで表示します。

リニアワークフローを導入することで、より自然な結果を得ることができるようになった…というアップデートのようです。

これにより、three.jsではプロパティやデフォルト値に変更がありました。

プロパティ名の変更

  1. THREE.WebGLRenderer.outputEncodingoutputColorSpaceに変更されました
  2. THREE.Texture.encodingcolorSpaceに変更されました
  3. sRGBEncodingSRGBColorSpaceに改名
  4. LinearEncodingLinearSRGBColorSpaceに改名

デフォルト値の変更

  1. outputColorSpaceのデフォルトは sRGB(THREE.SRGBColorSpace) になりました
  2. THREE.ColorManagement.enabledがデフォルトでtrueになりました

レンダラーの設定

レンダラーの設定は、デフォルトでSRGBColorSpaceになっているので、特に変更は必要ありません。
もし変更したい場合は、下記のように変更します。

renderer.outputColorSpace = THREE.SRGBColorSpace;

three.jsの中から値を拾ってくると下記のようになっていました。

const NoColorSpace = '';
const SRGBColorSpace = 'srgb';
const LinearSRGBColorSpace = 'srgb-linear';
const DisplayP3ColorSpace = 'display-p3';
const LinearDisplayP3ColorSpace = 'display-p3-linear';

このうち、NoColorSpaceはoutputColorSpaceに設定するとエラーになりましたが、それ以外は変化がみられました。

入力カラースペースの有効無効

入力カラースペースのカラーマネジメントはデフォルトで有効になっています。

テクスチャ、3D モデル、その他のソースから three.js に提供される色には、それぞれ関連付けられた色空間があります。Linear-sRGB 作業色空間にまだ含まれていない色は変換する必要があり、テクスチャには正しいtexture.colorSpace割り当てが与えられます。

参考:Three.js公式 Color management

無効に切り替えるには

THREE.ColorManagement.enabled = false;

とすればよいそうです。

テクスチャのカラースペース

テクスチャのカラースペースの設定方法は、colorSpaceプロパティを使います。

texture.colorSpace = THREE.SRGBColorSpace;

デフォルトではNoColorSpace(値は'')になっています。

ライティングの仕様変更

バージョンr155では、照明のコンテキストに大きな変更が加えらえれました。

Updates to lighting in three.js r155

この変更の背景は、物理的に正しいワークフローを可能にする単一の照明モードをエンジンに持たせることです。

バージョンr165より前のバージョンでは、

this.renderer.useLegacyLights = true;

とすることで、このライティングの変更の影響を受けないようにすることができましたが、バージョンr165でuseLegacyLightsプロパティが削除されました。

今回私はアンビエントライトを使っています。アンビエントライト、半球ライト、指向性ライト、ライトマップだと、既存のライト強度値を乗算することで、暗さを解消できるようです。

具体的には、intensityにMath.PIをかけることで、解消できました。

const light = new THREE.AmbientLight(0xFFFFFF, Math.PI);

まとめ

早い段階でカラースペースの問題にはたどり着いたのですが、それだけでは解決できず時間がかかりました。
カラーマネジメントに対応したコードに変更し、バージョンを順番に変更してみた結果、どうやらr154では色がちゃんと出ているのが、r155で暗くなったので、リリースノートを探して原因を探っていき、ライティングの仕様変更にたどり着いたという経緯でした。

まだまだthree.jsはアップデートがさかんで、時間がたつと仕様をおさらいしないといけないので、今後も注意が必要だと思いました。

参考

おすすめの記事 recommend blog

新着 new blog

github