Shader che passione!

Tempo fa gironzolando in rete, sono incappato in un trailer di Cuphead, mi ha colpito la cura dei dettagli di questo gioco, ho avuto la sensazione di essere stato catapultato in un cinema americano degli anni 30. Il trailer ha colori fantastici, il gioco è disegnato e colorato in stile anni 30 ed è visto con gli occhi di una persona degli anni 30. Mentre guardavo mi è balenato in mente: come vedrei Cuphead oggi nel 2017 ?

I filmati di quel tempo sono affetti da difetti delle pellicole cinematografiche dovuti al tempo ed all’uso: graffi, polvere, muffe, grasso delle dita e l’inarrestabile sindrome dell’aceto. Graffi e polvere sono già contenuti in Cuphead, anche nella migliore delle ipotesi di una perfetta conservazione in luogo fresco e asciutto ed assenza di ditate resta il problema della sindrome dell’aceto che è relativo alla emulsione delle pellicole a colori di quel periodo: il filmato inizia a perdere le componenti blu e verdi dell’immagine degradando lentamente verso il colore seppia e poi sempre più verso il rosso.

Vedremo come realizzare ed applicare questo effetto al gioco per mezzo di shader. In rete ci sono shader per tanti giochi, ma cosa sono e come si realizza uno shader ?

Il tutorial è rivolto a chi piacerebbe avvicinarsi al mondo degli shader ma non sa da dove iniziare. Cercherò di non utilizzare termini o concetti troppo specialistici, questo sarà un viaggio per fornire una metodologia ed i primi rudimenti di teoria ed applicazione dell’elaborazione delle immagini digitali.

INTRODUZIONE

Per creare un videogioco si applicano procedure (rendering) che permettono di generare mediante appositi programmi una immagine digitale a partire da un insieme di informazioni: descrizione degli oggetti tridimensionali, punto di vista, illuminazione, etc. Queste immagini non sono create direttamente a video, ma sono temporaneamente memorizzate nella memoria della scheda grafica ed inviate al monitor in un secondo momento. Prima di inviare le immagini al monitor queste possono essere ulteriormente modificate (post-processing) tramite l’implementazione di algoritmi, l’attività di implementazione degli algoritmi è detta “Shading”, lo specifico algoritmo implementato è detto “Shader”.

Bene! Adesso è arrivato il momento di sporcarci le mani: per fare i primi passi bisogna avere chiaro oltre che da un punto di vista intuitivo, che cosa è il colore, la luminosità ed il contrasto di una immagine anche da un punto di vista matematico/geometrico.

1. IMMAGINI IN TONI DI GRIGIO

Dette anche immagini in bianco e nero sono immagini con tonalità dal nero al bianco. Dal punto di vista digitale sono immagini a 8 bit (256 toni di grigio) dal Nero (valore 0) al Bianco (valore 255).
Rappresentiamo tale immagine per mezzo di un diagramma cartesiano (Fig.1.0) dove sulle ascisse mettiamo i toni di grigio dell’immagine originale e sulle ordinate i toni di grigio dell’immagine visualizzata. In questo spazio che diremo delle “immagini” i toni di grigio dell’immagine originale e quella visualizzata sono in corrispondenza biunivoca (1:1) e la relazione che li rappresenta è un segmento (insieme di punti discreti) che forma un angolo di 45° con l’asse delle ascisse.

Toni di grigio_IT

Fig. 1.0

1.1 LUMINOSITA’

La luminosità di una immagine è la quantità totale di luce che appare emettere. Controllare la luminosità di una immagine significa ottenere una immagine più chiara/scura rispetto all’immagine originale.

Nello spazio delle “immagini” corrisponde a traslare il segmento parallelamente a se stesso lungo l’asse delle ordinate. I toni di grigio sono però numeri positivi a 8 bit e questo comporta delle limitazioni. (Fig.1.1)
Traslare il segmento verso l’alto/basso corrisponde ad aumentare/diminuire il valore di tutti i toni di grigio della stessa quantità costante, questa operazione a seconda della immagine può comportare difetti di visualizzazione.

