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 |
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.
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 |
|---|---|
|
|
|
|
|
|
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). |
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
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
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.
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: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: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.