SPARKCREATIVE Tech Blog

https://www.spark-creative.jp/

【UE5】マテリアルインプットの追加

こんにちは!!!クライアントエンジニアの小林です。

今回はトゥーン・セル表現を題材にエンジン改造の続きとして、マテリアルインプットの追加をします。
ブログ内ではマテリアルインプットやマテリアルピン、ピンと呼んだりしていきます。
マテリアルインプットは、ベースカラーやメタリック、スペキュラなどを接続するコレのことです。

会社ブログっぽい宣伝

Game Tools & Middleware Forum 2023 (GTMF 2023)にSPARK社も出展いたします。

Copyright © GTMF All Rights Reserved.

VFXツールのスパークギアを体験できるらしいので興味のある方は是非お越しくださいませ。
こういうかっこいいエフェクトを作れるすごいツールです。

企業さんだけでなく弊社に興味のある方も「事前来場者登録」というフローを踏めば入場できるらしいのでぜひぜひ。
申請方法の手順は公式サイトさんにお任せします。

作業環境

・windows10
visual studio 2022
visual studio code
・UnrealEngine 5.0.3

改造内容

Toon Shadow Paramというピンを追加します。
名前の通りシャドウ系を調整するパラメータを格納します。

追加するピンはfloat4とします。
これは陰と影をそれぞれ調整をしたいためです。
xがToonShade、yがToonShadowとして扱っていきます。

今回はfloat4でまとめるのでToonShadowParamとしていますが、ToonShadeThreshとToonShadowThreshでピンを分けても問題ありません。
好きな方を採用してください。

改造ルール(前回同様)

UnrealEngineのエンジンコードに手を加える場合は、コメントを付けることが推奨されています。
推奨されているのですが記述フォーマットがおそらく決まっていません。
これに関しては正直決めてほしかったのですが、私の場合はこんな感じに書いています。

//// Add MaterialInput:ToonShadeThresh 2022/09/18 ////
    // 日付の前が改造内容
    float A = 0.0f;
//// Add MaterialInput:ToonShadeThresh 2022/09/18 ////

//// Add MaterialInput:ToonShadeThresh 2022/09/18 ////
#if 0
    float B = 0.0f;
#else
    // #if 0 ~ #else がUnrealEngineさんのオリジナルコード
    // #else ~ #endifが改造コード
    // オリジナルコードを残しておきたい場合にこの記述をしてます
    float B = 1.0f;
#endif
//// Add MaterialInput:ToonShadeThresh 2022/09/18 ////

Engine\Source

Developer\MaterialBaking\Private\MaterialBakingModule.cpp

メッシュ系のモジュールっぽいのですがどのように使われているのか理解していません。
他のピンが記述されているのでこれに倣って書いておきます。

ToonShadowParamの格納先はSubsurfaceColorなども格納されるGBufferDを使用するため8ビットフォーマットを指定します。
というか設定で16ビットにできたはずなのですが、これエミッシブ以外8ビット固定で、分岐書かれてないですね。どうしてるんだろう。謎が深まりました。

    PerPropertyFormat.Add(MP_CustomData0, PF_B8G8R8A8);
    PerPropertyFormat.Add(MP_CustomData1, PF_B8G8R8A8);
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    PerPropertyFormat.Add(MP_ToonShadowParam, PF_B8G8R8A8);
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    PerPropertyFormat.Add(TEXT("ClearCoatBottomNormal"), PF_B8G8R8A8);

Editor\MaterialEditor\Private\MaterialEditor.cpp

改造内容でも触れたとおりToonShadowParamピンはfloat4のためVectorParameterの方に記述します。
floatの場合は1つ上のUMaterialExpressionScalarParameterの方に記述してください。

            case MP_Normal:
            case MP_Tangent:
        //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
            case MP_ToonShadowParam:
        //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
                return UMaterialExpressionVectorParameter::StaticClass();

