先に結論だけ
Three.js r165において
-
WebGPURenderer
ではoutputColorSpace
はMaterialの透明度に対して影響を与えます -
WebGLRenderer
ではoutputColorSpace
を変更してもMaterialの透明度は影響されません
WebGPURendererでWebGLRendererと同じようにMaterialの透明度を扱いたい場合は、Colorクラスの変換関数を利用しましょう。
はじめに
この記事は、Three.js r165のWebGPURendererにおいて、outputColorSpace
とMaterialの透明度の関係性について調査した結果をまとめたものです。
対象とする読者
- Three.jsを使用している方
- WebGPURendererを使用しているか、使用する予定がある
対象とする環境
- Three.js r165
- Chrome v126.0.6478.63
この記事は、Three.js r165での挙動について記載しています。将来のバージョンでは挙動が変わるかもしれないので、記事を読む前にお手元の環境のバージョンを確認してください。
この記事で問題とする症状
WebGPURendererで、Materialのtransparent
をtrue、opacity
を0.2程度に設定すると、WebGLRendererよりも明るく表示される症状が発生しました。
この症状が発生する原因を調査し、解決策を見つけることがこの記事の目的です。
実験
この問題の原因を探るため、必要最低限のデモページを作成しました。
マテリアルはライトやシェーダーの影響を受けないMeshBasicMaterial
を使用しました。各レンダラーの色空間はデフォルトです。
実験結果
まず、RGBの値をすべて1に設定します。白を指定すると、RGB値はsRGB色空間とリニアsRGB色空間の影響を受けなくなります。
その上で、透明度を変更します。opacity
を0.2程度に設定すると、WebGLRendererとWebGPURendererで異なる結果が得られます。
最後に、outputColorSpace
プロパティを変更します。リニアsRGBに設定すると、WebGLRendererの結果は変わりませんが、WebGPURendererの結果が変わります。
Three.jsにおけるカラーマネージメントの概要
デモページの実験結果を理解するために、Three.jsにおけるカラーマネージメントの仕組みを読み解いていきます。
- ソース色空間 : テクスチャやRGB入力値などの色空間で、ソースごとに異なる
-
作業色空間 :
ColorManagement.workingColorSpace
に設定された色空間で、Three.jsのシェーダー内部で利用され、色情報の四則演算やライト計算などはこの色空間で行われる -
出力色空間 : レンダラーがCanvasに描画する際の色空間で、
outputColorSpace
プロパティで指定する
Three.jsはこの3つの色空間を相互変換しながら、レンダリングを行います。
それぞれの色空間のデフォルト値は以下の通りです。
- ソース色空間 : ソースによって異なる
- 作業色空間 : sRGB-linear
- 出力色空間 : sRGB
色変換を追う
それでは、色空間の変換を処理ごとに追っていきます。
Colorクラスの色指定関数には、カラースペースを引数として指定するものがあります。たとえば、setRGB()関数は、第四引数にソース色空間を指定できます。
setRGB関数の色空間引数は、ドキュメント上では固定の値として記載されていますが、ソースコード上ではColorManagementクラスのworkingColorSpace
プロパティを参照しています。
Colorクラスの色指定関数は、内部的にColorManagementクラスのtoWorkingColorSpace()関数を呼び出しています。この関数はソース色空間を作業色空間へ変換します。
return this.convert(color, sourceColorSpace, this._workingColorSpace);
setRGB()関数に色空間を指定しない場合、ソース色空間と作業色空間が同じなので変換は実行されません。
setHex関数やsetStyle関数はデフォルトのソース色空間がsRGBになっています。これはCSSとの互換性を考慮したためです。
Rendererによる色空間の変換
ColorManagement.workingColorSpace
プロパティは、レンダラー内部で使用される作業色空間を示しています。レンダラーはこの値で色の計算を行い、最後にレンダラーのoutputColorSpaceに変換してCanvasに描画します。
WebGPURendererでは、outputColorSpace
プロパティの変更でopacityのレンダリング結果が変わります。これは、作業色空間から出力色空間への変換が、透明度にも影響を与えていることを示します。
一方WebGLRendererでは、outputColorSpace
プロパティの変更でopacityのレンダリング結果が変わりません。opacityは色空間変換をバイバスします。
opacityを色変換する
WebGPURendererでWebGLRendererと同じようにMaterialの透明度を扱いたい場合は、Colorクラスの変換関数を利用しましょう。
const color = new Color();
color.setRGB(opacity, 0, 0, "srgb");
material.opacity = color.r;
Colorクラスを介して透明度を作業色空間に変換します。レンダラーはopacityを出力色空間に変換するため、WebGLRendererと同じ結果が得られます。
個人的な感想
透明度は色ではなく、本来は色空間に影響されないものです。そのため、opacityも色空間の変換に影響されないと思い込んでいました。WebGPURendererでは、Blenderなどの3Dツールとの連携も考慮して、このような変換が行われているのかもしれません。最小限のテストで挙動を確認することが大切です。