Shader-Sprache

Einführung

Godot verwendet eine Shader-Sprache ähnlich GLSL ES 3.0. Die meisten Datentypen und Funktionen werden unterstützt, und die wenigen verbleibenden werden wahrscheinlich im Laufe der Zeit hinzugefügt.

Wenn Sie bereits mit GLSL vertraut sind, ist die Godot-Shader-Migrationsanleitung eine Ressource, die Ihnen beim Übergang von regulärem GLSL zu Godots Shader-Sprache helfen wird.

Datentypen

Die meisten GLSL ES 3.0-Datentypen werden unterstützt:

Typ

Beschreibung

void

Void Datentyp, nur nützlich für Funktionen die nichts zurückliefern.

bool

Boolescher Datentyp, kann nur true oder false enthalten.

bvec2

Zwei-Komponenten-Vektor von Bools.

bvec3

Drei-Komponenten-Vektor mit Bools.

bvec4

Vier-Komponenten-Vektor mit Bools.

int

32 bit signed scalar integer.

ivec2

Zwei-Komponenten-Vektor mit Signed Integern.

ivec3

Drei-Komponenten-Vektor mit Signed Integern.

ivec4

Vier-Komponenten-Vektor mit Signed Integern.

uint

Unsigned Skalarer Integer, kann keine negativen Zahlen enthalten.

uvec2

Zwei-Komponenten-Vektor mit Unsigned Integern.

uvec3

Drei-Komponenten-Vektor mit Unsigned Integern.

uvec4

Vier-Komponenten-Vektor mit Unsigned Integern.

float

32 bit floating-point scalar.

vec2

Zwei-Komponenten-Vektor mit Float-Werten.

vec3

Drei-Komponenten-Vektor mit Float-Werten.

vec4

Vier-Komponenten-Vektor mit Float-Werten.

mat2

2x2 Matrix, in spaltenweiser Anordnung.

mat3

3x3 Matrix, in spaltenweiser Anordnung.

mat4

4x4 Matrix, in spaltenweiser Anordnung.

sampler2D

Samplertyp, um 2D Texturen zu binden, welche als Float gelesen werden.

isampler2D

Samplertyp, um 2D Texturen zu binden, welche als Signed Integer gelesen werden.

usampler2D

Samplertyp, um 2D Texturen zu binden, welche als Unsigned Integer gelesen werden.

sampler2DArray

Samplertyp, um 2D Texturfelder zu binden, welche als Float gelesen werden.

isampler2DArray

Samplertyp, um 2D Textur-Arrays zu binden, welche als Signed Integer gelesen werden.

usampler2DArray

Samplertyp, um 2D Textur-Arrays zu binden, welche als Unsigned Integer gelesen werden.

sampler3D

Samplertyp, um 3D-Texturen zu binden, welche als Float gelesen werden.

isampler3D

Samplertyp, um 3D-Texturen zu binden, welche als Signed Integer gelesen werden.

usampler3D

Samplertyp, um 3D-Texturen zu binden, welche als Unsigned Integer gelesen werden.

samplerCube

Samplertyp um Cubemaps zu binden, welche als Float gelesen werden.

samplerCubeArray

Sampler type for binding Cubemap arrays, which are read as float. Only supported in Forward+ and Mobile, not Compatibility.

samplerExternalOES

External sampler type. Only supported in Compatibility/Android platform.

Warnung

Local variables are not initialized to a default value such as 0.0. If you use a variable without assigning it first, it will contain whatever value was already present at that memory location, and unpredictable visual glitches will appear. However, uniforms and varyings are initialized to a default value.

Kommentare

The shading language supports the same comment syntax as used in C# and C++, using // for single-line comments and /* */ for multi-line comments:

// Single-line comment.
int a = 2;  // Another single-line comment.

/*
Multi-line comment.
The comment ends when the ending delimiter is found
(here, it's on the line below).
*/
int b = 3;