Editor\UnrealEd\Private\MaterialGraph.cpp

マテリアルピンの位置を指定しています。
位置指定というものの、コメントに追加はココから!!!と書かれているので指定(強制)ですね。

試しにCustomData1の後に追加してみましたが特にエラーは出なかったです。
エラーは出なかったのですが、わざわざコメントで書かれているので従っておきましょう。

試しに位置を変えてみた。
        MaterialInputs.Add(FMaterialInputInfo(FMaterialAttributeDefinitionMap::GetDisplayNameForMaterial(MP_ShadingModel, Material), MP_ShadingModel, LOCTEXT("ShadingModelToolTip", "Selects which shading model should be used per pixel")));
        // STRATA_DISABLED MaterialInputs.Add(FMaterialInputInfo(FMaterialAttributeDefinitionMap::GetDisplayNameForMaterial(MP_FrontMaterial, Material), MP_FrontMaterial, LOCTEXT("FrontMaterialToolTip", "Specify the front facing material")));

    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
        MaterialInputs.Add(FMaterialInputInfo(FMaterialAttributeDefinitionMap::GetDisplayNameForMaterial(MP_ToonShadowParam, Material), MP_ToonShadowParam, LOCTEXT("ToonShadowParamToolTip", "x:ToonShadeThresh y:ToonShadowThresh zw:empty")));
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////

        //^^^ New material properties go above here. ^^^^

Runtime\Engine\Classes\Materials\Material.h

    UPROPERTY()
    FScalarMaterialInput Refraction;

//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    /** 
     * x: ToonShadeThresh (0.0 ~ 1.0)
     * y: ToonShadowThresh (0.0 ~ 1.0)
     * zw: empty
     */
    UPROPERTY()
    FVectorMaterialInput ToonShadowParam;
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

#if WITH_EDITORONLY_DATA

Runtime\Engine\Classes\Materials\MaterialExpressionMakeMaterialAttributes.h

    UPROPERTY()
    FExpressionInput PixelDepthOffset;

    UPROPERTY()
    FExpressionInput ShadingModel;

//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    UPROPERTY()
    FExpressionInput ToonShadowParam;
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

Runtime\Engine\Private\Materials\HLSLMaterialTranslator.cpp

ここら辺はエディタでしか機能しない部分です。
2つ目の記述に注意点があります。

            Chunk[MP_PixelDepthOffset] = Material->CompilePropertyAndSetMaterialProperty(MP_PixelDepthOffset, this);
            
        //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
            Chunk[MP_ToonShadowParam] = Material->CompilePropertyAndSetMaterialProperty(MP_ToonShadowParam, this);
        //// Add MaterialInput:ToonShadowParam 2022/09/18 ////

これはMaterialTemplate.ush内の%sにコードを展開するという仕組みです。

ソースコードとシェーダーで展開順を合わせる必要があります。
正しい展開としては赤→緑→青の順です。

正しい展開

仮にソースコード側の記述を赤、青、緑の順で書くと画像のように不適切な位置に展開されます。
とこんな感じでシェーダーとソースで位置を同期しないと壊れます。

誤った展開

UnrealEngineにしては変更によわよわな部分ですが、この箇所はランタイムでは機能しないので割と適当な対応なんでしょうか。

    LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(MP_CustomData0, BaseDerivativeVariation) : TEXT("return 0.0f"));
    LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(MP_CustomData1, BaseDerivativeVariation) : TEXT("return 0.0f"));
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    LazyPrintf.PushParam(!bEnableExecutionFlow ? *GenerateFunctionCode(MP_ToonShadowParam, BaseDerivativeVariation) : TEXT("MaterialFloat4(0.5f, 0.5f, 0.0f, 0.0f);"));
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
#if 0
    static_assert((uint32)(EMaterialProperty::MP_MAX)-1 <= (8 * sizeof(MaterialAttributesPropertyConnectedBitmask)), "MaterialAttributesPropertyConnectedBitmask cannot contain entire EMaterialProperty enumeration.");
