Eccoci finalmente ad un nuovo appuntamento con il nostro percorso che finalmente ci introdurrà nel pieno dell’argomento WebGL. Dopo aver illustrato alcuni elementi fondamentali della grafica tridimensionale, arriviamo ad effettuare la panoramica di quale sia (nella norma) la pipeline di rendering!

Per pipeline di rendering intendiamo il processo che “converte” il nostro mondo virtuale da un ambiente tridimensionale (con tutti i suoi punti, poligoni, luci, etc…) ad un’immagine rasterizzata da visualizzare nel dispositivo bidimensionale desiderato… che normalmente è il display del nostro computer, smart phone, etc… 
La pipeline di rendering è quindi il processo che ci consente di produrre le immagini che aggiornate con una dovuta frequenza (misurata in fps e cioè frame per secondo) ci danno l’illusione dell’animazione delle nostre scene. 

Come possiamo ben immaginare il processo che porterà alla rappresentazione a video della nostra scena è particolarmente complesso, ma proveremo a ridurla a dei concetti comprensibili e chiari…facendo ovviamente maggiore attenzione alla pipeline di rendering di Open GL ES 2.0, che come abbiamo detto sono le librerie sulle quali WebGL si basa.

Qualche riflessione iniziale…
…ci aiuterà a focalizzare prima di tutto il concetto.
Il processo di rendering va innanzitutto analizzato come una serie di operazioni complesse che possono essere valutate come stati indipendenti, che per ridurre la complessità dei processi gestiscono indipendentemente e parallelamente delle singole porzioni dell’iter che ci conduce sino al rendering finale.
Il carico di lavoro è difatti incredibilmente gravoso per il numero notevole di operazioni estremamente articolate e computazionalmente pesanti, ma per mezzo di librerie disegnate “ad hoc” e del supporto di hardware prestante riusciamo a creare a video le illusioni tridimensionali alle quali ormai siamo abituati.

Pipeline di Rendering Generica

Provando a scomporre una pipeline di rendering generica ci ritroveremmo a  suddividerla in 3 macro fasi a loro volta suddivise in ulteriori “semplici” fasi:

  • Application

    • Definizione della scena 3d;

      • Questa prima fase consiste nella definizione delle primitive e delle sue parti;
    • Definizione degli input, definizione della fisica, collision detecting, culling, etc…
  • Geometry SubSystem

    • Trasformazione di Coordinate Locali e Globali;

      • Questa prima fase consiste nella trasformazione delle coordinate locali delle primitive portandole in un sistema di coordinate comune globale.
    • Definizione del punto di vista della scena 3d;

      • In questa fase una volta definito il punto di vista della scena 3d vengono trasformate le coordinate globali in modo che venga definita la scena secondo la direzione di vista e la posizione “dell’osservatore” della scena stessa.
    • Generazione delle coordinate texture;

      • Fase di mappatura delle coordinate delle texture sulle primitive.
    • Applicazione dei calcoli dell’illuminazione della scena 3d;

      • Fase in cui vengono applicati alla scena tutti i complessi calcoli per la definizione dell’illuminazione della scena a seconda del modello di illuminazione scelto.
    • Trasformazioni di proiezione;

      • In questa fase vengono applicate le trasformazioni alle coordinate in modo da essere proiettate su un piano a seconda del tipo di proiezione scelta (argomento che abbiamo già trattato precedentemente), in modo da ottenere le coordinate degli oggetti di scena che abbiano valori x e y sul piano di proiezione e che mantengano ancora la loro profondità sull’asse z per poter definire in seguito eventuali parti di oggetto nascoste da altre parti di altri oggetti.
    • Clipping della scena 3d;

      • Processo che ci consente di stabilire quali oggetti o parti di oggetti siano contenute nello spazio di vista definito, in modo da escludere dai calcoli le parti non necessarie.
    • Trasformazione delle coordinate allo spazio fisico del dispositivo;

      • Conversione delle coordinate di proiezione allo spazio fisico del dispositivo che rappresenterà la scena.
  • Raster SubSystem

    • Rasterizzazione delle primitive;

      • Processo di individuazione della mappatura tra le singole primitive ed i pixel del dispositivo. Per ogni pixel (o fragment) vengono quindi calcolate eventuali corrispondenze con le primitive valutando colori, profondità nell’asse z, shading e texturing.
        N.B. In realtà è importante notare che ciò che viene definito come fragment non è in verità un pixel, bensì la più piccola unità in cui può venire “scomposta” una primitiva. Normalmente la corrispondenza tra pixel e fragment è praticamente uguale, ma può variare in processi particolari come alcuni casi di antialiasing.
    • Z-Buffering;

      • Per ogni pixel viene effettuato il controllo sull’asse z in modo da ottenere l’effettiva visibilità dello stesso per prendere “in carico” solo ciò che è realmente visibile dall’osservatore.
    • Blending / AntiAliasing

      • Fasi in cui vengono definite altre caratteristiche valide per la rasterizzazione come le trasparenze, la scalettatura dei bordi degli oggetti (appunto aliasing), etc…

 