Zusätzlich können Sie Dokumentationskommentare verwenden, die im Inspektor angezeigt werden, wenn Sie den Mauszeiger über einen Shader-Parameter bewegen. Dokumentationskommentare werden derzeit nur unterstützt, wenn sie unmittelbar über einer Uniform-Deklaration stehen. Diese Dokumentationskommentare unterstützen nur die mehrzeilige Kommentarsyntax und müssen zwei führende Sternchen (/**) anstelle von nur einem (/*) verwenden:

/**
 * This is a documentation comment.
 * These lines will appear in the inspector when hovering the shader parameter
 * named "Something".
 * You can use [b]BBCode[/b] [i]formatting[/i] in the comment.
 */
uniform int something = 1;

Die Sternchen in den Folgezeilen sind nicht erforderlich, werden aber gemäß dem Shader Style-Guide empfohlen. Diese Sternchen werden vom Inspektor automatisch entfernt, so dass sie nicht in der QuickInfo erscheinen.

Casting

Genau wie bei GLSL ES 3.0 ist implizites Casting zwischen Skalaren und Vektoren gleicher Größe, aber unterschiedlichen Typs nicht zulässig. Casting von Typen unterschiedlicher Größe ist ebenfalls nicht zulässig. Die Konvertierung muss explizit über Konstruktoren erfolgen.

Beispiel:

float a = 2; // invalid
float a = 2.0; // valid
float a = float(2); // valid

Default-Integer-Konstanten sind signed, daher ist immer eine Umwandlung erforderlich, um in unsigned zu konvertieren:

int a = 2; // valid
uint a = 2; // invalid
uint a = uint(2); // valid

Member

Auf einzelne skalare Elemente von Vektortypen wird über die Elemente "x", "y", "z" und "w" zugegriffen. Alternativ dazu funktioniert auch die Verwendung von "r", "g", "b" und "a" und ist gleichwertig. Verwenden Sie, was für Ihre Bedürfnisse am besten geeignet ist.

For matrices, use the m[column][row] indexing syntax to access each scalar, or m[column] to access a vector by column index. For example, for accessing the y-component of the translation from a mat4 transform matrix (4th column, 2nd line) you use m[3][1] or m[3].y.

Konstruieren

Die Konstruktion von Vektortypen muss immer bestehen aus:

// The required amount of scalars
vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
// Complementary vectors and/or scalars
vec4 a = vec4(vec2(0.0, 1.0), vec2(2.0, 3.0));
vec4 a = vec4(vec3(0.0, 1.0, 2.0), 3.0);
// A single scalar for the whole vector
vec4 a = vec4(0.0);

Construction of matrix types requires vectors of the same dimension as the matrix, interpreted as columns. You can also build a diagonal matrix using matx(float) syntax. Accordingly, mat4(1.0) is an identity matrix.

mat2 m2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));
mat3 m3 = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0));
mat4 identity = mat4(1.0);

Matrizen können auch aus einer Matrix einer anderen Dimension gebildet werden. Es gibt zwei Regeln:

1. If a larger matrix is constructed from a smaller matrix, the additional rows and columns are set to the values they would have in an identity matrix. 2. If a smaller matrix is constructed from a larger matrix, the top, left submatrix of the larger matrix is used.

mat3 basis = mat3(MODEL_MATRIX);
mat4 m4 = mat4(basis);
mat2 m2 = mat2(m4);

Swizzling

Es ist möglich, eine beliebige Kombination von Komponenten in beliebiger Reihenfolge zu erhalten, solange das Ergebnis ein anderer Vektortyp (oder Skalar) ist. Dies ist einfacher zu zeigen als zu erklären:

vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
vec3 b = a.rgb; // Creates a vec3 with vec4 components.
vec3 b = a.ggg; // Also valid; creates a vec3 and fills it with a single vec4 component.
vec3 b = a.bgr; // "b" will be vec3(2.0, 1.0, 0.0).
vec3 b = a.xyz; // Also rgba, xyzw are equivalent.
vec3 b = a.stp; // And stpq (for texture coordinates).
float c = b.w; // Invalid, because "w" is not present in vec3 b.
vec3 c = b.xrt; // Invalid, mixing different styles is forbidden.
b.rrr = a.rgb; // Invalid, assignment with duplication.
b.bgr = a.rgb; // Valid assignment. "b"'s "blue" component will be "a"'s "red" and vice versa.

Präzision

Es ist möglich, Datentypen Präzisionsmodifikatoren hinzuzufügen. Verwenden Sie diese für Uniforms, Variablen, Argumente und Varyings:

lowp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // low precision, usually 8 bits per component mapped to 0-1
mediump vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // medium precision, usually 16 bits or half float
highp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // high precision, uses full float or integer range (32 bit default)

Die Verwendung einer geringeren Präzision für einige Operationen kann den Berechnung beschleunigen (auf Kosten einer geringeren Präzision). Dies wird in der Vertex-Prozessorfunktion selten benötigt (wo die meiste Zeit volle Präzision erforderlich ist), wird jedoch im Fragmentprozessor häufig nützlich.

Einige Architekturen (hauptsächlich mobile) können stark davon profitieren, aber es gibt auch Nachteile, beispielsweise den Mehraufwand zur Konvertierung zwischen Werten unterschiedlicher Präzision. Bitte lesen Sie die entsprechende Dokumentation zur Zielarchitektur, um mehr zu erfahren. In vielen Fällen verursachen mobile Treiber inkonsistentes oder unerwartetes Verhalten. Es ist deshalb am besten, keine Präzision anzugeben, wenn es nicht unbedingt erforderlich ist.

Arrays

Arrays sind Container für mehrere Variablen eines ähnlichen Typs.

Lokale Arrays

Lokale Arrays werden in Funktionen deklariert. Sie können alle erlaubten Datentypen verwenden, mit Ausnahme von Samplern. Die Array-Deklaration folgt einer C-ähnlichen Syntax: [const] + [Präzision] + Typname + Bezeichner + [Arraygröße].

