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
Usa i caratteri di avanzamento riga (LF) per separare le righe, non CRLF o CR. (predefinito dell'editor)
Usa un carattere di avanzamento riga alla fine di ogni file. (predefinito dell'editor)
Usa la codifica UTF-8 senza byte order mark. (predefinito dell'editor)
Usa 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);
}
Usa 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
Mantieni 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 usare 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 il snake_case per i nomi di funzioni e di variabili:
void some_function() {
float some_variable = 0.5;
}
Costanti
Scrivi le costanti in 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
Per formattare automaticamente i file shader, è possibile usare clang-format su uno o più file .gdshader, poiché la sintassi è così simile a un linguaggio in stile C.
Ciò nonostante, lo stile predefinito di clang-format non segue questa guida di stile, quindi è necessario salvare questo file come .clang-format nella cartella principale del progetto:
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
Dalla cartella principale del progetto, è quindi possibile eseguire clang-format -i percorso/al/file/shader.gdshader in un terminale per formattare un singolo file shader, oppure clang-format -i percorso/alla/cartella/*.gdshader per formattare tutti gli shader in una cartella.
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 rendere facile leggere il codice dall'alto verso il basso, per aiutare gli sviluppatori che leggono il codice per la prima volta a capirne come funziona 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
Dichiara 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.