Download

Modify material instance for individual instanced meshes

I use instanced meshes for the individual building pieces in my game (I use the foliage system to do this).

I’m wondering if there’s any way to apply modifications to the material for the individual instanced meshes. For example if i wanted to allow the player to paint individual pieces different colors.

There is a material node called PerInstanceRandom, which gets a random value between 0.0 to 1.0 on a per instance basis. So it would appear the material editor can identify one instance from another.

You can see in this example (where the shapes are a single InstancedStaticMeshComponent painted with the foliage tool) the material is treating the individual instances separately (not world pixel position):



I can store extra data in each instance in InstancedStaticMeshComponent, by setting a float in the PerInstanceSMData[i].Transform.WPlane.W field (which doesn’t seem to be used by anything). If I could just access this data in the material editor, this would unlock huge potential…

Wondering if I could write some HLSL code in the material editor to get a value from the instanced component.

Here is code to edit your foilage. We used this to apply snow or rain to our grass. :slight_smile: just use it to edit the Scalar Params of what you are after.

local InstancedFoliageActor FoliageActors;
local MaterialInstanceConstant CurMat, CurMat2, CurMat3;
local int j;
local float GetFloat;

foreach AllActors(Class'InstancedFoliageActor', FoliageActors)
{
    for(j = 0; j < FoliageActors.InstancedStaticMeshComponents.length; j++)
    {
       CurMat = MaterialInstanceConstant(FoliageActors.InstancedStaticMeshComponents[j].GetMaterial(0));
       CurMat2 = MaterialInstanceConstant(FoliageActors.InstancedStaticMeshComponents[j].GetMaterial(1));            
       CurMat3 = MaterialInstanceConstant(FoliageActors.InstancedStaticMeshComponents[j].GetMaterial(2));

        if (GRI.GetWeatherSetting() == 3)
        {
            if(CurMat != none)
            {
              CurMat.GetScalarParameterValue('Rain',GetFloat);                   
              if(GetFloat != 1)
                CurMat.SetScalarParameterValue('Rain', 1);
            }
            if(CurMat2 != none)
            {
              CurMat2.GetScalarParameterValue('Rain',GetFloat);  
              if(GetFloat != 1)
                CurMat2.SetScalarParameterValue('Rain', 1);
            }
            if(CurMat3 != none)
            {
              CurMat3.GetScalarParameterValue('Rain',GetFloat);                    
              if(GetFloat != 1)
                CurMat3.SetScalarParameterValue('Rain', 1);
            }
        }
        else if (GRI.GetWeatherSetting() == 4)
        {
            if(CurMat != none)
            {
              CurMat.GetScalarParameterValue('Snow',GetFloat); 
              if(GetFloat != 2)
                CurMat.SetScalarParameterValue('Snow', 2);
            }
            if(CurMat2 != none)
            {
              CurMat2.GetScalarParameterValue('Snow',GetFloat);  
              if(GetFloat != 2)
                CurMat2.SetScalarParameterValue('Snow', 2);
            }
            if(CurMat3 != none)
            { 
              CurMat3.GetScalarParameterValue('Snow',GetFloat);                 
              if(GetFloat != 2)
                CurMat3.SetScalarParameterValue('Snow', 2);
            }
        }
    }
}

That should work for what your after just edit it as you need.

Hey @gamepainters. I think you misunderstood what I am trying to do.

In your example, you are applying material changes that will affect every instanced mesh in each InstancedStaticMeshComponent.

I am trying to apply material changes to individual instanced meshes.

Each InstancedStaticMeshComponent has an array PerInstanceSMData. This array holds transform data for each instanced mesh to display.

In my example above, the shapes are all from one InstancedStaticMeshComponent. The mesh is rendered once, then instanced six times (six entries in its PerInstanceSMData array).

In the material editor, the PerInstanceRandom is able to differentiate between the individual instanced meshes, so I was hoping there might be a way to access data on each instanced mesh within the material. If I was able to do this, then I could potentially store some extra data in each PerInstanceSMData entry, and use that in the material to show different colors per instanced mesh, etc.

This way I could control rendering different colored shapes, from a single instanced static mesh.

what if you go like this
for(j = 0; j < FoliageActors.InstancedStaticMeshComponents.PerInstanceSMData.length; j++)

will that get your 6 instances?

Yes, but these are just array entries (they just dictate where instances of the mesh should be rendered).

I’m trying to find a way to identify these in the material editor.

If you were to set up one of these in your materials. then i think you could scan it with the below line and find the texture you are after.

GetParameterDesc(name ParameterName, out String OutDesc);

MaterialInstanceConstant(FoliageActors.InstancedStaticMeshComponents[j].Materials[i].GetParameterDesc(name ParameterName, out String OutDesc);

Just give each texture a same ParameterName name. but a different OutDesc and then you should be able to identify the ones you need and do what your after.

What you are describing here again would affect all instances of each InstancedStaticMeshComponent.

An InstancedStaticMeshComponent extends from StaticMeshComponent, but can render multiples of its static mesh. So a single InstancedStaticMeshComponent could be rendering 1000’s of meshes (which are all copies of a single mesh).

All of the individual instanced are defined by the PerInstanceSMData array (which includes a matrix of transform data, which holds data of the location and rotation for each instance).

What I am trying to figure out, is if I can somehow access the data in the PerInstanceSMData array from the material itself (not from unrealscript). This way I could render different colors via the material itself based on data I’ve set in the PerInstanceSMData.

the only way i know of getting a hold of what is inside a material is going thru a param of its instance to get access into it. To change what ever it is your after.

Just to clarify, the issue is not getting parameters from a material, it’s the other way around (accessing US data from within the material expression).

using the same material is a hard requirement for meshes to be instanced, which is why the material is always applied per mesh-batch and not per instance. this is true even in the editor.
if you want a different material for an instance you have to de-instance it. you can either delete the instance and create a new non-instanced version (regular static mesh), or you’ll need to somehow create a new mesh entry in your list of instanced meshes, assign a different material, and initialize an instance of it.

regarding PerInstanceRandom: under the hood the engine is capable of passing a number of per-instance-data to the shader so that it can use data that will be unique per instance, but:

  • this is just data passed to the same shader used with instancing rendering. it does not decouple the instancing (i.e. it does not apply a different shader/material)
  • these are just a per-instance buffer, i.e. one float/vector for the entire instance (you can’t pass one per vertex/pixel to do painting), and no passing of textures
  • in UE3 it’s not customizable, there’s only the hardcoded PerInstanceRandom (I believe UE4 has a bit more access to perinstancedata now but I could be wrong)

so while you could technically pass a solid color for each instance to color it uniformly like in your PerInstanceRandom example, UDK doesn’t really expose any of this functionality (and IIRC, even if you had UE3 source access it’s not really developed in an extensible way at all)

unfortunately I can’t suggest a solution with perinstance data in mind

Thanks for your input @Chosker. Yes I don’t think it’s going to be possible without updating the engine source. Which is a shame as it seems very possible to be done (based on the example with PerInstanceRandom). Oh well. Thanks anyway.