void fragment() {
    float arr[3];
}

Sie können zu Beginn wie folgt initialisiert werden:

float float_arr[3] = float[3] (1.0, 0.5, 0.0); // first constructor

int int_arr[3] = int[] (2, 1, 0); // second constructor

vec2 vec2_arr[3] = { vec2(1.0, 1.0), vec2(0.5, 0.5), vec2(0.0, 0.0) }; // third constructor

bool bool_arr[] = { true, true, false }; // fourth constructor - size is defined automatically from the element count

Sie können mehrere Arrays (auch mit unterschiedlichen Größen) in einem Ausdruck deklarieren:

float a[3] = float[3] (1.0, 0.5, 0.0),
b[2] = { 1.0, 0.5 },
c[] = { 0.7 },
d = 0.0,
e[5];

Verwenden Sie die Indizierungssyntax, um auf ein Array-Element zuzugreifen:

float arr[3];

arr[0] = 1.0; // setter

COLOR.r = arr[0]; // getter

Arrays haben auch eine Built-in-Funktion .length() (nicht zu verwechseln mit der Built-in-Funktion length()). Sie akzeptiert keine Parameter und gibt die Größe des Arrays zurück.

float arr[] = { 0.0, 1.0, 0.5, -1.0 };
for (int i = 0; i < arr.length(); i++) {
    // ...
}

Bemerkung

Wenn Sie einen Index verwenden, der entweder kleiner als 0 oder größer als die Array-Größe ist, stürzt der Shader ab und bricht das Rendern ab. Um dies zu verhindern, verwenden Sie die Funktionen length(), if oder clamp(), um sicherzustellen, dass der Index zwischen 0 und der Länge des Arrays liegt. Testen und prüfen Sie Ihren Code immer sorgfältig. Wenn Sie einen konstanten Ausdruck oder eine Zahl übergeben, prüft der Editor deren Grenzen, um diesen Absturz zu verhindern.

Globale Arrays

You can declare arrays in global space as either const or uniform:

shader_type spatial;

const lowp vec3 v[1] = lowp vec3[1] ( vec3(0, 0, 1) );
uniform lowp vec3 w[1];

void fragment() {
  ALBEDO = v[0] + w[0];
}

Bemerkung

Global arrays use the same syntax as local arrays, except with a const or uniform added to their declaration. Note that uniform arrays can't have a default value.

Konstanten

Verwenden Sie das Schlüsselwort const vor der Variablendeklaration, um diese Variable immutable zu machen, was bedeutet, dass sie nicht verändert werden kann. Alle Grundtypen, außer Sampler, können als Konstanten deklariert werden. Der Zugriff und die Verwendung eines konstanten Wertes ist etwas schneller als die Verwendung einer Uniform. Konstanten müssen bei ihrer Deklaration initialisiert werden.

const vec2 a = vec2(0.0, 1.0);
vec2 b;

a = b; // invalid
b = a; // valid

Konstanten können nicht verändert werden und können auch keine Hints haben, aber mehrere von ihnen (wenn sie den gleichen Typ haben) können in einem einzigen Ausdruck deklariert werden, z.B

const vec2 V1 = vec2(1, 1), V2 = vec2(2, 2);

Ähnlich wie Variablen können Arrays auch mit const deklariert werden.

const float arr[] = { 1.0, 0.5, 0.0 };

arr[0] = 1.0; // invalid

COLOR.r = arr[0]; // valid

Konstanten können sowohl global (außerhalb einer Funktion) als auch lokal (innerhalb einer Funktion) deklariert werden. Globale Konstanten sind nützlich, wenn Sie im gesamten Shader Zugriff auf einen Wert haben möchten, der nicht geändert werden muss. Wie Uniforms werden globale Konstanten von allen Shader-Stufen gemeinsam genutzt, aber sie sind außerhalb des Shaders nicht zugänglich.

shader_type spatial;

const float GOLDEN_RATIO = 1.618033988749894;

Konstanten vom Typ float müssen mit der Notation . nach dem Dezimalteil oder mit der wissenschaftlichen Notation initialisiert werden. Das optionale Post-Suffix f wird ebenfalls unterstützt.

float a = 1.0;
float b = 1.0f; // same, using suffix for clarity
float c = 1e-1; // gives 0.1 by using the scientific notation

Konstanten vom Typ uint (unsigned int) müssen mit dem Suffix u versehen werden, um sie von Signed Integern zu unterscheiden. Alternativ kann dies durch die Built-in-Konvertierungsfunktion uint(x) geschehen.

uint a = 1u;
uint b = uint(1);

Structs

Structs sind zusammengesetzte Typen, die für eine bessere Abstraktion des Shader-Codes verwendet werden können. Sie können auf globaler Ebene so deklariert werden:

struct PointLight {
    vec3 position;
    vec3 color;
    float intensity;
};

Nach der Deklaration können Sie sie wie folgt instanziieren und initialisieren:

void fragment()
{
    PointLight light;
    light.position = vec3(0.0);
    light.color = vec3(1.0, 0.0, 0.0);
    light.intensity = 0.5;
}

Oder verwenden Sie den Struct-Konstruktor für denselben Zweck:

PointLight light = PointLight(vec3(0.0), vec3(1.0, 0.0, 0.0), 0.5);

Strukturen können andere Strukturen oder Arrays enthalten, Sie können sie auch als globale Konstante instanziieren:

shader_type spatial;

...

struct Scene {
    PointLight lights[2];
};

const Scene scene = Scene(PointLight[2](PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0), PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0)));

void fragment()
{
    ALBEDO = scene.lights[0].color;
}

Sie können sie auch an Funktionen übergeben:

shader_type canvas_item;

...

Scene construct_scene(PointLight light1, PointLight light2) {
    return Scene({light1, light2});
}

void fragment()
{
    COLOR.rgb = construct_scene(PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), 1.0), PointLight(vec3(0.0, 0.0, 0.0), vec3(1.0, 0.0, 1.0), 1.0)).lights[0].color;
}

Operatoren

Die Godot Shader-Sprache unterstützt dieselben Operatoren wie GLSL ES 3.0. Unten ist die Liste von ihnen nach Rangfolge:

Rang

Klasse

Operator

1 (höchste)

Gruppierung in Klammern

()

2

unär

+, -, !, ~

3

multiplikativ

/, *, %

4

additiv

+, -

5

bitweiser Shift

<<, >>

6

relational

<, >, <=, >=

7

Gleichheit

==, !=

8

bitweises UND

&

9

bitweises Exklusiv-ODER

^

10

bitweises Inklusiv-ODER

|

11

logisches UND

&&

12 (niedrigste)

logisches Inklusiv-ODER

||

Bemerkung

Most operators that accept vectors or matrices (multiplication, division, etc) operate component-wise, meaning the function is applied to the first value of each vector and then on the second value of each vector, etc. Some examples:

Operation

Equivalent Scalar Operation

vec3(4, 5, 6) + 2

vec3(4 + 2, 5 + 2, 6 + 2)

vec2(3, 4) * vec2(10, 20)

vec2(3 * 10, 4 * 20)

mat2(vec2(1, 2), vec2(3, 4)) + 10

mat2(vec2(1 + 10, 2 + 10), vec2(3 + 10, 4 + 10))

The GLSL Language Specification says under section 5.10 Vector and Matrix Operations:

With a few exceptions, operations are component-wise. Usually, when an operator operates on a vector or matrix, it is operating independently on each component of the vector or matrix, in a component-wise fashion. [...] The exceptions are matrix multiplied by vector, vector multiplied by matrix, and matrix multiplied by matrix. These do not operate component-wise, but rather perform the correct linear algebraic multiply.

Flußkontrolle

Die Godot Shadersprache unterstützt die gebräuchlichsten Arten von Flußkontrolle:

// `if`, `else if` and `else`.
if (cond) {

} else if (other_cond) {

} else {

}

// Ternary operator.
// This is an expression that behaves like `if`/`else` and returns the value.
// If `cond` evaluates to `true`, `result` will be `9`.
// Otherwise, `result` will be `5`.
int result = cond ? 9 : 5;

// `switch`.
switch (i) { // `i` should be a signed integer expression.
    case -1:
        break;
    case 0:
        return; // `break` or `return` to avoid running the next `case`.
    case 1: // Fallthrough (no `break` or `return`): will run the next `case`.
    case 2:
        break;
    //...
    default: // Only run if no `case` above matches. Optional.
        break;
}

// `for` loop. Best used when the number of elements to iterate on
// is known in advance.
for (int i = 0; i < 10; i++) {

}

// `while` loop. Best used when the number of elements to iterate on
// is not known in advance.
while (cond) {

}

// `do while`. Like `while`, but always runs at least once even if `cond`
// never evaluates to `true`.
do {

} while (cond);

Denken Sie daran, dass bei modernen GPUs eine Endlosschleife existieren kann, die Ihre Anwendung (einschließlich des Editors) einfrieren kann. Godot kann Sie davor nicht schützen. Seien Sie also vorsichtig, um diesen Fehler zu vermeiden!

Wenn Sie Float-Werte mit einer Zahl vergleichen, achten Sie außerdem darauf, dass Sie sie mit einem Bereich und nicht mit einer genauen Zahl vergleichen.

Ein Vergleich wie if (Wert == 0.3) kann nicht zu true ausgewertet werden. Float-Mathematik ist oft annähernd und kann sich den Erwartungen entziehen. Sie kann sich auch je nach Hardware unterschiedlich verhalten.

Tun Sie das nicht.

float value = 0.1 + 0.2;

// May not evaluate to `true`!
if (value == 0.3) {
    // ...
}

Führen Sie stattdessen immer einen Bereichsvergleich mit einem Epsilonwert durch. Je größer die Float-Zahl (und je ungenauer die Float-Zahl), desto größer sollte der Epsilon-Wert sein.

