Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
149 changes: 149 additions & 0 deletions tutorials/shaders/shader_reference/shading_language.rst
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,155 @@ The syntax also supports subgroups (it's not mandatory to declare the base group

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
------------------

Expand Down