How to Get All Parameter Info from a Dynamic Material

There's a great post I came across on how to make parallax scrolling materials in Unreal Engine and I wanted practice a little bit on how to access some material information from C++.

Parallax Scrolling forest background by inside Unreal Engine. Forest art by Digital Moons.

Software Versions: Unreal Engine 5.2.1

Project Name: ProjectDDS

Earlier this year I came across an excellent blog post by Parallelcube that went over a way of how to add parallax scrolling to a material. It's a great blog post, check if you get a minute. And then the other day I wanted to practice some C++ inside Unreal so I looked at this example and wondered if I could have some fun programming a small portion of this feature. There's a Blueprint function where we set the parameter values on Tick and I wondered if I could somehow make it programmatically. The solution wasn't immediately apparent, I had to try a few things and look at couple different resources, but I eventually landed on a solution.

Check out the blog post by Parallelcube for a full breakdown of the parallax scrolling material, but essentially we connect all the relevant textures inside a material and then add an offset value to each layer every tick. Decided to go with a pretty cool and spooky parallax forest background by Digital Moons for this example. I first started by importing the textures into Unreal.

Unreal Engine 5 parallax scrolling texture imports
Imported forest textures

Next, following the Parallelcube tutorial, I created the material.

Unreal Engine 5 parallax scrolling material
Finished material with all the textures connected
Unreal Engine 5 parallax scrolling material top portion
A closer look at the top portion of the material.
Unreal Engine 5 parallax scrolling material bottom portion
A closer look at the bottom portion of the material. One unique thing I add to do on the last texture (the mist) was use the alpha channel for both FG and BG color.

The custom material functions and the material in general are explained more in depth in Parallelcube's post. In my example there were total of 10 layers, each with their own scalar parameter variable titled OffsetLayer{{number}}. After creating the material I made the Blueprint.

Unreal Engine 5 Blueprint parallax scrolling Event Graph Tick
The BP_Parallax_BG Event Graph where the some logic performed to pass an update value to the update function
Unreal Engine 5 parallax scrolling Blueprint update function
The Blueprints completed update function where we apply a new calculated value to each offset layer.
Unreal Engine 5 parallax scrolling Blueprint update function top portion
A closer look at the top portion of the update function.

After writing this function in Blueprints, I wondered if there was a way to optimize these actions in C++. There's nothing wrong with the Blueprint function, it works great, and if it fits your project then there's nothing change. We're just practicing and having fun, so I wanted to see what options were available in C++ for Material Instance Dynamics. Not pictured above, but I added a plane component to the Blueprint and inside the construction script I create an MID variable from the plane's first material.

I created a Blueprint Functions Library class and added a static pure function called CreateLayerOffsetArray. I think BFLs are fun to work with, I wrote about them here. The function simply returns an array of structs that contain the layer name and the new offset value.

MyBlueprintFunctionLibrary.h

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MyBlueprintFunctionLibrary.generated.h"

USTRUCT(BlueprintType)
struct FMyParallaxStruct
{
    GENERATED_BODY()

public:
    UPROPERTY(BlueprintReadWrite, EditAnywhere)
    FName LayerName;

    UPROPERTY(BlueprintReadWrite, EditAnywhere)
    float OffsetValue;
};

UCLASS()
class PROJECTDDS_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()

    UFUNCTION(BlueprintCallable, Category = "Materials")
    static TArray<FMyParallaxStruct> CreateLayerOffsetArray(UMaterialInstanceDynamic* MaterialInstanceDynamic, float OffsetTime);
};

MyBlueprintFunctionLibrary.h

#include "MyBlueprintFunctionLibrary.h"
#include "Materials/MaterialInstanceDynamic.h"

TArray<FMyParallaxStruct> UMyBlueprintFunctionLibrary::CreateLayerOffsetArray(UMaterialInstanceDynamic* MaterialInstanceDynamic, float OffsetTime)
{
    int32 Index = 0;
    float Offset = 1.0f;
    TArray<FMyParallaxStruct> ScalarParameterInfo;
    TArray<FMaterialParameterInfo> Parameters;
    TArray<FMaterialParameterInfo> OutParameterInfo;
    TArray<FGuid> OutParameterIds;
    
    MaterialInstanceDynamic->GetAllScalarParameterInfo(OutParameterInfo, OutParameterIds);
    
    for (const FMaterialParameterInfo& Param : OutParameterInfo)
    {
        Offset = Index != 0 ? Offset * 2 : Offset;
        FMyParallaxStruct NewInfo;
        NewInfo.LayerName = Param.Name;
        NewInfo.OffsetValue = Index != 0 ? OffsetTime * Offset * 0.05f : OffsetTime;

        ScalarParameterInfo.Add(NewInfo);
        Index++;
    }

    return ScalarParameterInfo;
}

I started by creating a custom struct called MyParallaxStruct that held an FName and float that will be used as the TArray type being returned in the end. Inside the CreateLayerOffsetArray the most notable function is MaterialInstanceDynamic->GetAllScalarParameterInfo that sets the OutParameterInfo TArray. This function is the most important one, it was a little challenging to find, and I had trouble with getting it to work in Blueprints, but once I had all the Scalar Parameter Info I was good to proceed. Next I simply loop through the OutParameterInfo array and update the ScalarParameterInfo with new values that hold the paramter name and the new offset value. Once finish, I returned to Blueprint and I was able to loop through the new array and apply the values accordingly.

Unreal Engine 5 parallax scrolling update function with static pure C++ function
The new update function using the CreateLayerOffsetArray C++ function

Now instead of creating dozens of nodes with hard coded names and values, I ended with a much easier to maintain Blueprint function. And again, nothing wrong with doing this in Blueprints, but if you want to write some code and maybe make the function re-usable with a variety of parallax background types, sometime a small C++ function can help out.

Parallax Scrolling forest background by inside Unreal Engine. Forest art by Digital Moons.
Final Result

Conclusion

Overall I had a good time rewriting the Blueprint code in C++. My original intention was to never use C++, but for some reason I was unable to get all the scalar parameter names from the Material Instance Dynamic variable. I must have been missing something. But regardless, I had fun using C++ and using the GetAllScalarParameterInfo function to grab some data and loop through to condense some of my interweaved Blueprint scripts.

Comments (0)

Sign in to leave a comment.

Delete Comment

-