Is it possible to change material parameters at runtime?

Hello,

To reduce draw calls in my level, I wrote some code that replaces all recurring AStaticMeshActors with instances of a “parent” actor on Begin Play. However, this only works if the material used has “Used with Instanced Static Meshes” ticked. I tried to iterate through every material and set the parameter to true, but it crashes :

if (Material) { Material->bUsedWithInstancedStaticMeshes = true; }

with the error :

=============================================================================

Unknown exception - code 00000001 (first/second chance not available)

"Fatal error: [File:D:\BuildFarm\buildmachine_++depot+UE4-Releases+4.10\Engine\Source\Run

UE4Editor_Core!FDebug::AssertFailed() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\core\private\misc\outputdevice.cpp:374]
UE4Editor_Engine!FMaterial::GetShader() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\engine\private\materials\materialshared.cpp:1525]
UE4Editor_Renderer!TBasePassDrawingPolicy::TBasePassDrawingPolicy() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\renderer\private\basepassrendering.h:547]
UE4Editor_Renderer!FDrawBasePassStaticMeshAction::Process() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\renderer\private\basepassrendering.cpp:257]
UE4Editor_Renderer!ProcessBasePassMesh() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\renderer\private\basepassrendering.h:973]
UE4Editor_Renderer!FBasePassOpaqueDrawingPolicyFactory::AddStaticMesh() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\renderer\private\basepassrendering.cpp:302]
UE4Editor_Renderer!FStaticMesh::AddToDrawLists() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\renderer\private\scenecore.cpp:331]
UE4Editor_Renderer!FPrimitiveSceneInfo::AddStaticMeshes() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\renderer\private\primitivesceneinfo.cpp:148]
UE4Editor_Renderer!FPrimitiveSceneInfo::AddToScene() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\renderer\private\primitivesceneinfo.cpp:190]
UE4Editor_Renderer!FScene::AddPrimitiveSceneInfo_RenderThread() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\renderer\private\rendererscene.cpp:352]
UE4Editor_Renderer!FScene::AddPrimitive'::2’::EURCMacro_FAddPrimitiveCommand::DoTask() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\renderer\private\rendererscene.cpp:551]
UE4Editor_Renderer!TGraphTask<FScene::AddPrimitive'::2’::EURCMacro_FAddPrimitiveCommand>::ExecuteTask() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\core\public\async\taskgraphinterfaces.h:779]
UE4Editor_Core!FTaskThread::ProcessTasks() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\core\private\async\taskgraph.cpp:539]
UE4Editor_Core!FTaskThread::ProcessTasksUntilQuit() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\core\private\async\taskgraph.cpp:340]
UE4Editor_RenderCore!RenderingThreadMain() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\rendercore\private\renderingthread.cpp:310]
UE4Editor_RenderCore!FRenderingThread::Run() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\rendercore\private\renderingthread.cpp:411]
UE4Editor_Core!FRunnableThreadWin::Run() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\core\private\windows\windowsrunnablethread.cpp:74]

I believe this is intended, and that you’re not supposed to change material parameters at runtime. However, It would really be a pain to go through all my materials and flag all the necessary ones for instancing. Would replacing all my materials with material instances bring a big loss of performance? Or do I really need to do this manually?

Any input would be greatly appreciated,

Arnaud

Hey Dathanar,

unfortunately I don’t know why you can’t Change this Parameter in runtime, but maybe trying to create a new MaterialDynamicInstance out of this Material works.

So

auto MID = StaticMeshComponent->CreateDynamicMaterialInstance
(
    0,
    StaticMeshComponent->GetMaterial(0)
);

MID->bUsedWithInstancedStaticMeshes = true;

StaticMeshComponent->SetMaterial(0, MID);

should do the trick :slight_smile:

I hope it works :smiley:

Greetings

Hello ,

Thanks for the swift response! I tried your fix but the game is still crashing. Here is the log entry after the assert :

===========================================
Fatal error: [File:D:\BuildFarm\buildmachine_++depot+UE4-Releases+4.10\Engine\Source\Runtime\RenderCore\Private\RenderingThread.cpp] [Line: 675]
Rendering thread exception:

Fatal error: [File:D:\BuildFarm\buildmachine_++depot+UE4-Releases+4.10\Engine\Source\Runtime\Engine\Private\Materials\MaterialShared.cpp] [Line: 1538]
Couldn’t find Shader TBasePassVSFCachedPointIndirectLightingPolicy for Material Resource Test_Mat!
With VF=FInstancedStaticMeshVertexFactory, Platform=PCD3D_SM5
ShouldCache: Mat=1, VF=1, Shader=1
MaterialUsageDesc: LightingModel=MSM_DefaultLit, BlendMode=BLEND_Opaque, SpecialEngine=0, TwoSided=0, TSNormal=1, InjectEmissiveIntoLPV=0, Masked=0, Distorted=0, BlockGI=0, Usage={bUsedWithInstancedStaticMeshes}

It looks like the parameter is correctly changed, but that it cannot find the shader required for Instanced Static Meshes. Maybe the shader it is looking for has to be created before play begins? I also tried changing manually the parameter on my material, and the game launched without problems.

Edit:Is there a way to compile shaders at runtime?

Thank you for your time,
Dathanar