Quick answer: Set use_colors = true on the MultiMesh, wire COLOR through a varying in the shader and multiply ALBEDO by it, and pick the right color format — CRGBA8 applies sRGB on write, RGBA_FLOAT is linear.
You draw 5000 cubes with MultiMeshInstance3D and call set_instance_color on each one with distinct colors. All of them render gray. Or all of them are too dark. Or the first 100 are right and the rest are default. MultiMesh instance color has three independent failure modes, and you usually hit all three together.
Three Failure Modes
1. use_colors is false. MultiMesh defaults to no color buffer. set_instance_color stores the value, but the GPU-side buffer never allocates. Enable multimesh.use_colors = true before setting any colors or the buffer is a no-op.
2. The shader doesn’t consume COLOR. Per-instance color arrives in the vertex shader as the built-in COLOR. If your shader doesn’t read it, the value is discarded. A plain StandardMaterial3D works; custom shaders need explicit handling.
3. Format mismatch. CRGBA8 applies sRGB gamma on write. A color you author as (0.5, 0.5, 0.5) writes as (0.73, 0.73, 0.73) in the buffer. If you expected linear, you get a brighter result than you set.
The Fix
var mm = MultiMesh.new()
mm.mesh = preload("res://cube.tres")
mm.use_colors = true
mm.color_format = MultiMesh.COLOR_FLOAT # or COLOR_8BIT
mm.instance_count = 5000
for i in 5000:
mm.set_instance_transform(i, random_transform())
mm.set_instance_color(i, Color.from_hsv(randf(), 0.8, 0.9))
$MultiMeshInstance3D.multimesh = mm
Custom Shader
shader_type spatial;
varying vec4 instance_color;
void vertex() {
instance_color = COLOR;
}
void fragment() {
ALBEDO = vec3(1.0) * instance_color.rgb;
ALPHA = instance_color.a;
}
CRGBA8 vs RGBA_FLOAT
CRGBA8: 4 bytes per instance, sRGB gamma on write. Best for regular authored colors. 5000 instances → 20 KB.
RGBA_FLOAT: 16 bytes per instance, linear. Best for HDR values (> 1.0) or colors computed in code. 5000 instances → 80 KB.
For a sea of cubes with designer-picked colors, CRGBA8 is fine. For a bloom-emitting particle system where you set emissive values, use RGBA_FLOAT.
Verifying
Set a known color like pure red on instance 0. If it renders red, the pipeline works. If it renders pink (sRGB-converted red) or gray (no color read), trace back through the checklist.
“MultiMesh instance color is three settings that must all agree. Break any one and the rendering looks vaguely wrong in a way that’s hard to diagnose.”
Related Issues
For MultiMesh visibility bugs, see Godot multimesh instance not visible. For MultiMesh culling, see Godot MultiMesh frustum culling.
Always set use_colors before any instance work. The buffer isn’t created until you do, and silently dropping set_instance_color calls is the default behavior.