Home Page Home Page Articoli Gamma Correction e Colori

Gamma Correction e Colori

Una trattazione breve ma con una base matematica che spiega in parole povere il problema del fattore gamma e i colori in generale
Autore: Stefano Cristiano Livello:
Colori
I colori in computer grafica sono perfettamente assimilabili ad uno spazio geometrico. Se avete mai usato un programma di disegno vi sarete sicuramente imbattuti nei colori dello spazio RGB, dove al posto degli assi X,Y e Z ci sono i tre colori Rosso Verde e Blu. Questo spazio è molto comodo per i nostri computer perché i colori sono additivi (sommando due mezzi rossi si ottiene un rosso pieno) ma sono dipendenti dal dispositivo di visualizzazione (monitor, stampante etc.). Parte di questi problemi possono essere risolti attraverso la Gamma Correction (vedi paragrafo Gamma Correction).

Nello spazio RGB ogni colore ha 3 coordinate o componenti ognuna delle quali indica l’intensità del colore associato (cioè i “canali” Rosso, Verde o Blu). Combinando questi tre colori nelle loro diverse intensità si ottengono tutti gli altri colori. Molto spesso si utilizza una singola variabile a 32 bit per rappresentare un colore. Se assegniamo ad ogni canale 256 possibili gradi di intensità vuol dire che ci bastano 8 bit per rappresentarlo (2 elevato alla 8 fa 256) e in 24 bit ci stanno tutti e tre i canali. Perfetto, una singola variabile DWORD può contenere queste informazioni e lascia spazio anche ad altri 8 bit che di solito vengono utilizzati per il canale alpha. Il canale alpha contiene informazioni sulla Trasparenza del colore. Con il canale alpha è possibile creare effetti di dissolvenza o più in generale controllare le proprietà degli oggetti trasparenti. Vedremo in futuro come la gestione delle trasparenze non sia proprio banale poiché durante il disegno se viene cambiato l’ordine di disegno di tali superfici il risultato potrebbe non essere quello voluto.

Facciamo qualche esempio di colore, utilizzando il codice esadecimale. Un numero 32 bit in esadecimale è scritto nella forma 0xXXXXXXXX. Partendo dai bit più significativi il formato in cui Direct3D interpreta informazioni sui colori è 0xAARRGGBB (con A=alpha, R=Red, G=Green, B=Blue). Vogliamo un rosso pieno? 0xFFFF0000. Un giallo? 0xFFFFFF00. un Blu semitrasparente ? 0x800000FF. Consiglio a tutti di prendere la mano con questa rappresentazione dei colori, magari utilizzando qualche programmino di grafica (come ad esempio il Paint Shop Pro).

In Direct3D si utilizzando le variabili di tipo D3DCOLOR per salvare le informazioni sul colore (che poi è una typedef per DWORD), attraverso le macro D3DCOLOR_ARGB o D3DCOLOR_XRGB.


D3DCOLOR blu_pieno = D3DCOLOR_XRGB(0,0,255);
D3DCOLOR rosso_semitrasparente = D3DCOLOR_ARGB(128,255,0,0);


Un altro modo di rappresentare i colori è nella forma floating point. Invece di assegnare 256 gradi di intensità per canale si assegna una variabile 32 bit float che può variare tra 0 e 1.

Lo zero ovviamente sta per intensità nulla mentre 1 sta per intesità piena. In pratica si assegna una “percentuale” di intensità al colore e questo permette, soprattutto nei calcoli di illuminazione, di ottenere una maggiore precisione. Direct3D usa la struttura D3DCOLOR_VALUE definita come segue.


typedef struct _D3DCOLORVALUE {
float r;
float g;
float b;
float a;

}D3DCOLORVALUE;


Una macro che permette di passare da una rappresentazione all’altra è la seguente:


#define D3DCOLOR_COLORVALUE(r,g,b,a) \
D3DCOLOR_RGBA((DWORD)((r)*255.f), \
(DWORD)((g)*255.f), \
(DWORD)((b)*255.f), \
(DWORD)((a)*255.f))



