La grafica computerizzata 3D e la rappresentazione grafica delle immagini


La grafica computerizzata 3D negli ultimi anni sta diventando sempre più utilizzata nella rappresentazione del mondo per immagini. Dalle immagini statiche alle animazioni, il mondo viene sempre più spesso mostrato con l’integrazione di immagini e video virtuali.

render 3d italia grafica computerizzata 3D

render 3d italia architettura

Quando ci occupiamo di grafica 3D, troviamo che l’argomento ha più in comune con la grafica vettoriale che con la grafica raster. Cioè, il contenuto di un’immagine viene specificato come un elenco di oggetti geometrici. La tecnica viene definita come modellazione geometrica. Il punto di partenza è costruire un “mondo 3D artificiale” come un insieme di forme geometriche semplici, disposte nello spazio tridimensionale. Gli oggetti possono avere attributi che, combinati con le proprietà globali del “mondo”, determinano l’aspetto degli oggetti. Spesso, la gamma di figure di base è molto limitata, tra cui forse solo i punti, segmenti di linea, e triangoli. Una forma più complessa come un poligono o sfera può essere costruita o approssimata come un insieme di forme più basilari. Per ricreare una immagine bidimensionale della scena, questa viene proiettata da tre dimensioni fino a due dimensioni. La proiezione è l’equivalente di una fotografia della scena.

