ゲームの空気感や空間の広がり、幻想的な雰囲気を表現する上で「Fog(フォグ/霧)」は非常に重要なエフェクトです。しかし、Universal Render Pipeline (URP) を用いて霧の立ち込める森や薄暗い洞窟を構築した際、「深いフォグの奥深くに配置した煙や炎のパーティクル(半透明オブジェクト)が、霧の影響を全く受けずにクッキリと濃い色で最前線に浮き出て見えてしまう」というレンダリング不良に直面したことはないでしょうか。これはグラフィックス設定の単純な見落としや、自作シェーダーの記述不足から発生します。本記事では、このフォグと半透明の描画バグが発生する内部的な仕組みと、初心者にも分かりやすい例え話、そして完全な解決策を具体的な手順とコード付きで徹底解説します。

1. 例え話で理解する:『アクリル板に描いた炎』と『白い紙のカーテン』

この「フォグと半透明パーティクルの矛盾」を視覚的に理解するために、「透明なアクリル板に描いた絵」と「半透明の白いカーテン」の重ね合わせを例えにして考えてみましょう。

あなたが、部屋の奥(遠景)に白いカーテン(フォグ)を何重にも引いて、奥の景色が白く霞んで見えないように演出しているとします。カーテンのすぐ後ろ(フォグの奥)には、赤々と燃える炎(パーティクル)があります。物理的に正しい見え方であれば、炎の光は白いカーテンを何枚も透過するため、手前からは「うっすらと白くぼやけたピンク色の光」として見えるはずです。

しかし、描画処理にバグがあると、演出スタッフ(レンダリングエンジン)は「炎は半透明でアクリル板に描かれているから、カーテンの計算が終わった後で一番上に重ねて飾ってね」と処理してしまいます。結果として、白いカーテンの手前(一番手前)に、くっきりとした真っ赤な炎のアクリル板がペタッと貼り付けられた状態になります。背景の山や家はカーテンで白く隠れているのに、そのさらに奥にあるはずの炎だけが、カーテンを突き抜けて生々しい色で最前面に浮き出てしまうのです。これが、フォグと半透明ブレンドの破綻バグの正体です。

2. 根本的な原因:レンダリングパスとフォグブレンドの欠落

技術的に言うと、UnityのUniversal Render Pipeline(URP)において、フォグはシェーダー内部の頂点シェーダー(Vertex Shader)およびフラグメントシェーダー(Fragment Shader)で計算されます。通常、3Dオブジェクトが描画される際、カメラからの距離(深度)に基づいてフォグの減衰率($0$〜$1$)が計算され、オブジェクト本来のカラーとフォグのカラー(白やグレーなど)が線形補間されてブレンドされます。

しかし、半透明(Transparent)キューで描画されるオブジェクト(パーティクルシステム用マテリアルなど)は、不透明オブジェクトの描画がすべて完了した後に、バックバッファに書き込まれます。このとき使用されるシェーダーに「フォグを計算する処理(HLSLコードやノード構造)」が記述されていない、あるいは無効化されていると、フォグの重ね合わせ処理が完全にバイパス(無視)され、元のテクスチャカラーのまま描画されてしまいます。これが「フォグを貫通して浮き出る」バグの原因です。

3. 実務で採用すべき3つの解決アプローチ

アセットの作成方法(Shader Graphで作っているか、手書きのHLSLコードか)や描画方式に応じて、以下のいずれかの方法でフォグを有効化します。

解決アプローチA:Shader Graphでの解決手順(最も一般的)

パーティクル用のマテリアルをShader Graphで自作している場合、解決は非常にシンプルです。

  1. 対象のShader Graphファイルを開きます。
  2. Graphの右上にある「Graph Settings」タブを選択します。
  3. Target Settings > Active Targets > Universal のセクションを展開します。
  4. インスペクター内の設定リストにある 「Apply Fog」 チェックボックスをONにします(デフォルトでOFFになっている場合があります)。
  5. Shader Graphを保存(Save Asset)します。

※これにより、Unityがシェーダーコードを生成する際、自動的にフォグの補間データとカラーブレンド処理が組み込まれ、フォグの奥にあるパーティクルが正しく霧に包まれるようになります。

解決アプローチB:手書きHLSLパーティクルシェーダーにおける実装方法

独自のHLSLコードでパーティクルシェーダーを実装している場合は、フォグ処理用マクロをコード内に正しくバインドする必要があります。以下は、URP対応のフォグ付きパーティクルシェーダーの最小実装テンプレートです。

Shader "Custom/URP_Particle_Fog"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" "RenderPipeline"="UniversalPipeline" }
        Blend SrcAlpha OneMinusSrcAlpha
        ZWrite Off

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // 1. フォグのバリアントをコンパイル対象に含めるマクロを定義
            #pragma multi_compile_fog

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float2 uv           : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS   : SV_POSITION;
                float2 uv           : TEXCOORD0;
                // 2. フォグ補間用の座標定義(マクロで自動配置)
                DECLARE_FOG_COORDS(1)
            };

            sampler2D _MainTex;

            Varyings vert(Attributes input)
            {
                Varyings output;
                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                output.uv = input.uv;

                // 3. 頂点シェーダーでカメラからの距離に基づいてフォグ座標を初期化
                float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
                INITIALIZE_FOG_COORDS(output, positionWS);

                return output;
            }

            float4 frag(Varyings input) : SV_Target
            {
                float4 col = tex2D(_MainTex, input.uv);

                // 4. フラグメントシェーダーで最終カラーにフォグをブレンド
                // ※ input.fogCoord と現在のカラーを基に、霧のカラーを合成します
                MIX_FOG(input, col.rgb);

                return col;
            }
            ENDHLSL
        }
    }
}

解決アプローチC:加算(Additive)パーティクルの黒ずみバグ対策

炎や光の表現によく使われる「加算(Additive)ブレンド」のパーティクルに通常のフォグを適用すると、「霧の中でエフェクトが白く濁ったり、周囲が黒ずんで不自然に汚れてしまう」という新たな問題が発生します。これは、加算ブレンドが「光をプラスする」計算であるため、通常の「フォグ色(グレーなど)で塗りつぶす」処理を行うと、黒い背景にグレーが足されて不自然に明るくなったり暗くなったりするためです。

このトラブルを防ぐためには、加算用マテリアル専用のシェーダーにて、フォグの影響を受ける際に「フォグの色を足すのではなく、フォグの濃さに応じてパーティクル自身のカラーを黒(無光)に近づけていく(ブレンド率を下げる)」というカスタム処理を記述します。具体的には、フォグ率 $f$ (1のとき霧なし、0のとき完全な霧)を用いて、最終出力を color.rgb * f と計算します。これにより、霧の奥にある光エフェクトが不自然に浮き出ず、自然にフェードアウトして消えていくような美しい表現が実現できます。

4. フォグ適用不良時のデバッグ・チェックリスト

設定したのに正しくフォグがかからない場合は、以下の項目を確認してください。

  • Render Pipeline Assetの設定: プロジェクトの「URP Asset」で、フォグ(Fog)の機能自体が有効になっているか。
  • Lightning設定の確認: Window > Rendering > Lighting ウィンドウを開き、「Environment」タブ内の「Other Settings > Fog」のチェックボックスがONになっているか。
  • マテリアルのブレンドモード: パーティクルのマテリアルのレンダータイプが正しく Transparent になっており、Queue3000(半透明領域)以降に設定されているか。