Questa macro esprime sinteticamente il rapporto tra le due rappresentazioni.

Altre rappresentazioni del colore
In alcuni casi, 32 bit per rappresentare un singolo colore potrebbero essere “troppi”, nel senso che potremmo non aver bisogno di tanta precisione nella rappresentazione delle sfumature. Potrebbe anche capitare di aver bisogno di maggior precisione su di un canale e meno su un altro e così via. Quello che sto cercando di dire è che non esiste solo il formato A8R8G8B8, ma questo è solo uno dei possibili formati. Se per esempio decidiamo di usare una variabile WORD per salvare le informazioni sul colore, avendo a disposizione 16 bit potremmo assegnarne quattro per canale (compreso alpha). Con 4 bit quante sfumature di colore per canale sono possibili? Ovviamente due alla quarta, cioè 16 tonalità: questo formato si chiama A4R4G4B4. Facciamo un esempio, immaginiamo di avere le singole componenti dei canali di un colore nella solita forma, dove ogni componente è compresa tra 0 e 255. Quello che dobbiamo fare è una semplicissima proporzione del tipo:

Canale8Bit: 255 = Canale4Bit: 15

Ovviamente da effettuarsi per ogni componente (Rosso,Verde,Blu e Alpha). La componente del canale nella nuova rappresentazione si ottiene ovviamente con:

Canale4Bit = Canale8Bit*15/255

In pratica scaliamo i nostri colori dall’intervallo [0 - 255] all’intervallo [0 – 15]. Una volta convertite le singole componenti dobbiamo assemblare il colore in una variabile WORD 16 bit, effettuando gli opportuni shift per posizionare la singola componente nel canale appropriato.

Ecco un esempio:


BYTE r = 125;
BYTE g = 225;
BYTE b = 0;
BYTE a = 212;

BYTE newA = (float)a*15.0f/255.0f;
BYTE newR = (float)r*15.0f/255.0f;
BYTE newG = (float)g*15.0f/255.0f;
BYTE newB = (float)b*15.0f/255.0f;

WORD color = WORD((newA<<12)|(newR<<8)|(newG<<4)|newB);



Se volessimo tornare indietro, dalla rappresentazione WORD a quella nelle singole componenti ci basta fare l’inverso:


WORD color=...; //Un colore qualsiasi

BYTE a = (UCHAR)( (float)((color>>12)&0xF)*255.0f/15.0f);
BYTE r = (UCHAR)( (float)((color>>8)&0xF)*255.0f/15.0f);
BYTE g = (UCHAR)( (float)((color>>4)&0xF)*255.0f/15.0f);
BYTE b = (UCHAR)( (float)((color)&0xF)*255.0f/15.0f);


Prima shiftiamo il canale dalla sua posizione, poi usiamo una maschera per isolare la singola componente e la scaliamo per l’inverso del fattore che abbiamo usato nel precedente listato.

Se invece avete deciso di usare la rappresentazione floating point dei colori, vi basta eliminare tutte le moltiplicazion per 255 presenti nei listati e vivrete felici.

L’enumerazione D3DFORMAT definisce i formati di colore supportati da DirectXGraphics, e se vi servono delle funzioni per convertire in lettura/scrittura dai vari formati potete dare un’occhiata al file DXGColors.cpp presente in _Framework\Utility, che fa il noioso lavoro fatto per il formato A4R4G4B4 per i formati di colore più diffusi.

In generale se non avete bisogno di leggere/scrivere pixel in formati diversi ad ogni frame (tecnica che comunque non vi consiglio per ragioni di performance) potete usare le funzioni D3DXLoadSurfaceFromXXXXXX presenti in D3DX che saranno analizzate nel prossimo capitolo. Queste funzioni si prendono il fastidio di effettuare tutte le conversioni da e verso i formati supportati.

Gamma Correction
Un altro problema che affligge gli schermi dei nostri PC è il cosiddetto fattore Gamma. Sicuramente utilizzando un programma di grafica vi sarete imbattuti nella frase “Gamma Correction” dove cambiando un valore o aggiustando manualmente una curva davate più o meno “luminosità” all’immagine. Bene: molto probabilmente l’idea di Gamma Correction che vi siete fatti è errata.