Toni di grigio-Luminosita_IT

Fig. 1.1

Guardiamo la traslazione verso l’alto indicata dalla freccia rossa con il segno “+” che corrisponde all’aumento della luminosità: nell’esempio si aggiunge 85 al valore dei toni di grigio dell’immagine visualizzata, ma i toni di grigio dell’immagine originale dopo il valore 170 sono tutti visualizzati con il valore 255 (bianco). L’aumento di luminosità aumenta la visibilità della immagine, ma a seconda della immagine da un certo punto in poi si perdono i dettagli essendo visualizzata completamente bianca, in questo caso l’aumento di luminosità è un difetto per la immagine visualizzata.

Guardiamo la traslazione verso il basso indicata dalla freccia rossa con il segno “-” che corrisponde ad una diminuzione della luminosità: nell’esempio si sottrae 85 al valore dei toni di grigio dell’immagine visualizzata, ma i toni di grigio dell’immagine originale tra 0 e 85 sono tutti visualizzati con il valore 0 (nero). La diminuzione di luminosità diminuisce la visibilità della immagine, ma a seconda della immagine da un certo punto in poi si perdono dettagli essendo visualizzata completamente nera, anche in questo caso la diminuzione di luminosità è un difetto per la immagine visualizzata.

1.2 CONTRASTO

Il contrasto in una immagine è la differenza di luminosità tra le parti luminose e le parti scure. Controllare il contrasto di una immagine significa ottenere una immagine in toni di grigio con maggiore/minore separazione delle tonalità rispetto all’immagine originale.

Nello spazio delle “immagini” corrisponde a ruotare il segmento rispetto all’origine degli assi cartesiani. I toni di grigio sono però numeri positivi a 8 bit e questo comporta delle limitazioni. (Fig.1.2)

Toni di grigio-Contrasto_IT

Fig. 1.2

Ruotare il segmento con angolo maggiore/minore di 45° corrisponde ad aumentare/diminuire la separazione tra i toni di grigio in proporzione dell’angolo scelto, questa operazione a seconda della immagine può comportare difetti di visualizzazione.

Guardiamo la rotazione antioraria indicata dalla freccia rossa con il segno “+” che corrisponde all’aumento di contrasto: nell’esempio i toni di grigio dell’immagine originale tra 0 e 85 sono visualizzati tra 0 e 255 e dopo il valore 85 sono tutti visualizzati con il valore 255 (bianco). L’aumento di contrasto aumenta la differenza di luminosità tra le parti luminose e le parti scure della immagine con la “espansione” dei toni di grigio in un rapporto di circa 1:3, questo significa che a 1 tono di grigio di differenza dell’immagine originale corrispondono 3 toni di grigio di differenza dell’immagine visualizzata, quindi i dettagli dell’immagine risultano più distinti a spese dei toni di grigio maggiori di 85. All’aumentare del contrasto a seconda della immagine da un certo punto in poi si perdono i dettagli diventando completamente bianca, questo è un difetto per l’immagine visualizzata.

Guardiamo la rotazione oraria indicata dalla freccia rossa con il segno “-” che corrisponde ad una diminuzione del contrasto: nell’esempio tutti i 256 toni di grigio dell’immagine originale sono visualizzati tra 0 e 85. La diminuzione di contrasto diminuisce la differenza di luminosità tra le parti luminose e le parti scure della immagine con la “compressione” i toni di grigio in un rapporto di circa 3:1, questo significa che a 3 toni di grigio di differenza dell’immagine originale corrisponde 1 solo tono di grigio dell’immagine visualizzata, quindi i dettagli dell’immagine risultano meno distinti, questo è un difetto per l’immagine visualizzata.

1.3 IMMAGINI A COLORI

I colori possono essere rappresentati i modi diversi, la rappresentazione che adotteremo sarà quella detta RGB. In questa rappresentazione tutti i colori sono definiti attraverso i tre colori fondamentali Rosso, Verde e Blu. Dal punto di vista di una immagine digitale un qualsiasi colore è indicato come RGB (rosso, verde, blu) dove rosso, verde e blu sono le sue componenti a 8 bit. (Fig.1.3)