#else
    static_assert((uint32)(EMaterialProperty::MP_MAX)-1-1 <= (8 * sizeof(MaterialAttributesPropertyConnectedBitmask)), "MaterialAttributesPropertyConnectedBitmask cannot contain entire EMaterialProperty enumeration.");
#endif
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

Runtime\Engine\Private\Materials\Material.cpp

//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
#if 0
    static_assert(MP_MAX == 33, "New material properties must have DoMaterialAttributeReorder called on them to ensure that any future reordering of property pins is correctly applied.");
#else
    static_assert(MP_MAX == 33+1, "New material properties must have DoMaterialAttributeReorder called on them to ensure that any future reordering of property pins is correctly applied.");
#endif
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
	DoMaterialAttributeReorder(&ShadingModelFromMaterialExpression, UEVer, RenderObjVer, UE5MainVer);
	DoMaterialAttributeReorder(&FrontMaterial, UEVer, RenderObjVer, UE5MainVer);
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
	DoMaterialAttributeReorder(&ToonShadowParam, UEVer, RenderObjVer, UE5MainVer);
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    case MP_ShadingModel: SetMaterialInputDescription(ShadingModelFromMaterialExpression, false, OutDescription); return true;
    case MP_FrontMaterial: SetMaterialInputDescription(FrontMaterial, false, OutDescription); return true;
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    case MP_ToonShadowParam: SetMaterialInputDescription(ToonShadowParam, false, OutDescription); return true;
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
        case MP_ShadingModel:            return ShadingModelFromMaterialExpression.CompileWithDefault(Compiler, Property);
        case MP_FrontMaterial:            return FrontMaterial.CompileWithDefault(Compiler, Property);
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
        case MP_ToonShadowParam:        return ToonShadowParam.CompileWithDefault(Compiler, Property);
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////

ToonLitモデルの時にのみToonShadowParamピンを有効にしています。

    case MP_FrontMaterial:
        {
            static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Strata"));
            const bool bStrataEnabled = CVar && CVar->GetValueOnAnyThread() > 0;
            Active = bStrataEnabled;
            break;
        }
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    case MP_ToonShadowParam:
        Active = ShadingModels.HasAnyShadingModel({ MSM_ToonLit });
        break;
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

Runtime\Engine\Private\Materials\MaterialCachedData.cpp

//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
#if 0
    static_assert((uint32)(EMaterialProperty::MP_MAX)-1 <= (8 * sizeof(MaterialAttributesPropertyConnectedBitmask)), "MaterialAttributesPropertyConnectedBitmask cannot contain entire EMaterialProperty enumeration.");
#else
    static_assert((uint32)(EMaterialProperty::MP_MAX)-1-1 <= (8 * sizeof(MaterialAttributesPropertyConnectedBitmask)), "MaterialAttributesPropertyConnectedBitmask cannot contain entire EMaterialProperty enumeration.");
#endif
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
            SetMatAttributeConditionally(EMaterialProperty::MP_PixelDepthOffset, MakeMatAttributes->PixelDepthOffset.IsConnected());
            SetMatAttributeConditionally(EMaterialProperty::MP_ShadingModel, MakeMatAttributes->ShadingModel.IsConnected());
        //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
            SetMatAttributeConditionally(EMaterialProperty::MP_ToonShadowParam, MakeMatAttributes->ToonShadowParam.IsConnected());
        //// Add MaterialInput:ToonShadowParam 2022/09/18 ////

Runtime\Engine\Private\Materials\MaterialExpressions.cpp

