UI画面(ショップの3Dプレビュー、プレイヤーのステータス画面、対戦開始前のキャラクター並び立ち演出など)の中にリアルタイムな3Dキャラクターモデルを組み込む際、カメラの映像を「RenderTexture(レンダーテクスチャ)」に書き込み、それをuGUIの「RawImage」コンポーネントで表示する手法は、Unity開発における最も一般的なアプローチです。しかし、この実装を行った際に、**「3Dモデルの色がやけに沈み込んで暗い」「影が真っ黒に潰れて顔色が悪く見える」「ハイライトが不自然に光りすぎている」**といったカラーの不整合問題が非常に多くのプロジェクトで発生します。本記事では、Linear(リニア)空間とGamma(ガンマ)空間の不一致が引き起こすこの描画バグの仕組みと、完全にカラーを同期させるための解決手順を解説します。
1. 根本的な原因:リニアカラースペースでのガンマ補正の二重処理
現代の高品質な3Dゲーム開発では、物理的に正しい光の計算を行うために、Unityのプロジェクト設定(Project Settings > Player > Other Settings)の「Color Space」に『Linear』が一般的に選択されます。このリニア空間において、テクスチャやレンダーバッファの色の伝達は以下のような変換プロセスを辿ります。
- テクスチャのインポート: sRGB(ガンマ補正がかかった人間が見て自然な画像)テクスチャが読み込まれる際、シェーダーに渡される前に自動的に「ガンマ解除(リニア化:色を数値的に正しくする処理)」が行われます。
- シェーダーでの計算: リニア空間で光の反射やブレンド計算を行います(ここで2倍や0.5倍といった光の加算が正しく計算されます)。
- 画面出力: 最終的にモニターに出力する際、モニターのガンマ特性(暗く表示する物理的特性)を相殺するため、再度「ガンマ補正(sRGBエンコード)」をかけて出力します。
バグが発生するのは、このフローの中に「RenderTexture」という中間バッファを挟む場合です。
RenderTextureをスクリプトやアセットから動的に生成する際、デフォルト設定のままだと、このバッファは**「リニア(Linear)バッファ」**として作成されます。このリニア RenderTexture に対してサブカメラが3Dモデルを描画し、ポストプロセス処理を行わずに RawImage に割り当てると、RawImageのデフォルトUIシェーダーは「入力されたテクスチャは通常のsRGBテクスチャである(=まだガンマ補正されていないデータ)」と解釈します。そのため、UIシェーダーは描画時にさらにリニア変換をかけようと処理を行ってしまい、カラー値が二重にリニア変換(べき乗)されて極端に暗くなり、影が真っ黒に潰れてしまうのです。
2. ポストプロセス(Tonemapping)の二重適用問題
もう一つのカラー崩壊の原因が、ポストプロセスエフェクトの重複適用です。3Dモデルをきれいに見せるために、プレビュー用サブカメラのポストプロセスを有効にして `Tonemapping` や `Color Grading` を RenderTexture に書き込みます。しかし、この RenderTexture が貼り付けられた RawImage が存在するUIキャンバス(UI Canvas)は、メインカメラによって描画されます。もし、メインカメラ側でも `Post-processing` が有効になっており、かつUIレイヤーがポストプロセスの適用マスク(Volume Mask)に含まれている場合、**すでにTonemappingが適用された RenderTexture の画像に対し、メインカメラ側でさらに2回目のTonemappingが重ねて実行されます。**
この結果、白飛び(トーンマップの二重適用によるコントラスト崩壊)が発生したり、全体の色相が黄色や青に不自然にシフトしてしまうカラーバグが引き起こされます。
3. 正しく鮮明な3DプレビューUIを構築するための解決手順
カラーの正確性を100%保ったまま3DプレビューUIを描画するためには、テクスチャフォーマットの最適化と、カメラパスの分離設定を行う必要があります。
① RenderTextureのsRGBフラグの正しい設定(C#)
C#スクリプトでRenderTextureを動的に作成する場合、`RenderTexture` のコンストラクタではなく、必ず `RenderTextureDescriptor` を使用し、`sRGB` プロパティを明示的に `true` に設定してください。これにより、Unityは作成されたテクスチャを「sRGB(ガンマバッファ)」としてマークし、UIシェーダーがサンプリングする際に自動で正しい色空間の変換がハンドリングされます。
using UnityEngine;
using UnityEngine.UI;
public class RenderToUiController : MonoBehaviour
{
public Camera sub3DCamera;
public RawImage uiRawImage;
private RenderTexture characterRenderTexture;
void Awake()
{
CreateCorrectRenderTexture();
}
void CreateCorrectRenderTexture()
{
// 1. RenderTextureDescriptor を作成
RenderTextureDescriptor desc = new RenderTextureDescriptor(
512, // 幅
512, // 高さ
RenderTextureFormat.ARGB32, // アルファチャンネル付きの標準カラーフォーマット
24 // デプスバッファの精度 (24ビット)
);
// 2. ★超重要:sRGBカラー空間を有効にする(Linear空間からの書き込み時に自動でガンマ補正される)
desc.sRGB = true;
desc.useMipMap = false;
desc.autoGenerateMips = false;
// 3. RenderTexture を生成
characterRenderTexture = new RenderTexture(desc);
characterRenderTexture.filterMode = FilterMode.Bilinear;
characterRenderTexture.wrapMode = TextureWrapMode.Clamp;
characterRenderTexture.Create();
// 4. カメラとUIに割り当て
sub3DCamera.targetTexture = characterRenderTexture;
uiRawImage.texture = characterRenderTexture;
}
void OnDestroy()
{
if (characterRenderTexture != null)
{
sub3DCamera.targetTexture = null;
uiRawImage.texture = null;
characterRenderTexture.Release();
Destroy(characterRenderTexture);
}
}
}
② UIカメラとポストプロセスの完全分離
ポストプロセスの二重適用を防ぐため、以下のカメラ設定を必ず実施してください。
- メインシーンを描画するメインカメラの「Post-processing」設定の対象範囲から、UIレイヤー(例: `UI` や `UI3D` レイヤー)を完全に除外します。具体的には、メインカメラの `Volume Mask` からUI関連のレイヤーのチェックを外します。
- UIを描画するためのUI専用Overlayカメラをカメラスタックに追加している場合は、そのUIカメラの設定内にある「Post-processing」のチェックボックスを **オフ** にします。これにより、UI描画時に余計なポストエフェクトが重なるのを防止できます。