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...
Linguaggio di shading
Introduzione
Godot utilizza un linguaggio di shading simile a GLSL ES 3.0. La maggior parte dei tipi di dati e delle funzioni sono supportati, e i pochi rimanenti verranno probabilmente aggiunti col tempo.
Se si è già familiari con GLSL, la Guida alla migrazione degli shader di Godot è una risorsa che aiuterà a passare dal normale GLSL al linguaggio di shading di Godot.
Tipi di dati
Sono supportati la maggior parte dei tipi di dati in GLSL ES 3.0:
Tipo |
Descrizione |
|---|---|
void |
Tipo di dati void, utile solo per funzioni che non restituiscono nulla. |
bool |
Tipo di dati booleano, può contenere solo |
bvec2 |
Vettore a due componenti booleani. |
bvec3 |
Vettore a tre componenti booleani. |
bvec4 |
Vettore a quattro componenti booleani. |
int |
Intero scalare con segno a 32 bit. |
ivec2 |
Vettore a due componenti interi con segno. |
ivec3 |
Vettore a tre componenti interi con segno. |
ivec4 |
Vettore a quattro componenti interi con segno. |
uint |
Intero scalare senza segno; non può contenere numeri negativi. |
uvec2 |
Vettore a due componenti interi senza segno. |
uvec3 |
Vettore a tre componenti interi senza segno. |
uvec4 |
Vettore a quattro componenti interi senza segno. |
float |
Numero scalare in virgola mobile a 32 bit. |
vec2 |
Vettore a due componenti in virgola mobile. |
vec3 |
Vettore a tre componenti in virgola mobile. |
vec4 |
Vettore a quattro componenti in virgola mobile. |
mat2 |
Matrice 2x2, in ordine di colonna maggiore. |
mat3 |
Matrice 3x3, in ordine di colonna maggiore. |
mat4 |
Matrice 4x4, in ordine di colonna maggiore. |
sampler2D |
Tipo di campionatore per associare texture 2D, lette come valori in virgola mobile. |
isampler2D |
Tipo di campionatore per associare texture 2D, lette come valori interi con segno. |
usampler2D |
Tipo di campionatore per associare texture 2D, lette come valori interi senza segno. |
sampler2DArray |
Tipo di campionatore per associare array di texture 2D, lette come valori in virgola mobile. |
isampler2DArray |
Tipo di campionatore per associare array di texture 2D, lette come valori interi con segno. |
usampler2DArray |
Tipo di campionatore per associare array di texture 2D, lette come valori interi senza segno. |
sampler3D |
Tipo di campionatore per associare texture 3D, lette come valori in virgola mobile. |
isampler3D |
Tipo di campionatore per associare texture 3D, lette come valori interi con segno. |
usampler3D |
Tipo di campionatore per associare texture 3D, lette come valori interi senza segno. |
samplerCube |
Tipo di campionatore per associare cubemap, lette come valori in virgola mobile. |
samplerCubeArray |
Tipo di campionatore per associare array di cubemap, lette come valori in virgola mobile. Supportato solo su Forward+ e Mobile, non Compatibilità. |
samplerExternalOES |
Tipo di campionatore esterno. Supportato solo sulla piattaforma Compatibilità/Android. |
È possibile anche inserire questi tipi all'interno di array o struct, che sono anch'essi utilizzabili come parametri di funzione o valori di ritorno. Gli array si possono usare come uniformi, ma le struct no.
Avvertimento
Le variabili locali non sono inizializzate a un valore predefinito come 0.0. Se si utilizza una variabile senza prima assegnarla, conterrà qualsiasi valore già presente in quell'area di memoria e si verificheranno imprevedibili anomalie visive. Tuttavia, le uniformi e i varying sono inizializzati a un valore predefinito.
Conversione del tipo
Proprio come GLSL ES 3.0, le conversioni (cast) implicite tra scalari e vettori della stessa dimensione ma di tipo diverso non sono consentite. Anche la conversione di tipi di dimensioni diverse non è consentita. Le conversioni si devono effettuare esplicitamente tramite costruttori.
Esempio:
float a = 2; // invalid
float a = 2.0; // valid
float a = float(2); // valid
Le costanti intere predefinite sono con segno, quindi è sempre necessario un cast per convertirle in interi senza segno:
int a = 2; // valid
uint a = 2; // invalid
uint a = uint(2); // valid
Membri
I singoli membri scalari dei tipi di vettori sono accessibili tramite i membri "x", "y", "z" e "w". Alternativamente, anche l'uso di "r", "g", "b" e "a" funziona ed è equivalente. Utilizzare quello che più si adatta alle proprie esigenze.
Per le matrici, utilizzare la sintassi di indicizzazione m[colonna][riga] per accedere a ciascun scalare, oppure m[colonna] per accedere a un vettore tramite l'indice di colonna. Ad esempio, per accedere alla componente y della traslazione da una matrice di trasformazione mat4 (4ª colonna, 2ª riga) si utilizza m[3][1] o m[3].y.
Costruzione
La costruzione dei tipi di vettori deve sempre passare:
// 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);
La costruzione di tipi di matrice richiede vettori della stessa dimensione della matrice, interpretati come colonne. È anche possibile costruire una matrice diagonale usando la sintassi matx(float). Di conseguenza, mat4(1.0) è una matrice di identità.
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);
Le matrici si possono anche costruire da una matrice di un'altra dimensione. Esistono due regole:
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. 1. 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
È possibile ottenere qualsiasi combinazione di componenti in qualsiasi ordine, purché il risultato sia un altro tipo di vettore (o scalare). Ciò è più facile da dimostrare che da spiegare:
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.
Precisione
È possibile aggiungere modificatori di precisione ai tipi di dati; utilizzarli per uniformi, variabili, argomenti e varying:
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)
L'utilizzo di una precisione inferiore per alcune operazioni può velocizzare i calcoli (a costo di una minore precisione). Questo è raramente necessario nella funzione di elaborazione dei vertici (dove la precisione completa è necessaria nella maggior parte dei casi), ma è spesso utile nell'elaborazione dei frammenti.
Alcune architetture (principalmente quelle mobili) possono beneficiarne notevolmente, ma ci sono inconvenienti, tra i quali il sovraccarico aggiuntivo dovuto alla conversione tra le precisioni. Per ulteriori informazioni, consultare la documentazione dell'architettura di destinazione. In molti casi, i driver mobili causano comportamenti incoerenti o imprevisti ed è preferibile evitare di specificare la precisione a meno che non sia necessario.
Array
Gli array sono contenitori per più variabili di un tipo simile.
Array locali
Gli array locali sono dichiarati nelle funzioni. Possono utilizzare tutti i tipi di dati consentiti, ad eccezione dei campionatori. La dichiarazione degli array segue una sintassi in stile C: [const] + [precisione] + nome tipo + identificatore + [dimensione array].
void fragment() {
float arr[3];
}
Possono essere inizializzati all'inizio come:
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
È possibile dichiarare più array (anche con dimensioni diverse) in un'unica espressione:
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];
Per accedere a un elemento di un array, utilizzare la sintassi di indicizzazione:
float arr[3];
arr[0] = 1.0; // setter
COLOR.r = arr[0]; // getter
Gli array hanno anche una funzione integrata .length() (da non confondere con la funzione integrata length()). Non accetta parametri e restituisce la dimensione dell'array.
float arr[] = { 0.0, 1.0, 0.5, -1.0 };
for (int i = 0; i < arr.length(); i++) {
// ...
}
Nota
Se si utilizza un indice inferiore a 0 o superiore alla dimensione di un array, lo shader si bloccherà e interromperà il rendering. Per evitare ciò, utilizzare le funzioni length(), if o clamp() per assicurarsi che l'indice sia compreso tra 0 e la lunghezza dell'array. Testare e verificare sempre attentamente il codice. Se si passa un'espressione costante o un numero, l'editor ne verificherà i limiti per evitare un arresto anomalo.
Array globali
È possibile dichiarare gli array nello spazio globale come const o 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];
}
Nota
Gli array globali utilizzano la stessa sintassi degli array locali, con l'aggiunta di const o uniform alla loro dichiarazione. Si noti che gli array uniformi non possono avere un valore predefinito.
Costanti
Utilizzare la parola chiave const prima della dichiarazione della variabile per renderla immutabile, ovvero non modificabile. Tutti i tipi base, ad eccezione dei campionatori, possono essere dichiarati come costanti. L'accesso e l'utilizzo di un valore costante sono leggermente più rapidi rispetto all'utilizzo di un valore uniforme. Le costanti devono essere inizializzate al momento della dichiarazione.
const vec2 a = vec2(0.0, 1.0);
vec2 b;
a = b; // invalid
b = a; // valid
Le costanti non possono essere modificate e inoltre non possono avere suggerimenti, ma più di esse (se hanno lo stesso tipo) possono essere dichiarate in una singola espressione, ad esempio
const vec2 V1 = vec2(1, 1), V2 = vec2(2, 2);
Similmente alle variabili, anche gli array possono essere dichiarati con const.
const float arr[] = { 1.0, 0.5, 0.0 };
arr[0] = 1.0; // invalid
COLOR.r = arr[0]; // valid
Le costanti possono essere dichiarate sia globalmente (all'esterno di qualsiasi funzione) sia localmente (all'interno di una funzione). Le costanti globali sono utili quando si desidera avere accesso a un valore in tutto lo shader che non necessita di essere modificato. Come le costanti uniformi, le costanti globali sono condivise tra tutte le fasi dello shader, ma non sono accessibili all'esterno dello shader.
shader_type spatial;
const float GOLDEN_RATIO = 1.618033988749894;
Le costanti di tipo float devono essere inizializzate utilizzando la notazione . dopo la parte decimale o la notazione scientifica. Il suffisso f facoltativo è anche supportato.
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
Le costanti di tipo uint (intero senza segno) devono avere il suffisso u per distinguerle dagli interi con segno. Alternativamente, è possibile utilizzare la funzione di conversione integrata uint(x).
uint a = 1u;
uint b = uint(1);
Struct
Le struct sono tipi composti che si possono utilizzare per una migliore astrazione del codice di shader. È possibile dichiararli a livello globale, ad esempio:
struct PointLight {
vec3 position;
vec3 color;
float intensity;
};
Dopo la dichiarazione, è possibile istanziarle e inizializzarle come segue:
void fragment()
{
PointLight light;
light.position = vec3(0.0);
light.color = vec3(1.0, 0.0, 0.0);
light.intensity = 0.5;
}
Oppure utilizzare il costruttore di struct per lo stesso scopo:
PointLight light = PointLight(vec3(0.0), vec3(1.0, 0.0, 0.0), 0.5);
Le struct possono contenere altre struct o array, è anche possibile istanziarle come costanti globali:
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;
}
È anche possibile passarle alle funzioni:
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;
}
Operatori
Il linguaggio di shading di Godot supporta lo stesso insieme di operatori di GLSL ES 3.0. Di seguito ne è riportato l'elenco in ordine di precedenza:
Precedenza |
Classe |
Operatore |
1 (la più alta) |
raggruppamento tra parentesi |
() |
2 |
unario |
+, -, !, ~ |
3 |
moltiplicativo |
/, *, % |
4 |
additivo |
+, - |
5 |
spostamento di bit |
<<, >> |
6 |
relazionale |
<, >, <=, >= |
7 |
uguaglianza |
==, != |
8 |
AND bit a bit |
& |
9 |
OR esclusivo bit a bit |
^ |
10 |
OR inclusivo bit a bit |
| |
11 |
AND logico |
&& |
12 (la più bassa) |
OR inclusivo logico |
|| |
Nota
La maggior parte degli operatori che accettano vettori o matrici (moltiplicazione, divisione, ecc.) operano componente per componente, ovvero la funzione viene applicata al primo valore di ciascun vettore e poi al secondo valore di ciascun vettore, e così via. Alcuni esempi:
Operazione |
Operazione scalare equivalente |
|---|---|
|
|
|
|
|
|
La Specifica del linguaggio GLSL afferma nella sezione 5.10 Operazioni su vettori e matrici:
Salvo poche eccezioni, le operazioni sono eseguite componente per componente. Di solito, quando un operatore opera su un vettore o una matrice, opera indipendentemente su ciascuna componente del vettore o della matrice, in modo componente per componente. [...] Le eccezioni sono matrice moltiplicata per vettore, vettore moltiplicato per matrice e matrice moltiplicata per matrice. Queste operazioni non operano componente per componente, ma eseguono la corretta moltiplicazione algebrica lineare.
Controllo di flusso
Il linguaggio di shading di Godot supporta i tipi più comuni di controllo di flusso:
// `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);
Bisogna tenere presente che nelle GPU moderne può esistere un ciclo infinito che può bloccare l'applicazione (editor incluso). Godot non può proteggere da questo, quindi bisogna fare attenzione a non commettere questo errore!
Inoltre, quando si confrontano valori in virgola mobile con un numero, assicurarsi di confrontarli con un intervallo anziché con un numero esatto.
Un confronto come if (value == 0.3) potrebbe non restituire true. La matematica in virgola mobile è spesso approssimativa e può sfidare le aspettative. Può anche comportarsi in modo diverso a seconda dell'hardware.
Non fare così.
float value = 0.1 + 0.2;
// May not evaluate to `true`!
if (value == 0.3) {
// ...
}
Invece, esegui sempre un confronto di intervallo con un valore epsilon. Maggiore è il numero in virgola mobile (e meno preciso è il numero in virgola mobile), maggiore dovrebbe essere il valore epsilon.
const float EPSILON = 0.0001;
if (value >= 0.3 - EPSILON && value <= 0.3 + EPSILON) {
// ...
}
Consultare floating-point-gui.de per ulteriori informazioni.
Scartare
Le funzioni fragment, light e custom (chiamate da fragment o light) possono utilizzare la parola chiave discard. Se utilizzata, il frammento viene scartato e nulla viene scritto.
Attenzione, discard ha un impatto sulle prestazioni quando viene utilizzato, poiché impedirà al prepassaggio di profondità di essere efficace su qualsiasi superficie che utilizzi lo shader. Inoltre, un pixel scartato deve comunque essere renderizzato nello shader di vertici, il che significa che uno shader che usa discard su tutti i suoi pixel è comunque più costoso da renderizzare rispetto a non renderizzare alcun oggetto in primo luogo.
Funzioni
È possibile definire funzioni in uno shader di Godot. La sintassi utilizzata è la seguente:
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;
}
È possibile utilizzare solo funzioni definite sopra (più in alto nell'editor) la funzione da cui vengono richiamate. Ridefinire una funzione già definita sopra (o il cui nome è una funzione integrata) causerà un errore.
Gli argomenti delle funzioni possono avere qualificatori speciali:
in: significa che l'argomento è solo per la lettura (predefinito).
out: significa che l'argomento è solo per scrittura.
inout: significa che l'argomento è interamente passato per riferimento.
const: significa che l'argomento è una costante e non può essere modificato, può essere combinato con il qualificatore in.
Esempio qui sotto:
void sum2(int a, int b, inout int result) {
result = a + b;
}
L'overloading delle funzioni è supportato. È possibile definire più funzioni con lo stesso nome, ma argomenti diversi. Si noti che l'implicit casting nelle chiamate di funzioni sovraccaricate non è consentito, ad esempio da int a float (da 1 a 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);
}
Varying
Per inviare dati dalla funzione processore dei vertici a quella dei frammenti (o della luce), si utilizzano i varying. Questi sono impostati per ogni vertice primitivo nel processore dei vertici e il valore è interpolato per ogni pixel nel processore dei frammenti.
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
}
È possibile utilizzare un array come varying:
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
}
È anche possibile inviare dati dai processori fragment ai processi light attraverso la parola chiave varying. Per fare ciò, è possibile assegnarla a fragment e utilizzarla successivamente nella funzione light.
shader_type spatial;
varying vec3 some_light;
void fragment() {
some_light = ALBEDO * 100.0; // Make a shining light.
}
void light() {
DIFFUSE_LIGHT = some_light;
}
Si noti che il varying non può essere assegnato nelle funzioni personalizzate o in una funzione processore di luce come:
shader_type spatial;
varying float test;
void foo() {
test = 0.0; // Error.
}
void vertex() {
test = 0.0;
}
void light() {
test = 0.0; // Error too.
}
Questa limitazione è stata introdotta per impedire un uso incorretto prima dell'inizializzazione.
Qualificatori di interpolazione
Alcuni valori sono interpolati durante la pipeline di shading. È possibile modificare il modo in cui si effettuano queste interpolazioni utilizzando i qualificatori di interpolazione.
shader_type spatial;
varying flat vec3 our_color;
void vertex() {
our_color = COLOR.rgb;
}
void fragment() {
ALBEDO = our_color;
}
Esistono due possibili qualificatori di interpolazione:
Qualificatore |
Descrizione |
|---|---|
flat |
Il valore non è interpolato. |
smooth |
Il valore è interpolato in modo corretto per la prospettiva. Questo è il qualificatore predefinito. |
Uniformi
È possibile passare valori agli shader tramite le uniformi, che sono definite nell'ambito globale dello shader, all'esterno delle funzioni. Quando uno shader è successivamente assegnato a un materiale, le uniformi appariranno come parametri modificabili nell'ispettore del materiale. Le uniformi non si possono scrivere dall'interno dello shader. Qualsiasi tipo di dati, ad eccezione di void, può essere un'uniforme.
shader_type spatial;
uniform float some_value;
uniform vec3 colors[3];
È possibile impostare le uniformi nell'editor, nell'ispettore del materiale. Altrimenti, è possibile impostarle da codice.
Indicazioni di uniformi
Godot fornisce indicazioni facoltative di uniformi per far capire al compilatore a cosa serve l'uniforme e in che modo l'editor dovrebbe consentire agli utenti di modificarla.
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;
Alle uniformi possono anche essere assegnati valori predefiniti:
shader_type spatial;
uniform vec4 some_vector = vec4(0.0);
uniform vec4 some_color : source_color = vec4(1.0);
Tenere presente che quando si aggiunge un valore predefinito e un'indicazione, il valore predefinito va inserito dopo l'indicazione.
Di seguito, elenco completo di indicazioni di uniformi:
Tipo |
Suggerimento |
Descrizione |
|---|---|---|
vec3, vec4 |
source_color |
Utilizzato come colore. |
int |
hint_enum("Stringa1", "Stringa2") |
Visualizza l'input intero come widget a discesa nell'editor. |
int, float |
hint_range(min, max[, passo]) |
Limitato ai valori in un intervallo (con min/max/passo). |
sampler2D |
source_color |
Utilizzato come colore albedo. |
sampler2D |
hint_normal |
Utilizzato come mappa di normali. |
sampler2D |
hint_default_white |
Come valore o colore albedo, il valore predefinito è il bianco opaco. |
sampler2D |
hint_default_black |
Come valore o colore albedo, il valore predefinito è il nero opaco. |
sampler2D |
hint_default_transparent |
Come valore o colore albedo, il valore predefinito è il nero trasparente. |
sampler2D |
hint_anisotropy |
Come flowmap, predefinito a destra. |
sampler2D |
hint_roughness[_r, _g, _b, _a, _normal, _gray] |
Utilizzato per limitare la rugosità durante l'importazione (tenta di ridurre l'aliasing speculare). |
sampler2D |
filter[_nearest, _linear][_mipmap][_anisotropic] |
Abilitato il filtraggio specificato di texture. |
sampler2D |
repeat[_enable, _disable] |
Abilitata la ripetizione di texture. |
sampler2D |
hint_screen_texture |
La texture è la texture dello schermo. |
sampler2D |
hint_depth_texture |
La texture è la texture di profondità. |
sampler2D |
hint_normal_roughness_texture |
La texture è la texture di rugosità normale (supportata solo in Forward+). |
Utilizzare hint_enum
È possibile accedere ai valori int sotto forma di un widget a discesa leggibile tramite l'uniforme hint_enum:
uniform int noise_type : hint_enum("OpenSimplex2", "Cellular", "Perlin", "Value") = 0;
È possibile assegnare valori espliciti all'uniforme hint_enum utilizzando una sintassi con due punti, simile a quella di GDScript:
uniform int character_speed: hint_enum("Slow:30", "Average:60", "Very Fast:200") = 60;
Il valore verrà memorizzato come un intero, corrispondente all'indice dell'opzione selezionata (ovvero 0, 1 o 2) o al valore assegnato tramite la sintassi con due punti (ad esempio 30, 60 o 200). Quando si imposta il valore tramite set_shader_parameter(), è necessario utilizzare il valore intero, non il nome String.
Utilizzo di source_color
Qualsiasi texture che contenga dati colore sRGB necessita dell'indicazione source_color per essere campionata correttamente. Questo perché Godot renderizza uno spazio colore lineare, ma alcune texture contengono dati colore sRGB. Se questa indicazione non è utilizzata, la texture apparirà sbiadita.
Le texture di albedo e colore dovrebbero generalmente avere un'indicazione source_color. Le texture normali, di ruvidità, metalliche e di altezza generalmente non necessitano di un'indicazione source_color.
L'utilizzo dell'indicazione source_color è obbligatorio nei renderer Forward+ e Mobile e negli shader canvas_item quando HDR 2D è abilitato. L'indicazione source_color è facoltativa per il renderer Compatibilità e per gli shader canvas_item se HDR 2D è disabilitato. Tuttavia, si consiglia di utilizzare sempre l'indicazione source_color, perché funziona anche se il renderer cambia o HDR 2D viene disabilitato.
Gruppi di uniformi
Per raggruppare più uniformi in una sezione dell'ispettore, è possibile utilizzare la parola chiave group_uniform in questo modo:
group_uniforms MyGroup;
uniform sampler2D test;
È possibile chiudere il gruppo tramite:
group_uniforms;
La sintassi supporta anche i sottogruppi (non è obbligatorio dichiarare prima il gruppo base):
group_uniforms MyGroup.MySubgroup;
Uniformi globali
A volte, è necessario modificare un parametro in molti shader diversi alla volta. Con un'uniforme regolare, questo richiede molto lavoro, poiché tutti gli shader devono essere rintracciati e l'uniforme deve essere impostata per ognuno di essi. Le uniformi globali consentono di creare e aggiornare uniformi che saranno disponibili in tutti gli shader, in ogni tipo di shader (canvas_item, spatial, particles, sky e fog).
Le uniformi globali sono particolarmente utili per gli effetti ambientali che influenzano molti oggetti in una scena, come ad esempio piegare il fogliame quando il giocatore è nelle vicinanze o muovere gli oggetti con il vento.
Nota
Le uniformi globali non sono la stessa cosa dell'ambito globale di uno shader individuale. Mentre le uniformi regolari sono definite all'esterno delle funzioni dello shader, pertanto rientrando nell'ambito globale dello shader, le uniformi globali sono globali per tutti gli shader dell'intero progetto (ma all'interno di ogni shader rientrano anche nell'ambito globale).
Per creare un'uniforme globale, aprire le Impostazioni del progetto e andare alla scheda Globali per shader. Specificare un nome per l'uniforme (con distinzione tra maiuscole e minuscole) e un tipo, poi cliccare su Aggiungi nell'angolo in alto a destra della finestra. È possibile quindi modificare il valore assegnato all'uniforme cliccando sul valore nell'elenco delle uniformi:
Aggiunta di un'uniforme globale nella scheda Globali per shader delle Impostazioni del progetto
Dopo aver creato un'uniforme globale, è possibile utilizzarla in uno shader come segue:
shader_type canvas_item;
global uniform vec4 my_color;
void fragment() {
COLOR = my_color.rgb;
}
Si noti che l'uniforme globale deve esistere nelle Impostazioni del progetto al momento del salvataggio dello shader, altrimenti la compilazione fallirà. Sebbene sia possibile assegnare un valore predefinito tramite global uniform vec4 my_color = ... nel codice di shader, questo verrà ignorato poiché l'uniforme globale deve comunque essere sempre definita nelle Impostazioni del progetto.
Per modificare il valore di un'uniforme globale in fase di esecuzione, utilizzare il metodo RenderingServer.global_shader_parameter_set in uno script:
RenderingServer.global_shader_parameter_set("my_color", Color(0.3, 0.6, 1.0))
I valori uniformi globali si possono assegnare quante volte è desiderato senza compromettere le prestazioni, poiché l'impostazione dei dati non richiede la sincronizzazione tra CPU e GPU.
È anche possibile aggiungere o rimuovere uniformi globali in fase di esecuzione:
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")
Aggiungere o rimuovere gli uniformi globali in fase di esecuzione ha un impatto sulle prestazioni, anche se non è così pronunciato rispetto a ottenere i valori uniformi globali da uno script (vedere l'avvertimento di seguito).
Avvertimento
Sebbene sia possibile interrogare il valore di un'uniforme globale in fase di esecuzione in uno script tramite RenderingServer.global_shader_parameter_get("uniform_name"), ciò comporta una notevole riduzione delle prestazioni poiché il thread di rendering deve sincronizzarsi con il thread chiamante.
Pertanto, non è consigliabile leggere continuamente i valori delle uniformi di shader globale in uno script. Se è necessario leggere i valori in uno script dopo averli impostati, si consiglia di creare un autoload in cui memorizzare i valori da interrogare mentre li si imposta come uniformi globali.
Uniformi per istanza
Nota
Le uniformi per istanza sono disponibili solo negli shader canvas_item (2D) e spatial (3D).
A volte, si desidera modificare un parametro su ciascun nodo che utilizza lo stesso materiale. Ad esempio, in una foresta piena di alberi, se si desidera che ogni albero abbia un colore leggermente diverso modificabile manualmente. Senza uniformi per istanza, ciò richiede la creazione di un unico materiale per ogni albero (ognuno con una tonalità leggermente diversa). Ciò rende la gestione dei materiali più complessa, ed è anche un sovraccarico sulle prestazioni, poiché la scena richiede più istanze uniche di un materiale. Qui si potrebbero anche utilizzare i colori dei vertici, ma richiederebbero la creazione di copie uniche della mesh per ogni colore diverso, il che risulta in un ulteriore sovraccarico sulle prestazioni.
Le uniformi per istanza sono impostate su ogni GeometryInstance3D, anziché su ogni istanza di Material. Tenere ciò in considerazione quando si lavora con mesh a cui sono assegnati più materiali, o con configurazioni MultiMesh.
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;
}
Dopo aver salvato lo shader, è possibile modificare il valore dell'uniforme per istanza attraverso l'ispettore:
Impostazione del valore di un uniforme per istanza nella sezione GeometryInstance3D dell'ispettore
I valori di uniformi per istanza si possono anche impostare in fase di esecuzione attraverso il metodo set_instance_shader_parameter su un nodo che eredita da GeometryInstance3D:
$MeshInstance3D.set_instance_shader_parameter("my_color", Color(0.3, 0.6, 1.0))
Quando si utilizzano uniformi per istanza, esistono alcune restrizioni da tenere a mente:
Le uniformi per istanza non supportano texture o array, ma solo i tipi scalari e vettoriali standard. Come alternativa, è possibile passare un array di texture come una solita uniforme, poi passare l'indice della texture da disegnare tramite un'uniforme per istanza.
Nota
Nelle versioni di GLSL prima di 4.0 (ovvero GLSL 3.3 e più basse), non è possibile indicizzare direttamente un array di texture tramite un'uniforme per istanza, poiché gli array di campionatori si possono indicizzare solo tramite espressioni costanti in fase di compilazione. Ciò influisce sugli shader compilati con il renderer Compatibilità.
If you are affected, use the
switchstatement to select the texture:
uniform sampler2D texture_array[2];
instance uniform int texture_index;
void fragment() {
vec4 color;
switch (texture_index) {
case 0:
color = texture(texture_array[0], UV);
break;
case 1:
color = texture(texture_array[1], UV);
break;
}
COLOR = color;
}
Esiste un limite massimo pratico di 16 uniformi per istanza, per ogni shader.
Se la mesh utilizza più materiali, i parametri del primo materiale trovato avranno la precedenza su quelli successivi, a meno che non abbiano lo stesso nome, indice e tipo. In tal caso, tutti i parametri vengono modificati correttamente.
Se ci si imbatte nella suddetta situazione, si possono evitare conflitti specificando manualmente l'indice (0-15) dell'uniforme per istanza utilizzando l'indicazione
instance_index:
instance uniform vec4 my_color : source_color, instance_index(5);
Impostare gli uniformi da codice
È possibile impostare le uniformi da GDScript attraverso il metodo set_shader_parameter():
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)])
Nota
Il primo argomento di set_shader_parameter() è il nome dell'uniforme nello shader. Deve corrispondere esattamente al nome dell'uniforme nello shader, altrimenti non verrà riconosciuto.
GDScript utilizza tipi di variabili diversi da GLSL, quindi quando si passano variabili da GDScript agli shader, Godot ne converte automaticamente il tipo. Di seguito è riportata una tabella dei tipi corrispondenti:
Tipo GLSL |
Tipo GDScript |
Note |
|---|---|---|
bool |
bool |
|
bvec2 |
int |
Intero compresso in bit, in cui il bit 0 (LSB) corrisponde a x. Ad esempio, un bvec2 di (bx, by) si potrebbe creare nel seguente modo: bvec2_input: int = (int(bx)) | (int(by) << 1)
|
bvec3 |
int |
Intero compresso in bit, in cui il bit 0 (LSB) corrisponde a x. |
bvec4 |
int |
Intero compresso in bit, in cui il bit 0 (LSB) corrisponde a x. |
int |
int |
|
ivec2 |
Vector2i |
|
ivec3 |
Vector3i |
|
ivec4 |
Vector4i |
|
uint |
int |
|
uvec2 |
Vector2i |
|
uvec3 |
Vector3i |
|
uvec4 |
Vector4i |
|
float |
float |
|
vec2 |
Vector2 |
|
vec3 |
Vector3, Color |
Quando Color è utilizzato, sarà interpretato come (r, g, b). |
vec4 |
Vector4, Color, Rect2, Plane, Quaternion |
Quando Color è utilizzato, sarà interpretato come (r, g, b, a). Quando Rect2 è utilizzato, sarà interpretato come (position.x, position.y, size.x, size.y). Quando Plane è utilizzato, sarà interpretato come (normal.x, normal.y, normal.z, d). |
mat2 |
Transform2D |
|
mat3 |
Basis |
|
mat4 |
Projection, Transform3D |
Quando un Transform3D è utilizzato, il vettore w viene impostato sull'identità. |
sampler2D |
Texture2D |
|
isampler2D |
Texture2D |
|
usampler2D |
Texture2D |
|
sampler2DArray |
Texture2DArray |
|
isampler2DArray |
Texture2DArray |
|
usampler2DArray |
Texture2DArray |
|
sampler3D |
Texture3D |
|
isampler3D |
Texture3D |
|
usampler3D |
Texture3D |
|
samplerCube |
Cubemap |
Consultare Cambiare il tipo da importare per istruzioni su come importare cubemap da utilizzare in Godot. |
samplerCubeArray |
CubemapArray |
Supportato solo in Forward+ e Mobile, non in Compatibilità. |
samplerExternalOES |
ExternalTexture |
Supportato solo sulla piattaforma Compatibilità/Android. |
Nota
Prestare attenzione quando si impostano le uniformi degli shader da GDScript, poiché non sarà generato alcun errore se il tipo non corrisponde. Lo shader mostrerà solo un comportamento indefinito. Nello specifico, ciò include impostare un int/float di GDScript (a 64 bit) in un int/float del linguaggio di shader di Godot (a 32 bit). Ciò potrebbe portare a conseguenze indesiderate nei casi in cui è richiesta un'elevata precisione.
Limiti per gli uniformi
Esiste un limite alla dimensione totale delle uniformi di shader che è possibile utilizzare in un singolo shader. Sulla maggior parte delle piattaforme desktop, questo limite è di 65536 byte, ovvero 4096 uniformi vec4. Sulle piattaforme mobili, il limite è in genere di 16384 byte, ovvero 1024 uniformi vec4. Le uniformi vettoriali più piccole di un vec4, come vec2 o vec3, sono imbottite alla dimensione di un vec4. Le uniformi scalari come int o float non sono imbottite, mentre bool è imbottito alla dimensione di un int.
Gli array contano come la dimensione totale del loro contenuto. Se c'è bisogno di un array uniforme più grande di questo limite, si potrebbe impacchettare i dati in una texture, poiché il contenuto di una texture non conta per questo limite, ma solo la dimensione dell'uniforme di campionatore.
Variabili integrate
Sono disponibili numerose variabili integrate, come UV, COLOR e VERTEX. Le variabili disponibili dipendono dal tipo di shader (spatial, canvas_item, particle, ecc.) e dalla funzione usata (vertex, fragment, light, start, process, sky o fog). Per un elenco delle variabili integrate disponibili, consultare le pagine corrispondenti:
Funzioni integrate
Sono supportate numerose funzioni integrate, conformi a GLSL ES 3.0. Consultare la pagina Funzioni integrate per maggiori dettagli.
Commenti
Il linguaggio di shading supporta la stessa sintassi per i commenti utilizzata in C# e C++, utilizzando
//per i commenti su una sola riga e/* */per i commenti su più righe:Inoltre, è possibile utilizzare i commenti di documentazione, visualizzati nell'ispettore quando si passa il cursore su un parametro dello shader. I commenti di documentazione sono attualmente supportati solo se inseriti immediatamente sopra una dichiarazione
uniform. Questi commenti di documentazione supportano solo la sintassi per i commenti su più righe e devono utilizzare due asterischi iniziali (/**) invece di uno solo (/*):Gli asterischi nelle righe successive non sono obbligatori, ma sono consigliati come da Guida di stile per gli shader. Questi asterischi sono rimossi automaticamente dall'ispettore, quindi non appariranno nel suggerimento.