UnityのUniversal Render Pipeline (URP) を用いたモバイルゲームやVRゲームの開発において、GPUのフィルレート負荷(ピクセルシェーダー計算負荷)を削減するための非常に効果的な最適化手法が「Render Scale(描画倍率)」の調整です。Render Scaleを 0.70.5 に下げることで、描画するピクセル数を大幅に減らし、劇的なフレームレート向上を得ることができます。

しかし、この設定を行った際、「3Dモデルだけでなく、ゲームのUI(uGUIのボタンや文字、TextMeshProなど)まで一緒に低解像度化してボケボケになってしまう」という致命的なグラフィックス低下バグに直面することが多々あります。本記事では、このUIぼやけが発生するURP特有のカメラスタックの仕組みと、UIのシャープな解像度を維持したまま3D描画だけを軽量化する設定・解決手順を詳しく解説します。

1. 根本原因:Camera Stacking によるレンダーターゲット解像度の共有

URPにおいてUIを3Dオブジェクトの手前に綺麗に重ねて表示する際、一般的に推奨されるのが「Camera Stacking(カメラスタッキング)」です。これは、3D世界を描画する「Baseカメラ」のStackリストに、UI描画用の「Overlayカメラ」を登録して合成する仕組みです。

しかし、URPの仕様上、カメラスタック内のすべてのカメラは、Baseカメラが生成する同一のテンポラリ・レンダーターゲットバッファに対して描画を行います。

URPのRender Scale低下に伴うカメラスタック内の解像度低下とUIの引き伸ばしボケのメカニズム図

図:3D BaseカメラのRender ScaleがスタックされたUI Overlayカメラの解像度まで強制的に下げてしまう仕組み

URPアセットの Render Scale(またはスクリプトによる UniversalRenderPipelineAsset.renderScale)を変更すると、このレンダーターゲットの解像度が 画面物理解像度 × Render Scale にスケールダウンされます。例えば、フルHD(1920x1080)の画面で Render Scale を 0.5 に設定した場合、Baseカメラのレンダーターゲットは半分の 960x540 に縮小されます。

この状態でUI Overlayカメラが描画を行うと、UIのボタンやTextMeshProの文字も 960x540 の極めて低い解像度のバッファ上にラスタライズされます。 その後、最終的なポストプロセスや画面へのバックバッファ転送時に、再び 1920x1080 の物理サイズに(ニアレストネイバーまたはバイリニアで)引き伸ばされるため、UI全体がひどくぼやけて表示されることになります。

2. 解決策①:最も簡単かつ軽量な「Screen Space - Overlay」への移行

UIが3D空間のエフェクト(UIの隙間に流れる3Dパーティクルなど)と複雑に交差する必要がなく、UIの上に3Dカメラのポストプロセスをかける必要もない場合は、Canvasの描画モードを「Screen Space - Overlay」に変更することが最もシンプルかつパフォーマンス的にも最良の選択肢です。

設定手順:

  1. ヒエラルキーで UI の Canvas オブジェクトを選択します。
  2. インスペクターで Canvas コンポーネントを確認します。
  3. Render ModeScreen Space - Camera から Screen Space - Overlay に変更します。
  4. 不要になった UI Camera(Overlayカメラとして使用していたカメラ)を、Main Cameraの Stack リストから削除します。

Screen Space - Overlay モードで描画されるUIは、URPの3Dレンダリングループが完了した後に、グラフィックスドライバレベルでスマートフォンの画面(物理バックバッファ)に直接フル解像度で上書き描画されます。そのため、3D Baseカメラの Render Scale が 0.1 であろうと、UIは常にデバイス本来の最大解像度でシャープに描画されます。また、カメラスタックのオーバーヘッドが減るため、パフォーマンスも向上します。

3. 解決策②:「マルチBase Camera」構成によるUIカメラのスケール分離

「UIの前に3Dパーティクルエフェクトを表示させたい」「UI全体に対してブルーム(Bloom)などのポストプロセスを別個にかけたい」などのデザイン要件がある場合、解決策①のOverlayモードは使用できず、どうしてもカメラを介した描画が必要になります。

この場合は、UIカメラを「Overlayカメラ」にするのではなく、「別のBaseカメラ」として定義し、画面上に順番に重ねて描画する(マルチBase Camera構成)ことで、UIの解像度(Render Scale = 1.0)を維持します。

