Skip to content

Commit

Permalink
Document global and per-instance uniforms in Shading language
Browse files Browse the repository at this point in the history
  • Loading branch information
Calinou committed Nov 22, 2022
1 parent b3f571c commit 1e02b90
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 0 deletions.
Binary file not shown.
Binary file not shown.
185 changes: 185 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,191 @@ 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 a lot of instances of a material.
This takes a lot of work as all these instances need to be tracked and the
uniform needs to be set for each of them.

Example use cases include:

1. **Providing player information to all relevant shaders.** You may want to let
your shaders know where the player is. As an example, the grass and some
bushes will bend when the player is close, simulating being pushed.
2. **Modifying the world without actually modifying it.** Modern games have a lot
of grass, yet you can do precise cuts to it, burn it, etc. How do they do it?
Is every bit of grass a mesh and an object? Not really, games generally just
use a texture aligned to the world (that often moves with you), with different
colors meaning different things. In this case they can use texture channels to
specify that the grass has been cut, burnt, etc. The vertex shader can make
the vertices disappear, or change the color based on this global uniform
texture.
3. **Controlling the weather and environment.** Nowadays, games have realistic
weather, with leaves and trees moving with the wind. Do they make animations
for each piece of grass? Not really, you can save the wind strength and
direction in a global uniform, then procedurally move the foliage with
different speed and intensity. This can even affect particle system shaders,
making fire and smoke propagate in the direction of the wind. Shooters often
have what is called a turbulence texture, which is a texture that contains
the local wind direction around the player. All relevant shaders can access
it and it's what allows explosions to move trees and grass around.
4. **Making regular materials interact with the environment.** This is often
overlooked. We think materials are edited and that's it, but that's not
always the case. Imagine it starts raining and you want your objects to
become wet. You need to change the metallic and roughness factors. For
floors, you want to draw droplets splashing. For walls, you want to draw the
water pouring down. You could control this with regular uniforms, but it
would be more time-consuming to set this on every material when it starts
raining.
5. **Providing team colors.** In team-based games, you can use global uniforms
to provide "friendly" and "enemy" colors that can automatically be used
everywhere relevant (player materials, map textures, special effects, GUI
elements, ...). This can be used to let players choose team colors freely, for
better accessibility. The "friendly" and "enemy" states themselves can be
determined by using a :ref:`per-instance uniform
<doc_shading_language_per_instance_uniforms>` that indicates the instance's
current team.

Gqlobal uniforms can help you add a lot of detail to your game. In Godot, they
work for every shader type, including the recently introduced sky shaders.

All these use cases are feasible thanks to global uniforms, which can be used in
every shader type (``canvas_item``, ``spatial``, ``particles``, ``sky`` and
``fog``).

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.

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 comparing 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_set("uniform_name")``, this
has a large performance penalty as the CPU must wait for the GPU to be
synchronized every time the value is queried.

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

0 comments on commit 1e02b90

Please sign in to comment.