【URP】MRTのブレンドステートの個別変更
はじめに
今回の記事では、MRT(Multi Render Targets)でのブレンドステートを
RTごとに変更する方法についてまとめてみようと思います。
例えば、RT0 は Blend One Zero
で、 RT1 は Blend One One
にする、といったことが可能となります。
RT0不透明ブレンドで、RT1は加算ブレンド
関連記事 (MRT)
環境
今回は、以下の2つの環境にて動作確認しました。
- UniversalRP 10.8.1 / Unity 2020.3.32f1
- UniversalRP 14.0.10 / Unity 2022.3.21f1
サンプル (GitHub)
Unity 2020.3.32f1のサンプルは以下にあります
https://github.com/rngtm/Unity_MRT_Sample/tree/main/MRT_BlendStateSample_2020.3.32
Unity2022.3.21f1のサンプルは以下にあります
https://github.com/rngtm/Unity_MRT_Sample/tree/main/MRT_BlendStateSample_2022.3.21
Chapter0. ブレンドステートについて
シェーダー上で Blend One Zero
と記述した場合、
そのシェーダーが実行される時のブレンドステートは Blend One Zero
となります
Pass
{
Blend One Zero
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
ブレンドステートの確認
ブレンドステートはFrameDebugger上で確認できます。
URP10.8.1の場合
URP14.0.10の場合
Chapter1. MRT実行する環境をセットアップする
つぎに、MRTを実行するための環境を整える手順を紹介します。
ブレンドステートを変更する方法を手っ取り早く知りたい方は、
Chapter2まで読み飛ばしていただいて構いません。
MRT対応シェーダー
今回は、シェーダープロパティ _Color1
を RT0へ、 _Color2
をRT1 へ出力するシンプルなシェーダーを使用します。
このシェーダーを実行してMRTへ出力するためには、ScriptableRenderPass
と ScriptableRendererFeature
を作成する必要があります。
Shader "Unlit/NewUnlitShader"
{
Properties
{
_Color0 ("Color 0", Color) = (0.4, 0.4, 0.4, 1)
_Color1 ("Color 1", Color) = (0, 0.4, 0, 1)
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Transparent" }
LOD 100
CGINCLUDE
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
// MRT出力用の構造体
struct MRTOutput
{
half4 color0 : SV_Target0;
half4 color1 : SV_Target1;
};
half4 _Color0;
half4 _Color1;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
half4 frag (v2f i) : SV_Target
{
return _Color0;
}
MRTOutput fragMRT (v2f i) : SV_Target
{
MRTOutput output = (MRTOutput)0;
output.color0 = _Color0;
output.color1 = _Color1;
return output;
}
ENDCG
Pass
{
Blend One One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass
{
Name "Render MRT"
Blend One Zero
ZWrite Off
// MRT用のパス
Tags { "LightMode"="MyTag" }
CGPROGRAM
#pragma vertex vert
#pragma fragment fragMRT
ENDCG
}
}
}
ScriptableRenderPass
2枚のテクスチャ _ColorTexture1
と _ColorTexture2
をレンダーターゲット指定し、
描画を行うScriptableRenderPassを以下に示します。
Unity2020.3.32f1 の場合 (URP10.8.1)
Unity2022.3.21f1 の場合 (URP14.0.10)
ScriptableRendererFeature
上記のMyRenderObjectsPass
を実行し、描画を行うScriptableRendererFeature
を以下に示します。
こちらは Unity2020とUnity2022の両方で動作します。
using UnityEngine.Experimental.Rendering.Universal;
using UnityEngine.Rendering.Universal;
namespace MyRendering
{
public class MyRenderObjects : ScriptableRendererFeature
{
MyRenderObjectsPass renderObjectsPass;
public override void Create()
{
var lightModeTags = new string[]
{
"MyTag"
};
renderObjectsPass = new MyRenderObjectsPass(
"MyTag",
RenderPassEvent.AfterRenderingTransparents,
lightModeTags,
RenderQueueType.Opaque,
-1);
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(renderObjectsPass);
}
}
}
シーンのセットアップ
次に、Sphereを2つ作成してシーンに配置し、先ほどのシェーダーをSphereにアタッチします。
FrameDebuggerを見ると、RT0 へ _Color0
が、 RT1 へ _Color1
が書きこまれていることが確認できます。
どちらもブレンドステートはシェーダー上で設定したBlend One Zero
となっています。
Chapter2. 2番目のレンダーターゲットだけ加算ブレンドにする
RenderStateBlock
オブジェクトをレンダリングする時、ScriptableRenderContext.DrawRendererを実行しています。
4番目の引数の RenderStateBlock
を使用することで、ブレンドステートをオーバーライドできます。
context.DrawRenderers(
renderingData.cullResults,
ref drawingSettings,
ref m_FilteringSettings,
ref m_RenderStateBlock);
以下の4つがオーバーライド可能です。
- ブレンド
- ラスタライズ
- デプス
- ステンシル
public struct RenderStateBlock : IEquatable<RenderStateBlock>
{
private BlendState m_BlendState;
private RasterState m_RasterState;
private DepthState m_DepthState;
private StencilState m_StencilState;
private int m_StencilReference;
...
}
ブレンドステートの変更
ブレンドステートを変更する場合、以下のように記述します。
m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Blend)
{
blendState = new BlendState(separateMRTBlend: true)
{
// RT0 : Blend One Zero
blendState0 = RenderTargetBlendState.defaultValue,
// RT1 : Blend One One
blendState1 = new RenderTargetBlendState(
sourceColorBlendMode: BlendMode.One,
destinationColorBlendMode: BlendMode.One)
}
};
以下のように書くこともできます。
m_RenderStateBlock.mask |= RenderStateMask.Blend;
m_RenderStateBlock.blendState = new BlendState(separateMRTBlend: true)
{
// RT0 : Blend One Zero
blendState0 = RenderTargetBlendState.defaultValue,
// RT1 : Blend One One
blendState1 = new RenderTargetBlendState(
sourceColorBlendMode: BlendMode.One,
destinationColorBlendMode: BlendMode.One)
};
ここで作成したBlendStateBlockは、ScriptableRenderContext.DrawRendererの引数として渡しています
context.DrawRenderers(
renderingData.cullResults,
ref drawingSettings,
ref m_FilteringSettings,
ref m_RenderStateBlock);
結果
RT1 だけが加算ブレンド Blend One One, One Zero
になっていることが確認できます。
URP 10.8.1
Discussion