godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
87.9k stars 19.86k forks source link

Instance uniforms in overlay material cause base material instance uniforms to have incorrect values #90140

Open A-Hugh-Mann opened 4 months ago

A-Hugh-Mann commented 4 months ago

Tested versions

Reproducible in Godot v4.3.dev (29b3d9e9e), currently unable to test in other versions at this time.

System information

Arch Linux Vulkan (Forward+) AMD Radeon RX 6600 (RADV NAVI23) AMD Ryzen 9 5900X

Issue description

When adding a material overlay which includes a shader that has instance uniforms to a mesh instance that is already using a shader material that has instance uniforms, that meshes base materials instance uniforms seem to be overritten by the data contained within the instance uniforms of the overlay material.

What doesn't work: If the following code is run, the red channel of the color provided to the instance uniform is overridden by value given to the shader parameter of the overlay shader, in this code example, this will result in red being 0.0 in all meshes, as nobody is writing anything into that, and it defaults to 0.0

What is expected: It should look how it looks if you run the same code, but comment out line 40 to have no overlay to cause this to happen.

Also running any code to modify that "brightness" parameter clearly shows what is happening here. Notice that the overlay material literally does nothing, it immediately has a discard; statement as its entire fragment shader.

Steps to reproduce

Create a new project, create a default scene with a Node3D and set it to be the "main scene".

attach a script to the root node of that scene with this code:

extends Node3D
func _ready() -> void:
    var env := WorldEnvironment.new()
    env.environment = Environment.new()
    env.environment.background_mode = Environment.BG_SKY
    env.environment.sky = Sky.new()
    env.environment.sky.sky_material = ProceduralSkyMaterial.new()
    add_child(env)

    var cam := Camera3D.new()
    cam.position = Vector3(0.0, 0.0, 10.0)
    cam.current = true
    add_child(cam)

    var base_shader := Shader.new()
    base_shader.code = \
            "shader_type spatial;" + \
            "instance uniform vec3 color = vec3(1.0);" + \
            "void fragment() { ALBEDO = color; }"
    var base_material := ShaderMaterial.new()
    base_material.shader = base_shader

    var overlay_shader := Shader.new()
    overlay_shader.code = \
            "shader_type spatial;" + \
            "instance uniform float brightness = 0.0;" + \
            "void fragment() { discard; }"
    var overlay_material := ShaderMaterial.new()
    overlay_material.shader = overlay_shader

    for iii: int in 25:
         var mesh := MeshInstance3D.new()
         mesh.position = Vector3(
                 floor(float(iii) / 5.0) - 2.5, float(iii % 5) - 2.5, 0.0) * 2.0
         mesh.rotation.x = PI * float(iii) * 0.0625
         mesh.rotation.y = PI * float(iii) * 0.125

         mesh.mesh = BoxMesh.new()
         mesh.mesh.material = base_material
         mesh.material_overlay = overlay_material # Commenting out this line "fixes it"
         mesh.set_instance_shader_parameter(&"color",
                 Color(float(iii) / 5.0, float(iii % 5), 0.0))
         add_child(mesh)

Minimal reproduction project (MRP)

N / A

huwpascoe commented 4 months ago

Does changing the draw order make any difference? like material_overlay.render_priority = 1

A-Hugh-Mann commented 4 months ago

seems to make no difference if render priority is changed.

AThousandShips commented 4 months ago

You need to assign them unique IDs, see:

And see the documentation