const float EPSILON = 0.0001;
if (value >= 0.3 - EPSILON && value <= 0.3 + EPSILON) {
    // ...
}

Siehe Floating-point-gui.de für weitere Informationen.

Verwerfen

Fragment, light, and custom functions (called from fragment or light) can use the discard keyword. If used, the fragment is discarded and nothing is written.

Beachten Sie, daß die Verwendung von discard Performance kostet, da sie verhindert, daß der Tiefen-Vordurchlauf auf allen Oberflächen, die den Shader benutzen, wirksam wird. Außerdem muss ein verworfenes Pixel immer noch im Vertex-Shader gerendert werden, was bedeutet, dass ein Shader, der discard auf alle seine Pixel anwendet, immer noch teurer zu rendern ist, als wenn gar kein Objekt gerendert wird.

Funktionen

Es ist möglich, Funktionen in einem Godot-Shader zu definieren. Sie verwenden die folgende Syntax:

ret_type func_name(args) {
    return ret_type; // if returning a value
}

// a more specific example:

int sum2(int a, int b) {
    return a + b;
}

Sie können nur Funktionen verwenden, die über der Funktion, von der aus Sie sie aufrufen, definiert wurden (höher im Editor). Die Neudefinition einer Funktion, die bereits oben definiert wurde (oder ein Built-in-Funktionsname ist), führt zu einem Fehler.

Funktionsargumente können spezielle Qualifier haben:

  • in: bedeutet, dass das Argument nur gelesen werden kann (Default).

  • out: bedeutet, dass das Argument nur geschrieben werden kann.

  • inout: bedeutet, dass das Argument komplett als Referenz übergeben wird.

  • const: Bedeutet, dass das Argument eine Konstante ist und nicht geändert werden kann; kann mit dem Qualifizierer in kombiniert werden.

Hier ein Beispiel:

void sum2(int a, int b, inout int result) {
    result = a + b;
}

Function overloading is supported. You can define multiple functions with the same name, but different arguments. Note that implicit casting in overloaded function calls is not allowed, such as from int to float (1 to 1.0).

vec3 get_color(int t) {
    return vec3(1, 0, 0); // Red color.
}
vec3 get_color(float t) {
    return vec3(0, 1, 0); // Green color.
}
void fragment() {
    vec3 red = get_color(1);
    vec3 green = get_color(1.0);
}

Varyings

Um Daten vom Vertex an die Fragment- (oder Licht-) Prozessorfunktion zu senden, werden Varyings verwendet. Sie werden für jeden primitiven Vertex im Vertex-Prozessor gesetzt, und der Wert wird für jedes Pixel im Fragment-Prozessor interpoliert.

shader_type spatial;

varying vec3 some_color;

void vertex() {
    some_color = NORMAL; // Make the normal the color.
}

void fragment() {
    ALBEDO = some_color;
}

void light() {
    DIFFUSE_LIGHT = some_color * 100; // optionally
}

Ein Varying kann auch ein Array sein:

shader_type spatial;

varying float var_arr[3];

void vertex() {
    var_arr[0] = 1.0;
    var_arr[1] = 0.0;
}

void fragment() {
    ALBEDO = vec3(var_arr[0], var_arr[1], var_arr[2]); // red color
}

Es ist auch möglich, mit dem Schlüsselwort varying Daten vom Fragment an Licht-Prozessoren zu senden. Dazu können Sie es im Fragment zuweisen und später in der Licht-Funktion verwenden.

shader_type spatial;

varying vec3 some_light;

void fragment() {
    some_light = ALBEDO * 100.0; // Make a shining light.
}

void light() {
    DIFFUSE_LIGHT = some_light;
}

Beachten Sie, dass Varyings nicht in benutzerdefinierten Funktionen oder einer Licht-Prozessor-Funktion zugewiesen werden können, wie:

shader_type spatial;

varying float test;

void foo() {
    test = 0.0; // Error.
}

void vertex() {
    test = 0.0;
}

void light() {
    test = 0.0; // Error too.
}

Diese Einschränkung wurde eingeführt, um eine falsche Verwendung vor der Initialisierung zu verhindern.

Interpolations-Qualifier

Bestimmte Werte werden während der Shading-Pipeline interpoliert. Sie können die Ausführung dieser Interpolationen mithilfe von Interpolations-Qualifiern ändern.

shader_type spatial;

varying flat vec3 our_color;

void vertex() {
    our_color = COLOR.rgb;
}

void fragment() {
    ALBEDO = our_color;
}

Es gibt zwei mögliche Interpolations-Qualifier:

Qualifier

Beschreibung

flat

Der Wert ist nicht interpoliert.

smooth

Der Wert ist interpoliert in einer perspektivisch korrekten Art. Dies ist der Default.

Uniforms