Partiamo da un’asserto: in computer grafica i colori vengono trattati come se fossero lineari. Con questo voglio affermare che se sommiamo un mezzo verde con un altro mezzo verde otteniamo un verde pieno. In teoria questo dovrebbe essere vero ma in pratica non lo è.

Tutti i monitor o display che dir si voglia NON si comportano in maniera lineare. Raddoppiare l’intensità di voltaggio NON significa ottenere effettivamente a schermo un colore il doppio più luminoso del precedente.

Il termine gamma proviene dall’equazione che si usa per descrivere l’intensità in uscita di un Tubo Catodico per un dato voltaggio applicato:



Il termine gamma compare all’esponente e serve proprio per indicare questa non linearità di risposta del monitor agli input ricevuti. La “c” è una costante fissa diversa da monitor a monitor che non c’interessa.

Se questo termine Gamma fosse sempre costante potremmo tutti vivere felici e contenti. La sfortuna vuole però che esso vari tra circa 1.4 e 2.6 nei monitor dei nostri PC. Questo vuol dire che il gioco da noi programmato potrebbe risultare “troppo scuro” o “troppo chiaro” quando tenteremo di farlo girare su altri PC. Grazie al cielo DirectX ci viene in aiuto attraverso una serie di funzioni.

In primo luogo è possibile effettuare “legalmente” la correzione gamma solo quando l’applicazione è in fullscreen. Questa considerazione è del tutto personale giacchè la documentazione delle DirectX non è molto chiara a riguardo. Che cosa succede a due applicazioni in finestre separate che vogliono settare un fattore gamma diverso? Lo schermo inizia a lampeggiare? Beh, quando siamo in finestra è meglio lasciar fare la gamma correction a GDI.

Il primo passo da fare è controllare se la scheda video supporta il cambio di gamma:


D3DCAPS8 caps;
g_device->GetDeviceCaps(&caps);
if(caps.Caps2&D3DCAPS2_FULLSCREENGAMMA)
//Il device supporta il gamma a tutto schermo



Una volta che ci siamo accertati di questo riempiamo una struttura di tipo D3DGAMMARAMP, costituita da tre array di WORD (ognuno di 256 elementi) che ci permettono di “rimappare” i colori a nostro piacimento.


typedef struct _D3DGAMMARAMP
{

WORD red [256];
WORD green[256];
WORD blue [256];

} D3DGAMMARAMP;


Possiamo quindi effettuare la correzione gamma per ogni singolo canale di colore. Questo perché la risposta del monitor potrebbe variare da colore a colore. Per semplicità tratteremo solo il gamma “classico” uniforme, impostando sempre uguali i fattori di correzione per tutti i canali. Ogni colore può essere corretto attraverso un valore WORD quindi abbiamo 16 bit di precisione. Quale fattore di correzione scegliere? Semplice, lo facciamo scegliere all’utente. Il programma “Gamma Correction” mostra come. Vengono disegnati a schermo due rettangoli, di cui uno riempito con un mezzo grigio un altro fatto di rettangoli alternati bianchi e neri di larghezza un pixel. Guardando da una certa distanza il quadrato a righe alterne bianche e nere, il nostro occhio dovrebbe percepire esattamente la metà dell’intensità massima che il nostro monitor è in grado di gestire. Confrontando questa luminosità con quella del quadrato grigio di lato vi accorgerete che uno vi sembrerà più scuro dell’altro, nonostante TEORICAMENTE non dovrebbe essere così. Con i tasti potete aggiustare il fattore gamma e trovare quello più adatto al vostro monitor.

Per poter meglio capire cosa fa il codice del programma analizziamo meglio il nostro problema.



Se la risposta del monitor fosse lineare (qundi gamma uguale a 1) il fattore gamma_grigio dovrebbe essere pari a 128. Sul mio monitor tale fattore è uguale a 188.