Colori_IT

Fig. 1.3

Di tutto lo spettro dei colori siamo in grado di rappresentare 16777216 (256x256x256) colori. In una immagine digitale questi 24bit sono chiamati bit-colore. Nella figura in esempio il colore Arancione corrisponde alla terna di valori RGB(255,128,0).
Tutto quello che è stato detto a riguardo di luminosità e contrasto vale identicamente per le componenti dei colori. I toni di grigio sono un sottoinsieme dei colori le cui componenti hanno lo stesso valore: ad esempio il tono di grigio 127 corrisponde al colore RGB(127,127,127).

1.4 RIEPILOGO

Ora da un punto di vista matematico sappiamo che cosa è:

  • Una immagine in toni di grigio
  • La Luminosità
  • Il Contrasto
  • Una immagine a colori

Inoltre sappiamo controllare queste grandezze

  • la luminosità: si aumentano/diminuiscono tutti i toni di grigio della stessa quantità e sappiamo che a seconda dell’immagine questo potrebbe causare la perdita di dettagli. La variazione di luminosità comporta che l’angolo del “segmento” può assumere solo due valori 0° e 45°.

  • Il contrasto: si aumenta/diminuisce la separazione tra i toni di grigio in proporzione dell’angolo scelto e sappiamo che a seconda dell’immagine questo potrebbe causare la perdita di dettagli/visibilità. La variazione del contrasto comporta che l’angolo del “segmento” può assumere valori compresi tra 0° e 90°, in particolare 45° è l’angolo che permette la corrispondenza biunivoca tra i toni di grigio dell’immagine originale e quella visualizzata.

Nota: queste nozioni di base sono già sufficienti per eseguire una semplice calibrazione del monitor attraverso i suoi controlli di luminosità e contrasto progettando opportune immagini di calibrazione e senza strumentazione specifica oppure verificare per mezzo delle suddette immagini la calibrazione eseguita con strumentazione specifica.

2. GLI SHADER

Agli albori i linguaggi di programmazione erano in assembly completamente orientati allo hardware della macchina. In seguito ci furono diverse standardizzazioni dei linguaggi e la creazione di uno strato software (API, interfaccia di programmazione di un’applicazione) tra il software di rendering e le GPU con sintassi C/C++ spostando la complessità del codice assembly alla fase di compilazione del codice.

I linguaggi grafici hanno avuto origine a partire dal 1982 nella SGI (Silicon Graphics Incorporated) azienda sviluppatrice di terminali grafici che sviluppò successivamente la workstation grafica denominata IRIX. Sino a metà degli anni 90 openGL di SGI fu lo standard di fatto. A metà anni 90 Microsoft adottò OpenGL come sistema 3D per Windows NT. A breve Microsoft acquistò RenderMorphics la cui Reality Lab creò una libreria 3D standardizzata orientata alla scrittura di videogiochi denominata Direct3D.

Microsoft, SGI e HP-MS fecero uno sforzo di unificazione di Direct3D e OpenGL che produsse XGS (sistema Fahrenheit) che però fallì dopo breve tempo.

Attualmente i linguaggi di programmazione di shading sono due:

  • GLSL (OpenGL Shading Language) linguaggio per shading OpenGL
  • HLSL (High Level Shading Language) per shading Direct3D

Sono i linguaggi utilizzati sia da chi scrive shader nelle softwarehouse e sia dai programmatori di shader indipendenti dal produttore del videogioco.
In realtà c’è un terzo linguaggio “Cg” che però è di più alto livello il cui compilatore può generare sia codice OpenGL che HLSL.
Questi sono sono utilizzati per modificare l’aspetto visivo di un videogioco (“modding”)