Passing values to shaders is possible with uniforms, which are defined in the global scope of the shader, outside of functions. When a shader is later assigned to a material, the uniforms will appear as editable parameters in the material's inspector. Uniforms can't be written from within the shader. Any data type except for void can be a uniform.

shader_type spatial;

uniform float some_value;

uniform vec3 colors[3];

You can set uniforms in the editor in the material's inspector. Alternately, you can set them from code.

Uniform hints

Godot provides optional uniform hints to make the compiler understand what the uniform is used for, and how the editor should allow users to modify it.

shader_type spatial;

uniform vec4 color : source_color;
uniform float amount : hint_range(0, 1);
uniform vec4 other_color : source_color = vec4(1.0); // Default values go after the hint.
uniform sampler2D image : source_color;

Uniforms können auch Default-Werte zugewiesen werden:

shader_type spatial;

uniform vec4 some_vector = vec4(0.0);
uniform vec4 some_color : source_color = vec4(1.0);

Beachten Sie, dass beim Hinzufügen eines Default-Wertes und eines Hints der Defaultwert nach dem Hint kommt.

Full list of uniform hints below:

Typ

Hint

Beschreibung

vec3, vec4

source_color

Verwendet als Farbe.

int

hint_enum("String1", "String2")

Displays int input as a dropdown widget in the editor.

int, float

hint_range(min, max[, step])

Beschränkt auf Werte in einem Bereich (mit Min/Max/Schrittweite).

sampler2D

source_color

Verwendet als Albedofarbe.

sampler2D

hint_normal

Verwendet als Normal Map.

sampler2D

hint_default_white

Verwendet als Albedofarbe, Default ist undurchsichtig weiß.

sampler2D

hint_default_black

Verwendet als Albedofarbe, Default ist undurchsichtig schwarz.

sampler2D

hint_default_transparent

Verwendet als Albedofarbe, Default ist transparent schwarz.

sampler2D

hint_anisotropy

Verwendet als Flow Map, Default nach rechts.

sampler2D

hint_roughness[_r, _g, _b, _a, _normal, _gray]

Verwendet als Rauheitsbegrenzer beim Import (versucht, Specular Aliasing zu reduzieren). _normal ist eine Normal Map, die den Rauhigkeitsbegrenzer steuert, wobei die Rauhigkeit in Bereichen mit hochfrequenten Details zunimmt.

sampler2D

filter[_nearest, _linear][_mipmap][_anisotropic]

Aktiviert die angegebene Texturfilterung.

sampler2D

repeat[_enable, _disable]

Aktiviert die Texturwiederholung.

sampler2D

hint_screen_texture

Textur ist die Bildschirms-Textur.

sampler2D

hint_depth_texture

Textur ist die Tiefentextur.

sampler2D

hint_normal_roughness_texture

Textur ist die normale Rauheitstextur (nur in Forward+ unterstützt).

Using hint_enum

You can access int values as a readable dropdown widget using the hint_enum uniform:

uniform int noise_type : hint_enum("OpenSimplex2", "Cellular", "Perlin", "Value") = 0;

You can assign explicit values to the hint_enum uniform using colon syntax similar to GDScript:

uniform int character_speed: hint_enum("Slow:30", "Average:60", "Very Fast:200") = 60;

The value will be stored as an integer, corresponding to the index of the selected option (i.e. 0, 1, or 2) or the value assigned by colon syntax (i.e. 30, 60, or 200). When setting the value with set_shader_parameter(), you must use the integer value, not the String name.

Using source_color

Any texture which contains sRGB color data requires a source_color hint in order to be correctly sampled. This is because Godot renders in linear color space, but some textures contain sRGB color data. If this hint is not used, the texture will appear washed out.

Albedo and color textures should typically have a source_color hint. Normal, roughness, metallic, and height textures typically do not need a source_color hint.

Using source_color hint is required in the Forward+ and Mobile renderers, and in canvas_item shaders when HDR 2D is enabled. The source_color hint is optional for the Compatibility renderer, and for canvas_item shaders if HDR 2D is disabled. However, it is recommended to always use the source_color hint, because it works even if you change renderers or disable HDR 2D.

Uniform groups

To group multiple uniforms in a section in the inspector, you can use a group_uniform keyword like this:

group_uniforms MyGroup;
uniform sampler2D test;

Sie können die Gruppe so schließen:

group_uniforms;

Die Syntax unterstützt auch Untergruppen (es ist nicht zwingend erforderlich, die Basisgruppe vorher zu deklarieren):

group_uniforms MyGroup.MySubgroup;

Globale Uniforms

Manchmal möchte man einen Parameter in vielen verschiedenen Shadern auf einmal ändern. Mit einem regulären Uniform erfordert dies eine Menge Arbeit, da all diese Shader getrackt werden müssen und das Uniform für jeden von ihnen eingestellt werden muss. Globale Uniforms erlauben es Ihnen, Uniforms zu erstellen und zu aktualisieren, die in allen Shadern, in jedem Shadertyp (canvas_item, spatial, particles, sky und fog) verfügbar sind.

