Unityで自作シェーダを書いてカスタムポストエフェクトを実装する方法です。
はじめに
まずはじめに、Unityにおけるポストエフェクトの掛け方は二通りの方法があります。
Post Processing Stackという公式アセットを使う方法と、使わない方法です。
本記事ではPost Processing Stackを使わずにポストエフェクトを実装する方法を紹介します。
二つの方法の違いについては以下の記事で紹介していますので、必要に応じて参照してください。
ポストエフェクトとは?
ポストエフェクトの考え方はシンプルです。
まず、カメラが色んなものを描画した結果として、画面と同じサイズのテクスチャを得られます。
これの各ピクセルについて、シェーダで色をいじってそれを新たなレンダリング結果とするだけです。
このピクセルの編集工程がすなわちポストエフェクトで、編集の仕方により色々な効果を掛けることができます。
シェーダを書く
ポストエフェクトをかける最小コードのシェーダを描いてみます。
Shader "PostEffect" { Properties{ _MainTex ("Texture", 2D) = "white" {} } SubShader { Cull Off ZTest Always ZWrite Off Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); return 1. - col; } ENDCG } } }
カリングやZライトは不要なのでOFFに、ZTestは常に通す設定にしておきます。
_MainTexプロパティには、ポストエフェクトをかける前のレンダリング結果が入ってくる前提で作ります。
処理としては色を反転しているだけです。
スクリプトを書く
次に、カメラにアタッチするためのスクリプトを書きます。
using UnityEngine; [ExecuteInEditMode] public class PostEffect : MonoBehaviour { [SerializeField] private Material _material; private void OnRenderImage(RenderTexture source, RenderTexture dest){ Graphics.Blit(source, dest, _material); } }
OnRenderImage() はカメラによるレンダリングが完了した際に呼ばれます。
第一引数にレンダリング結果が入ってくるので、それを編集したものを第二引数に書き込むことで、レンダリング結果を編集することができます。
Graphics.Blit() は第一引数のテクスチャを、第二引数のテクスチャに、第三引数のマテリアルを用いてコピーします。
スクリプトとシェーダを反映する
これらを使ってポストエフェクトを掛けます。
まずこんな感じにシーンを作ります。
次にカメラにスクリプトをアタッチします。
その後、先ほど作ったシェーダをもつマテリアルを作成し、スクリプトの変数 _material にインスペクタから入れるだけです。
正常にポストエフェクトがかかることが確認できました。
シーンビューでもかかるようにする
この状態だとシーンビューではポストエフェクトを確認できません。
もし掛けたい場合は、スクリプトに ImageEffectAllowedInSceneView アトリビュートを追加するだけです。
using UnityEngine; [ExecuteInEditMode, ImageEffectAllowedInSceneView] public class PostEffect : MonoBehaviour { [SerializeField] private Material _material; private void OnRenderImage(RenderTexture source, RenderTexture dest){ Graphics.Blit(source, dest, _material); } }
これでシーンビューにもポストエフェクトがかかりました。
シーンビューでも確認したい場合はこのアトリビュートを付けておくといいかと思います。
以上です。