//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
#if 0
    static_assert(MP_MAX == 33, 
#else
    static_assert(MP_MAX == 33+1,
#endif
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
        "New material properties should be added to the end of the inputs for this expression. \
    case MP_PixelDepthOffset: Ret = PixelDepthOffset.Compile(Compiler); Expression = PixelDepthOffset.Expression; break;
    case MP_ShadingModel: Ret = ShadingModel.Compile(Compiler); Expression = ShadingModel.Expression; break;
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    case MP_ToonShadowParam: Ret = ToonShadowParam.Compile(Compiler); Expression = ToonShadowParam.Expression; break;
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
#if 0
    static_assert(MP_MAX == 33, 
#else
    static_assert(MP_MAX == 33+1,
#endif
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
        "New material properties should be added to the end of the outputs for this expression. \

今回はfloat4なので1,1,1,1,1と書いています。floatなら1,1,0,0,1、float2なら1,1,1,0,0と書きます。

    Outputs.Add(FExpressionOutput(TEXT("PixelDepthOffset"), 1, 1, 0, 0, 0));
    Outputs.Add(FExpressionOutput(TEXT("ShadingModel"), 0, 0, 0, 0, 0));
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    Outputs.Add(FExpressionOutput(TEXT("ToonShadowParam"), 1, 1, 1, 1, 1));
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

OutputIndexの加算を忘れずに、SetMaskの引数は先ほど同様です。

        Outputs[OutputIndex].SetMask(1, 1, 0, 0, 0); ++OutputIndex;// PixelDepthOffset
        Outputs[OutputIndex].SetMask(0, 0, 0, 0, 0); // ShadingModelFromMaterialExpression
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
        ++OutputIndex; Outputs[OutputIndex].SetMask(1, 1, 1, 1, 1); // ToonShadowParam 
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
        PropertyToIOIndexMap.Add(MP_PixelDepthOffset,       24);
        PropertyToIOIndexMap.Add(MP_ShadingModel,           25);
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
        PropertyToIOIndexMap.Add(MP_ToonShadowParam,        26);
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////

Runtime\Engine\Private\Materials\MaterialShared.cpp

GUIDは適当に書いても被ることは(滅多に)ないので16進数で適当に書くか、FGuid::NewGuidの結果を書くでもいいです。

    Add(FGuid(0xD9423FFF, 0xD77E4D82, 0x8FF9CF5E, 0x055D1255), TEXT("ShadingModel"),            MP_ShadingModel,            MCT_ShadingModel, FVector4(0, 0, 0, 0), SF_Pixel, INDEX_NONE, false, &CompileShadingModelBlendFunction);
    Add(FGuid(0x5973A03E, 0x13A74E08, 0x92D0CEDD, 0xF2936CF8), TEXT("FrontMaterial"),           MP_FrontMaterial,            MCT_Strata, FVector4(0,0,0,0), SF_Pixel, INDEX_NONE, false, &CompileStrataBlendFunction);
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    Add(FGuid(0x81158701, 0x4B0622A3, 0xE4DECDB1, 0x74880ED1), TEXT("ToonShadowParam"),         MP_ToonShadowParam,         MCT_Float4, FVector4(.5,.5,0,0), SF_Pixel);
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

エディタ上で表示されるマテリアルピン名で、モデルによってピン名を変えることもできます。
CustomData1,2ピンを参考にすると分かりやすいです。

	case MP_FrontMaterial:
		return LOCTEXT("FrontMaterial", "Front Material");
	case MP_CustomOutput:
		return FText::FromString(GetAttributeName(AttributeID));
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
	case MP_ToonShadowParam:
		CustomPinNames.Add({ MSM_ToonLit, "Toon Shadow Param" });
		return FText::FromString(GetPinNameFromShadingModelField(Material->GetShadingModels(), CustomPinNames, "Toon Shadow Param"));
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

Runtime\Engine\Private\ShaderCompiler\ShaderGenerationUtil.cpp

GBufferDを使いたいのでスロットのGBS_CustomDataを有効にします。

    // doesn't write to GBuffer
    if (Mat.MATERIAL_SHADINGMODEL_THIN_TRANSLUCENT)
    {
    }

//// Add ShadingModel:ToonLit 2022/09/17 ////
    if (Mat.MATERIAL_SHADINGMODEL_TOON_LIT)
    {
        SetStandardGBufferSlots(Slots, bWriteEmissive, bHasTangent, bHasVelocity, bHasStaticLighting, bIsStrataMaterial);
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
        Slots[GBS_CustomData] = bUseCustomData;
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    }
//// Add ShadingModel:ToonLit 2022/09/17 ////

Runtime\Engine\Public\MaterialExpressionIO.h

    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    #if 0
        static_assert((uint32)(MP_MAX)-1 <= (8 * sizeof(PropertyConnectedBitmask)), "PropertyConnectedBitmask cannot contain entire EMaterialProperty enumeration.");
    #else
        static_assert((uint32)(MP_MAX)-1-1 <= (8 * sizeof(PropertyConnectedBitmask)), "PropertyConnectedBitmask cannot contain entire EMaterialProperty enumeration.");
    #endif
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////

Runtime\Engine\Public\SceneTypes.h

本来であれば最初の方に書くべきだったのですが、SourceTreeのファイルステータス順に沿って書いていたら最後の方になってしまいました。

    MP_ShadingModel UMETA(Hidden),
    MP_FrontMaterial UMETA(Hidden),
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    MP_ToonShadowParam UMETA(Hidden),
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

    //^^^ New material properties go above here ^^^^

Runtime\RenderCore\Private\ShaderMaterialDerivedHelpers.cpp

実際に使われるのはBasePassCommon.ushな気がするので、デバッグ系だとは思うのですが本当に謎。
Gバッファの制御に使ってるのかな。

    Dst.USES_GBUFFER = (FEATURE_LEVEL >= ERHIFeatureLevel::SM4_REMOVED && (Mat.MATERIALBLENDING_SOLID || Mat.MATERIALBLENDING_MASKED) && !Lightmap.SIMPLE_FORWARD_SHADING && !SrcGlobal.FORWARD_SHADING);

    // Only some shader models actually need custom data.
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
    Dst.WRITES_CUSTOMDATA_TO_GBUFFER = (Dst.USES_GBUFFER && (Mat.MATERIAL_SHADINGMODEL_SUBSURFACE || Mat.MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN || Mat.MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE || Mat.MATERIAL_SHADINGMODEL_CLEAR_COAT || Mat.MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || Mat.MATERIAL_SHADINGMODEL_HAIR || Mat.MATERIAL_SHADINGMODEL_CLOTH || Mat.MATERIAL_SHADINGMODEL_EYE || Mat.MATERIAL_SHADINGMODEL_TOON_LIT));
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

Engine\Shaders

Private\BasePassCommon.ush

ToonLitモデルもGBufferDを使いますよ。と追加しています。

#define USES_GBUFFER                        (FEATURE_LEVEL >= FEATURE_LEVEL_SM4 && (MATERIALBLENDING_SOLID || MATERIALBLENDING_MASKED) && !SIMPLE_FORWARD_SHADING && !FORWARD_SHADING)

// Only some shader models actually need custom data.
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
#define WRITES_CUSTOMDATA_TO_GBUFFER        (USES_GBUFFER && (MATERIAL_SHADINGMODEL_SUBSURFACE || MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN || MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE || MATERIAL_SHADINGMODEL_CLEAR_COAT || MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || MATERIAL_SHADINGMODEL_HAIR || MATERIAL_SHADINGMODEL_CLOTH || MATERIAL_SHADINGMODEL_EYE || MATERIAL_SHADINGMODEL_TOON_LIT))
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

Private\DeferredLightingCommon.ush

ディレクショナルライトパスでCustomDataに格納されたToonShadowParamを使用して計算しています。
地味にライティング個所を前回から改修していますが、本題とは方向性が違うので省略します。

                #if REFERENCE_QUALITY
                    Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, SVPos );
                #else
                    Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, LightData.bInverseSquared );
                #endif
            }

            Lighting.Specular *= LightData.SpecularScale;
            
        //// Add ShadingModel:ToonLit 2022/09/17 ////
            if(GBuffer.ShadingModelID == SHADINGMODELID_TOON_LIT)
            {
                if (LightData.bRadialLight)
                {
                    LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, LightColor * LightMask, bNeedsSeparateSubsurfaceLightAccumulation );
                    LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, LightColor * LightMask * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
                }
                else
                {
                //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
                    float ToonShadeThresh = GBuffer.CustomData.x * 2.0f - 1.0f;
                    float ToonShadowThresh = GBuffer.CustomData.y;
                    float ToonNoL = dot(N, L) > ToonShadeThresh ? 1.0f : 0.0f;
                    float ToonSurfaceShadow = Shadow.SurfaceShadow > ToonShadowThresh ? 1.0f : 0.0f;
                    LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, LightColor * LightMask * ToonSurfaceShadow * ToonNoL, bNeedsSeparateSubsurfaceLightAccumulation );
                //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
                    //LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, LightColor * LightMask * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
                }
            }
            else
            {
                LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, LightColor * LightMask * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
                LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, LightColor * LightMask * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
            }
        //// Add ShadingModel:ToonLit 2022/09/17 ////