Globale Uniforms sind besonders nützlich für Umgebungseffekte, die sich auf viele Objekte in einer Szene auswirken, z.B. wenn sich das Laub biegt, wenn der Spieler in der Nähe ist, oder wenn sich Objekte mit dem Wind bewegen.

Bemerkung

Global uniforms are not the same as global scope for an individual shader. While regular uniforms are defined outside of shader functions and are therefore the global scope of the shader, global uniforms are global to all shaders in the entire project (but within each shader, are also in the global scope).

Um ein globales Uniform zu erstellen, öffnen Sie die Projekteinstellungen und wechseln Sie zum Shader-Globals-Tab. Geben Sie einen Namen für den Uniform (Groß- und Kleinschreibung beachten) und einen Typ an und klicken Sie dann auf Hinzufügen in der oberen rechten Ecke des Dialogs. Sie können dann den dem Uniform zugewiesenen Wert bearbeiten, indem Sie auf den Wert in der Liste der Uniforms klicken:

Hinzufügen eines globalen Uniforms auf dem Shader-Globals-Tab in den Projekteinstellungen

Hinzufügen eines globalen Uniforms auf dem Shader-Globals-Tab in den Projekteinstellungen

Nachdem Sie ein globales Uniform erstellt haben, können Sie es wie folgt in einem Shader verwenden:

shader_type canvas_item;

global uniform vec4 my_color;

void fragment() {
    COLOR = my_color.rgb;
}

Beachten Sie, dass das globale Uniform zum Zeitpunkt des Speicherns des Shaders in den Projekteinstellungen vorhanden sein muss, da sonst die Kompilierung fehlschlägt. Sie können zwar im Shader-Code mit global uniform vec4 my_color = ... einen Default-Wert zuweisen, dieser wird jedoch ignoriert, da das globale Uniform ohnehin immer in den Projekteinstellungen definiert sein muss.

To change the value of a global uniform at runtime, use the RenderingServer.global_shader_parameter_set method in a script:

RenderingServer.global_shader_parameter_set("my_color", Color(0.3, 0.6, 1.0))

Die Zuweisung globaler Unform-Werte kann beliebig oft erfolgen, ohne die Performance zu beeinträchtigen, da das Setzen von Daten keine Synchronisierung zwischen CPU und GPU erfordert.

You can also add or remove global uniforms at runtime:

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 runtime has a performance cost, although it's not as pronounced compared to getting global uniform values from a script (see the warning below).

Warnung

While you can query the value of a global uniform at runtime 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.

Daher ist es nicht empfehlenswert, globale Shader-Uniform-Werte kontinuierlich in einem Skript zu lesen. Wenn Sie Werte in einem Skript lesen müssen, nachdem Sie sie gesetzt haben, sollten Sie ein Autoload erstellen, in dem Sie die Werte, die Sie abfragen müssen, zur gleichen Zeit speichern, in der Sie sie als globale Uniforms setzen.

Pro-Instanz-Uniforms

Bemerkung

Per-instance uniforms are available in both canvas_item (2D) and spatial (3D) shaders.

Manchmal möchte man einen Parameter an jedem Node mit Hilfe des Materials ändern. Zum Beispiel in einem Wald voller Bäume, wenn Sie wollen, dass jeder Baum eine leicht unterschiedliche Farbe hat, die von Hand bearbeitet werden kann. Ohne instanzspezifische Uniforms muss für jeden Baum ein eigenes Material erstellt werden (jedes mit einem leicht unterschiedlichen Farbton). Dies macht die Materialverwaltung komplexer und hat auch einen Performance-Overhead, da die Szene mehr einzelne Materialinstanzen benötigt. Vertex-Farben könnten auch hier verwendet werden, aber sie würden die Erstellung einzigartiger Kopien des Meshes für jede unterschiedliche Farbe erfordern, was auch einen Performance-Overhead hat.

Pro-Instanz-Uniforms werden für jede GeometryInstance3D statt für jede Materialinstanz festgelegt. Berücksichtigen Sie dies bei der Arbeit mit Meshes, denen mehrere Materialien zugewiesen sind, oder bei 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;
}

Nachdem Sie den Shader gespeichert haben, können Sie den Wert des Instanzuniforms mit dem Inspektor ändern:

Einstellen des Wertes eines Pro-Instanz-Uniforms im GeometryInstance3D-Abschnitt des Inspektors

Einstellen des Wertes eines Pro-Instanz-Uniforms im GeometryInstance3D-Abschnitt des Inspektors

Per-instance uniform values can also be set at runtime using set_instance_shader_parameter method on a node that inherits from GeometryInstance3D:

$MeshInstance3D.set_instance_shader_parameter("my_color", Color(0.3, 0.6, 1.0))

