概要
リッチなNiagaraを作るにあたって、NiagaraからMaterialへ動的に値を伝播させるDynamicMaterialParameterモジュールは結構重要になります。
パラメータはfloat4 x4セット、合計16個しか渡せないとはいえ、あれば表現の幅は大きく変わるはず。
MeshRendererやSpriteRendererでは当然のように使われているDynamicMaterialParameterモジュールですが、現状(UE5.5)DecalRendererでは使用できません。
アーティストは泣く泣くDecalColor(float4)を分解して使うわけですね。
本記事ではDecalRendererでもDynamicMaterialParameterモジュールを使用できるようにする方法をまとめます。
エンジンソースの変更が含まれますので責任の取れるプログラマ向けです。
開発環境
UE5.2
実装
ソースの変更箇所はそこそこ多いですが、大体は既存のDecalColor
の処理に則って追加しています。
独自に定義したDECAL_RENDERER_EXTENSION
で囲った部分が追加したコードです。
SceneManagement.h
ここでDeferredDecalの更新用パラメータを定義します。
class FDeferredDecalProxy
{
:
#if DECAL_RENDERER_EXTENSION
uint32 DecalMaterialParamValidMask = 0x0000;
FLinearColor DecalDynamicMaterialParameter0 = FLinearColor::Black;
FLinearColor DecalDynamicMaterialParameter1 = FLinearColor::Black;
FLinearColor DecalDynamicMaterialParameter2 = FLinearColor::Black;
FLinearColor DecalDynamicMaterialParameter3 = FLinearColor::Black;
#endif // DECAL_RENDERER_EXTENSION
:
};
:
struct FDeferredDecalUpdateParams
{
:
#if DECAL_RENDERER_EXTENSION
uint32 MaterialParamValidMask = 0x0000;
FLinearColor DynamicMaterialParameter0 = FLinearColor::Black;
FLinearColor DynamicMaterialParameter1 = FLinearColor::Black;
FLinearColor DynamicMaterialParameter2 = FLinearColor::Black;
FLinearColor DynamicMaterialParameter3 = FLinearColor::Black;
#endif // DECAL_RENDERER_EXTENSION
:
};
RenderScene.cpp
ここではシェーダー側で参照する値を更新しています。
void FScene::BatchUpdateDecals(TArray<FDeferredDecalUpdateParams>&& UpdateParams)
{
ENQUEUE_RENDER_COMMAND(FBatchUpdateDecalsCommand)(
[Scene=this, UpdateParams_RT=MoveTemp(UpdateParams)] (FRHICommandListBase&)
{
for (const FDeferredDecalUpdateParams& DecalUpdate : UpdateParams_RT )
{
:
#if DECAL_RENDERER_EXTENSION
DecalUpdate.DecalProxy->DecalMaterialParamValidMask = DecalUpdate.MaterialParamValidMask;
DecalUpdate.DecalProxy->DecalDynamicMaterialParameter0 = DecalUpdate.DynamicMaterialParameter0;
DecalUpdate.DecalProxy->DecalDynamicMaterialParameter1 = DecalUpdate.DynamicMaterialParameter1;
DecalUpdate.DecalProxy->DecalDynamicMaterialParameter2 = DecalUpdate.DynamicMaterialParameter2;
DecalUpdate.DecalProxy->DecalDynamicMaterialParameter3 = DecalUpdate.DynamicMaterialParameter3;
#endif // DECAL_RENDERER_EXTENSION
:
}
NiagaraDecalRendererProperties.h/.cpp
ここではNiagaraのDecalRendererにBindingを追加する処理と、DynamicMaterialParameterモジュールのValueと有効/無効切り替えMaskの更新を追加しています。
class UNiagaraDecalRendererProperties : public UNiagaraRendererProperties
{
public:
:
#if DECAL_RENDERER_EXTENSION
// ここでNiagaraのDecalRendererにBindingを追加..
UPROPERTY(EditAnywhere, Category = "Bindings")
FNiagaraVariableAttributeBinding DynamicMaterialBinding;
UPROPERTY(EditAnywhere, Category = "Bindings")
FNiagaraVariableAttributeBinding DynamicMaterialBinding1;
UPROPERTY(EditAnywhere, Category = "Bindings")
FNiagaraVariableAttributeBinding DynamicMaterialBinding2;
UPROPERTY(EditAnywhere, Category = "Bindings")
FNiagaraVariableAttributeBinding DynamicMaterialBinding3;
// BindingへのAccessorを追加.
UPROPERTY()
uint32 MaterialParamValidMask = 0;
FNiagaraDataSetAccessor<FVector4f> MaterialParam0DataSetAccessor;
FNiagaraDataSetAccessor<FVector4f> MaterialParam1DataSetAccessor;
FNiagaraDataSetAccessor<FVector4f> MaterialParam2DataSetAccessor;
FNiagaraDataSetAccessor<FVector4f> MaterialParam3DataSetAccessor;
#endif // DECAL_RENDERER_EXTENSION
};
namespace NiagaraDecalRendererPropertiesLocal
{
:
static void SetupBindings(UNiagaraDecalRendererProperties* Props)
{
:
#if DECAL_RENDERER_EXTENSION
Props->DynamicMaterialBinding = FNiagaraConstants::GetAttributeDefaultBinding(SYS_PARAM_PARTICLES_DYNAMIC_MATERIAL_PARAM);
Props->DynamicMaterialBinding1 = FNiagaraConstants::GetAttributeDefaultBinding(SYS_PARAM_PARTICLES_DYNAMIC_MATERIAL_PARAM_1);
Props->DynamicMaterialBinding2 = FNiagaraConstants::GetAttributeDefaultBinding(SYS_PARAM_PARTICLES_DYNAMIC_MATERIAL_PARAM_2);
Props->DynamicMaterialBinding3 = FNiagaraConstants::GetAttributeDefaultBinding(SYS_PARAM_PARTICLES_DYNAMIC_MATERIAL_PARAM_3);
#endif // DECAL_RENDERER_EXTENSION
:
}
}
UNiagaraDecalRendererProperties::UNiagaraDecalRendererProperties()
{
AttributeBindings =
{
:
#if DECAL_RENDERER_EXTENSION
&DynamicMaterialBinding,
&DynamicMaterialBinding1,
&DynamicMaterialBinding2,
&DynamicMaterialBinding3,
#endif // DECAL_RENDERER_EXTENSION
:
}
:
}
void UNiagaraDecalRendererProperties::CacheFromCompiledData(const FNiagaraDataSetCompiledData* CompiledData)
{
:
#if DECAL_RENDERER_EXTENSION
InitParticleDataSetAccessor(MaterialParam0DataSetAccessor, CompiledData, DynamicMaterialBinding);
InitParticleDataSetAccessor(MaterialParam1DataSetAccessor, CompiledData, DynamicMaterialBinding1);
InitParticleDataSetAccessor(MaterialParam2DataSetAccessor, CompiledData, DynamicMaterialBinding2);
InitParticleDataSetAccessor(MaterialParam3DataSetAccessor, CompiledData, DynamicMaterialBinding3);
#if WITH_EDITORONLY_DATA
auto FindNameInCompiledData = [&](FName BindingName)
{
int32 Index = CompiledData->Variables.IndexOfByPredicate(
[&](const FNiagaraVariable& InVariable)
{
return InVariable.GetName() == BindingName;
}
);
return Index != INDEX_NONE ? BindingName : NAME_None;
};
MaterialParamValidMask = GetDynamicParameterCombinedChannelMask(
FindNameInCompiledData(DynamicMaterialBinding.GetDataSetBindableVariable().GetName()),
FindNameInCompiledData(DynamicMaterialBinding1.GetDataSetBindableVariable().GetName()),
FindNameInCompiledData(DynamicMaterialBinding2.GetDataSetBindableVariable().GetName()),
FindNameInCompiledData(DynamicMaterialBinding3.GetDataSetBindableVariable().GetName())
);
#endif // WITH_EDITORONLY_DATA
#endif // DECAL_RENDERER_EXTENSION
DecalRenderingShared.h/.cpp
struct FTransientDecalRenderData
{
:
#if DECAL_RENDERER_EXTENSION
uint32 MaterialParamValidMask;
FLinearColor DynamicParameter;
FLinearColor DynamicParameter1;
FLinearColor DynamicParameter2;
FLinearColor DynamicParameter3;
#endif // DECAL_RENDERER_EXTENSION
:
};
FTransientDecalRenderData::FTransientDecalRenderData(const FDeferredDecalProxy& InDecalProxy, float InConservativeRadius, float InFadeAlpha, EShaderPlatform ShaderPlatform, ERHIFeatureLevel::Type FeatureLevel)
:
#if DECAL_RENDERER_EXTENSION
, MaterialParamValidMask(InDecalProxy.DecalMaterialParamValidMask)
, DynamicParameter(InDecalProxy.DecalDynamicMaterialParameter0)
, DynamicParameter1(InDecalProxy.DecalDynamicMaterialParameter1)
, DynamicParameter2(InDecalProxy.DecalDynamicMaterialParameter2)
, DynamicParameter3(InDecalProxy.DecalDynamicMaterialParameter3)
#endif // DECAL_RENDERER_EXTENSION
:
:
class FDeferredDecalPS : public FMaterialShader
{
:
FDeferredDecalPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FMaterialShader(Initializer)
{
:
#if DECAL_RENDERER_EXTENSION
MaterialParamValidMask.Bind(Initializer.ParameterMap, TEXT("MaterialParamValidMask"));
DynamicParameter.Bind(Initializer.ParameterMap, TEXT("DynamicParameter"));
DynamicParameter1.Bind(Initializer.ParameterMap, TEXT("DynamicParameter1"));
DynamicParameter2.Bind(Initializer.ParameterMap, TEXT("DynamicParameter2"));
DynamicParameter3.Bind(Initializer.ParameterMap, TEXT("DynamicParameter3"));
#endif // DECAL_RENDERER_EXTENSION
:
}
:
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FViewInfo& View, const FDeferredDecalProxy& DecalProxy, const FMaterialRenderProxy* MaterialProxy, const FMaterial* MaterialResource, const float FadeAlphaValue = 1.0f, const FScene* Scene = nullptr)
{
:
#if DECAL_RENDERER_EXTENSION
SetShaderValue(BatchedParameters, MaterialParamValidMask, DecalProxy.DecalMaterialParamValidMask);
SetShaderValue(BatchedParameters, DynamicParameter, DecalProxy.DecalDynamicMaterialParameter0);
SetShaderValue(BatchedParameters, DynamicParameter1, DecalProxy.DecalDynamicMaterialParameter1);
SetShaderValue(BatchedParameters, DynamicParameter2, DecalProxy.DecalDynamicMaterialParameter2);
SetShaderValue(BatchedParameters, DynamicParameter3, DecalProxy.DecalDynamicMaterialParameter3);
#endif // DECAL_RENDERER_EXTENSION
:
}
:
LAYOUT_FIELD(FShaderParameter, DecalParams);
LAYOUT_FIELD(FShaderParameter, DecalColorParam);
#if DECAL_RENDERER_EXTENSION
LAYOUT_FIELD(FShaderParameter, MaterialParamValidMask);
LAYOUT_FIELD(FShaderParameter, DynamicParameter);
LAYOUT_FIELD(FShaderParameter, DynamicParameter1);
LAYOUT_FIELD(FShaderParameter, DynamicParameter2);
LAYOUT_FIELD(FShaderParameter, DynamicParameter3);
#endif // DECAL_RENDERER_EXTENSION
:
};
NiagaraRendererDecals.cpp
ここではPropertiesで追加したBindingの値をFDeferredDecalUpdateParams
に設定しています。
大体DecalColor
の処理と同様なので細かい部分は割愛します。
FNiagaraDynamicDataBase* FNiagaraRendererDecals::GenerateDynamicData(const FNiagaraSceneProxy* Proxy, const UNiagaraRendererProperties* InProperties, const FNiagaraEmitterInstance* Emitter) const
{
:
#if DECAL_RENDERER_EXTENSION
const FLinearColor LocalDefaultDynamicMaterialParameter0 = ParameterStore.GetParameterValueOrDefault(RendererProperties->DynamicMaterialBinding.GetParamMapBindableVariable(), FLinearColor::Black);
const FLinearColor LocalDefaultDynamicMaterialParameter1 = ParameterStore.GetParameterValueOrDefault(RendererProperties->DynamicMaterialBinding1.GetParamMapBindableVariable(), FLinearColor::Black);
const FLinearColor LocalDefaultDynamicMaterialParameter2 = ParameterStore.GetParameterValueOrDefault(RendererProperties->DynamicMaterialBinding2.GetParamMapBindableVariable(), FLinearColor::Black);
const FLinearColor LocalDefaultDynamicMaterialParameter3 = ParameterStore.GetParameterValueOrDefault(RendererProperties->DynamicMaterialBinding3.GetParamMapBindableVariable(), FLinearColor::Black);
#endif // DECAL_RENDERER_EXTENSION
:
if (RendererProperties->SourceMode == ENiagaraRendererSourceDataMode::Particles)
{
:
#if DECAL_RENDERER_EXTENSION
const auto DynamicParameterReader = RendererProperties->MaterialParam0DataSetAccessor.GetReader(DataSet);
const auto DynamicParameterReader1 = RendererProperties->MaterialParam1DataSetAccessor.GetReader(DataSet);
const auto DynamicParameterReader2 = RendererProperties->MaterialParam2DataSetAccessor.GetReader(DataSet);
const auto DynamicParameterReader3 = RendererProperties->MaterialParam3DataSetAccessor.GetReader(DataSet);
#endif // DECAL_RENDERER_EXTENSION
:
for (uint32 ParticleIndex = 0; ParticleIndex < DataToRender->GetNumInstances(); ++ParticleIndex)
{
:
#if DECAL_RENDERER_EXTENSION
UpdateParams.DynamicMaterialParameter0 = DynamicMaterialParameter0;
UpdateParams.DynamicMaterialParameter1 = DynamicMaterialParameter1;
UpdateParams.DynamicMaterialParameter2 = DynamicMaterialParameter2;
UpdateParams.DynamicMaterialParameter3 = DynamicMaterialParameter3;
#endif // DECAL_RENDERER_EXTENSION
}
}
:
}
DeferredDecal.usf
ついにシェーダーコードです。でも大したことはしていません。
パラメータを追加して、Niagaraで使用するMaterialParameter.Particle
に設定しているだけです。
ここで定義したパラメータ名はDecalRenderingShared.h/cppで指定します。
:
// Decal color can be accessed using the material node
float4 DecalColorParam;
#if DECAL_RENDERER_EXTENSION
uint MaterialParamValidMask;
float4 DynamicParameter;
float4 DynamicParameter1;
float4 DynamicParameter2;
float4 DynamicParameter3;
#endif // DECAL_RENDERER_EXTENSION
:
void FPixelShaderInOut_MainPS(inout FPixelShaderIn In, inout FPixelShaderOut Out, uint ArrayIndex)
{
:
#if DECAL_RENDERER_EXTENSION
#if (DYNAMIC_PARAMETERS_MASK != 0)
MaterialParameters.Particle.DynamicParameterValidMask = MaterialParamValidMask;
#endif
#if (DYNAMIC_PARAMETERS_MASK & 1)
MaterialParameters.Particle.DynamicParameter = DynamicParameter;
#endif
#if (DYNAMIC_PARAMETERS_MASK & 2)
MaterialParameters.Particle.DynamicParameter1 = DynamicParameter1;
#endif
#if (DYNAMIC_PARAMETERS_MASK & 4)
MaterialParameters.Particle.DynamicParameter2 = DynamicParameter2;
#endif
#if (DYNAMIC_PARAMETERS_MASK & 8)
MaterialParameters.Particle.DynamicParameter3 = DynamicParameter3;
#endif
#endif // DECAL_RENDERER_EXTENSION
:
}
MaterialTemplate.ush
ここではDynamicParameterを使用する条件を変更しています。
デフォルトではFNiagaraVertexFactoryBase
を継承したクラスで有効にされるフラグをみる条件になっていますが、Decalは既存のDeferredDecalの処理を使用している関係でVertexFactoryは使用していません。
そのため条件になっているVertexFactory関連のフラグをコメントアウトしたり、DecalRendererで有効になるフラグを使用するようにしています。
:
struct FMaterialParticleParameters
{
:
// #if DECAL_RENDERER_EXTENSION ifプリプロセッサを挟むためコメントアウト.
// 追加コード.
#if DYNAMIC_PARAMETERS_MASK != 0
// 元コード.
//#if NIAGARA_PARTICLE_FACTORY && (DYNAMIC_PARAMETERS_MASK != 0)
uint DynamicParameterValidMask;
// #endif // DECAL_RENDERER_EXTENSION
:
};
:
float4 GetDynamicParameter(FMaterialParticleParameters Parameters, float4 Default, int ParameterIndex=0)
{
// #if DECAL_RENDERER_EXTENSION ifプリプロセッサを挟むためコメントアウト.
// 追加コード.
#if NIAGARA_PARTICLE_FACTORY || (DECAL_PRIMITIVE)
// 元コード.
//#if (NIAGARA_PARTICLE_FACTORY)
// #endif // DECAL_RENDERER_EXTENSION
変更箇所多すぎィ!
結果
DynamicParameterのRGBをベースカラー、Aをオパシティとして使用するシンプルなDeferredDecalマテリアルを用意しました。
DynamicParameterのデフォルト値は[1.0, 0.0, 0.0, 0.5]にしてあるので、マテリアルを直接レベルに配置すると半透明の赤いDecalが出ます。
このマテリアルをNiagaraのDecalRendererに設定します。
サイズが違うのはNiagara側でDecalSizeを設定しているからです。
DynamicParameterModuleを追加して任意の値を設定します。
今回は[0.0, 0.0, 1.0, 1.0]としました。
NiagaraのDynamicParameterModuleの値が反映されましたね。
まとめ
DecalRendererでもDynamicParameterが使用できるようになりました。
これでDecalエフェクトの表現の幅が広がりますね。
変更箇所が多いので運用・保守にはご注意ください。