設定手順:

  1. UI描画専用のカメラ(例:UI Camera)を選択します。
  2. インスペクターの Camera コンポーネントで、Render TypeOverlay から Base に変更します。
  3. Clear Flags(または Background Type)を Uninitialized または Depth Only(あるいは Don't Clear)に設定し、3Dカメラが描画した背景の絵を消さずに残すようにします。
  4. Render Pipeline Metadata(またはURP設定)で、UI Camera用の Allow Dynamic Resolution などのチェックや、解像度に影響を与える設定がオフになっていることを確認します。
  5. Depth パラメータ(描画順)を調整し、3D用Baseカメラ(例:Depth = -1)よりも、UI用Baseカメラ(例:Depth = 1)の値が大きくなるように設定します。
  6. Main Cameraの Stack リストから UI Camera を削除します(UI Cameraはそれ単体で動作するため)。

これで、3D描画(Render Scale = 0.5)の処理が終了したバックバッファに対して、UIカメラ(Render Scale = 1.0)がフル解像度のキャンバスサイズで直接UIを描画して重ね合わせるため、UIのぼやけを完全に防ぎつつ、3Dエフェクトの重ね合わせやUI専用のレンダリングパイプラインを維持することができます。

4. レンダリング構成ごとの比較とトレードオフ

構成方法 メリット デメリット・注意点
1. Base (3D) + Overlay (UI)
(従来のカメラスタック)
同一バッファ内でまとめてポストプロセスを適用でき、セットアップが簡単。 Render Scaleの影響を直接受ける。 3Dを軽量化するとUIの文字が判読不能になる。
2. Screen Space - Overlay
(推奨・最適解)
最も軽量。Render Scaleの影響を受けず、常にデバイス最大解像度でシャープ。スタックオーバーヘッドなし。 UIの手前に3Dオブジェクトや3Dパーティクルを表示させることが困難。ポストエフェクトがUIにかからない。
3. マルチBase Camera構成
(デザイン重視の解決策)
UIの手前に3Dエフェクトを表示可能。UIカメラ専用のPost Processingを適用可能。 複数のBaseカメラが個別にレンダーパスを走らせるため、カメラスタックに比べてわずかにドローコールやクリア処理のオーバーヘッド(帯域負荷)が増加する。

5. スクリプトからRender Scaleのみを動的調整する実装手法

ゲームのグラフィックス設定画面(High/Medium/Low)や、ゲーム中のFPS低下時にC#スクリプトからRender Scaleを動的に変更しつつ、UIの鮮明さを保つための実用的なコード例を紹介します。

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class RenderScaleController : MonoBehaviour
{
    [Range(0.1f, 2.0f)]
    public float targetRenderScale = 1.0f;

    void Start()
    {
        // 現在のURPアセットのRender Scaleを適用
        ApplyRenderScale(targetRenderScale);
    }

    // スライダーなどのUIイベントから呼び出すためのメソッド
    public void SetRenderScale(float value)
    {
        targetRenderScale = Mathf.Clamp(value, 0.1f, 2.0f);
        ApplyRenderScale(targetRenderScale);
    }

    private void ApplyRenderScale(float scale)
    {
        // URPのアクティブなパイプラインアセットを取得
        if (GraphicsSettings.currentRenderPipeline is UniversalRenderPipelineAsset urpAsset)
        {
            // アセットのrenderScaleプロパティに値をセット
            // ※これにより、現在実行中の3D Baseカメラの描画ターゲット解像度が動的に変更されます
            urpAsset.renderScale = scale;
            Debug.Log($"[GraphicsSettings] URP Render Scale を {scale:F2} に設定しました。");
        }
        else
        {
            Debug.LogWarning("現在のアクティブなレンダーパイプラインがURPではありません。");
        }
    }
}

このスクリプトと「Screen Space - Overlay」または「マルチBase Camera構成」を組み合わせることで、3Dモデルだけがピンボケあるいはギザギザになりながらも、画面上のスコア表示やチャット文字、メニュー画面はクッキリしたフォント解像度のまま表示され、ユーザー体験を損なわずに省電力化や熱暴走対策(サーマルスロットリング回避)を行うことが可能になります。