Bei der Verwendung Pro-Instanz-Uniforms gibt es einige Einschränkungen, die Sie beachten sollten:

  • Pro-Instanz-Uniforms unterstützen keine Texturen, nur reguläre Skalar- und Vektortypen. Als Workaround können Sie ein Textur-Array als reguläres Uniform übergeben und dann den Index der zu zeichnenden Textur mit einem Pro-Instance-Uniform übergeben.

  • Es gibt eine praktische Höchstgrenze von 16 Instanz-Uniforms pro Shader.

  • Wenn Ihr Mesh mehrere Materialien verwendet, haben die Parameter für das erste gefundene Mesh-Material Vorrang vor den nachfolgenden, es sei denn, sie haben denselben Namen, Index und Typ. In diesem Fall werden alle Parameter korrekt berücksichtigt.

  • Wenn Sie in die oben beschriebene Situation geraten, können Sie Konflikte vermeiden, indem Sie den Index (0-15) des Instanzuniforms mit Hilfe des instance_index-Hints manuell angeben:

instance uniform vec4 my_color : source_color, instance_index(5);

Setting uniforms from code

You can set uniforms from GDScript using the set_shader_parameter() method:

material.set_shader_parameter("some_value", some_value)

material.set_shader_parameter("colors", [Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)])

Bemerkung

The first argument to set_shader_parameter() is the name of the uniform in the shader. It must match exactly to the name of the uniform in the shader or else it will not be recognized.

GDScript verwendet andere Variablentypen als GLSL. Wenn Sie also Variablen von GDScript an Shader übergeben, konvertiert Godot den Typ automatisch. Unten finden Sie eine Tabelle der entsprechenden Typen:

GLSL-Typ

GDScript-Typ

Anmerkungen

bool

bool

bvec2

int

Bitweise gepackter int, wobei Bit 0 (LSB) dem Wert x entspricht.

Ein bvec2 von (bx, by) könnte zum Beispiel folgendermaßen erstellt werden:

bvec2_input: int = (int(bx)) | (int(by) << 1)

bvec3

int

Bitweise gepackter int, wobei Bit 0 (LSB) dem Wert x entspricht.

bvec4

int

Bitweise gepackter int, wobei Bit 0 (LSB) dem Wert x entspricht.

int

int

ivec2

Vector2i

ivec3

Vector3i

ivec4

Vector4i

uint

int

uvec2

Vector2i

uvec3

Vector3i

uvec4

Vector4i

float

float

vec2

Vector2

vec3

Vector3, Color

Wenn Farbe verwendet wird, wird sie als (r, g, b) interpretiert.

vec4

Vector4, Color, Rect2, Plane, Quaternion

Wenn Farbe verwendet wird, wird sie als (r, g, b, a) interpretiert.

Wenn Rect2 verwendet wird, wird es als (position.x, position.y, size.x, size.y) interpretiert.

Wenn Plane verwendet wird, wird es als (normal.x, normal.y, normal.z, d) interpretiert.

mat2

Transform2D

mat3

Basis

mat4

Projection, Transform3D

Wenn ein Transform3D verwendet wird, wird der w-Vektor auf den Einheitsvektor gesetzt.

sampler2D

Texture2D

isampler2D

Texture2D

usampler2D

Texture2D

sampler2DArray

Texture2DArray

isampler2DArray

Texture2DArray

usampler2DArray

Texture2DArray

sampler3D

Texture3D

isampler3D

Texture3D

usampler3D

Texture3D

samplerCube

Cubemap

See Ändern des Importtyps for instructions on importing cubemaps for use in Godot.

samplerCubeArray

CubemapArray

Only supported in Forward+ and Mobile, not Compatibility.

samplerExternalOES

ExternalTexture

Only supported in Compatibility/Android platform.

Bemerkung

Be careful when setting shader uniforms from GDScript, since no error will be thrown if the type does not match. Your shader will just exhibit undefined behavior. Specifically, this includes setting a GDScript int/float (64 bit) into a Godot shader language int/float (32 bit). This may lead to unintended consequences in cases where high precision is required.

Uniform limits

There is a limit to the total size of shader uniforms that you can use in a single shader. On most desktop platforms, this limit is 65536 bytes, or 4096 vec4 uniforms. On mobile platforms, the limit is typically 16384 bytes, or 1024 vec4 uniforms. Vector uniforms smaller than a vec4, such as vec2 or vec3, are padded to the size of a vec4. Scalar uniforms such as int or float are not padded, and bool is padded to the size of an int.

Arrays count as the total size of their contents. If you need a uniform array that is larger than this limit, consider packing the data into a texture instead, since the contents of a texture do not count towards this limit, only the size of the sampler uniform.

Built-in-Variablen

A large number of built-in variables are available, like UV, COLOR and VERTEX. What variables are available depends on the type of shader (spatial, canvas_item, particle, etc) and the function used (vertex, fragment, light, start, process, ``sky, or fog). For a list of the built-in variables that are available, please see the corresponding pages:

Built-in-Funktionen

A large number of built-in functions are supported, conforming to GLSL ES 3.0. See the Built-in functions page for details.