La progettazione e l’uso degli shader consiste di tre fasi:

  1. Scrittura delle FUNZIONI di shading

    per creare un effetto grafico bisogna avere la giusta preparazione tecnica nel campo della elaborazione delle immagini digitali e la conoscenza dei linguaggi GSLS/HLSL. Queste funzioni sono scritte da professionisti del settore.

  2. Configurazione dei PARAMETRI numerici utilizzati dalle funzioni di shading

    L’effetto grafico delle funzioni di shading può essere reso più facilmente configurabile (dal programmatore delle funzioni di shading) attraverso il passaggio di valori numerici alle funzioni. Il lavoro principale dei modder che realizzano specifici preset per i giochi è quello di stabilire gli opportuni valori dei parametri per ottenere l’effetto grafico desiderato. Noi lo faremo con Cuphead.

  3. ATTIVAZIONE degli shader

    Abbiamo il gioco e le funzioni di shading, come inserire gli shader nel gioco?
    Il metodo utilizzato è quello detto di “iniezione”. Si utilizza un automatismo di Windows: quando un eseguibile richiama una funzione di una qualsiasi libreria, Windows prima cerca questa libreria nella directory dove è presente l’eseguibile, se non la trova continua la ricerca nelle directory di sistema. Quindi per eseguire gli shader bisogna mettere nella directory dove è presente l’eseguibile del programma le nuove librerie i cui nomi ed estensioni sono gli stessi delle librerie originali, le nuove librerie contengono anche gli shader opportunamente richiamati. Questa attività può essere eseguita manualmente oppure tramite un programma detto di “iniezione” degli shader nel gioco.

Esistono shader scritti da programmatori indipendenti: ENBSeries, GemFX, SweetFX e loro derivazioni. Sia per potenzialità che per facilità d’uso, non è un caso che ci siano una pletora di preset, useremo SweetFX di “CeeJay” e l’iniettore Reshade di “Crosire”. SweetFX è uno shader universale per modding ed a partire dalla versione 2.0 supporta da DirectX 8 a DirectX 11 e OpenGL, inoltre supporta le versioni di Windows x32/x64. ReShade è il più avanzato iniettore esistente. A partire da Reshade versione 3.0 l’iniettore integra SweetFX, è compatibile con DirectX 12 ed ha un proprio menu nel gioco.

L’obiettivo del tutorial è di tipo istruttivo ed è finalizzato all’uso degli shader, quindi non useremo Reshade 3.0, ma la precedente versione di uso più chiaro “ReShade 2.0.3 with SweetFX 2.0” resa deprecata a partire da Reshade 3.0. Questa versione si trova ancora in rete, ma non sul sito ufficiale.

Decomprimiamo ReShade 2.0.3 with SweetFX 2.0 (.rar, 1367 Kb), all’interno del file compresso troveremo:

SweetFX and Reshade_Files

Fig. 2.0

Il codice degli shader disponibili è contenuto nella sotto-directory “Shaders”. Il file che modificheremo è “SweetFX_settings.txt” ed il programma iniettore è “ReShade Setup.exe”.

2.1 SWEETFX

Il file è composto da tre sezioni:

  1. Description: In questa sezione sono riportate le note dell’autore.

  2. Choose effects: Qui si decide quale effetto applicare, i valori possibili sono “1” per lo shader abilitato e “0” per lo shader disabilitato. Quando si modifica l’aspetto di un videogioco il modo corretto di procedere è quello di disabilitare tutti gli effetti e poi abilitare uno alla volta solo quelli che si utilizzano.

  3. Effect settings: Qui ci sono i parametri degli effetti utilizzati dallo shader, modificando questi parametri l’effetto cambia. Per ogni parametro c’è la descrizione, sono indicati i valori minimo/massimo ed il valore predefinito. Alcuni parametri sono di facile comprensione mentre per altri è meno semplice, volta per volta si cercano le informazioni necessarie per capire cosa fa quel parametro e poi si verifica l’effetto.

2.1.1 EFFETTO “SINDROME DELL’ACETO”

All’inizio dell’articolo abbiamo detto di volere applicare l’effetto sindrome dell’aceto o effetto seppia a Cuphead. Per realizzare un effetto non c’è un modo unico di procedere. Bisogna fare alcune prove e poi in base all’esperienza scegliere quello che realizza l’effetto migliore: prima desaturiamo completamente l’immagine in modo neutrale rispetto ai colori RGB trasformandola in toni di grigio e poi la coloriamo con la tonalità di colore che sceglieremo. Per fare questo utilizzeremo i due shader “MONOCHROME” per desaturare le immagini e “SEPIA” per colorarle. Possiamo editare il file “SweetFX_settings.txt” con un qualsiasi editor di testo come il Blocco Note di Windows.

