リアルタイムの3Dキャラクターやアセットをレンダーテクスチャ(RenderTexture)を仲介してUI(RawImage)に描画した際、元のアセットと色調が一致せず、暗く汚れた色になったり白くボケたりするバグは、色空間(カラースペース)の解釈エラーによって発生する非常に頻出する問題です。本記事では、この『リニアとガンマのすれ違い』を『サングラスをかけた二重調光』の例え話を交えて分かりやすく解説し、完璧に色調を同期させるための設定・実装手順を提示します。

1. 例え話で理解する:『サングラスをかけた二重の度違い調光』

このカラースペースのミスマッチを直感的に理解するために、『サングラスをかけたまま行う絵の仕上げ作業』を例えに考えてみましょう。

あなたが明るい屋外で、非常に美しい絵画(3Dモデル)を描いているとします。その際、あなたは『強い日差しよけのサングラス(ガンマ補正)』をかけた状態でキャンバスを眺め、ちょうどいい明るさになるように調色(リニア計算によるライティング)をして仕上げました。絵はあなたのサングラス越しに『完璧なトーン』になっています。

次に、この絵を暗いギャラリー(UI画面)に展示するため、運搬用の専用ケース(RenderTexture)に入れます。しかし、運搬スタッフ(Unityの設定)が『このケース内の絵は、まだ日差しよけされていない生データだ』と勘違いし、勝手にケース内の絵の明るさを数値的に補正(誤ったリニア変換)してしまいました。さらに、ギャラリーの展示スタッフ(UIシェーダー)も『サングラス(デコード処理)』をかけた状態で絵を眺めて展示位置の明るさを調整します。この結果、元の絵画のカラーバランスが二重に歪んでしまい、サングラスを外した普通の観客がギャラリーを訪れて絵を見た瞬間、完成したはずの絵が極端に黒ずんで沈んで見える(あるいは光を当てすぎて白飛びする)という色彩崩壊が発生します。

これが、RenderTextureにおける『カラースペース不整合バグ』のメカニズムです。Linear空間で動作するUnityは、テクスチャがsRGB(ガンマ)かリニアかをマークすることによって、描画時と表示時にそれぞれ自動で補正を行います。RenderTextureの設定(マーク)を誤ると、この補正が重複して適用(二重にべき乗がかかり暗化)されたり、適用漏れ(白飛び)を引き起こしてしまうのです。

2. 解決策A:RenderTextureアセットの『sRGB』設定の有効化

プロジェクトフォルダ内にRenderTextureアセット(`.renderTexture` ファイル)を作成し、それを RawImage に直接割り当てて使用している場合は、インスペクターの設定からマークを正しく修正します。

設定手順:

  1. プロジェクトウィンドウから、該当するRenderTextureアセットを選択します。
  2. インスペクターウィンドウを確認します。
  3. 『Color Format』の設定において、名前に『sRGB』が付いているフォーマット(例: `R8G8B8A8_SRGB` など)を明示的に選択します(Unityのバージョンによっては『sRGB (Color Texture)』という直接のチェックボックスが表示されますので、それを **オン** にします)。

これにより、Unityの描画エンジンはこのRenderTextureを『ガンマ空間のカラーデータ(sRGB)』として正しく認識し、UIシステムがサンプリングして画面にレンダリングする際に、正しいリニア変換を自動で行うため、黒ずみバグが瞬時に解消されます。

3. 解決策B:C#スクリプトで動的にsRGBを有効化する

C#スクリプトを使用して動的にRenderTextureを作成する場合は、`RenderTextureDescriptor` で `sRGB` プロパティを明示的に `true` に指定してアロケーションを実行します。

using UnityEngine;
using UnityEngine.UI;

public class CharacterPreviewColorFix : MonoBehaviour
{
    public Camera subCamera;
    public RawImage rawImage;
    private RenderTexture targetRt;

    void Start()
    {
        InitializeRenderTexture();
    }

    void InitializeRenderTexture()
    {
        // 1. 解像度と精度を指定して記述子を作成
        RenderTextureDescriptor desc = new RenderTextureDescriptor(1024, 1024, RenderTextureFormat.ARGB32, 24);
        
        // 2. ★超重要:sRGB設定を明示的に true にする(自動ガンマ補正をフックする)
        desc.sRGB = true;
        desc.useMipMap = false;
        desc.autoGenerateMips = false;

        // 3. テクスチャを生成
        targetRt = new RenderTexture(desc);
        targetRt.filterMode = FilterMode.Bilinear;
        targetRt.Create();

        // カメラのターゲットとUIの参照を設定
        subCamera.targetTexture = targetRt;
        rawImage.texture = targetRt;
    }

    void OnDestroy()
    {
        if (targetRt != null)
        {
            targetRt.Release();
            Destroy(targetRt);
        }
    }
}

このスクリプトを通して動的確保されたバッファは、リニア空間で計算された3Dのカラーデータを自動でsRGBフォーマットへエンコードして保存します。そのため、uGUIのRawImage描画時に生じる二重変換を完全に防止し、元のアセットと100%同一のカラーバランスでUI上にプレビューを表示できます。