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.
Since the Godot shader language is close to C-style languages and GLSL, this guide is inspired by Godot's own GLSL formatting. You can view examples of GLSL files in Godot's source code here.
Style guides aren't meant as hard rulebooks. At times, you may not be able to apply some of the guidelines below. When that happens, use your best judgment, and ask fellow developers for insights.
In general, keeping your code consistent in your projects and within your team is more important than following this guide to a tee.
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));
Line breaks and blank lines
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
}
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.