In primo luogo è necessario considerare la geometria. Si comincia con uno spazio vuoto 3D o “mondo”. Naturalmente, questo spazio esiste solo concettualmente, ma è utile pensare ad esso come reale ed essere in grado di visualizzare tale spazio nella vostra mente. Lo spazio ha bisogno di un sistema di coordinate che associa ciascun punto nello spazio con tre numeri, generalmente indicato come il x, y, z: le coordinate del punto. Questo sistema di coordinate è denominato “coordinate mondo”.
Vogliamo costruire una scena all’interno del mondo, composto di oggetti geometrici. Ad esempio, siamo in grado di specificare un segmento di linea nella scena, dando le coordinate dei suoi due punti finali, e siamo in grado di indicare un triangolo, dando le coordinate dei suoi tre vertici. Gli elementi costitutivi più piccoli con i quali dobbiamo lavorare, come segmenti di linea e triangoli, sono chiamati primitive geometriche. Diversi sistemi grafici rendono diversi set di primitive disponibili, ma in molti casi solo le forme molto semplici come linee e triangoli sono considerati primitivi. Una scena complessa può contenere un gran numero di primitive, e sarebbe molto difficile creare la scena dando coordinate esplicite per ogni singola primitiva. La soluzione, come ogni programmatore dovrebbe immediatamente intuire, è quella di unire insieme le primitive in componenti riutilizzabili. Ad esempio, per una scena che contiene diverse automobili, si può creare un modello geometrico di una ruota. Un’automobile può essere modellata come quattro ruote unendo poi modelli di altri componenti. E potremmo poi utilizzare diverse copie del modello di automobile nella scena. Notare che una volta che un modello geometrico è stato progettato, può essere utilizzato come componente in modelli più complessi. Questo procedimento è indicato come modellazione gerarchica.
Supponiamo di aver costruito un modello di una ruota partendo dalle primitive geometriche. Quando tale ruota è spostata nella sua posizione in un modello di automobile, le coordinate di tutti le sue primitive dovranno essere adeguate. Così che cosa abbiamo ottenuto esattamente con la costruzione della ruota? Il punto è che tutte le coordinate nella ruota sono regolate allo stesso modo. Cioè, per posizionare la ruota in un’automobile, dobbiamo solo specificare una singola regolazione che viene applicata alla ruota nel suo complesso. Il tipo di “aggiustamento” utilizzato è chiamato trasformazione geometrica. Una trasformazione geometrica viene utilizzata per regolare la dimensione, l’orientamento e la posizione di un oggetto geometrico. Quando si crea un modello di un’automobile, si parte costruendo una singola ruota. Si devono poi applicare quattro trasformazioni diverse per il modello di ruota per aggiungere quattro copie della ruota per l’automobile. Allo stesso modo, siamo in grado di aggiungere diverse automobili a una scena mediante l’applicazione di diverse trasformazioni per lo stesso modello di automobile.
I tre tipi fondamentali di trasformazione geometrica sono chiamati scala, rotazione e traslazione. Una trasformazione in scala è utilizzata per impostare la dimensione di un oggetto, cioè, per renderlo più grande o più piccolo rispetto ad un fattore specificato. Una rotazione viene utilizzata per impostare l’orientamento di un oggetto, facendolo ruotare di un determinato angolo su un asse. Una traslazione è utilizzata per impostare la posizione di un oggetto, spostandolo di una determinata quantità dalla sua posizione originale.
Successivamente, l’aspetto… Le forme geometriche da sole non sono molto interessanti. Bisogna essere in grado di impostare il loro aspetto. Questo viene fatto assegnando attributi agli oggetti geometrici. Un attributo evidente è il colore, ma ottenere un aspetto realistico si rivela essere molto più complicato rispetto allo specificare semplicemente un colore per ogni primitiva. In grafica 3D, invece che di colore, di solito si parla di materiale. Il termine materiale qui si riferisce alle proprietà che determinano l’aspetto visivo intrinseco di una superficie. Essenzialmente, questo significa dare l’idea di come la superficie interagisce con la luce che la colpisce. Le proprietà del materiale possono includere un colore di base così come altre proprietà come lucentezza, rugosità, e trasparenza.
Uno dei tipi più utili di proprietà di un materiale è una texture. In termini più generali, una texture è un modo di variare le proprietà del materiale da punto a punto su una superficie. L’uso più comune di texture è quello di permettere colori diversi per diversi punti. Questo viene fatto utilizzando un’immagine 2D come una trama, che può essere applicata ad una superficie in modo che l’immagine sembri “dipinto” sulla superficie. Tuttavia, la struttura può anche fare riferimento alla modifica dei valori per cose come la trasparenza o ruvidità (bumpiness). Le textures ci permettono di aggiungere dettagli di una scena senza l’utilizzo di un numero enorme di primitive geometriche; è possibile invece utilizzare un numero minore di primitive texturizzate.
Un materiale è una proprietà intrinseca di un oggetto, ma l’aspetto reale dell’oggetto dipende anche dall’ambiente in cui l’oggetto viene visualizzato. Nel mondo reale, non si vede nulla a meno che non vi è una certa luce nell’ambiente. Lo stesso vale in grafica 3D: si deve aggiungere l’illuminazione simulata di una scena: ci possono essere diverse fonti di luce in una scena. Ogni sorgente di luce può avere un proprio colore, intensità, e la direzione o la posizione. La luce da quelle fonti dovrà quindi interagire con le proprietà del materiale degli oggetti nella scena. Il supporto per l’illuminazione in un sistema grafico può variare da abbastanza semplice a molto complesso e computazionalmente intensivo.
Infine, l’immagine… In generale, l’obiettivo finale della grafica 3D è produrre immagini 2D del mondo 3D. La trasformazione da 3D a 2D comporta la visualizzazione e la proiezione. Il mondo appare diverso se visto da diversi punti di vista. Per impostare un punto di vista, abbiamo bisogno di specificare la posizione dello spettatore e la direzione che lo spettatore sta guardando. È inoltre necessario specificare una direzione “sopra”, una direzione che sarà rivolta verso l’alto nell’immagine finale. Questo può essere pensato come mettere una “camera virtuale” nella scena. Una volta che la “vista” è impostata, il mondo visto da questo punto di vista può essere proiettato in 2D. La proiezione è analoga allo scattare una foto con la fotocamera.
La fase finale della grafica 3D è quella di assegnare i colori ai singoli pixel dell’immagine 2D. Questo processo è chiamato rasterizzazione, e l’intero processo di produzione di un’immagine viene indicato come rendering della scena.
In molti casi l’obiettivo finale non è creare una singola immagine, ma creare un’animazione, costituita da una sequenza di immagini che mostrano il mondo in tempi diversi. In un’animazione, ci sono piccoli cambiamenti da un’immagine nella sequenza all’altra. Quasi ogni aspetto di una scena può cambiare durante un’animazione, tra cui le coordinate di primitive, trasformazioni, le proprietà del materiale, e la vista. Ad esempio, un oggetto può essere fatto crescere nel corso di un’animazione gradualmente aumentando il fattore di scala in una trasformazione di scala che viene applicata all’oggetto. E cambiando la vista durante un’animazione si può dare l’effetto di spostarsi o volare attraverso la scena. Naturalmente, può essere difficile calcolare le modifiche necessarie. Ci sono molte tecniche utili per il calcolo. Uno delle più importanti è quella di utilizzare un “motore fisico”, che calcola il movimento e l’interazione degli oggetti in base alle effettive leggi della fisica.

Useremo la specifica OpenGL come base primaria per la programmazione grafica 3D. La versione originale di OpenGL è stata pubblicata nel 1992 da una società chiamata Silicon Graphics, che era nota per le sue workstation potenti per la grafica, computer costosi progettati per applicazioni grafiche intensive. (Oggi, probabilmente si ha più potenza grafica su uno smartphone) OpenGL è supportata dall’ hardware grafico nella maggior parte dei dispositivi informatici moderni, tra cui computer desktop, laptop e molti dispositivi mobili. In questa sezione vi darà un po’ di background sulla storia di OpenGL e circa l’hardware grafico che lo supporta.

