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...
Guida di stile per gli shader
Questa guida di stile elenca le convenzioni per scrivere shader eleganti. L'obiettivo è incoraggiare la scrittura di codice pulito e leggibile e promuovere la conformità tra progetti, discussioni e tutorial. Ci auguriamo che questa supporti anche lo sviluppo di strumenti di formattazione automatica.
Poiché il linguaggio di shader di Godot è simile ai linguaggi in stile C e a GLSL, questa guida è ispirata dalla formattazione GLSL propria di Godot. È possibile consultare esempi dei file GLSL nel codice sorgente di Godot qui.
Le guide di stile non sono intese come regole rigide. A volte, potresti non essere in grado di applicare alcune delle linee guida riportate di seguito. In tal caso, usa il tuo buon senso e chiedi consiglio ad altri sviluppatori.
In generale, è più importante mantenere consistente il codice nei progetti e tra il team piuttosto che seguire questa guida alla lettera.
Nota
Godot's built-in shader editor uses a lot of these conventions by default. Let it help you.
Ecco un esempio completo di shader basato su queste linee guida:
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;
}
Formattazione
Codifica e caratteri speciali
Utilizzare i caratteri di avanzamento riga (LF) per interrompere le righe, non CRLF o CR. (predefinito dell'editor)
Utilizzare un carattere di avanzamento riga alla fine di ogni file. (predefinito dell'editor)
Utilizzare la codifica UTF-8 senza byte order mark. (predefinito dell'editor)
Utilizzare Tabulazioni invece di spazi per l'indentazione. (predefinito dell'editor)
Indentazione
Ogni livello di indentazione dovrebbe essere più grande di una tabulazione rispetto al blocco che lo contiene.
Bene:
void fragment() {
COLOR = vec3(1.0, 1.0, 1.0);
}
Male:
void fragment() {
COLOR = vec3(1.0, 1.0, 1.0);
}
Utilizzare 2 livelli di indentazione per distinguere le righe di continuazione dai regolari blocchi di codice.
Bene:
vec2 st = vec2(
atan(NORMAL.x, NORMAL.z),
acos(NORMAL.y));
Male:
vec2 st = vec2(
atan(NORMAL.x, NORMAL.z),
acos(NORMAL.y));
Ritorni a capo e righe vuote
Come regola generale di indentazione, seguire lo stile 1TBS'' <https://en.wikipedia.org/wiki/Indentation_style#Variant:_1TBS_(OTBS)>`_, che raccomanda di posizionare la parentesi graffa associata a un'istruzione di controllo sulla stessa riga. Utilizzare sempre le parentesi graffe per le istruzioni, anche se coprono una sola riga. Questo ne facilita il refactoring ed evita errori quando si aggiungono più righe a un'istruzione ``if o simili.
Bene:
void fragment() {
if (true) {
// ...
}
}
Male:
void fragment()
{
if (true)
// ...
}
Righe vuote
Circonda le definizioni delle funzioni con una (e una sola) riga vuota:
void do_something() {
// ...
}
void fragment() {
// ...
}
Utilizzare una (e una sola) riga vuota all'interno delle funzioni per separare le sezioni logiche.
Lunghezza delle righe
Mantenere le singole righe di codice al di sotto di 100 caratteri.
Se possibile, cercare di mantenere le righe al di sotto degli 80 caratteri. Questo aiuta a leggere il codice su display di piccole dimensioni e con due shader aperti uno accanto all'altro in un editor di testo esterno. Ad esempio, quando si esamina una revisione differenziale.
Una sola istruzione per riga
Non combinare mai molteplici istruzioni sulla stessa riga.
Bene:
void fragment() {
ALBEDO = vec3(1.0);
EMISSION = vec3(1.0);
}
Male:
void fragment() {
ALBEDO = vec3(1.0); EMISSION = vec3(1.0);
}
L'unica eccezione a questa regola è l'operatore ternario:
void fragment() {
bool should_be_white = true;
ALBEDO = should_be_white ? vec3(1.0) : vec3(0.0);
}
Commenti di documentazione
Utilizzare il seguente formato per i commenti di documentazione sopra le uniformi, con due asterischi iniziali (/**) e asterischi successivi su ogni riga:
/**
* 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;
Questi commenti appariranno passando il cursore su una proprietà nell'ispettore. Se non si desidera che il commento sia visibile nell'ispettore, utilizzare la regolare sintassi per i commenti (// ... o /* ... */ con un solo asterisco iniziale).
Spazio vuoto
Utilizzare sempre uno spazio tra gli operatori e dopo le virgole. Inoltre, evitare spazi superflui nelle chiamate di funzioni.
Bene:
COLOR.r = 5.0;
COLOR.r = COLOR.g + 0.1;
COLOR.b = some_function(1.0, 2.0);
Male:
COLOR.r=5.0;
COLOR.r = COLOR.g+0.1;
COLOR.b = some_function (1.0,2.0);
Non utilizzare spazi per allineare le espressioni verticalmente:
ALBEDO.r = 1.0;
EMISSION.r = 1.0;
Numeri a virgola mobile
Specificare sempre almeno una cifra sia per la parte intera sia per la parte frazionaria. Ciò aiuta a distinguere tra numeri a virgola mobile e interi, nonché tra numeri maggiori di 1 e minori di 1.
Bene:
void fragment() {
ALBEDO.rgb = vec3(5.0, 0.1, 0.2);
}
Male:
void fragment() {
ALBEDO.rgb = vec3(5., .1, .2);
}
Accesso ai membri di un vettore
Utilizzare r, g, b e a per accedere ai membri di un vettore se contiene un colore. Se il vettore contiene qualsiasi altra cosa, utilizzare x, y, z e w. Ciò permette a chi legge il codice di comprendere meglio cosa rappresentano i dati sottostanti.
Bene:
COLOR.rgb = vec3(5.0, 0.1, 0.2);
Male:
COLOR.xyz = vec3(5.0, 0.1, 0.2);
Convenzioni di denominazione
Queste convenzioni di denominazione seguono lo stile del Godot Engine. Se non rispettate, il proprio codice andrà in conflitto con le convenzioni integrate, portando a codice non conforme.
Funzioni e variabili
Utilizzare snake_case per denominare funzioni e variabili:
void some_function() {
float some_variable = 0.5;
}
Costanti
Scrivere le costanti con CONSTANT_CASE, vale a dire in maiuscolo con un trattino basso (_) per separare le parole:
const float GOLDEN_RATIO = 1.618;
Direttive del preprocessore
Le direttive Preprocessore di shader si devono scrivere in CONSTANT_CASE. Le direttive si devono scrivere senza alcuna indentazione prima di esse, anche se annidate all'interno di una funzione.
Per preservare il flusso naturale di indentazione quando gli errori di shader sono stampati sulla console, non si dovrebbe aggiungere un'indentazione aggiuntiva nei blocchi #if, #ifdef o #ifndef:
Bene:
#define HEIGHTMAP_ENABLED
void fragment() {
vec2 position = vec2(1.0, 2.0);
#ifdef HEIGHTMAP_ENABLED
sample_heightmap(position);
#endif
}
Male:
#define heightmap_enabled
void fragment() {
vec2 position = vec2(1.0, 2.0);
#ifdef heightmap_enabled
sample_heightmap(position);
#endif
}
Applying formatting automatically
To automatically format shader files, you can use
clang-format on one or several
.gdshader files, as the syntax is close enough to a C-style language.
However, the default style in clang-format doesn't follow this style guide,
so you need to save this file as .clang-format in your project's root folder:
BasedOnStyle: LLVM
AlignAfterOpenBracket: DontAlign
AlignOperands: DontAlign
AlignTrailingComments:
Kind: Never
OverEmptyLines: 0
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortFunctionsOnASingleLine: Inline
BreakConstructorInitializers: AfterColon
ColumnLimit: 0
ContinuationIndentWidth: 8
IndentCaseLabels: true
IndentWidth: 4
InsertBraces: true
KeepEmptyLinesAtTheStartOfBlocks: false
RemoveSemicolon: true
SpacesInLineCommentPrefix:
Minimum: 0 # We want a minimum of 1 for comments, but allow 0 for disabled code.
Maximum: -1
TabWidth: 4
UseTab: Always
While in the project root, you can then call clang-format -i path/to/shader.gdshader
in a terminal to format a single shader file, or clang-format -i path/to/folder/*.gdshader
to format all shaders in a folder.
Ordine del codice
Suggeriamo di organizzare il codice degli shader in questo modo:
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
Abbiamo ottimizzato l'ordine per semplificare la lettura del codice dall'alto verso il basso, per aiutare gli sviluppatori che leggono il codice per la prima volta a capirne il funzionamento e per evitare errori riguardanti l'ordine di dichiarazione delle variabili.
Questo ordine del codice segue due regole generali:
Prima i metadati e le proprietà, poi i metodi.
"Pubblico" viene prima di "privato". Nel contesto di un linguaggio per shader, "pubblico" si riferisce a ciò che è facilmente regolabile dall'utente (le uniformi).
Variabili locali
Dichiarare le variabili locali il più vicino possibile al loro primo utilizzo. Questo rende più facile seguire il codice, senza dover scorrere troppo per trovare il punto in cui viene dichiarata la variabile.
Spaziatura dei commenti
I commenti normali dovrebbero iniziare con uno spazio, ma non con il codice che si disabilita. Questo aiuta a distinguere i commenti di testo dal codice disabilitato.
Bene:
Male:
Non utilizzare la sintassi per commenti su più righe se il commento può essere inserito in una sola riga:
/* This is another comment. */Nota
Nell'editor di shader, per commentare il codice selezionato (o de-commentarlo), premere Ctrl + K. Questa funzione aggiunge o rimuove
//all'inizio delle righe selezionate.