1722 文字
9 分
【備忘録】Unityの色空間に関するあれこれ

これはいったいなんのこと?#

これは、私がめちゃくちゃ理解に苦労したUnityの色空間がどのように扱われているかを理解するために色々調べたりしたものを備忘録として記録しておきたくて作りました。

間違っている情報を書いている可能性があるので、参考程度に見てください。

前提知識#

Unityには

  • Linear

  • sRGB (ガンマ空間とも言う)

この2種類の色空間があります。

それぞれの色空間について簡単に説明すると…

色空間説明
LinearUnity内部(シェーダーなど) で扱われる標準的な色空間
sRGBLinear色空間のものをガンマ調整などして、人間の目に見やすい形で色変換された色空間

これらは、テクスチャの内部ピクセルデータは変わらないですが、シェーダーを通るときに、どのように処理されるかが変わります。

通常、テクスチャはほぼ全てsRGBの色空間で見えています。テクスチャファイルのインスペクタなどを見ると、sRGBにチェックが入っていると思います。

シェーダー内での色空間関連の処理について#

先程、シェーダーという言葉が出てきましたね。ここでのシェーダーというのは、UnityのStandard Shaderや、Liltoonなどのことを指します。

私が今回苦労したのは、Color Changer For Unityでのリアルタイムプレビューにおける、GPUのシェーダー計算を使ったピクセル処理です。

実際の色空間の処理について#

シェーダー内では、テクスチャを読み込むときもあれば、テクスチャを書き込むといった処理が行われます。この読み込み時、書き込み時に色空間に関する処理は実行されます。

Unityにおけるシェーダーでは、色空間の変換機能を持っています。これの実行箇所を整理してましょう。

テクスチャのサンプリング時#

テクスチャのサンプリング方法結果
Indexでのアクセスそのままの値
シェーダーのサンプリング関数を使ったアクセステクスチャがsRGBであればLinearに変換
Linearだとそのままの値

テクスチャの書き込み時#

元テクスチャの色空間書き込み先のテクスチャの色空間結果
sRGB (シェーダー内でLinearに変換されている状態)Linear変換されない
LinearLinear変換されない
sRGB (シェーダー内でLinearに変換されている状態)sRGBLinearからsRGBに変換される(結果的に同じ色空間に戻される)
LinearsRGBLinearからsRGBに変換される

実際これだけなのですが、これの理解ですごい苦労しちゃって、 その苦労した内容を下に書いていきます。

実際に私が躓いてしまった事例#

まず、これについて話すには、私のツールのColor Chaneger For UnityTexTransToolの連携について説明する必要があります。

まずTexTransToolのMLICについて#

TexTransToolにはMLIC(Multi Layer Image Canvas) という、Unityのヒエラルキー内で画像のレイヤー構造を作り、それを割り当てたテクスチャと置き換える機能があります。

連携について#

そこで、「非破壊色改変ツールであるColor Changer For Unityの色改変をMLICのレイヤーとして扱えたらすっごいお得だよね」という考えから、連携しようという話になりました。

連携方法について簡単に説明すると…

TexTransTool側では外部スクリプト用のMLIC Interfaceを作るから、それをコンポーネント内で実装してね!」

といった内容でした。

外部スクリプト側の実装について#

外部スクリプト側では、指定されたMLIC Interface経由でRender Textureが渡され、それを直接書き換えるといった実装内容でした。

Color Changer For Unity実装について#

当時の色変換時の実装について、順番に説明していきます。

  1. TexTransToolからLinear空間のRender Textureが渡されます。

  2. シェーダー内で元の色を取得するためのコピーを作るために、sRGB空間のRender Textureを新規作成

  3. コピーのRender TextureGraphics.Blitで全てのピクセルをコピーする。

  4. シェーダーに、「コピーのRender Textureを元に、元Render Textureに1ピクセルづつ計算して書き込んでね」という指示を出し、TexTransToolから渡されたRender Textureに上書きで書き込み。

  5. 終了

なぜか色がおかしい…#

上記の手順だと、なぜかすごく白っぽいテクスチャが生成されるのです。どこが間違っていたのでしょうか。

正解は…「2のsRGBでRender Textureを新規作成したところ」でした

原因#

なぜこれがダメだったかと言うと、次の3のGraphics.Blitでコピーを作成する時、

これ、元のRender TextureがLinearだけど、書き込み先ってsRGBだよね。変換して書き込んどくよ~

という変換処理が入ります。なので、このRender TextureにはsRGBとして書き込まれます。

このコピーしたsRGBのRender Textureを元に、TexTransToolのLinearのRender Textureに書き込もうとするとどうなるでしょうか?

私のツールはIndexでの参照なので、サンプリング時の自動変換は掛かりません。なので、sRGBのピクセルが直接LinearのRender Textureに書き込まれるわけですね。(書き込み時は、相手がLinearのため、変換なしで直接書き込まれる)

なので、計算量が0でも、コピーしたRender Textureの色が変わっているため、色がおかしくなっていたのでした。

対処方法#

これの対処は至って簡単、コピーを作成する時にLinear色空間にするというだけでいいのです。

私の間違った思い込み#

シェーダーでテクスチャをサンプリングする際、全てのサンプリング時に自動で変換されると思ってました。私のツールはIndexでの参照だったので変換処理はないのですが、あると勝手に思い込んで混乱していました。

書いてて気付いたこと…#

これ、シェーダー内で直接TexTransToolのRender Textureを参照して、書き込めば良くない?

はい、気付いてしまいました。私が苦労してた部分が全て無駄だったということを…

まあ勉強になったのでいいとしましょう。

と思って、Color Changer For Unityのコードを変えたら動かなくなりました。いつか見直します。