こんにちは!情熱開発部プログラム課の倉平です。
最近懐かしくなってスーパーファミコンで遊びました。
特にドラゴンクエスト5が好きで子供の頃よく遊んでいたんですよね。
子供の頃の自分はどんなパーティーで遊んでいたか気になり、久しぶりに起動してみましたが…。
「まことに ざんねんですが ぼうけんのしょ1は 消えてしまいました。」
「まことに ざんねんですが ぼうけんのしょ2は 消えてしまいました。」
「まことに ざんねんですが ぼうけんのしょ3は 消えてしまいました。」
子供の頃の思い出は全て消えていました…。
何回見てもこの演出は心臓に悪いですね。
バックアップ機能があるソフトは内部の電池が切れている可能性があるので、
電池の交換をおススメします。
では、今回のテーマは「レトロゲームの様なモザイクフェードを実装してみた」です。
レトロゲームでモザイク処理を掛けながらフェードを行っているのを見て、
Unityでも実装出来ないか挑戦してみました。
実装したUnityの環境
- URP
 - Unity 2020.3.32f1
 
下準備
まずは通常のフェード処理を実装。

白フェードがイン、アウトするだけのシンプルなフェード処理です。
こちらのフェード処理を改良してモザイクフェードを実装します。
モザイク処理Shader作成
モザイク処理用のshaderを作成します。
Shader "Test/MosaicShader"
{
    Properties
    {
        _MosaicBlockNum("Mosaic Block Num", Int) = 100
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            #include "UnityCG.cginc"
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };
            sampler2D _CameraOpaqueTexture;
            float4 _CameraOpaqueTexture_ST;
            int _MosaicBlockNum;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _CameraOpaqueTexture);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
            fixed4 frag(v2f i) : SV_Target
            {
                // ブロックごとに区切る
                i.uv.x = floor(i.uv.x * _MosaicBlockNum) / _MosaicBlockNum;
                i.uv.y = floor(i.uv.y * _MosaicBlockNum) / _MosaicBlockNum;
                // sample the texture
                fixed4 col = tex2D(_CameraOpaqueTexture, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}モザイク処理シェーダーです。
Unityのデフォルトで作成できるUnlitShaderを改良したものになります。
カメラ描画をGrabしてモザイク処理を掛けています。
URPでは_GrabPassが使用できない為、代用機能の「_CameraOpaqueTexture」を使用しています。
使用する際はScriptableRenderPipelineAssetの「Opaque Texture」にチェックを入れてください。

肝心のモザイク処理はこちらの部分です。
fixed4 frag(v2f i) : SV_Target
{
    // ブロックごとに区切る
    i.uv.x = floor(i.uv.x * _MosaicBlockNum) / _MosaicBlockNum;
    i.uv.y = floor(i.uv.y * _MosaicBlockNum) / _MosaicBlockNum;こちらの2行を追加するだけでモザイク処理が掛かります。
モザイクの縦/横のブロック数が_MosaicBlockNumで指定した数になるようにUV座標を変換しています。
_MosaicBlockNumの数値を変えることでモザイクの濃さを調整出来ます。

モザイクフェードを実装
作成したモザイク処理shaderをフェード処理で使用してみました。

面白味がある絵になりましたね。
画面のアスペクト比に対応しよう
作成したモザイク処理を良く見ると…。

モザイクが長方形になっていますね。
これはUV座標変換計算時にXY両方とも「_MosaicBlockNum」を参照していたからです。
これでは画面のアスペクト比を変更する際にモザイクの形も変ってしまいます。
どんなアスペクト比でも同一の形を保つには「_MosaicBlockNum」を縦/横の2種類用意します。
Properties
{
    // 縦横ごとのモザイクブロック数
    _MosaicBlockXNum("Mosaic Block Size X", Int) = 100
    _MosaicBlockYNum("Mosaic Block Size Y", Int) = 100
}UV座標計算も合わせて変更します。
fixed4 frag(v2f i) : SV_Target
{
    // ブロックごとに区切る
    i.uv.x = floor(i.uv.x * _MosaicBlockXNum) / _MosaicBlockXNum;
    i.uv.y = floor(i.uv.y * _MosaicBlockYNum) / _MosaicBlockYNum;これでアスペクト比対応が出来ました。
モザイクの濃さを設定する際にXYのブロック数の計算をしますが、
Screen.width、Screen.heightを元に計算を行うことで、簡易にアスペクト比を考慮した計算が出来ます。
では実際にアスペクト比を考慮したモザイクフェードを再生してみましょう。


別のアスペクト比画面でも見てみましょう。


アスペクト比が変わってもモザイクの形が保たれているのが確認できます。
最後に
モザイク処理shaderは様々な処理に応用出来るので色々試して見てください。
こちらは私が過去にブログで扱ったポストプロセッシングの描画分けを応用した物です。
任意のオブジェクトだけにモザイク処理を掛けています。
描画分けの記事はこちら。

参考サイト
今回の記事を書くにあたり、以下のサイトを参考にさせていただきました。
【免責事項】
本サイトでの情報を利用することによる損害等に対し、
株式会社ロジカルビートは一切の責任を負いません。




