Using Arrays in Materials

Hey guys,

I try to export a Material I wrote in Unity to Unreal.
Unfortunately I cannot find a way to loop through Arrays in Materials.

What I know so far:

  • I can create custom expressions to use a for-loop
    • Problem: There are no arrays which I can loop
  • There are Material Parameter Collection Assets, which store a Scalar and a Vector Array
    - Problem: I cannot get the Collection Arrays in the custom expressions. Its weird, because the generated
    HLSL code uses MaterialCollection0.Vectors[0]. But the Compiler doesnt recognize
    MaterialCollection0, when I try to apply the changes
  • I can use Textures to store data and loop through it
    - Problem: I tried to manipulate the data of a Texture2D and set a texture parameter value to it, but it didnt work.
    Maybe I overlooked something with this attempt.

It would be awesome if someone could help me with my problem :slight_smile:

Thanks in advance for you answers

Greetings

I’m working on a similar issue, and it appears that we have to use a Blueprint instead of being able to do it just in the Material.

It would be really nice to be able to cycle through, say Emissive Color masks in the Material, though.

I solved it with Material Parameter Collections.
Actually they can only be read if at least one Parameter is set as an input for your custom expression.
If you need further informations I can provide them

I have been trying to use array in material editor. Do you think I can populate my array via code ? or even more using user defined struct as an array?

Unfortunately I didnt find a way to send custom structs to the shader.
I used MaterialParameterCollection to send specific data via code to my Material.

To do this:

  1. Create a MaterialParameterCollection Asset in the Unreal Editor

  2. Add a specific number of elements to the Array (like 10, 20, 128, 256…)

  3. Get this Asset in code by using ConstructorHelpers:
    static ConstructorHelpers::FObjectFinder MaterialParamCollection(TEXT(“MaterialParameterCollection’/Game/Materials/EchoCollection.EchoCollection’”));

  4. Next is to fill the MaterialParameterCollection:

    FCollectionVectorParameter VectorParam;
    VectorParam.DefaultValue = FLinearColor(1, 0, 0);

    //Generate any Name to find this Parameter in you Collection
    FString Name = FString(“Index_”) + FString::FromInt(index) + FString(“_”);
    VectorParam.ParameterName = *Name;

    // This line and the line below should not be different. I dont know anymore why I chose to do this twice :P. Maybe there is a difference
    ParameterCollection->VectorParameters[index++] = VectorParam;

    UKismetMaterialLibrary::SetVectorParameterValue(GetWorld(), ParameterCollection, VectorParam.ParameterName, VectorParam.DefaultValue);

    //The same procedure goes for FCollectionScalarParameter

To actually iterate through this array you have to write custom expression code in your materials

!!!Warning!!! You have to use one Vector or Scalar Parameter from the Collection as an Input. Otherwise the shader doesnt know your collection

for(int i = 0; i < NumberOfParams; ++i)
{
    float4 VectorParam = MaterialCollection0.Vectors[i];
    //Do something...
}

This should be it :slight_smile:

4 Likes

Hi McStrife,

Sorry, for unearthing this. Would you please be able to provide a HLSL code extract for accessing individual floats in an input float array? I am trying to do something with HLSL and the custom nodes, but its all new, and I cannot access individual array values properlly.

Cheers,
Ben.

hey sorry to revive this old thread, but it was the only “almost” answer i found.
Just one thing: using float4 VectorParam = MaterialCollection0.Vectors[i]; trows an error in UE4
Invalid Subscript Vectors
If i leave it out my custom node complains that the input (a Material Parameters Collection) is not an array, so it can’t be indexed…
What is the proper way to iterate through a MPC parameters?

I tried the Material Parameter Collections solution, but was not able to get it fully working.

MaterialCollection0.Vectors[i] was available and partly worked, but I did not find any way to access a scalar value. Does anyone have an Idea how to access a scalar in the code for the custom node?
MaterialCollection0.Scalars[i] did not work.

The second problem I encountered was that while it actually worked with Vectors, the input did not update when the MPC value was changed. It just used the value that was set as default in the MPC.

A solution for these 2 problems would be amazing!

All Scalar parameter are stored as Vectors in front of Vector parameter ,you can use debug node to show the vector values.

MaterialCollectiom0.Vectors[i]works in Unreal4. But when I test it in Unreal5, it reports"invalid format for vector swizzle ''Vectors"


Anyone know how to achieve this in Unreal 5?

I got this method working ( the vector array inside the MPC) but I found it to be prohibitively expensive. MPCs support up to 1024 array elements but on a 3090 I found that my GPU time was spiking into the 20-30ms range when I tried iterating more than a couple hundred. This cost scales per pixel as well, so the more of your screen that is taken up by shaders implementing this function, the worse off you are.

I think loops in shaders is generally a bad idea.

Yes, generally they are b/c the cost is multiplicative vs the number of pixels that have to run that code. You can pay for it, sure, and you surely will, but was it worth it? :smiley:

As for the root-question: how to use multiple, external-values in a material (typically in an array)?

  • Material Parameter Collection (MPC) : this exists as a 1024 scalar + 1024 4vec value bundle, as a distinct-object. a given material can reference 2 of these. the MPC-values can be set in C/Blueprints. The cost to use X of these scales with X, like any other variables you might be using…
  • Custom Data: you can take the primitive/mesh of the thing you are working on and embed custom-data with it. it’s ‘like’ an MPC but per-object. ref: Storing Custom Data in Unreal Engine Materials Per-Primitive | Unreal Engine 5.4 Documentation | Epic Developer Community
  • Material Instance Dynamic (MID): you expose scalar/4vec values as parameters in the material and then set them directly in the particular material-instance(s) for that/those thing(s) are applied-to. So far as I know this is a slower-path (peanut-gallery, confirm?)

FYI, you can also use Texture Arrays in a material and you can save yourself samplers/slots since you pick X out of a list vs using a sampler for every texture.

Pic

As well, UDIM support is available for virtual textures: Streaming Virtual Texturing in Unreal Engine | Unreal Engine 5.4 Documentation | Epic Developer Community (scroll down)

If you are going to loop through a bunch of vectors in a shader, it probably makes sense to encode those values into a texture if possible. This will allow the GPU to sample the values relatively efficiently during each loop since all of the data will be on the GPU and memory with minimal back and forth between the CPU.

This is partially how a technique like Parallax Occlusion Mapping can loop hundreds of times without taking 30ms (although it’s still best to keep loops to a minimum).