Nei primi computer desktop, il contenuto dello schermo veniva gestitio direttamente dalla CPU. Ad esempio, per disegnare un segmento di linea sullo schermo, la CPU avrebbe eseguito un ciclo per impostare il colore di ogni pixel che si trova lungo la linea. Inutile dire che la grafica potrebbe prendere un sacco di tempo di calcolo della CPU. E le prestazioni grafiche erano molto lente, rispetto a quello che ci aspettiamo oggi. Allora, cosa è cambiato? I computer sono molto più veloci in generale, naturalmente, ma il grande cambiamento è che nei moderni computer, l’elaborazione grafica è fatta da un componente specializzato chiamato GPU o Graphics Processing Unit. Una GPU include processori per effettuare i calcoli di grafica; infatti, può includere un gran numero di tali processori che lavorano in parallelo per velocizzare notevolmente le operazioni grafiche. Esso comprende anche una propria memoria dedicata per la memorizzazione di cose come le immagini ed elenchi di coordinate. Processori GPU hanno un accesso molto veloce per i dati archiviati nella memoria a più GPU più veloce di loro l’accesso ai dati memorizzati nella memoria principale del computer.

Per disegnare una linea o eseguire qualche altra operazione grafica, la CPU deve semplicemente inviare comandi, insieme a tutti i dati necessari per la GPU, che è responsabile in realtà dell’esercizio di tali comandi. La CPU alleggerisce la maggior parte del lavoro di grafica per la GPU, che è ottimizzato per svolgere quel lavoro molto rapidamente. Il set di comandi che la GPU capisce compongono l’API della GPU. OpenGL è un esempio di API grafiche, e la maggior parte delle GPU supporta OpenGL, nel senso che possano capire i comandi OpenGL, o per lo meno che i comandi OpenGL possono efficacemente essere tradotti in comandi che la GPU può capire.

OpenGL non è l’unico API grafico. L’alternativa più nota è probabilmente Direct3D, una API grafica 3D utilizzata per Microsoft Windows. OpenGL è più ampiamente disponibile, in quanto non è limitato a Microsoft, ma Direct3D è supportata dalla maggior parte delle schede grafiche, e spesso ha introdotto nuove funzionalità prima di OpenGL.

Ho detto che OpenGL è una API, ma in realtà si tratta di una serie di API che sono state oggetto di ripetute estensioni revisioni. La versione corrente, nei primi mesi del 2015, è di 4.5, ed è molto diversa dalla versione 1.0 del 1992. Inoltre, vi è una versione specializzata denominata OpenGL ES per i “sistemi embedded”, come telefoni cellulari e tablet. E c’è anche WebGL, per l’uso nei browser Web, che è fondamentalmente una port di OpenGL ES 2.0. E ‘utile sapere qualcosa su come e perché OpenGL è cambiato.

Prima di tutto, dovete sapere che OpenGL è stato progettato come un sistema “client/server”. Il server, che è responsabile del controllo di visualizzazione del computer e di eseguire elaborati grafici, esegue i comandi emessi dal cliente. In genere, il server è una GPU, compresi i suoi processori grafici e di memoria. Il server esegue i comandi OpenGL. Il client è la CPU nello stesso computer, insieme al programma applicativo che è in esecuzione. I comandi OpenGL provengono dal programma in esecuzione sulla CPU. Tuttavia, in realtà è possibile eseguire programmi OpenGL in remoto su una rete. Cioè, è possibile eseguire un programma applicativo su un computer remoto (il client OpenGL), mentre i calcoli di grafica e il display sono fatte sul computer che in realtà si sta utilizzando (il server OpenGL).

L’idea fondamentale è che il client e il server sono componenti separati, e vi è un canale di comunicazione tra questi componenti. I comandi OpenGL ed i dati di cui hanno bisogno sono comunicati dal cliente (la CPU) al server (la GPU) su quel canale. La capacità del canale può essere un fattore limitante nelle prestazioni grafiche. Pensate di disegnare un’immagine sullo schermo. Se la GPU può elaborare l’immagine in microsecondi, ma ci vogliono millisecondi per inviare i dati per l’immagine dalla CPU alla GPU, quindi la grande velocità della GPU è irrilevante – la maggior parte del tempo che ci vuole per disegnare l’immagine è tempo di comunicazione.
Per questo motivo, uno dei fattori guida nell’evoluzione della OpenGL è stato il desiderio di limitare la quantità di comunicazione necessaria tra la CPU e la GPU. Un approccio è quello di memorizzare le informazioni nella memoria della GPU. Se alcuni dati stanno per essere utilizzati più volte, possono essere trasmessi alla GPU una volta e memorizzati nella memoria là dove saranno immediatamente accessibili alla GPU. Un altro approccio è quello di cercare di diminuire il numero di comandi OpenGL che devono essere trasmessi alla GPU per disegnare una data immagine.

