123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- .. _doc_shader_preprocessor:
- Shader preprocessor
- ===================
- Why use a shader preprocessor?
- ------------------------------
- In programming languages, a *preprocessor* allows changing the code before the
- compiler reads it. Unlike the compiler, the preprocessor does not care about
- whether the syntax of the preprocessed code is valid. The preprocessor always
- performs what the *directives* tell it to do. A directive is a statement
- starting with a hash symbol (``#``). It is not a *keyword* of the shader
- language (such as ``if`` or ``for``), but a special kind of token within the
- language.
- From Godot 4.0 onwards, you can use a shader preprocessor within text-based
- shaders. The syntax is similar to what most GLSL shader compilers support
- (which in turn is similar to the C/C++ preprocessor).
- .. note::
- The shader preprocessor is not available in :ref:`visual shaders <doc_visual_shaders>`.
- If you need to introduce preprocessor statements to a visual shader, you can
- convert it to a text-based shader using the **Convert to Shader** option in
- the VisualShader inspector resource dropdown. This conversion is a one-way
- operation; text shaders cannot be converted back to visual shaders.
- Directives
- ----------
- General syntax
- ^^^^^^^^^^^^^^
- - Preprocessor directives do not use brackets (``{}``), but can use parentheses.
- - Preprocessor directives **never** end with semicolons (with the exception of ``#define``,
- where this is allowed but potentially dangerous).
- - Preprocessor directives can span several lines by ending each line with a
- backslash (``\``). The first line break *not* featuring a backslash will end
- the preprocessor statement.
- #define
- ^^^^^^^
- **Syntax:** ``#define <identifier> [replacement_code]``.
- Defines the identifier after that directive as a macro, and replaces all
- successive occurrences of it with the replacement code given in the shader.
- Replacement is performed on a "whole words" basis, which means no replacement is
- performed if the string is part of another string (without any spaces or
- operators separating it).
- Defines with replacements may also have one or more *arguments*, which can then
- be passed when referencing the define (similar to a function call).
- If the replacement code is not defined, the identifier may only be used with
- ``#ifdef`` or ``#ifndef`` directives.
- If the *concatenation* symbol (``##``) is present in the replacement code then
- it will be removed upon macro insertion, together with any space surrounding
- it, and join the surrounding words and arguments into a new token.
- .. code-block:: glsl
- uniform sampler2D material0;
- #define SAMPLE(N) vec4 tex##N = texture(material##N, UV)
- void fragment() {
- SAMPLE(0);
- ALBEDO = tex0.rgb;
- }
- Compared to constants (``const CONSTANT = value;``), ``#define`` can be used
- anywhere within the shader (including in uniform hints).
- ``#define`` can also be used to insert arbitrary shader code at any location,
- while constants can't do that.
- .. code-block:: glsl
- shader_type spatial;
- // Notice the lack of semicolon at the end of the line, as the replacement text
- // shouldn't insert a semicolon on its own.
- // If the directive ends with a semicolon, the semicolon is inserted in every usage
- // of the directive, even when this causes a syntax error.
- #define USE_MY_COLOR
- #define MY_COLOR vec3(1, 0, 0)
- // Replacement with arguments.
- // All arguments are required (no default values can be provided).
- #define BRIGHTEN_COLOR(r, g, b) vec3(r + 0.5, g + 0.5, b + 0.5)
- // Multiline replacement using backslashes for continuation:
- #define SAMPLE(param1, param2, param3, param4) long_function_call( \
- param1, \
- param2, \
- param3, \
- param4 \
- )
- void fragment() {
- #ifdef USE_MY_COLOR
- ALBEDO = MY_COLOR;
- #endif
- }
- Defining a ``#define`` for an identifier that is already defined results in an
- error. To prevent this, use ``#undef <identifier>``.
- #undef
- ^^^^^^
- **Syntax:** ``#undef identifier``
- The ``#undef`` directive may be used to cancel a previously defined ``#define`` directive:
- .. code-block:: glsl
- #define MY_COLOR vec3(1, 0, 0)
- vec3 get_red_color() {
- return MY_COLOR;
- }
- #undef MY_COLOR
- #define MY_COLOR vec3(0, 1, 0)
- vec3 get_green_color() {
- return MY_COLOR;
- }
- // Like in most preprocessors, undefining a define that was not previously defined is allowed
- // (and won't print any warning or error).
- #undef THIS_DOES_NOT_EXIST
- Without ``#undef`` in the above example, there would be a macro redefinition error.
- #if
- ^^^
- **Syntax:** ``#if <condition>``
- The ``#if`` directive checks whether the ``condition`` passed. If it evaluates
- to a non-zero value, the code block is included, otherwise it is skipped.
- To evaluate correctly, the condition must be an expression giving a simple
- floating-point, integer or boolean result. There may be multiple condition
- blocks connected by ``&&`` (AND) or ``||`` (OR) operators. It may be continued
- by a ``#else`` block, but **must** be ended with the ``#endif`` directive.
- .. code-block:: glsl
- #define VAR 3
- #define USE_LIGHT 0 // Evaluates to `false`.
- #define USE_COLOR 1 // Evaluates to `true`.
- #if VAR == 3 && (USE_LIGHT || USE_COLOR)
- // Condition is `true`. Include this portion in the final shader.
- #endif
- Using the ``defined()`` *preprocessor function*, you can check whether the
- passed identifier is defined a by ``#define`` placed above that directive. This
- is useful for creating multiple shader versions in the same file. It may be
- continued by a ``#else`` block, but must be ended with the ``#endif`` directive.
- The ``defined()`` function's result can be negated by using the ``!`` (boolean NOT)
- symbol in front of it. This can be used to check whether a define is *not* set.
- .. code-block:: glsl
- #define USE_LIGHT
- #define USE_COLOR
- // Correct syntax:
- #if defined(USE_LIGHT) || defined(USE_COLOR) || !defined(USE_REFRACTION)
- // Condition is `true`. Include this portion in the final shader.
- #endif
- Be careful, as ``defined()`` must only wrap a single identifier within parentheses, never more:
- .. code-block:: glsl
- // Incorrect syntax (parentheses are not placed where they should be):
- #if defined(USE_LIGHT || USE_COLOR || !USE_REFRACTION)
- // This will cause an error or not behave as expected.
- #endif
- .. tip::
- In the shader editor, preprocessor branches that evaluate to ``false`` (and
- are therefore excluded from the final compiled shader) will appear grayed
- out. This does not apply to runtime ``if`` statements.
- **#if preprocessor versus if statement: Performance caveats**
- The :ref:`shading language <doc_shading_language>` supports runtime ``if`` statements:
- .. code-block:: glsl
- uniform bool USE_LIGHT = true;
- if (USE_LIGHT) {
- // This part is included in the compiled shader, and always run.
- } else {
- // This part is included in the compiled shader, but never run.
- }
- If the uniform is never changed, this behaves identical to the following usage
- of the ``#if`` preprocessor statement:
- .. code-block:: glsl
- #define USE_LIGHT
- #if defined(USE_LIGHT)
- // This part is included in the compiled shader, and always run.
- #else
- // This part is *not* included in the compiled shader (and therefore never run).
- #endif
- However, the ``#if`` variant can be faster in certain scenarios. This is because
- all runtime branches in a shader are still compiled and variables within
- those branches may still take up register space, even if they are never run in
- practice.
- Modern GPUs are `quite effective <https://medium.com/@jasonbooth_86226/branching-on-a-gpu-18bfc83694f2>`__
- at performing "static" branching. "Static" branching refers to ``if`` statements where
- *all* pixels/vertices evaluate to the same result in a given shader invocation. However,
- high amounts of :abbr:`VGPRs (Vector General-Purpose Register)` (which can be caused by
- having too many branches) can still slow down shader execution significantly.
- #elif
- ^^^^^
- The ``#elif`` directive stands for "else if" and checks the condition passed if
- the above ``#if`` evaluated to ``false``. ``#elif`` can only be used within an
- ``#if`` block. It is possible to use several ``#elif`` statements after an ``#if`` statement.
- .. code-block:: glsl
- #define VAR 2
- #if VAR == 0
- // Not included.
- #elif VAR == 1
- // Not included.
- #elif VAR == 2
- // Condition is `true`. Include this portion in the final shader.
- #else
- // Not included.
- #endif
- Like with ``#if``, the ``defined()`` preprocessor function can be used:
- .. code-block:: glsl
- #define SHADOW_QUALITY_MEDIUM
- #if defined(SHADOW_QUALITY_HIGH)
- // High shadow quality.
- #elif defined(SHADOW_QUALITY_MEDIUM)
- // Medium shadow quality.
- #else
- // Low shadow quality.
- #endif
- #ifdef
- ^^^^^^
- **Syntax:** ``#ifdef <identifier>``
- This is a shorthand for ``#if defined(...)``. Checks whether the passed
- identifier is defined by ``#define`` placed above that directive. This is useful
- for creating multiple shader versions in the same file. It may be continued by a
- ``#else`` block, but must be ended with the ``#endif`` directive.
- .. code-block:: glsl
- #define USE_LIGHT
- #ifdef USE_LIGHT
- // USE_LIGHT is defined. Include this portion in the final shader.
- #endif
- The processor does *not* support ``#elifdef`` as a shortcut for ``#elif defined(...)``.
- Instead, use the following series of ``#ifdef`` and ``#else`` when you need more
- than two branches:
- .. code-block:: glsl
- #define SHADOW_QUALITY_MEDIUM
- #ifdef SHADOW_QUALITY_HIGH
- // High shadow quality.
- #else
- #ifdef SHADOW_QUALITY_MEDIUM
- // Medium shadow quality.
- #else
- // Low shadow quality.
- #endif // This ends `SHADOW_QUALITY_MEDIUM`'s branch.
- #endif // This ends `SHADOW_QUALITY_HIGH`'s branch.
- #ifndef
- ^^^^^^^
- **Syntax:** ``#ifndef <identifier>``
- This is a shorthand for ``#if !defined(...)``. Similar to ``#ifdef``, but checks
- whether the passed identifier is **not** defined by ``#define`` before that
- directive.
- This is the exact opposite of ``#ifdef``; it will always match in situations
- where ``#ifdef`` would never match, and vice versa.
- .. code-block:: glsl
- #define USE_LIGHT
- #ifndef USE_LIGHT
- // Evaluates to `false`. This portion won't be included in the final shader.
- #endif
- #ifndef USE_COLOR
- // Evaluates to `true`. This portion will be included in the final shader.
- #endif
- #else
- ^^^^^
- **Syntax:** ``#else``
- Defines the optional block which is included when the previously defined ``#if``,
- ``#elif``, ``#ifdef`` or ``#ifndef`` directive evaluates to false.
- .. code-block:: glsl
- shader_type spatial;
- #define MY_COLOR vec3(1.0, 0, 0)
- void fragment() {
- #ifdef MY_COLOR
- ALBEDO = MY_COLOR;
- #else
- ALBEDO = vec3(0, 0, 1.0);
- #endif
- }
- #endif
- ^^^^^^
- **Syntax:** ``#endif``
- Used as terminator for the ``#if``, ``#ifdef``, ``#ifndef`` or subsequent ``#else`` directives.
- #error
- ^^^^^^
- **Syntax:** ``#error <message>``
- The ``#error`` directive forces the preprocessor to emit an error with optional message.
- For example, it's useful when used within ``#if`` block to provide a strict limitation of the
- defined value.
- .. code-block:: glsl
- #define MAX_LOD 3
- #define LOD 4
- #if LOD > MAX_LOD
- #error LOD exceeds MAX_LOD
- #endif
- #include
- ^^^^^^^^
- **Syntax:** ``#include "path"``
- The ``#include`` directive includes the *entire* content of a shader include
- file in a shader. ``"path"`` can be an absolute ``res://`` path or relative to
- the current shader file. Relative paths are only allowed in shaders that are
- saved to ``.gdshader`` or ``.gdshaderinc`` files, while absolute paths can be
- used in shaders that are built into a scene/resource file.
- You can create new shader includes by using the **File > Create Shader Include**
- menu option of the shader editor, or by creating a new :ref:`ShaderInclude<class_ShaderInclude>` resource
- in the FileSystem dock.
- Shader includes can be included from within any shader, or other shader include, at
- any point in the file.
- When including shader includes in the global scope of a shader, it is recommended
- to do this after the initial ``shader_type`` statement.
- You can also include shader includes from within the body a function. Please note that
- the shader editor is likely going to report errors for your shader include's code, as it
- may not be valid outside of the context that it was written for. You can either choose
- to ignore these errors (the shader will still compile fine), or you can wrap the include
- in an ``#ifdef`` block that checks for a define from your shader.
- ``#include`` is useful for creating libraries of helper functions (or macros)
- and reducing code duplication. When using ``#include``, be careful about naming
- collisions, as redefining functions or macros is not allowed.
- ``#include`` is subject to several restrictions:
- - Only shader include resources (ending with ``.gdshaderinc``) can be included.
- ``.gdshader`` files cannot be included by another shader, but a
- ``.gdshaderinc`` file can include other ``.gdshaderinc`` files.
- - Cyclic dependencies are **not** allowed and will result in an error.
- - To avoid infinite recursion, include depth is limited to 25 steps.
- Example shader include file:
- .. code-block:: glsl
- // fancy_color.gdshaderinc
- // While technically allowed, there is usually no `shader_type` declaration in include files.
- vec3 get_fancy_color() {
- return vec3(0.3, 0.6, 0.9);
- }
- Example base shader (using the include file we created above):
- .. code-block:: glsl
- // material.gdshader
- shader_type spatial;
- #include "res://fancy_color.gdshaderinc"
- void fragment() {
- // No error, as we've included a definition for `get_fancy_color()` via the shader include.
- COLOR = get_fancy_color();
- }
- #pragma
- ^^^^^^^
- **Syntax:** ``#pragma value``
- The ``#pragma`` directive provides additional information to the preprocessor or compiler.
- Currently, it may have only one value: ``disable_preprocessor``. If you don't need
- the preprocessor, use that directive to speed up shader compilation by excluding
- the preprocessor step.
- .. code-block:: glsl
- #pragma disable_preprocessor
- #if USE_LIGHT
- // This causes a shader compilation error, as the `#if USE_LIGHT` and `#endif`
- // are included as-is in the final shader code.
- #endif
- Built-in defines
- ----------------
- Current renderer
- ^^^^^^^^^^^^^^^^
- Since Godot 4.4, you can check which renderer is currently used with the built-in
- defines ``CURRENT_RENDERER``, ``RENDERER_COMPATIBILITY``, ``RENDERER_MOBILE``,
- and ``RENDERER_FORWARD_PLUS``:
- - ``CURRENT_RENDERER`` is set to either ``0``, ``1``, or ``2`` depending on the
- current renderer.
- - ``RENDERER_COMPATIBILITY`` is always ``0``.
- - ``RENDERER_MOBILE`` is always ``1``.
- - ``RENDERER_FORWARD_PLUS`` is always ``2``.
- As an example, this shader sets ``ALBEDO`` to a different color in each renderer:
- .. code-block:: glsl
- shader_type spatial;
- void fragment() {
- #if CURRENT_RENDERER == RENDERER_COMPATIBILITY
- ALBEDO = vec3(0.0, 0.0, 1.0);
- #elif CURRENT_RENDERER == RENDERER_MOBILE
- ALBEDO = vec3(1.0, 0.0, 0.0);
- #else // CURRENT_RENDERER == RENDERER_FORWARD_PLUS
- ALBEDO = vec3(0.0, 1.0, 0.0);
- #endif
- }
|