Il codice dell’esempio è una diretta trasposizione di queste equazioni, prende il reciproco del valore di gamma ed eleva tutti i colori a 1/gamma (scalando per 65535 che rappresenta i 16 bit di precisione, massimo valore inseribile in una WORD):


float gamma(UINT i)
{
return logf(0.5f)/logf(i/255.f);
}

void UpdateGammaRamp()
{

float correction_gamma = 1/gamma(g_gamma);
for (UINT i = 0; i < 256; i++)
{

g_ramp.red = g_ramp.green = g_ramp.blue =
(int)(65535*pow(i/255.f, correction_gamma));
}


g_device->SetGammaRamp(D3DSGR_NO_CALIBRATION,&g_ramp);
}


Il discorso di calibrazione del monitor è in realtà abbastanza più complesso, ma per le applicazioni videoludiche questo è ampiamente più di quello che ci serve. Alcuni usano il Gamma per creare effetti di dissolvenza di scena, ma esistono metodi migliori per farlo.Da notare che la classe DXGGammaDeviceConfig non fa altro che quello qui descritto, mostrando la dialog citata precedentemente. Il codice sembra lungo ma in realtà è solo noiosissima e pallosissima gestione di controlli dialog necessari alla regolazione in maniera interattiva, il codice che effettua praticamente la gamma correction è quello qui presentato.
Voto medio articolo: 5.0 Numero Voti: 2
Stefano Cristiano

Stefano Cristiano

3D Coder. Sto lavorando ad un engine chiamato "Muse Engine" di cui potete leggere al mio sito http://pagghiu.gameprog.it Profilo completo

Articoli collegati

Integrare le Direct3D 8.1 con MFC Usando Visual Studio 6.0
In questo articolo mostreremo come creare una semplice applicazione SDI (Single Document Interface), in cui mostrare un oggetto tridimensionale in formato *.X e di farlo ruotare intorno all'asse Y.
Autore: Biagio Iannuzzi | Difficoltà:
Particle System
Brevi cenni su come implementare un particle system in DirectX
Autore: Stefano Cristiano | Difficoltà: | Voto:
Texture Blending (Parte II) Quake 3 Arena Shader System
Questo articolo è simile alla Parte I e ripete molti aspetti però tratta anche un pò degli shaders di Quake 3 Arena.
Autore: Stefano Cristiano | Difficoltà: | Voto:
Texture Blending (Parte I)
Come gestire gli effetti di blending tra texture. Multitexturing e Multipassing. Effect Files
Autore: Stefano Cristiano | Difficoltà: | Voto:
Sprite
Come creare uno "sprite" e animarlo. Special thanks to Elevator2 per gli sprites di megaman
Autore: Stefano Cristiano | Difficoltà: | Voto:
Texture
Introduzione al concetto di texture, di coordinate texture. Come animare le coordinate texture
Autore: Stefano Cristiano | Difficoltà:
Grafica 2D in DirectX. Vertex Buffers
Introduzione alla grafica 2D. Utilizzo dei vertex buffers
Autore: Stefano Cristiano | Difficoltà: | Voto:
Debbugging di applicazioni DirectX
Consigli e strategie per il Debugging delle applicazioni DirectX.
Autore: Stefano Cristiano | Difficoltà: | Voto:
Framework DXGWrapper
Spiega la filosofia di fondo del framework ideato dal sottoscritto ed usato da tutti i samples degli articoli relativi a DirectX.
Autore: Stefano Cristiano | Difficoltà: | Voto:
Inizializzazione e schermo
Si parla dell'inizializzazione dello schermo e delle modalità video in DirectX e di come gestire gli eventi "lost device", come scrivere un Game Loop e alcune info sul VSYNC
Autore: Stefano Cristiano | Difficoltà: | Commenti: 1 | Voto:
Introduzione a DirectX
Introduzione al mondo DirectX , requisiti, tools necessari, configurazione dell’ambiente di sviluppo.
Autore: Stefano Cristiano | Difficoltà: | Commenti: 2 | Voto:
Copyright © dotNetHell.it 2002-2017
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5