D3 Q4 Modding - livello avanzato

Pixel Shader
Creazione di un effetto grafico
tramite Pixel Shader\Fragment Program


0. Introduzione al tutorial.

In questo tutorial impareremo cosa sia un “Pixel Shader” e come realizzare un semplice effetto di shading sui pixel sfruttando il sistema supportato da tutti i giochi basati sul motore Id Tech4, anche chiamato Trinity, che sono Doom3, Quake4 e Prey. Le informazioni in questo tutorial sono quindi compatibili con tutti e tre i giochi appena citati.


1. Introduzione al Pixel\Vertex Shader



In modo molto semplice diciamo che con Pixel e/o Vertex Shader si intende uno speciale programma realizzato in HLSL (High Level Shading Language) o in GLSL (OpenGL Shading Language) utilizzato per la modifica di pixel e/o vertici in tempo reale all’interno di un motore grafico, utile per creare alcuni effetti speciali, tra i quali l’acqua e vari effetti di luce. Ad oggi esistono varie versioni di Pixel e Vertex Shader, che sono direttamente implementati nelle librerie DirectX (e compatibili con le librerie OpenGL dalla versione 2.0) e nelle GPU. Per questo motivo c’è da notare, e far presente ad un possibile utilizzatore del nostro shader (o prodotto che lo utilizza), la versione dello shader (“Model Shader” per indicare sia Pixel che Verter Shader) che si utilizza. In questo elenco, qui sotto, sono riportate le varie versioni di Pixel Shader e le versioni della libreria DirectX a cui sono abbinati.

Versione Pixel Shader Versione DirectX
1.0/1.1 8.0
1.2/1.3 8.0a
1.4 8.1
2.0/a/b 9.0/b
3.0 9.0c
4.0 10.0


Pertanto, non sarà possibile utilizzare uno shader realizzato seguendo le specifiche del Pixel Shader 4.0 su hardware che non supportino le DirectX 10.0.

In OpenGL, libreria su cui è basato il motore grafico Trinity, non esistono più versioni di Pixel Shader (PS), vengono infatti sfruttate le versioni (e le regole\features da esse introdotte) degli shader delle DirectX ma il programma viene creato utilizzando il GLSL, che è il linguaggio di shading delle OpenGL introdotto dalla OpenGL ARB, anche denominato Fragment Program (FP).

   

2. Creazione del Material Shader

Per prima cosa, per sfruttare un Pixel e/o Vertex Shader è necessario creare una Material Shader, un materiale, che verrà utilizzato dal motore grafico per legare lo shader vero e proprio ad un brush o un modello 3d.

Il modo più semplice è creando una nuova cartella dentro alla cartella “base” del gioco su cui realizziamo il nostro Pixel Shader (“base” per Doom3 e Prey, “q4base” per Quake4) chiamandola “materials”; attenzione è obbligatorio che il nome della cartella sia proprio “materials”.

Una volta creata la cartella si procede creando un semplice file di testo contenente le informazioni per il materiale, quindi si apre il Blocco Note di Windows, e si scrive quanto riportato qui sotto all’interno del file:

textures/shaders/shader_test
{
    {
    vertexProgram shader_test.vfp
    fragmentProgram shader_test.vfp
    fragmentMap 0 _currentRender
    }
}

 E’ possibile cambiare alcune cose nello shader, anche se io consiglio di lasciare, almeno per questo primo test, esattamente tutto come è scritto in questo tutorial, renderà il processo più semplice.

Tra le cose modificabili troviamo per esempio la locazione in cui troveremo il Material Shader nell’editor (textures/shaders/shader_test), il nome del file che contiene il nostro Pixel e/o Vertex Shader (shader_test.vfp), il numero della texture temporanea che useremo nel nostro Pixel Shader (0) e l’input, sempre del nostro Pixel Shader, (_currentRender).

Ora dobbiamo salvare il file nella cartella “materials” che abbiamo creato prima; bisogna però ricordarsi di salvare il file non come file di testo (ovvero con estensione .txt) ma con l’estensione .mtr. Per fare ciò si può impostare nella finestra di salvataggio il valore “Tutti i file” nella riga “Salva come”, posto immediatamente sotto allo spazio in cui si inserisce il nome con cui salvare il file, e poi immettere come nome del file “testshader.mtr”. Oppure si può salvare il file con estensione .txt, quindi apparirebbe come “testshader.txt” e rinominare il file cambiandogli l’estensione in .mtr da Esplora Risorse (bisogna aver la visualizzazione delle estensioni attiva).

Ora il nostro Material Shader è pronto.
   

3. Creazione e analisi dello Shader grafico

Lo shader grafico è ciò che effettua il vero lavoro per creare il nostro effetto grafico; per farlo funzionare dobbiamo creare una cartella in “base” (“base” per Doom3 e Prey, “q4base” per Quake4) e chiamarla “glprog”, non c’è possibilità di cambiargli nome, poi, all’interno della nuova cartella va fatto come per il Material Shader, dobbiamo aprire blocco note, incollarci il codice qui sotto riportato e salvare il file, questa volta col nome “shader_test.vfp”. Questo file l’abbiamo dichiarato all’interno del Material Shader (vedere il codice poco sopra) può avere qualsiasi nome, a patto che nel Material Shader vi sia riportato il medesimo, sennò non verrà collegato al materiale.


#--------------------------
!!ARBvp1.0
OPTION ARB_position_invariant;
END
#--------------------------

!!ARBfp1.0
OPTION ARB_precision_hint_fastest;

TEMP temp1, temp2;
MUL temp1, fragment.position, program.env[1];
MUL temp1, temp1, program.env[0];
TEX temp2, temp1, texture[0], 2D;
MUL temp2, temp2, 5;
MOV result.color, 0;
MOV result.color.y, temp2;

