自作のポストエフェクトをPost Processingのカスタムエフェクトとして実装する方法です。
- Post Processingを導入する
- シェーダを書く
- PostProcessEffectSettingsを書く
- PostProcessEffectRendererを書く
- 使ってみる
- シェーダがビルドに含まれない?
- カスタムポストエフェクトの適用順序を変更する
- Inspectorを拡張する
- FXAAを使う場合は書き方に注意する
- 関連
- 参考サイト
Unity2018.3.1f1
Post Processing 2.1.2
Post Processingを導入する
Post Processingの導入方法は下記の記事にまとめてありますので必要に応じて参照してください。
シェーダを書く
まずはシェーダを書きます。
今回はレンダリング結果を拡大できるポストエフェクトを書いてみます。
Shader "Hidden/Custom/Scaling" { SubShader { Cull Off ZWrite Off ZTest Always Pass { // CGPROGRAMではなくHLSLPROGRAMにする HLSLPROGRAM #pragma vertex VertDefault #pragma fragment Frag // VaryingsDefaultとかを使うためにインクルードする必要あり #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl" TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex); float _Scale; float4 Frag(VaryingsDefault i) : SV_Target { // テクスチャの取り方とかはCGとは異なるので注意 i.texcoord -= 0.5; // 0~1の値を-0.5~0.5の値にする i.texcoord *= 1.0 - _Scale; // スケーリング i.texcoord += 0.5; float4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord); return color; } ENDHLSL } } }
このシェーダはSRPとの互換性を保つことを前提としているためいくつか従来の書き方と異なる点があります。
CGPROGRAM
ではなくHLSLPROGRAM
を使うStdLib.hlsl
をインクルードする必要がある- テクスチャのサンプリング方法など、一部APIが従来のものと異なる
3については下記にAPIがまとまっているのでこれを参照すると良いようです。
まあここは少しずつ調べていけばいいでしょう。
PostProcessEffectSettingsを書く
次にPostProcessEffectSettings
というクラスを継承したクラスを作ります。
ScriptableObject
を継承しているのでクラス名とファイル名は一致させてください。
using System; using UnityEngine; // UnityEngine.Rendering.PostProcessingをusing using UnityEngine.Rendering.PostProcessing; [Serializable] // 必ずSerializableアトリビュートを付ける [PostProcess(typeof(ScalingRenderer), PostProcessEvent.AfterStack, "Custom/Scaling", true)] public sealed class Scaling : PostProcessEffectSettings { [Range(0f, 1f)] public FloatParameter scale = new FloatParameter { value = 0.0f }; // 有効化する条件はこうやって指定する(ちゃんとやっておいたほうがパフォーマンスにつながりそう) public override bool IsEnabledAndSupported(PostProcessRenderContext context) { return base.IsEnabledAndSupported(context) || scale != 0; } }
簡単な説明はコメントに書いていますが、以下に何点か補足します。
まずクラスにはPostProcess
アトリビュートをつけます。
この第一引数には対応するPostProcessEffectRenderer
を指定しますが、これは次節で説明します。
第二引数にはポストエフェクトを掛けるタイミングを設定しますが、これも適用順序の節で後述します。
第三引数はポストエフェクトをInspectorから選択するときのパスを指定します。
第四引数はポストエフェクトをSceneビューにも適用するかどうかのフラグで、デフォルトはtrueで省略可能です。
また、Inspectorから調整できるようにするパラメータはXxxParameter
という型で定義します。
組み込まれている型は下記から確認できます。
現状定義されているものは次の通りです。
- FloatParameter
- IntParameter
- BoolParameter
- ColorParameter
- Vector2Parameter
- Vector3Parameter
- Vector4Parameter
- SplineParameter
- TextureParameter
このパラメータについてはParameterOverride
クラスを継承すれば自作できるようです。
PostProcessEffectRendererを書く
次にPostProcessEffectRenderer
を継承したクラスを作ります。
こちらはファイル名とクラス名を合わせる必要はないので前節のコードと同じファイルに定義しても問題ないです。
using UnityEngine; using UnityEngine.Rendering.PostProcessing; public sealed class ScalingRenderer : PostProcessEffectRenderer<Scale> { // 初期化時の処理 public override void Init() { base.Init(); } public override void Render(PostProcessRenderContext context) { // 内部的にプールされているMaterialPropertyBlockが保存されているPropertySheetを取得 var sheet = context.propertySheets.Get(Shader.Find("Hidden/Custom/Scaling")); // MaterialPropertyBlockに対してプロパティをセット sheet.properties.SetFloat("_Scale", settings.scale); // CommandBufferのBlitFullscreenTriangleを使って描画 context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0); } // 破棄時の処理 public override void Release() { base.Release(); } }
説明はコメントに書いた通りです。
内部的にはMaterialPropertyBlock
とCommandBuffer
を使って実装しているようです。
使ってみる
これで実装は完了したので使ってみます。
Post Process Volume
のAdd effect...ボタンからCustom > Scalingを選択します。
結果は次のようになります。
ちゃんと動いていることが確認できました。
シェーダがビルドに含まれない?
前々節のPostProcessEffectRenderer
ではシェーダをShader.Find()
で検索しています。
そのため、Always Includedにの設定をするかResourcesに入れないとビルドに含まれないようです。
if the shader is never referenced in any of your scenes it won't get built and the effect will not work when running the game outside of the editor. Either add it to a Resources folder or put it in the Always Included Shaders list in Edit -> Project Settings -> Graphics.
Writing Custom Effects | Package Manager UI website
これは非常にミスりそうです。
未検証ですが、こんな感じでPostProcessEffectSettings
にシリアライズしておけばいい気もします。
using System; using UnityEngine; using UnityEngine.Rendering.PostProcessing; [Serializable] [PostProcess(typeof(ScalingRenderer), PostProcessEvent.AfterStack, "Custom/Scaling", true)] public sealed class Scale : PostProcessEffectSettings { // シェーダの参照をScriptableObjectに持たせておく public Shader shader; [Range(0f, 1f)] public FloatParameter scale = new FloatParameter { value = 0.0f }; } public sealed class ScalingRenderer : PostProcessEffectRenderer<Scale> { public override void Render(PostProcessRenderContext context) { // ScriptableObjectからシェーダを取得 var sheet = context.propertySheets.Get(settings.shader); sheet.properties.SetFloat("_Scale", settings.scale); context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0); } }
カスタムポストエフェクトの適用順序を変更する
次にポストエフェクトを掛けるタイミングの制御の仕方を説明します。
まず、PostProcessEffectSettings
につけるPostProcess
アトリビュートの第二引数で、
組み込みエフェクトの前に掛けるか後に掛けるかといった大枠のタイミングが指定ができます。
第二引数 | 意味 |
---|---|
PostProcessEvent.BeforeTransparent | 半透明描画の前に処理 |
PostProcessEvent.BeforeStack | 組み込みエフェクトの前に処理 |
PostProcessEvent.AfterStack | 組み込みエフェクトの後に処理(ただしFXAAよりは前) |
また、Post Process LayerのCustom Effect Sortingを使うとエフェクト毎の適用順序を変更できます。
Inspectorを拡張する
カスタムポストエフェクトのInspectorを拡張する場合はPostProcessEffectEditor
を継承したクラスを作成します。
using UnityEditor; using UnityEditor.Rendering.PostProcessing; [PostProcessEditor(typeof(Scaling))] public sealed class ScalingEditor : PostProcessEffectEditor<Scaling> { SerializedParameterOverride _scale; public override void OnEnable() { _scale = FindParameterOverride(x => x.scale); } public override void OnInspectorGUI() { EditorGUILayout.LabelField("Scale Settings", EditorStyles.boldLabel); using (new EditorGUI.IndentLevelScope()) { PropertyField(_scale); } } }
OnInspectorGUI()
の書き方は普通のInspector拡張と同じようにEditorGUILayout
などを使って組んでいけばよさそうです。
FXAAを使う場合は書き方に注意する
FXAAを使っている場合、FXAAはカスタムポストエフェクトの後に処理されることになります。
このとき、FXAAはレンダリング結果のアルファ値に輝度情報が入っている前提で処理をします。
よってFXAAを有効にしていて、かつ処理順序をPostProcessEvent.AfterStack
にする場合には、
アルファ値を変更しないように気を付ける必要があるようです。