7.1. Il G-Buffer

5
Il tuo voto: Nessuno Media: 5 (1 vote)

Col termine G-Buffer (Geometric-Buffers ) si intende un set di render-targets che permettono il salvataggio di dati di varia natura per ogni pixel del viewport corrente. Può essere quindi individuato come un back-buffer, su cui renderizzare la scena, composto in questo caso da molteplici textures. I dati aggiuntivi cui potremo accedere in un secondo momento possono essere: la distanza dalla posizione della telecamera, la distanza dal piano dell schermo, la normale (generalmente in world-space) anche con la possibilità di implementare il normal-mapping, il colore diffuso (textures comprese) di una superficie, proprietà di un materiale, ecc...

La caratteristica fondamentale del G-Buffer è che, proprio per la sua struttura, può adattarsi a qualsiasi esigenza del programmatore o dei grafici; è infatti implementato soprattutto nella tecnica del deferred-shading (dove l'ombreggiatura è eseguita dopo aver renderizzato tutta la scena) e molto spesso anche negli effetti in post-processo come: il rilevamento dei bordi, il cortoon rendering, la profondità di campo, ecc...; è da notare inoltre che, tramite questa tecnica, un Pixel Shader può accedere ai dati di qualsiasi pixel del viewport corrente.

Quando si utilizza il G-Buffer la pipeline di rendering si divide principalmente in due passi: il primo è il rendering della geometria nei vari render-targets, mentre il secondo è l'applicazione di questi render-targets ad un rettangolo che copre completamente l'area dello schermo e l'utilizzo di un Pixel Shader per realizzare l'effetto voluto.

È da notare che, per la grande quantità di dati in spostamento tra la memoria del PC e la RAM dedicata della scheda video, questo approccio in applicazioni real-time è divenuto possibile  solamente da qualche anno a questa parte; il vantaggio di renderizzare la scena una sola volta era infatti completamente annullato dal tempo impiegato per rendere disponibili le textures agli shaders. Un'ulteriore miglioramento da questo punto di vista è stato apportato col supporto da parte delle GPU della tecnica chiamata Multiple Render Targets (MRT) per cui tutte le textures su cui renderizzare sono scritte simultaneamente.

Esempio di Pixel Shader utilizzato per scrivere simultaneamente più targets:

  1 uniform float4 diffuseColor;
  2 
  3 struct PS_OUT
  4 {
  5     float4 Depth : COLOR0;
  6     float4 Normal : COLOR1;
  7     float4 Color : COLOR2;
  8 };
  9 
 10 PS_OUT gbufferPS(float2 TexCoord : TEXCOORD0,
 11                 float Depth : TEXCOORD1,
 12                 float3 Normal : TEXCOORD2)
 13 {
 14     PS_OUT Out;
 15 
 16     // Scrittura nei render-targets
 17     // Inverso della distanza (così è lineare in screen-space)
 18     Out.Depth = 1.0 / Depth;
 19 
 20     // Normale compressa dal range -1.0 ... 1.0 al range 0.0 ... 1.0
 21     Out.Normal = float4(normalize(Normal) * 0.5 + 0.5, 1.0);
 22 
 23     // Colore diffuso
 24     Out.Color = diffuseColor;
 25 
 26     return Out;
 27 }

Come è possibile vedere dal codice è necessario creare una struttura che sarà utilizzata come valore di ritorno del Pixel Shader, al cui interno sono definiti tanti campi quanti sono i target su cui scrivere. In questo caso il tipo di dati deve essere obbligatoriamente float4 anche se non sono utilizzate tutte le quattro componenti e la semantica specificata deve utilizzare le parole chiave COLOR0, COLOR1, COLOR2, COLOR3, ecc... Il numero suffisso alla parola chiave corrisponde al numero del target, impostato da codice C#, su cui scrivere il valore. Il Vertex Shader è stato escluso dall'esempio per brevità dato che non presenta implementazioni specifiche.