END

Ogni shader contiene una parte dedicata ai vertici (Vertex Shader) e una ai Pixel (Pixel Shader), in questo tutorial ci siamo concentrati solo sul Pixel Shader per effettuare un piccolo lavoro di modifica dei pixel.

Lo shader effettua alcuni semplici passi, prende l’immagine renderizzata dal motore, la schiarisce un po’ e ne legge solo il colore del canale verde, in questo modo, il risultato è un oggetto di colore verde luminoso che sembra semitrasparente, ma che in realtà non è. In un futuro tutorial analizzeremo le istruzioni più comuni e il loro utilizzo.

Analisi:

#--------------------------
!!ARBvp1.0
OPTION ARB_position_invariant;
END
#--------------------------

In questa parte di codice bisognerebbe scrivere il Vertex Shader, ma dato che a noi non interessa, almeno in questo tutorial, non immettiamo alcuna riga di codice tra la riga d’inizio “!!ARBvp1.0” (notare che ARBvp indica “ARB vertex program”) e quella di fine “END” eccetto quella standard “OPTION ARB_position_invariant;” che indica che non muoviamo alcun vertice.


!!ARBfp1.0
OPTION ARB_precision_hint_fastest;

 Codice iniziale del Pixel Shader (notare che “ARBfp” indica “ARB fragment program”), la seconda riga indica che non ci serve un calcolo ad alta precisione ma puntiamo di più sulla velocità di esecuzione.


TEMP temp1, temp2;

Questo serve per dichiarare due variabili, temp1 e temp2 in cui salveremo delle informazioni più avanti.


MUL temp1, fragment.position, program.env[1];

 In questa riga effettuiamo una moltiplicazione tra “fragment.position” (che serve per recuperare la posizione di un pixel sullo schermo) e “program.env[1]” (che è una operazione matematica propria del motore grafico che serve a dirci in che luogo si trova un pixel specifico sullo schermo) e salviamo il risultato in “temp1”. La moltiplicazione dei due ci da come risultato una mappatura 1:1 dell’interno schermo.


MUL temp1, temp1, program.env[0];

In questa riga effettuiamo un’altra moltiplicazione, questa volta moltiplichiamo “temp1” (che abbiamo calcolato nella riga sopra e contiene informazioni sulla posizione dei pixel sullo schermo) con “program.env[0]” (altra operazione interna al motore grafico, serve per ricavare la posizione di un pixel sull’immagine renderizzata), il risultato è salvato nuovamente in “temp1” e sarà l’esatta coordinata di ogni pixel in scala.


TEX temp2, temp1, texture[0], 2D;

 In questa riga generiamo una texture (un’immagine) la quale sarà immagazzinata in “temp2”. Essa sarà composta dalle informazioni sulle coordinate dei pixels contenute in “temp1” e dall’immagine attualmente renderizzata sullo schermo ovvero “texture[0]” (ricordate che nel Material Shader avevamo posizionato uno 0 -zero- tra il comando “fragmentMap ” e “_currentrender”, quello 0 era il valore della texture che avremmo poi utilizzato qui), infine “2D” indica che stiamo creando un’immagine bidimensionale.


MUL temp2, temp2, 5;

In questa riga effettuiamo l’ultima moltiplicazione del nostro shader (ultima in questo caso, non c’è limite). L’operazione immagazzinerà in “temp2” l’immagine di contenuta in “temp2” moltiplicata 5 volte (questo serve per schiarire l’immagine).


MOV result.color, 0;

In questa riga effettuiamo un’operazione di movimento, o spostamento, in pratica diamo a “result.color” (che è il risultato del nostro pixel shader) il valore contenuto al secondo membro, che è 0, il che significa che in questo modo rendiamo 0 il contenuto di “result.color”, immagine completamente nera.


MOV result.color.x, temp2;

Ultima operazione, in questa riga effettuiamo nuovamente uno spostamento, muoviamo il contenuto di “temp2” (che è una foto di quello che si vede nell’area di schermo che fissiamo, su cui è applicato il nostro shader, moltiplicato per 5 volte) in “result.color.y” (l’ulteriore “.y” abbinato a “result.color”, che abbiamo analizzato poco sopra, indica che le informazioni andranno spostate solo nel canale x, che è quello del colore verde, XYZW=RGBA*).

*RGBA: Red (rosso), Green (verde), Blue (blu) Alpha (canale di trasparenza)


END

Istruzione che indica il termine del nostro Pixel Shader, o Fragment Program.

   

4. Applicazione dello Shader creato

A questo punto il nostro Pixel Shader è pronto per essere usato nel gioco, nel Material Shader l’abbiamo impostato per essere un materiale per conto proprio, il che significa che possiamo utilizzarlo nell’editor dei livelli per, per esempio, dei Brush semplicemente selezionando nel “Inspectors”, scheda “Media”, sotto “texture/shaders/shader_test” il nostro shader come se fosse una semplice texture. Possiamo anche copiare il contenuto del Material Shader del nostro Pixel Shader al posto del Material Shader di, per esempio, il modello 3d di un personaggio così da applicare ad esso il nostro nuovo effetto grafico. Il risultato sarà come si vede nell’immagine ad inizio di questo tutorial.

Con questo è tutto, buon lavoro e divertimento a tutti!


       

_____________________________________________________________________________

Ok. Questo è quanto. Se hai dei dubbi su qualche punto, posta pure le tue domande sul nostro Forum.


Creative Commons License
Pixel Shader - per Doom3 - Quake4 - PREY
Tutorial by Gyppi for HG&LD  
- 2 novembre 2007 -