Applying different shader parameters per-subviewport

Godot Version

4.4

Question

I’m running into some difficulties with applying shader parameters to objects in subviewports when creating a character list. Users can create characters that get reskinned using an albedo texture and selected parameters, and they’re displayed in a list when starting a game. The characters are displayed in a scene that contains a subviewport with the reskinned character.

The issue is that each one keeps getting reskinned as the final one in the list. I’ve printed out the character node and shader materials and they all have unique IDs and shader parameters, but all get displayed as the last one to get reskinned. Duplicating shader materials and changing the shader to use instance uniforms and set_instance_shader_parameter didn’t change anything. I was able to get them to appear correctly when checking the box for own_world_3d on the subviewport, but even with just six characters in the list I get a massive frame drop for a few seconds (my understanding is this is due to each world starting its own physics calculations?).

Is there a way to work around this without turning on own_world_3d? Or to somehow mitigate the impact of that setting? Thank you!


You need to make sure that the shader you are using is unique recursively. Just because a shader has a unique ID doesn’t mean it is a unique shader - it just means it is the version of the same shader applied to that object. If you’re making and applying the shader programmatically, you need to create a new instance of the shader each time.

1 Like

Thanks! Unfortunately, making the shader unique recursively didn’t seem to make a difference, and neither did creating a new ShaderMaterial at runtime and either duplicating the shader or loading the shader directly from the gdshader file. Even after loading, I was printing out the shader parameters and they were all set to different color values while displaying the colors from the last one in the list. I have some other functionality in-game that uses duplication of ShaderMaterials and setting different parameters on them and that’s working fine, so I’m thinking it might be something specific to how subviewports are handled.

The physics simulation only starts inside subviewports if there’s a physics body, so I was able to turn on own_world_3d and replace my CharacterBody3D scene with a scene where the root node extends MeshInstance3D, with a script implementing the reskinning functionality. That’s working well visually without the performance hit, so that’ll probably be my workaround for the time being.

1 Like