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++.
Updated: Oct. 30, 2024
Software Versions: Unreal Engine 5.4.4 | Rider 2024.2.7
Project Name: MyProject
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 it out 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 on 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.
Next, following the Parallelcube tutorial, I created the material.
The custom material functions and the material in general are explained more in depth in Parallelcube's post. In my example there were a total of 10 layers, each with their own scalar parameter variable titled OffsetLayer_{{number}}
. After creating the material I made the Blueprint.
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 library functions 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 MYPROJECT_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.
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.
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.
Harrison McGuire