La Pipeline di WebGL (e quindi di OpenGL ES 2.0!)

Abbiamo più volte detto che le specifiche WebGL sono basate sulle specifiche di OpenGL ES 2.0 che null’altro sono che le librerie grafiche OpenGL ottimizzate per i dispositivi mobili (ve ne dico uno a caso…mmm…iPhone! :D).
Analizzare quindi la pipeline di rendering di WebGL equivale praticamente ad analizzare la  pipeline di OpenGL ES 2.0.

Ovviamente grazie a WebGL sono molti gli sgravi che avremo nella gestione dell’intero processo di rendering dato che molte delle fasi più complesse saranno proprio gestite dalla libreria.
La pipeline di WebGL è implementata per mezzo di Shader Programmabili  dalle specifiche di OpenGL ES 2.0 e dalle specifiche di OpenGL ES Shading Language Specification (più brevemente dette GLSL).  
Con il termine Shader (letteralmente, "ombreggiatore") si intende un’insieme di istruzioni software che permettono di definire particolari operazioni ed effetti (ad esempio di illuminazione o colorazione) per il rendering degli elementi della scena 3D. Gli shader vengono elaborati per mezzo delle pipeline di rendering delle GPU adibite alla trasformazione di una scena 3D che possa essere rappresentata sullo schermo. Ottimo come sempre è l’approfondimento di Wikipedia in merito.

E’ necessario analizzare quindi la pipeline di WebGL per poter capire bene le differenze.

  • Vertex Array / Buffer Object

    • La prima fase di pipeline consiste nel “raccogliere” in array ben ordinati le varie parti della nostra scena ed i vertici relativi. La raccolta di queste informazioni può avvenire in svariato modo, ma è utile anche in questa fase recuperare le matrici di proiezione, le informazioni per il “punto di vista della scena”, etc…
  • Vertex Shader

    • Le informazioni raccolte nella precedente fase vengono quindi passate a questa seconda fase. Il Vertex Shader è il metodo programmabile per poter gestire i vertici, ed è utilizzato per le operazioni di calcolo delle proiezioni, per modificare le posizioni dei vertici, per il calcolo dell’illuminazione, etc..
      Le operazioni vengono svolte una volta per ogni vertice elaborando i dati dello stesso con le varie matrici (ad esempio quella di proiezione).
      I dati che vengono prodotti dal Vertex Shader sono poi contenuti in svariate variabili (di cui alcune obbligatorie) che vengono passati alla fase successiva della pipeline;
  • Primitive Assembly

    • In questa fase vengono elaborate le primitive per mezzo di specifici comandi di disegno forniti dalla libreria, che consentono di definire gli oggetti di scena. In questa fase le operazioni vengono effettuate valutando i dati passati dalla precedente fase, ed è in questa fase che vengono eseguite le complesse operazioni di Clipping e Culling.
  • Rasterization

    • In questa fase vengono convertite le primitive in informazioni bidimensionali detti Fragments che saranno poi elaborati nella fase successiva.
  • Fragment Shader

    • Il Fragment Shader è il metodo programmabile per poter gestire i Fragments prima generati. Le operazioni vengono svolte per ogni singolo Fragments (e quindi nella maggior parte dei casi per ogni pixel) e consentono di implementare effetti d'illuminazione e sui colori, effettuare texture mapping ed applicare effetti particolari come le rifrazioni, il bump mapping, le ombre, etc…
      In ogni modo lo scopo del Fragment Shader è restituire il colore per ciascuno dei Fragments processati.
  • Per-Fragment Operation

    • In questa fase vengono svolte ulteriori operazioni di testing sui Fragments (Pixel Ownership Test, Scissor Text, etc…), oltre che operazioni di Blending e Dithering.
  • FrameBuffer

    • I dati processati verranno infine passati al Frame Buffer che è ciò che verrà visualizzato in output.


La vera differenza (o per lo meno quella che attualmente può interessarci) tra la pipeline di OpenGL ES 2.0 e WebGL sarà chiara nelle prossime lezioni, ma anticipiamo semplicemente che sta nella fruizione degli Shader che saranno passati a WebGL per mezzo di javascript…ma abbiamo tempo per approfondire! :)

Spero che quest’ultima lezione introduttiva vi sia stata utile per cogliere come sempre spunto per approfondire l’argomento…e vi invito al prossimo appuntamento in cui finalmente metteremo le mani in pasta!  Come chiunque voglia fare (per fare bene!) prenderemo molto spunto dalle “bibliche” lezioni di NeHe su OpenGL e delle conversioni WebGL apportate sui vari siti specializzati (come l’MDN di Mozilla).


Alla prossima quindi! 

Francesco Sciuti

The following two tabs change content below.

Francesco Sciuti

Freelance a Vroom Agency
Amante dello sviluppo web, della grafica 3d e della buona musica (che non guasta mai!), 'web developpa' tutto il giorno...e prova a trovare sempre il bandolo della matassa in questo allegro ma sconfinato mondo.

//life motto
if(sad() === true) {
    sad().stop();
    beAwesome();
}