Godot 引擎 - 改变 3d 复制物体的颜色

godot 之前都是主要使用它的 2d 功能,最近创建 3d 场景时遇到一个问题, 无法正确改变复制出来的物体的颜色,我们来看一下如何解决

准备测试场景

我们用 godot 3.0 创建一个简单场景来重现一下这个问题

sphere.tscn

在这个场景中我们画一个白球

新建场景,创建根节点 sphere

为 sphere 添加 MeshInstance 节点,创建一个 SphereMesh

创建一个 SpatialMaterial,确认它的颜色是白色,保存场景

你可能发现这个球似乎是蓝色,没有关系,是因为它倒映了 godot 默认的蓝天的颜色

test.tscn

在这个场景中我们利用 sphere.tscn 并复制成三个球

新建场景,加入根节点 test,引用 sphere.tscn,添加相机

接下来我们调整相机的角度,最后预览一下确定能看到引用的球

其中相机的 translation 设为 -30,0,0、rotation degree 设为 0,-90,0

为 test 节点附加脚本,复制两个物体,然后运行场景观察结果

在脚本中,我们复制了两个球,相关脚本内容如下:

func _ready():
	# Called when the node is added to the scene for the first time.
	# Initialization here
	var new_sphere = $sphere.duplicate()
	new_sphere.translate(Vector3(0,0,5))
	add_child(new_sphere)

	new_sphere = $sphere.duplicate()
	new_sphere.translate(Vector3(0,0,10))
	add_child(new_sphere)

接下来我们修改一下相机的环境,去除 godot 默认的蓝天,加一点环境光

修改复制球的颜色

我们来试试看修改脚本来改变复制球的颜色

func _ready():
	# Called when the node is added to the scene for the first time.
	# Initialization here
	var new_sphere = $sphere.duplicate()
	new_sphere.translate(Vector3(0,0,5))
	var mat = new_sphere.get_node("MeshInstance").mesh.material
	mat.albedo_color = Color(1,0,0)
	add_child(new_sphere)

	new_sphere = $sphere.duplicate()
	new_sphere.translate(Vector3(0,0,10))
	add_child(new_sphere)

然后运行一下 test 场景

包含原球,三个球的颜色都变成了红色,这就是我们要解决的问题

解决

复制材料

我们首先想到的是,albedo_color 是一个引用,三个球都引用了相同的颜色, 如果这个是真,那么由于 albedo_color 是从属于 material 的, 所以三个球的材料也是相同的,那么我们尝试复制一份材料

func _ready():
	# Called when the node is added to the scene for the first time.
	# Initialization here
	var new_sphere = $sphere.duplicate()
	new_sphere.translate(Vector3(0,0,5))
	var mesh_inst = new_sphere.get_node("MeshInstance")
	var mat = mesh_inst.mesh.material.duplicate()
	mat.albedo_color = Color(1,0,0)
	mesh_inst.mesh.material = mat
	add_child(new_sphere)

	new_sphere = $sphere.duplicate()
	new_sphere.translate(Vector3(0,0,10))
	add_child(new_sphere)

运行 test 场景,问题没有解决,依然是三个红球

复制 MeshInstance

我们再往上推一层,材料是属于 mesh 的,那么我们来复制一份 mesh, 注意之前复制材料的动作需要保留,否则的话,虽然 mesh 不同,材料相同, 依然会显示相同的颜色

func _ready():
	# Called when the node is added to the scene for the first time.
	# Initialization here
	var new_sphere = $sphere.duplicate()
	new_sphere.translate(Vector3(0,0,5))

	var mesh_inst = new_sphere.get_node("MeshInstance")
	var new_mesh = mesh_inst.mesh.duplicate()
	var mat = new_mesh.material.duplicate()

	mat.albedo_color = Color(1,0,0)
	new_mesh.material = mat
	mesh_inst.mesh = new_mesh
	add_child(new_sphere)

	new_sphere = $sphere.duplicate()
	new_sphere.translate(Vector3(0,0,10))
	add_child(new_sphere)

运行一下场景,得到这样的结果

这就跟我们的预期相符了:原球没有改变,复制球变为红色,继续从原球复制也可以保持原来的属性

总结

从以上实验我们可以看到 godot 中,基于引用的原因,mesh 和 material 是绑定在一起的, 看上去你可能没有办法只改变 material 而不改变 mesh,因为一旦某个 mesh 的 material 改变, 所有指向这个 mesh 的物体的材料都会发生变化