Private\DeferredShadingCommon.ush

GBufferDからCustomDataにセットする際に使われる判定にToonLitモデルを追加します。

bool HasCustomGBufferData(int ShadingModelID)
{
    return ShadingModelID == SHADINGMODELID_SUBSURFACE
        || ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN
        || ShadingModelID == SHADINGMODELID_CLEAR_COAT
        || ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE
        || ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE
        || ShadingModelID == SHADINGMODELID_HAIR
        || ShadingModelID == SHADINGMODELID_CLOTH
        || ShadingModelID == SHADINGMODELID_EYE
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
        || ShadingModelID == SHADINGMODELID_TOON_LIT;
    //// Add MaterialInput:ToonShadowParam 2022/09/18 ////
}

Private\MaterialTemplate.ush

half GetMaterialCustomData1(in out FMaterialPixelParameters Parameters)
{
%s;
}

//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
half4 GetMaterialToonShadowParam(in out FMaterialPixelParameters Parameters)
{
%s;
}

half GetMaterialToonShadeThresh(in out FMaterialPixelParameters Parameters)
{
	return GetMaterialToonShadowParam(Parameters).x;
}

half GetMaterialToonShadowThresh(in out FMaterialPixelParameters Parameters)
{
	return GetMaterialToonShadowParam(Parameters).y;
}
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////

Private\ShadingModelsMaterial.ush

GBufferに格納する値に対して前処理を行いたい場合は一般的にはここに記述します。

        #if NUM_MATERIAL_OUTPUTS_GETTANGENTOUTPUT > 0
            float3 Tangent = GetTangentOutput0(MaterialParameters);
            GBuffer.CustomData.yz = UnitVectorToOctahedron( normalize(Tangent) ) * 0.5 + 0.5;
        #endif
    #endif
    }
#endif
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
#if MATERIAL_SHADINGMODEL_TOON_LIT
    else if (ShadingModel == SHADINGMODELID_TOON_LIT)
    {
        GBuffer.CustomData.x = saturate(GetMaterialToonShadeThresh(MaterialParameters))
        GBuffer.CustomData.y = saturate(GetMaterialToonShadowThresh(MaterialParameters));
        GBuffer.CustomData.zw = 0.0f;
    }
#endif
//// Add MaterialInput:ToonShadowParam 2022/09/18 ////
}

追加したマテリアルピンを使ってみる

といってもスカラーパラメータを差し込むだけですが。

想定通り閾値をパラメータから調整できるようになりました。


おわり

お疲れさまでした!!!