godotengine / godot

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

Getting shader parameters(via GDScript) always returns null after setting shader/ShaderMaterial with GDScript #80861

Open FleshLich opened 1 year ago

FleshLich commented 1 year ago

Godot version

v4.1.1.stable.official [bd6af8e0e]

System information

Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1080 (NVIDIA; 31.0.15.3667) - Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz (8 Threads)

Issue description

When trying to get a shader parameter with GDScript, AFTER setting the shader with GDScript, the "get_shader_parameter" function returns null.

A few things to note:

  1. Setting the desired parameter, even before the shader itself is set, fixes this problem.
  2. If the ShaderMaterial is set in the editor beforehand, this problem will not occur with any of the parameters that are present in the loaded material. For example, if you load a material with the parameter named "Threshold" in the editor, the "get_shader_parameter" function would return the correct value for any shader with a parameter named "Threshold", even if the other shaders are loaded through code.
  3. Even when the "get_shader_parameter" function incorrectly returns null, the proper values can still be seen in editor with remote debugging

Steps to reproduce

  1. Set up a scene with a visible canvas_item(e.g a TextureRect) and assign it a script
  2. Create a gdshader file with a least one uniform value
  3. Create a ShaderMaterial via ShaderMaterial.new()
  4. Assign the previously created shader to the previously created ShaderMaterial via the load() function
  5. Assign the ShaderMaterial
  6. Print the value of one of the uniforms with "get_shader_parameter", it should return null, even if a default value was set in the gdshader

How to use MPR

Press "1" on your keyboard. This will set the shader via GDScript and then get and print the value of the shader parameter(should be null). Press "2" on your keyboard. This will create a ShaderMaterial and then set a shader parameter. Once the shader parameter is set, the shader itself is then set. The shader parameter is then gotten and printed(should be a random float value)

Minimal reproduction project

Shader Loading MRP.zip

bitsawer commented 1 year ago

This is the intended behavior, not a bug. If the value has not been set either in GDScript or editor inspector, it returns null to signal this. You can fetch the values given in shader file using this:

print(target.material.shader.get_shader_uniform_list())

But beware as it has to fetch them from RenderingServer which can be slow. I'll mark this as documentation issue because there has been many questions like this and the behavior is a bit unintuitive, I'll try to update the documentation at some point.

clayjohn commented 1 year ago

But beware as it has to fetch them from RenderingServer which can be slow. I'll mark this as documentation issue because there has been many questions like this and the behavior is a bit unintuitive, I'll try to update the documentation at some point.

At this point we almost need to do a WARN_PRINT_ONCE if a user tries to retrieve a shader value right after setting it.

Alternatively, we could cache the entire set of uniforms in the Material resource... It sucks, but that's what we do for other properties