Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Shader Style-Guide
Dieser Style Guide listet Konventionen zum Schreiben eleganter Shader auf. Er soll zu sauberem, lesbarem Code ermutigen und Konsistenz in Projekten, Diskussionen und Anleitungen fördern. Dies wird hoffentlich auch zur Entwicklung automatisierter Formatierungswerkzeuge beitragen.
Da die Godot-Shader-Sprache den Sprachen im C-Stil und GLSL sehr nahe steht, ist diese Anleitung von der Godot-eigenen GLSL-Formatierung inspiriert. Beispiele für eine GLSL-Datei finden Sie im Quellcode von Godot hier.
Style Guides sind nicht als dogmatisch einzuhaltende Regelwerke zu verstehen. Einige der folgenden Richtlinien sind möglicherweise nicht stets anwendbar. In diesem Fall sollte nach bestem Wissen und Gewissen gehandelt und ggf. Erfahrungswerte bei anderen Entwicklern eingeholt werden.
Im Allgemeinen ist es wichtiger, Ihren Code in Ihren Projekten und in Ihrem Team konsistent zu halten, als dieser Anleitung blind zu folgen.
Bemerkung
Der in Godot integrierte Shader-Editor verwendet standardmäßig viele dieser Konventionen. Nutzen Sie ihn.
Hier ist ein vollständiges Shader-Beispiel, das auf diesen Richtlinien basiert:
shader_type canvas_item;
// Screen-space shader to adjust a 2D scene's brightness, contrast
// and saturation. Taken from
// https://github.com/godotengine/godot-demo-projects/blob/master/2d/screen_space_shaders/shaders/BCS.gdshader
uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap;
uniform float brightness = 0.8;
uniform float contrast = 1.5;
uniform float saturation = 1.8;
void fragment() {
vec3 c = textureLod(screen_texture, SCREEN_UV, 0.0).rgb;
c.rgb = mix(vec3(0.0), c.rgb, brightness);
c.rgb = mix(vec3(0.5), c.rgb, contrast);
c.rgb = mix(vec3(dot(vec3(1.0), c.rgb) * 0.33333), c.rgb, saturation);
COLOR.rgb = c;
}
Formatierung
Encoding und Sonderzeichen
Verwenden Sie Line Feed (LF) für Zeilenumbrüche, nicht CRLF oder CR. (Default im Editor)
Verwenden Sie ein Line Feed am Ende jeder Datei. (Default im Editor)
Verwenden Sie die UTF-8-Encoding ohne ein byte order mark. (Default im Editor)
Verwenden Sie Tabs anstelle von Leerzeichen für Einrückungen. (Default im Editor)
Einrückung
Jede Einrückungsstufe sollte um eins größer sein als die des umgebenden Blocks.
Gut:
void fragment() {
COLOR = vec3(1.0, 1.0, 1.0);
}
Schlecht:
void fragment() {
COLOR = vec3(1.0, 1.0, 1.0);
}
Verwenden Sie 2 Einrückungsebenen, um Fortsetzungszeilen von normalen Codeblöcken zu unterscheiden.
Gut:
vec2 st = vec2(
atan(NORMAL.x, NORMAL.z),
acos(NORMAL.y));
Schlecht:
vec2 st = vec2(
atan(NORMAL.x, NORMAL.z),
acos(NORMAL.y));
Zeilenumbrüche und Leerzeilen
Als allgemeine Einrückungsregel gilt der "1TBS-Style", der empfiehlt, die zu einer Steueranweisung gehörende geschweifte Klammer auf derselben Zeile zu platzieren. Verwenden Sie für Anweisungen immer geschweifte Klammern, auch wenn sie sich nur über eine Zeile erstrecken. Dies erleichtert das Refactoring und vermeidet Fehler beim Hinzufügen weiterer Zeilen zu einer if
-Anweisung oder ähnlichem.
Gut:
void fragment() {
if (true) {
// ...
}
}
Schlecht:
void fragment()
{
if (true)
// ...
}
Leerzeilen
Vor und nach einer Funktion bzw. Klassendefinition sollte eine Leerzeile stehen (und nur eine):
void do_something() {
// ...
}
void fragment() {
// ...
}
Verwenden Sie eine (und nur eine) Leerzeile innerhalb von Funktionen, um logische Abschnitte zu trennen.
Zeilenlänge
Halten Sie einzelne Codezeilen unter 100 Zeichen.
Falls möglich, versuchen Sie Zeilen unter 80 Zeichen zu halten. Dies hilft beim Lesen des Codes auf kleinen Displays und mit zwei nebeneinander geöffneten Shadern in einem externen Texteditor. Zum Beispiel bei der Betrachtung einer Unterschieds-Revision.
Eine Anweisung pro Zeile
Kombinieren Sie niemals mehrere Anweisungen in einer einzigen Zeile.
Gut:
void fragment() {
ALBEDO = vec3(1.0);
EMISSION = vec3(1.0);
}
Schlecht:
void fragment() {
ALBEDO = vec3(1.0); EMISSION = vec3(1.0);
}
Die einzige Ausnahme dieser Regel ist der tenäre Operator:
void fragment() {
bool should_be_white = true;
ALBEDO = should_be_white ? vec3(1.0) : vec3(0.0);
}
Dokumentations-Kommentare
Verwenden Sie das folgende Format für Dokumentationskommentare über Uniforms, mit zwei führenden Sternchen (/**
) und nachfolgenden Sternchen in jeder Zeile:
/**
* 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;
Diese Kommentare werden angezeigt, wenn Sie den Mauszeiger über eine Property im Inspektor bewegen. Wenn Sie nicht möchten, dass der Kommentar im Inspektor sichtbar ist, verwenden Sie stattdessen die Standard-Kommentarsyntax (// ...
oder /* ... */
mit nur einem führenden Sternchen).
Leerzeichen
Setzen Sie immer ein Leerzeichen, um Operatoren herum und nach Kommas. Vermeiden Sie auch zusätzliche Leerzeichen in Funktionsaufrufen.
Gut:
COLOR.r = 5.0;
COLOR.r = COLOR.g + 0.1;
COLOR.b = some_function(1.0, 2.0);
Schlecht:
COLOR.r=5.0;
COLOR.r = COLOR.g+0.1;
COLOR.b = some_function (1.0,2.0);
Verwenden Sie keine Leerzeichen, um Ausdrücke vertikal auszurichten:
ALBEDO.r = 1.0;
EMISSION.r = 1.0;
Float-Zahlen
Geben Sie immer mindestens eine Ziffer für den Vorkommateil und den Nachkommateil an. Dies erleichtert die Unterscheidung zwischen Float-Zahlen und Integer-Zahlen, sowie die Unterscheidung von Zahlen größer als 1 von Zahlen kleiner als 1.
Gut:
void fragment() {
ALBEDO.rgb = vec3(5.0, 0.1, 0.2);
}
Schlecht:
void fragment() {
ALBEDO.rgb = vec3(5., .1, .2);
}
Zugriff auf Vektorelemente
Verwenden Sie r
, g
, b
und a
beim Zugriff auf die Member eines Vektors, wenn dieser eine Farbe enthält. Wenn der Vektor etwas anderes als eine Farbe enthält, verwenden Sie x
, y
, z
und w
. Dadurch können diejenigen, die Ihren Code lesen, besser verstehen, was die zugrunde liegenden Daten darstellen.
Gut:
COLOR.rgb = vec3(5.0, 0.1, 0.2);
Schlecht:
COLOR.xyz = vec3(5.0, 0.1, 0.2);
Namenskonventionen
Diese Namenskonventionen folgen dem Godot Engine-Stil. Bei Nichtbeachtung würde Ihr Code mit den Built-in-Namenskonventionen kollidieren, was zu inkonsistentem Code führt.
Funktionen und Variablen
Verwenden Sie snake_case, um Funktionen und Variablen zu benennen:
void some_function() {
float some_variable = 0.5;
}
Konstanten
Schreiben Sie Konstanten mit CONSTANT_CASE, d.h. in Großbuchstaben mit einem Unterstrich (_) zur Trennung der Wörter:
const float GOLDEN_RATIO = 1.618;
Präprozessor-Direktiven
Shader-Präprozessor-Direktiven sollten in CONSTANT__CASE geschrieben werden. Direktiven sollten ohne Einrückung vor ihnen geschrieben werden, auch wenn sie in einer Funktion verschachtelt sind.
Um den natürlichen Fluss der Einrückung zu erhalten, wenn Shader-Fehler auf der Konsole ausgegeben werden, sollte keine zusätzliche Einrückung innerhalb von #if
, #ifdef
oder #ifndef
Blöcken hinzugefügt werden:
Gut:
#define HEIGHTMAP_ENABLED
void fragment() {
vec2 position = vec2(1.0, 2.0);
#ifdef HEIGHTMAP_ENABLED
sample_heightmap(position);
#endif
}
Schlecht:
#define heightmap_enabled
void fragment() {
vec2 position = vec2(1.0, 2.0);
#ifdef heightmap_enabled
sample_heightmap(position);
#endif
}
Code-Reihenfolge
Wir empfehlen, Shader-Code auf diese Weise aufzubauen:
01. shader type declaration
02. render mode declaration
03. // docstring
04. uniforms
05. constants
06. varyings
07. other functions
08. vertex() function
09. fragment() function
10. light() function
Wir haben die Reihenfolge optimiert, um das Lesen des Codes von oben nach unten zu vereinfachen, Entwicklern das erste Lesen des Codes zu erleichtern und Fehler im Zusammenhang mit der Reihenfolge der Variablendeklarationen zu vermeiden.
Diese Code-Reihenfolge folgt zwei Faustregeln:
Zuerst Metadaten und Propertys, gefolgt von Methoden.
"Public" steht vor "private". Im Kontext einer Shader-Sprache bezieht sich "public" auf das, was vom Benutzer leicht angepasst werden kann (Uniforms).
Lokale Variablen
Deklarieren Sie lokale Variablen so nah wie möglich am Ort ihrer ersten Verwendung. Dies erleichtert das Lesen des Codes, ohne zu viel scrollen zu müssen, um herauszufinden, wo die Variable deklariert wurde.
Leerzeichen bei Kommentaren
Reguläre Kommentare sollten mit einem Leerzeichen beginnen, nicht aber auskommentierter Code. Dies hilft, Kommentare von inaktivem Code zu unterscheiden.
Gut:
Schlecht:
Verwenden Sie keine mehrzeilige Kommentarsyntax, wenn Ihr Kommentar in eine einzelne Zeile passt:
/* This is another comment. */
Bemerkung
Drücken Sie im Shader-Editor Strg + K um den ausgewählten Code zu einem Kommentar zu machen (oder den Kommentar zu entfernen). Diese Funktion fügt am Anfang der ausgewählten Zeilen
//
hinzu oder entfernt sie.