Browse Source

Document global and per-instance uniforms in Shading language (#6414)

Hugo Locurcio 2 years ago
parent
commit
319106ba2b

BIN
tutorials/shaders/shader_reference/img/shading_language_adding_global_uniforms.webp


BIN
tutorials/shaders/shader_reference/img/shading_language_per_instance_uniforms_inspector.webp


+ 149 - 0
tutorials/shaders/shader_reference/shading_language.rst

@@ -839,6 +839,155 @@ The syntax also supports subgroups (it's not mandatory to declare the base group
 
 
     group_uniforms MyGroup.MySubgroup;
     group_uniforms MyGroup.MySubgroup;
 
 
+Global uniforms
+^^^^^^^^^^^^^^^
+
+Sometimes, you want to modify a parameter in many different shaders at once.
+With a regular uniform, this takes a lot of work as all these shaders need to be
+tracked and the uniform needs to be set for each of them. Global uniforms allow
+you to create and update uniforms that will be available in all shaders, in
+every shader type (``canvas_item``, ``spatial``, ``particles``, ``sky`` and
+``fog``).
+
+Global uniforms are especially useful for environmental effects that affect many
+objects in a scene, like having foliage bend when the player is nearby, or having
+objects move with the wind.
+
+To create a global uniform, open the **Project Settings** then go to the
+**Shader Globals** tab. Specify a name for the uniform (case-sensitive) and a
+type, then click **Add** in the top-right corner of the dialog. You can then
+edit the value assigned to the uniform by clicking the value in the list of
+uniforms:
+
+.. figure:: img/shading_language_adding_global_uniforms.webp
+   :align: center
+   :alt: Adding a global uniform in the Shader Globals tab of the Project Settings
+
+   Adding a global uniform in the Shader Globals tab of the Project Settings
+
+After creating a global uniform, you can use it in a shader as follows:
+
+::
+
+    shader_type canvas_item;
+
+    global uniform vec4 my_color;
+
+    void fragment() {
+        COLOR = my_color.rgb;
+    }
+
+Note that the global uniform *must* exist in the Project Settings at the time
+the shader is saved, or compilation will fail. While you can assign a default
+value using ``global uniform vec4 my_color = ...`` in the shader code, it will
+be ignored as the global uniform must always be defined in the Project Settings
+anyway.
+
+To change the value of a global uniform at run-time, use the
+:ref:`RenderingServer.global_shader_parameter_set <class_RenderingServer_method_global_shader_parameter_set>`
+method in a script:
+
+::
+
+    RenderingServer.global_shader_parameter_set("my_color", Color(0.3, 0.6, 1.0))
+
+Assigning global uniform values can be done as many times as desired without
+impacting performance, as setting data doesn't require synchronization between
+the CPU and GPU.
+
+You can also add or remove global uniforms at run-time:
+
+::
+
+    RenderingServer.global_shader_parameter_add("my_color", RenderingServer.GLOBAL_VAR_TYPE_COLOR, Color(0.3, 0.6, 1.0))
+    RenderingServer.global_shader_parameter_remove("my_color")
+
+Adding or removing global uniforms at run-time has a performance cost, although
+it's not as pronounced compared to getting global uniform values from a script
+(see the warning below).
+
+.. warning::
+
+    While you *can* query the value of a global uniform at run-time in a script
+    using ``RenderingServer.global_shader_parameter_get("uniform_name")``, this
+    has a large performance penalty as the rendering thread needs to synchronize
+    with the calling thread.
+
+    Therefore, it's not recommended to read global shader uniform values
+    continuously in a script. If you need to read values in a script after
+    setting them, consider creating an :ref:`autoload <doc_singletons_autoload>`
+    where you store the values you need to query at the same time you're setting
+    them as global uniforms.
+
+.. _doc_shading_language_per_instance_uniforms:
+
+Per-instance uniforms
+^^^^^^^^^^^^^^^^^^^^^
+
+.. note::
+
+    Per-instance uniforms are only available in ``spatial`` (3D) shaders.
+
+Sometimes, you want to modify a parameter on each node using the material. As an
+example, in a forest full of trees, when you want each tree to have a slightly
+different color that is editable by hand. Without per-instance uniforms, this
+requires creating a unique material for each tree (each with a slightly
+different hue). This makes material management more complex, and also has a
+performance overhead due to the scene requiring more unique material instances.
+Vertex colors could also be used here, but they'd require creating unique copies
+of the mesh for each different color, which also has a performance overhead.
+
+Per-instance uniforms are set on each GeometryInstance3D, rather than on each
+Material instance. Take this into account when working with meshes that have
+multiple materials assigned to them, or MultiMesh setups.
+
+::
+
+    shader_type spatial;
+
+    // Provide a hint to edit as a color. Optionally, a default value can be provided.
+    // If no default value is provided, the type's default is used (e.g. opaque black for colors).
+    instance uniform vec4 my_color : source_color = vec4(1.0, 0.5, 0.0, 1.0);
+
+    void fragment() {
+        ALBEDO = my_color.rgb;
+    }
+
+After saving the shader, you can change the per-instance uniform's value using
+the inspector:
+
+.. figure:: img/shading_language_per_instance_uniforms_inspector.webp
+   :align: center
+   :alt: Setting a per-instance uniform's value in the GeometryInstance3D section of the inspector
+
+   Setting a per-instance uniform's value in the GeometryInstance3D section of the inspector
+
+Per-instance uniform values can also be set at run-time using
+`set_instance_shader_parameter<class_GeometryInstance3D_method_set_instance_shader_parameter>`
+method on a node that inherits from :ref:`class_GeometryInstance3D`:
+
+::
+
+    $MeshInstance3D.set_instance_shader_parameter("my_color", Color(0.3, 0.6, 1.0))
+
+When using per-instance uniforms, there are some restrictions you should be aware of:
+
+- **Per-instance uniforms do not support textures**, only regular scalar and
+  vector types. As a workaround, you can pass a texture array as a regular
+  uniform, then pass the index of the texture to be drawn using a per-instance
+  uniform.
+- There is a practical maximum limit of 16 instance uniforms per shader.
+- If your mesh uses multiple materials, the parameters for the first mesh
+  material found will "win" over the subsequent ones, unless they have the same
+  name, index *and* type. In this case, all parameters are affected correctly.
+- If you run into the above situation, you can avoid clashes by manually
+  specifying the index (0-15) of the instance uniform by using the
+  ``instance_index`` hint:
+
+::
+
+    instance uniform vec4 my_color : source_color, instance_index(5);
+
 Built-in variables
 Built-in variables
 ------------------
 ------------------