2.1.1a SHADER “MONOCHROME”

Nella sezione “Choose effect” prima di tutto disabilitiamo gli shader non utilizzati (fissando il valore a 0) ed abilitiamo lo shader MONOCHROME:

  • #define USE_MONOCHROME 1 //[0 or 1] Monochrome : Monochrome makes the colors disappear.

Nella sezione “Effect settings” fissiamo il valore dei parametri dello shader MONOCHROME:
/*————————————————————.
/—————Monochrome settings—————/
‘————————————————————*/

  • Monochrome_conversion_values float3(0.33,0.33,0.33) //[0.00 to 1.00] Percentage of RGB to include (should sum up to 1.00)

  • Monochrome_color_saturation 0.00 //[0.00 to 2.00] Percentage of saturation to keep. Default is 0.00, values above 1.00 boost saturation above normal.

Con il primo parametro desaturiamo le componenti RGB in modo “neutrale” (tutte e tre al 33%). A seconda delle immagini si potrebbero ottenere risultati migliori/peggiori privilegiando la desaturazione di una componente a scapito di un altra.
Con il secondo parametro stabiliamo che ogni pixel dell’immagine è completamente desaturato.

2.1.1b SHADER “SEPIA”

Nella sezione “Choose effect” abilitiamo lo shader SEPIA:

  • #define USE_SEPIA 1 //[0 or 1] Sepia : Sepia tones the image.

Nella sezione “Effect settings” fissiamo il valore dei parametri dello shader SEPIA:
/*————————————————————.
/———————Sepia settings———————/
‘————————————————————*/

  • ColorTone float3(1.12, 0.66, 0.20) //[0.00 to 2.55, 0.00 to 2.55, 0.00 to 2.55] What color to tint the image

  • GreyPower 0.00 //[0.00 to 1.00] How much desaturate the image before tinting it

  • SepiaPower 0.60 //[0.00 to 1.00] How much to tint the image

Lo shader si chiama “SEPIA” con il valore predefinito dell’autore. Ovviamente cambiando il valore del primo parametro (ColorTone) possiamo utilizzare qualsiasi colore, come punto di partenza consideriamo una tipica colorazione seppia indicata dalle coordinate RGB (112, 66, 20).
Con il secondo parametro (GreyPower) scegliamo quanto desaturare l’immagine. L’immagine è stata già desaturata con lo shader MONOCHROME, quindi mettiamo questo parametro a 0.00. Avremmo potuto fare tutto con lo shader SEPIA, ma lo shader MONOCHROME ha in più il controllo sulla desaturazione delle singole componenti RGB dell’immagine, ai fini del tutorial e per i valori scelti per i parametri è solo l’occasione per vedere come configurare gli shader.
Con il terzo parametro (SepiaPower) scegliamo l’intensità della tonalità di colore.

Dopo avere salvato le modifiche apportate al file è giunto il momento di iniettare gli shader.

2.2 RESHADE

  1. Eseguiamo “ReShade Setup.exe”, si aprirà la finestra di dialogo in Fig.2.1 (Shot 1)

  2. Reshade cercherà di riconoscere tramite l’eseguibile del gioco/applicazione la versione DirectX/OpenGL utilizzata, è sufficiente cliccare su “Select Game” per selezionare l’eseguibile del gioco/applicazione. Se il riconoscimento automatico avrà successo copierà nella directory del gioco/applicazione i file indicati in Fig.2.1 (Shot da 2 a 5)

  3. Se il riconoscimento automatico fallisce (Fig.2.1 – Shot 6) allora è necessario selezionare manualmente la versione DirectX/OpenGL

  4. Dopo la selezione di DirectX/OpenGL cliccare su “Run Game”

  5. Per disinstallare gli shader è sufficiente cancellare i file indicati in Fig.2.1 (Shot da 2 a 5)

