How does one change shader parameters of a child object of an instantiated scene using script?

Godot Version

4.6.3-stable

Question

How does one change shader parameters of a child object of an instantiated scene using script?

I have a procedural rock scene, which has a child node MeshInstance3D with surface material overrise using a ShaderMaterial. I then have a different scene in which I instantiate that rock via script, and then want to change some of the values of the instance.

Below is what I have currently:

# calculate spacing
var step_width = max(field_width * field_density, rock_size)
var step_length = max(field_length * field_density, rock_size)
var step_height = max(field_height * field_density, rock_size)
	
# loop over each space
for x in step_width:
	for y in step_length:
		for z in step_height:
			if randf() > field_density:
				# instantiate rock
				var temp_rock = proc_rock.instantiate()
				# get instance surface material override
				var temp_smo = temp_rock.get_node("MeshInstance3D").get_surface_override_material(0).duplicate(true)
					
				# alter instance transform
				temp_rock.transform.origin = Vector3(x * (1 + 0.5 * randf()) * step_width, y * (1 + 0.5 * randf()) * step_length, z * (1 + 0.5 * randf()) * step_height)
				temp_rock.rotation = Vector3(deg_to_rad(360 * randf()), deg_to_rad(360 * randf()), deg_to_rad(360 * randf()))
				temp_rock.scale = Vector3(rock_size * (1 + 0.5 * randf()), rock_size * (1 + 0.5 * randf()), rock_size * (1 + 0.5 * randf()))
					
				# alter instance surface material override shader
				temp_smo.set_shader_parameter("noise_scale", noise_scale * (1 + 0.5 * randf()))
				temp_smo.set_shader_parameter("spikiness", spikiness * (1 + 0.5 * randf()))
				temp_smo.set_shader_parameter("roughness", roughness * (1 + 0.5 * randf()))
				temp_smo.set_shader_parameter("metallic", metallic * (1 + 0.5 * randf()))
				temp_smo.set_shader_parameter("base_colour", base_colour * (1 + 0.5 * randf()))
				temp_smo.set_shader_parameter("deformation_offset", deformation_offset * (1 + 0.5 * randf()))
					
				# return instance surface material override
				temp_rock.get_node("MeshInstance3D").set_surface_override_material(0, temp_smo)
					
				# add instance to scene
				add_child(temp_rock)

However, when I run the script, the instanced rocks don’t have any of their shader parameters changed (nor scale for some reason).

I’m very new to Godot and relatively new to game design, but I have some prior experience in other programming languages. Explanation and help would be appreciated!

Try adding it to the scene before making all those changes. You cannot change the scale of a root node, and it’s a root node until you add it to the tree. The shader problem may be the same problem. Functionally, all those changes will happen in a single frame, so the player will never see them. But that’s a really heavy function for spawning a rock.

# calculate spacing
var step_width = max(field_width * field_density, rock_size)
var step_length = max(field_length * field_density, rock_size)
var step_height = max(field_height * field_density, rock_size)
	
# loop over each space
for x in step_width:
	for y in step_length:
		for z in step_height:
			if randf() > field_density:
				# instantiate rock
				var temp_rock = proc_rock.instantiate()
				# get instance surface material override
				var temp_smo = temp_rock.get_node("MeshInstance3D").get_surface_override_material(0).duplicate(true)
					
				# alter instance transform
				temp_rock.transform.origin = Vector3(x * (1 + 0.5 * randf()) * step_width, y * (1 + 0.5 * randf()) * step_length, z * (1 + 0.5 * randf()) * step_height)
				temp_rock.rotation = Vector3(deg_to_rad(360 * randf()), deg_to_rad(360 * randf()), deg_to_rad(360 * randf()))

				# add instance to scene
				add_child(temp_rock)

				temp_rock.scale = Vector3(rock_size * (1 + 0.5 * randf()), rock_size * (1 + 0.5 * randf()), rock_size * (1 + 0.5 * randf()))
					
				# alter instance surface material override shader
				temp_smo.set_shader_parameter("noise_scale", noise_scale * (1 + 0.5 * randf()))
				temp_smo.set_shader_parameter("spikiness", spikiness * (1 + 0.5 * randf()))
				temp_smo.set_shader_parameter("roughness", roughness * (1 + 0.5 * randf()))
				temp_smo.set_shader_parameter("metallic", metallic * (1 + 0.5 * randf()))
				temp_smo.set_shader_parameter("base_colour", base_colour * (1 + 0.5 * randf()))
				temp_smo.set_shader_parameter("deformation_offset", deformation_offset * (1 + 0.5 * randf()))
					
				# return instance surface material override
				temp_rock.get_node("MeshInstance3D").set_surface_override_material(0, temp_smo)