OpenGL disegna primitive, come i triangoli. Specificando una primitiva significa specificare le coordinate e gli attributi per ciascuno dei suoi vertici. Nell’OpenGL 1.0 originale, un comando separato veniva utilizzato per specificare le coordinate di ciascun vertice, e un comando era necessario ogni volta che il valore di un attributo cambiava. Per disegnare un singolo triangolo sarebbero serviti tre o più comandi. Il disegno di un oggetto complesso costituito da migliaia di triangoli avrebbe comportato l’uso di molte migliaia di comandi. Anche in OpenGL 1.1, è stato possibile disegnare un oggetto con un unico comando, invece di migliaia. Tutti i dati dell’oggetto venivano caricati in array, che potevano poi essere inviati con un singolo passaggio alla GPU. Purtroppo, se l’oggetto fosse stato disegnato più di una volta, allora i dati dovrebbero essere stati ritrasmessi ogni volta che l’oggetto è stato elaborato. Questo è stato risolto in OpenGL 1.5 con Vertex Buffer Objects. Un VBO è un blocco di memoria nella GPU che può memorizzare le coordinate o valori di attributo per un insieme di vertici. Ciò consente di riutilizzare i dati senza dover ritrasmettere dalla CPU alla GPU ogni volta che viene utilizzato.

Similmente, OpenGL 1.1 introdusse oggetti texture per rendere possibile immagazzinare molte immagini nella GPU per utilizzarle come texture. Ciò significa che immagini di texture che devono essere riutilizzate più volte possono essere caricati una volta nella GPU, in modo che la GPU possa passare facilmente tra le immagini senza doverle ricaricare.

Da quando sono state aggiunte nuove funzionalità a OpenGL, l’API è cresciuto in dimensioni. Ma la crescita è stata ancora superata con l’invenzione di nuove tecniche più sofisticate per fare grafica. Alcune di queste nuove tecniche sono stati aggiunti alla OpenGL, ma il problema è che non importa quante funzioni si aggiungono, ci saranno sempre richieste di nuove caratteristiche – e lamentele che tutte le nuove funzionalità stanno rendendo le cose troppo complicate! OpenGL era una macchina gigantesca, con nuovi pezzi sempre aggiunti su di essa, ma ancora non piaceva a tutti. La vera soluzione fu quella di rendere la la macchina programmabile. Con OpenGL 2.0, è stato possibile scrivere programmi da eseguire come parte della computazione grafica nella GPU. I programmi vengono eseguiti sulla GPU alla velocità della GPU. Un programmatore che vuole usare una nuova tecnica grafica può scrivere un programma per implementare la funzionalità e semplicemente consegnarlo alla GPU. L’API OpenGL non deve essere cambiata. L’unica cosa che l’API deve sostenere è la possibilità di inviare i programmi per la GPU per l’esecuzione.

I programmi sono chiamati shaders (anche se il termine non descrive davvero ciò che la maggior parte di loro fa in realtà). I primi shader da introdurre erano vertex shader e shader di frammenti. Quando una primitiva è disegnata, deve essere fatto un lavoro per ogni vertice della primitiva, come l’applicazione di una trasformazione geometrica alle coordinate vertice o utilizzando gli attributi e illuminazione ambiente globale per calcolare il colore di quel vertice. Un vertex shader è un programma che può assumere il compito di fare questi calcoli “per-vertex”. Analogamente, alcuni lavoro deve essere fatto per ciascun pixel all’interno della primitiva. Uno shader frammento può assumere il compito di effettuare tali calcoli “per-pixel”. (Shader Fragment sono anche chiamati pixel shader.)
L’idea di hardware grafici programmabili ha avuto molto successo, così tanto successo che in OpenGL 3.0, il solito per-vertice e per-frammento di elaborazione è stata sconsigliata (il che significa che il suo uso è stato scoraggiato). E in OpenGL 3.1, è stato rimosso dallo standard OpenGL, anche se è ancora presente come estensione opzionale. In pratica, tutte le caratteristiche originali di OpenGL sono ancora supportati in versioni desktop di OpenGL e probabilmente continueranno ad essere disponibili in futuro. Sul lato sistema embedded, tuttavia, con OpenGL ES 2.0 e successivamente, l’uso di shaders è obbligatorio, e una grande parte della OpenGL 1.1 API è stato completamente rimosso. WebGL, la versione di OpenGL per l’utilizzo nei browser web, è basata su OpenGL ES 2.0, e richiede anche shader per ottenere nulla fatto. Tuttavia, inizieremo il nostro studio di OpenGL con la versione 1.1. La maggior parte dei concetti e molti dei dettagli da tale versione, sono ancora attuali, e offre un punto di ingresso più facile per qualcuno di nuovo alla programmazione grafica 3D.