ReshadeAutodetect-BOT
ReshadeDirect3D8-BOT
ReshadeDirect3D9-BOT
ReshadeDirect3D10-11-BOT
ReshadeOpenGL-BOT
ReshadeFailed-BOT

Fig. 2.1

RACCOMANDAZIONI:

  • Non bisogna installare gli shader nelle directory di sistema di Windows

  • Bisogna disabilitare gli stessi shader presenti nelle opzioni di gioco in quanto potrebbero entrare in conflitto con risultati imprevedibili

  • Attualmente per alcuni giochi online sono presenti sistemi “anticheater” che impediscono l’esecuzione del gioco e nei casi estremi portare al ban del giocatore. Reshade come anche i sistemi di benchmark potrebbero essere erroneamente riconosciuti come sistemi di “cheat”. Bisogna informarsi prima di utilizzarli.

3. CUPHEAD “MODDING”

Immagini del gioco originali a colori:

0.11 Color-BOT
0.15 Color-BOT
0.20 Color-BOT
0.25 Color-PNG
0.27 Color-BOT
0.41 Color-BOT

Fig. 3.1

In queste immagini possiamo vedere l’effetto del nostro filtro “seppia”: ColorTone float3(1.12, 0.66, 0.20)

0.11-Sepia1-BOT
0.15-Sepia1-BOT
0.20-Sepia1-BOT
0.25-Sepia1-BOT
0.27-Sepia1-BOT
0.41-Sepia1-BOT

Fig. 3.2

La sindrome dell’aceto in stato avanzato consiste nella degradazione delle componenti verde e blu ed esaltazione della componente rossa: ColorTone float3(1.60, 0.50, 0.00)

0.11 Sepia2-BOT
0.15 Sepia2-BOT
0.20 Sepia2-BOT
0.25 Sepia2-BOT
0.27 Sepia2-BOT
0.41 Sepia2-BOT

Fig. 3.3

Questo è un effetto “steampunk” di un vecchio televisore widescreen bianco e nero. Solitamente le immagini dei vecchi televisori bianco e nero hanno tonalità bluastre: ColorTone float3(0.70, 1.40, 2.20)

0.11 BW-BOT
0.15 BW-BOT
0.20 BW-BOT
0.25 BW-BOT
0.27 BW-BOT
0.41 BW-BOT

Fig. 3.4

LINK UTILI

Homepage ufficiale di SweetFX ( Autore: CeeJayDK )

Homepage ufficiale di Reshade ( Autore: Crosire )

Il manuale definitivo per l’uso di SweetFX

Homepage ufficiale dello “Studio MDHR” creatore di Cuphead

eNJoy aND STay TuNeD WiTH uS!

Raffaele “MOS” Sanapo

DISCLAIMER
Questo è un tutorial con finalità didattiche e le informazioni sono fornite “così come sono”. I link ai contenuti verso siti web di terzi sono forniti soltanto per vostra comodità e informazione. Non c’è alcun controllo e non si assume alcuna responsabilità dei contenuti o siti web altrui che sono collegati. Si declina ogni responsabilità per qualsiasi perdita, costo, danno o altro diretto o indiretto derivante dall’uso del tutorial. L’uso delle informazioni ottenute o scaricate da questo tutorial è a vostro esclusivo rischio e pericolo. Tutti i nomi di prodotti, loghi e marchi sono di proprietà dei legittimi proprietari. Tutti i nomi di aziende, prodotti e servizi utilizzati in questo tutorial sono solo a scopo di identificativo. L’ uso di questi nomi, loghi e marchi non implica approvazione degli stessi.

Raffaele Sanapo

“We don't stop playing because we grow old; we grow old because we stop playing.” (George Bernard Shaw)

%d blogger hanno fatto clic su Mi Piace per questo:

Utilizzando il sito, accetti l'utilizzo dei cookie da parte nostra. maggiori informazioni

Questo sito utilizza i cookie per fornire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie, cliccando su "Accetta" o continuando la navigazione permetti il loro utilizzo.

Chiudi