Home Page Home Page Articoli Texture Blending (Parte II) Quake 3 Arena Shader System

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 Livello:
Shaders? E che sono?
Innanzi tutto cerchiamo di fare chiarezza sulle terminologie utilizzate. Gli “shader” di cui voglio parlare NON sono né i famosi Vertex Shader né tanto meno i Pixel Shader, argomento di lunghe dissertazioni nei prossimi numeri.

Con il termine “shader” voglio indicare un componente di un motore 3D che si occupa della resa grafica di un poligono da mandare a schermo. In altre parole decide quante e quali texture devono essere usate, modifica le coordinate di mapping, la trasparenza impostando tutti i render state necessari per disegnare il poligono in un certo modo. Senza perdere troppo tempo potete vedere gli shader in azione nel blasonato Quake 3 Arena (potete cercare informazioni sugli shader di quake3arena su qualsiasi sito dedicato a Q3) oppure nell’esempio DXG7_EffectFile di questo articolo. Gli shader in Q3A sono semplici script di testo tipo:

MioShader
{
{
map pent_metalbridge06.tga
blendFunc GL_DST_COLOR GL_ZERO
rgbGen identity
}
{
map pent_glow.tga
blendFunc GL_ONE GL_ONE
tcMod rotate 50
}
}



Esistono anche degli editor per evitare di scriverli a mano, ad esempio il http://home.tvd.be/sf15772/q3ase.html ">Q3ASE

In questo shader il poligono è disegnato fondendo in trasparenza due texture in formato TGA (formato che supporta il canale alpha della trasparenza). La seconda texture (pent_glow.tga) viene ruotata attorno al suo centro di 50 gradi al secondo (tcMod rotate). BlendFunc indica la modalità con la quale la texture viene “fusa” con quella successiva. Non trovate che sia tutto molto semplice? Usando istruzioni poco più complesse possiamo ottenere effetti davvero niente male. Per approfondimenti vi rimando alla documentazione degli shader di quake 3 arena allegata all’editor http://qeradiant.com/gtkradiant.shtml ">GTKRadiant.

Gli Shaders sono quindi un termine generico usato per indicare le proprietà di rendering di una superficie. Essi possono essere programmati utilizzando diverse tecnologie quali Texture Blending, Vertex e Pixel Shader. Quake3Arena e molti altri “Shader system” (quello di nebula device oppure Fly3DSDK etc.) usano solo la prima tecnologia, che seppur un po’ datata permette di ottenere risultati ammirevoli. In un certo senso i pixel shader sono parenti del multi texturing ma MOLTO più flessibili e programmabili (i guru non si offendano quanto che ho scritto!).

Senti, io non ho ancora capito cos’è questo “Texture Blending”!

Calma calma, sarà tutto più chiaro a breve. Il modo migliore per capire il Texture Blending a mio parere è aprire un qualsiasi programma di grafica che supporti i “layers”, ad esempio Adobe Photoshop, o Corel PhotoPaint o il più modesto Paint Shop Pro. Come potete ben vedere ogni “layer” (livello) è un’immagine che si fonde (blending) in qualche modo con l’immagine del layer precedente, un po’ come avere dei lucidi trasparenti messi una sopra l’altro. In questi programmi potete anche dire COME un livello deve essere disegnato sul precedente: ad esempio eseguendo il blending additivo, sottrattivo o qualunque altra modalità. Sostanzialmente cambiando questo parametro si cambia l’OPERAZIONE che viene effettuata tra i pixel di due livelli. Selezionando il blending additivo per esempio tra due ipotetici layers viene effettuata la seguente operazione per ogni pixel:

PixelLayer3 = PixelLayer1 + PixelLayer2
Con il blending sottrattivo invece

PixelLayer3 = PixelLayer1 - PixelLayer2
Cambiando gli operatori e combinando più di due layer tra loro si possono ottenere effetti di qualunque tipo.

L’analogia con tali programmi immediata: il Texture Blending consente, infatti, di disegnare in tempo reale poligoni a schermo effettuando il blending di diverse texture.
In questa figura:



combiniamo due textures con diversi operatori di blending. La prima riga effettua il blending usando il canale alpha della seconda texture mentre la prima riga somma semplicemente il valore dei pixel di ognuno dei layer (blending additivo).

Multi pass blending e Multi Texturing

Ora che sappiamo cos’è il texture blending cercheremo di capire in che modo esso è implementato in Direct3D. Immaginiamo di avere un insieme di N layers.

La prima cosa che ci viene in mente per effettuare il blending dell’i-esimo layer è quello di leggere il valore del pixel nel backbuffer di rendering, effettuare l’operazione desiderata e scrivere il pixel nuovamente calcolato di nuovo nel backbuffer, mettendolo a disposizione del prossimo layer. Iterando questo processo per tutti gli N layer otterremo il risultato corretto a schermo. Questo processo viene chiamato Multi Pass Blending, perché è necessario ridisegnare il poligono sullo schermo una volta per ogni layer. Capiamo bene che questo metodo è molto più oneroso in termini di tempo di calcolo perché ogni poligono verrà ritrasformato e ridisegnato N volte, una per layer. Il Multi Texturing è un’alternativa al Multi Pass Blending ed è supportato da qualunque acceleratore 3D decente.

In pratica si possono passare a Direct3D in una sola volta le texture da usare, le operazioni di blending ed il poligono da disegnare, evitando così di effettuare ulteriori “pass” e riducendo la quantità di dati da mandare all’acceleratore. Il poligono verrà trasformato una sola volta. Solo il pixel finale, calcolato alla fine delle operazioni di blending, sarà scritto nel backbuffer.

La differenza tra multi pass e multi texturing è quindi solo una differenza di prestazioni, non di funzionalità, fanno entrambi la stessa cosa.

Nel nostro programma quindi dovremo fare in modo che venga usato quando possibile il Multi Texturing al posto del Multi Pass Blending. Il problema però non è semplice perché ogni acceleratore supporta un diverso numero di “Texture Stage” (layer) utilizzabili contemporaneamente.

Quello che voglio dire è che se il nostro shader è composto di 8 layer e un acceleratore supporta 2 texture stage per volta ed un altro 3 dovremo fare attenzione. Ma come faccio a disegnare 8 layers se il mio acceleratore supporta solo 2 texture stage per volta?Semplice, useremo insieme Multi Pass e Multi Texturing. In questo caso particolare disegneremo 4 volte il poligono effettuando il blending dei poligoni a due a due. Se avessimo usato solo il Multi Pass avremmo avuto bisogno di disegnare il poligono 8 volte!

Detto questo potrebbe sembrare tutto semplicissimo ma in pratica non lo è. Il problema consiste nel fatto che non tutti gli acceleratori 3D supportano TUTTE le operazioni tra Texture Stage, anzi lo stesso acceleratore molte volte supporta alcune operazioni in uno stage ed altre in uno stage diverso. Sapendo schede 3D ci sono in giro verrebbe da mettersi le mani nei capelli…

Grazie al cielo DirectXGraphics ci viene in aiuto con i suoi fantastici “Effect”.

Effect
Un “Effect File” è uno script simile ad uno shader di Q3A. Gli Effect di DXG costituiscono uno shader system di molto più potente di quello di Q3A per vari motivi. Ad esempio permettono di usare contemporaneamente Vertex, Pixel Shader e Texture Blending. Un’altra caratteristica molto importante sono le “technique” (tecniche): immaginiamo di voler programmare un effetto particolare che richiede il blending di 3 Texture Stage. Possiamo creare diverse tecniche in modo da sfruttare al massimo l’hardware disponibile. Ad esempio se la scheda supporta 3 texture stage possiamo disegnare la scena in un solo “pass” (una sola volta). Se non fosse così ricorreremmo al Multi Passing, oppure potremmo eliminare un Texture Stage non strettamente necessario. Il nostro effetto non sarà bello come la versione con 3 stage ma se non altro girerà anche su computer non recentissimi.

Nell’esempio “Water” del DirectXSDK sono utilizzati gli Effect File, provate e vedete. Se poi aprite il file $$DXSDKPATH$$\samples\Multimedia\Media\Water.sha con un qualsiasi editor di testo, capirete come sono state realizzate le diverse tecniche.

Ok, ora abbiamo parlato anche troppo, è il momento di vedere un po’ come possiamo programmare un effect file che effettua il blending di due texture. Aprite un file di testo e scrivete il contenuto del Listato 1 contenuto nel file Basic.Fx


//File Basic.fx

Texture DiffuseTexture;
Texture BlendedTexture;

//Esempio di Texture Blending attraverso Multi Texturing
Technique MultiTexturing
{
Pass P0
{

AlphaBlendEnable = false;
PixelShader = NULL;

//STAGE 1 (primo layer)
Texture[0] = ;
ColorArg1[0] = Texture;
ColorOp[0] = SelectArg1;

AlphaOp[0] = Disable;

//STAGE 2 (secondo layer)
Texture[1] = ;
ColorArg1[1] = Texture;
ColorOp[1] = Add;
ColorArg2[1] = Current;
AlphaOp[1] = Disable;

//Disabilitiamo il prossimo stage
ColorOp[2] = Disable;
AlphaOp[2] = Disable;

}

}


Il formato di questo effect file è così semplice che quasi si commenta da sé.

Iniziamo ricordando che ogni effect file può ricevere degli argomenti come input. Questi argomenti sono dichiarati all’inizio del file. Nel nostro caso abbiamo richiesto in input due Texture di nome DiffuseTexture e BlendedTexture. Se avessimo voluto avremmo potuto richiedere argomenti di tipo DWORD, FLOAT, VECTOR, MATRIX, VERTEXSHADER e PIXELSHADER.

Il secondo passo è quello di dichiarare la tecnica seguita da un identificativo a nostro piacere e apriamo una parentesi graffa, come se programmassimo in C.

Il terzo passo è la dichiarazione dei “pass” necessari, ovverosia delle operazioni da eseguire in un solo ciclo di rendering del poligono. Nel nostro caso vogliamo fare il blending di due texture in un solo pass, attraverso il multi texturing. All’interno della dichiarazione del pass inseriamo tutti i Render State necessari per ottenere il nostro effetto. Esaminiamoli ora ad uno ad uno:

AlphaBlendEnable = false; PixelShader = NULL;

Significa che NON desideriamo usare il Multi Pass Blending, e cioè il risultato del nostro pass coprirà tutto ciò che è stato disegnato in precedenza nel backbuffer e che NON desideriamo usare un Pixel Shader. Ora viene il momento di configurare i vari layer o texture stage. Da notare che tali stage sono numerati da 0 in poi e le proprietà di uno specifico stage si indicano nel formato:

RenderState[NumeroStage] = Valore;

La riga seguente seleziona la texture DiffuseTexture nella prima Texture Unit. Per ogni singolo pixel dello stage viene eseguita l’operazione

RISULTATO STAGE 0 = ColorArg1 ColorOP ColorArg2.

Nel nostro caso indichiamo come primo argomento il valore del pixel preso dalla texture e come operazione la selezione di tale pixel. Il Risultato sarà in definitiva.

RISULTATO STAGE 0 = (Pixel Nella Texture DiffuseTexture)

Il risultato dello stage 0 sarà input per il prossimo stage che effettua questa operazione:

RISULTATO STAGE 1= (Pixel Nella Texture BlendedTexture) + (RISULTATO STAGE 0)

Ovverosia un blending additivo.

Oltre alle operazioni tra i canali rosso, verde e blu di un pixel, Direct3D ci permette anche di eseguire operazioni tra i canali ALPHA (alphaArg1[n] alphaOP alphaArg2[n]), ma per ora non ci interessano. Le ultime due righe disabilitano il primo stage NON utilizzato, perché magari potrebbe contenere delle altre operazioni che si sommerebbero a quelle che abbiamo eseguito nei nostri stage modificando il risultato finale. Disabilitando uno stage la pipeline di calcolo del multitexturing si ferma senza nemmeno provare a controllare stage ulteriori.

La prima tecnica, che usa il multi texturing, è finita. Ora si tratta di riscrivere la stessa tecnica usando il multi passing. Il Listato 2 ci mostra come fare:


//Technique MultiPassing

{

Pass P0

{
AlphaBlendEnable = false;

PixelShader = NULL;
Texture[0] = ;
ColorOp[0] = SelectArg1;
ColorArg1[0] = Texture;
ColorOp[1] = Disable;

}

Pass P1

{

AlphaBlendEnable = true;
SrcBlend = one;
DestBlend = one;
Texture[0] = ;
ColorOp[0] = SelectArg1;
ColorArg1[0] = Texture;
ColorOp[1] = Disable;

}

}


Ora come potete ben vedere abbiamo due pass, cioè disegneremo la scena due volte quando useremo questo effetto. Impostiamo come colorOP in entrambi gli stage la selezione del pixel nella texture. Il lavoro del blending è fatto da AlphaBlendEnable = true. Impostando i campi SrcBlend e DestBlend verrà eseguita l’operazione.

RISULTATO PASS P1 = (RISULTATO PASS P0)*SrcBlend + (PASS P1)*DestBlend
Avendo specificato ONE sia per srcblend che per destblend la nostra equazione si riduce a

RISULTATO PASS P1 = (RISULTATO PASS P0) + (PASS P1).

Ovverosia il blending additivo che desideravamo :-)

Ok, ora il nostro script è completo. Quello che ci serve ora è capire come funziona il tutto nel nostro programma in C++ e non esiste niente di più facile. Ecco il Listato 3


//Fase di Loading
LPD3DXEFFECT effect;
hr = D3DXCreateEffectFromFile(GetDevice(), "Basic.fx",&effect,0);
CHECKHR(hr);

D3DXTECHNIQUE_DESC technique;
effect->FindNextValidTechnique(NULL, &technique);
effect->SetTechnique(technique.Index);
multitexturing = stricmp(technique.Name,"MultiTexturing")==0;
currentEffect->SetTexture("DiffuseTexture", lpTex0);
currentEffect->SetTexture("BlendedTexture", lpTex1);
//…codice tagliato..

//Fase di disegno
UINT uPasses;
currentEffect->Begin(&uPasses, 0 );
for(UINT uPass = 0; uPass < uPasses; uPass++)
{
currentEffect->Pass(uPass);
lpDev->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2);
}
currentEffect->End();


Tramite la prima riga chiediamo a D3DX di leggere il file “Basic.fx”, compilarlo e metterlo in effect. L’ultimo parametro è usato per ritornare in una interfaccia ID3DXBuffer eventuali errori di compilazione.

Se tutto è andato per il meglio chiediamo a D3DX di trovare la prima tecnica valida. Questo metodo deciderà per esempio se usare il multi texturing oppure il multi pass blending in base alle caratteristiche hardware dell’acceleratore correntemente in uso. Con SetTechnique(…) impostiamo tale tecnica come attiva e controlliamo se stiamo usando multitexturing oppure multipassing. Successivamente passiamo al nostro effetto i due parametri in input richiesti, le texture di cui effettuare il blending. Attenzione, i nomi dei parametri passati sono CASE SENSITIVE, cioè dovete stare attenti a maiuscole e minuscole. Come ogni oggetto D3DX in OnInvalidateDevice() chiamiamo effect->OnLostDevice() e in OnRestoreDevice() chiamiamo effect->OnResetDevice(), in modo che l’effetto possa rilasciare e ricreare gli oggetti Device Dependant nel caso il device sia stato perso (ad es. con Alt + Tab quando si è a tutto schermo).

In DXG7_EffectFile::OnDraw() invece avviene il disegno vero e proprio dei triangoli. Come potete leggere dal codice scaricabile dal sito di Game Programming, vengono creati due semplici triangoli per formare un quadrato. Per disegnarli chiamo effect->Begin(..) passando un parametro che l’effetto userà per comunicarci quanti “pass” sono necessari. Quello che dovremo fare è impostare il passo corrente e disegnare il nostro triangolo.

Fatto questo per tutti i triangoli e per tutti i pass, possiamo chiamare effect->End() che ripristina tutti i render state modificati in precedenza (come ID3DXSprite, vi ricordate?). Se avessimo passato il flag D3DXFX_DONOTSAVESTATE come secondo parametro in Begin() avremmo potuto ottimizzare ulteriormente questo codice perché l’effetto così non salva e ripristina i render state modificati.

Tutto qui. Sarà l’effetto che provvederà a cambiare texture, e i render state per ogni pass.

Internamente l’effect file usa gli state block (quelli analizzati nel precedente numero di GP) che sono quindi più performanti di fare tante chiamate a IDirect3DDevice8::SetRenderState(…).

Ho aggiunto la possibilità tramite i tasti F1 ed F2 di cambiare tra l’effect file Basic.fx ed uno identico chiamato Basic_Alpha.fx che modifica solamente la modalità di blending. Invece di effettuare un blending additivo usa il canale alpha della seconda texture. Se aprite infatti la seconda texture in formato .tga con il PhotoShop vedrete come essa contiene una maschera per il canale alpha. Volete giocare ancora con le modalità di blending? Il DXSDK contiene un programma di none MFCTex che fa proprio al caso vostro.

Esempio DX8_EffectFileAnimato
L’esempio DXG8_EffectFileAnimato è quasi identico a quello appena descritto, con la differenza che questa volte animeremo le coordinate texture dei nostri triangoli e aggiungeremo un altro effect. Le modifiche da fare presenti nel Listato 4 sono semplicissime, e i file di effetto si chiamano questa volta BasicAnimato.fx, Basic_AlphaAnimato.fx e Basic_tFactorAnimato:


//Nei file BasicAnimato.fx e Basic_AlphaAnimato

Texture DiffuseTexture;
Texture BlendedTexture;
Matrix DiffuseTMatrix;
Matrix BlendedTMatrix;


Technique MultiTexturing
{

Pass P0
{

//...omissis

TextureTransformFlags[0] = Count2;
TextureTransformFlags[1] = Count2;
TextureTransform[0] = ;
TextureTransform[1] = ;
}

}

Technique MultiPassing
{

Pass P0

{
//...omissis

TextureTransformFlags[0] = Count2;
TextureTransform[0] = ;
}

Pass P1
{

//...omissis
TextureTransform[0] = ;
}
}


Come potete notare ora richiediamo in input anche due matrici che saranno usate per trasformare le coordinate texture dei nostri triangoli. Queste matrici possono contenere qualunque trasformazione (rotazioni e traslazioni ecc.). E’ importante ricordare che noi passiamo matrici 4x4 ma per la trasformazione delle texture queste matrici sono considerate come 3x3, vengono ignorate l’ultima riga e l’ultima colonna.

Il Render State TextureTransformFlags impostato a COUNT2 indica a D3D di usare le matrici passate in TextureTransform per trasformare le coordinate texture. Da notare che nella tecnica MultiTexturing impostiamo TextureTransformFlags e TextureTransform per ogni stage mentre nell’esempio MultiPassing usiamo solo il primo stage due volte, quindi nel secondo pass (P1) basterà cambiare la matrice di trasformazione in BlendedTMatrix.


D3DXMATRIX t1,t2;

//Una funzione turbolenza sul primo stage...

//omissis....

//Una semplice rotazione sul secondo

D3DXMATRIX tmp1,tmp2;
D3DXMatrixIdentity(&tmp1);
D3DXMatrixIdentity(&tmp2);
D3DXMatrixRotationZ(&t2,time);

tmp1._31 = -0.5f;
tmp1._32 = -0.5f;
tmp2._31 = 0.5f;
tmp2._32 = 0.5f;
t2 = tmp1*t2*tmp2;

tFactor= 255*((sin(time*5)+1.0f)2.0f);
tFactor = D3DCOLOR_ARGB(tFactor,tFactor,tFactor,tFactor);
currentEffect->SetMatrix("DiffuseTMatrix",&t1);

currentEffect->SetMatrix("BlendedTMatrix",&t2);
currentEffect->SetDword("tFactorValue", tFactor);


La prima parte del codice genera una funzione turbolenza (simile al tcMod Turb di Quake3Arena) mentre la seconda ruota la texture attorno al suo centro. Da notare che uso i campi _31 e _32 per le traslazioni, giacché stiamo lavorando con delle matrici 4x4 “camuffate” da 3x3. Se usassi D3DXMatrixTranslation(…) non funzionerebbe nulla. Sapendo che la rotazione è sempre relativa all’angolo sinistro in alto della texture, siamo costretti a traslarla di metà (0.5f) in entrambe le direzioni, ruotarla e rimetterla a posto. Concatenando tutte queste trasformazioni in una sola matrice, otterremo l’effetto voluto. Tfactor invece viene fatto variare da 0 a 255 e da 255 a 0 attraverso una funzione seno.Leffect file effect_TFactorAnimato.fx aggiunge un terzo pass per disegnare una ulteriore texture (di nome SecondBlendedTexture) che NON ha canale alpha. La disegnamo con blending additivo e indichiamo a d3d di modularne il valore con questo fattore TFACTOR, per regolarne la trasparenza. Nel Listato 6 potete leggere il codice di questo effect:


//Dal file Basic_TFactorAnimato.fx

Pass P1

{

TextureFactor = ;
AlphaBlendEnable = true;
SrcBlend = one;
DestBlend = one;

Texture[0] = ;
ColorArg1[0] = Texture;
ColorArg2[0] = TFactor;

ColorOp[0] = Modulate;
AlphaArg1[0] = Texture;
AlphaOp[0] = Modulate;
AlphaArg2[0] = TFactor;

//Disabilitiamo il prossimo stage

ColorOp[1] = Disable;
AlphaOp[1] = Disable;
TextureTransformFlags[0] = Disable ;

}


“Modulare” in linguaggio DirectX significa moltiplicare. Di conseguenza il risultato di questo pass sarà:
RISULTATO PASS P1 =(RISULTATO PASS PRECEDENTE)+(pixel in SecondBlendedTexture ) * Tfactor
Risulta ovvio il fatto che quando Tfactor è zero, azzera il secondo termine di questa equazione, rendendo la SecondBlendedTexture completamente trasparente. Quando Tfactor è 0xffffffff invece SecondBlendedTexture sarà aggiunta al risultato del pass precedente (ricordate che stiamo facendo un blending additivo). Per tutti i valori intermedi la SecondBlendedTexture sarà sfumata in trasparenza sul risultato del pass precedente. TFactor in definitiva serve per avere un controllo globale sul canale alpha e colore di uno stage, quando non è necessario il controllo SUL SINGOLO PIXEL della texture (nel qual caso dovete fornire una texture con canale alpha, come ad esempio BlendedTexture) ma attenzione, TFactor non è supportato da alcune vecchie schede.Vedete un po’ l’esempio DXG8_EffectFileAnimato: che ne dite? Niente male :-)

Effect Files + Q3A Shaders
Nell’esempio DXG8 controlliamo le trasformazioni delle coordinate texture direttamente in C++. Che bello sarebbe se potessimo controllarle via script … Perché gli shader di Quake 3 Arena devono essere più belli dei nostri?

Noi programmatori 3D non siamo mai contenti, così decidiamo di supportare lo stesso sistema usato dal famoso engine (e non solo da lui). In Q3Shader.h e Q3Shader.cpp troverete un’implementazione basilare di questo sistema. Ho supportato solo il comando tcMod per semplicità, ma potete estenderlo per supportare tutti i comandi che volete. Questo shader deve avere lo stesso nome del file di effetct (.fx) con il suffisso .dxgshader. Nel nostro esempio ho chiamato i files provaq3.fx e provaq3.fx.dxgshader. Riporto nel Listato 7 di seguito un semplice esempio:


DXG8Shader

{
//Primo Stage
{
map firegorre.tga
tcMod scale 2 1
tcMod scroll 0.1 0.1
tcMod turb 1 0.1 0.5 1

}

//Secondo Stage
{

map blocks18cgeomtrn2.tga
tcMod rotate 50
tcMod stretch sin 1.3 0.2 0 0.5
}

}


Nel primo stage indichiamo la texture da usare e alcune trasformazioni da eseguire sulle coordinate texture. Queste trasformazioni dovranno essere applicate NELL’ORDINE IN CUI COMPAIONO.

Niente di più semplice, si tratta di costruire qualche matrice e concatenarle attraverso semplici moltiplicazioni matriciali. La funzione che svolge questo lavoro è Q3Transform::Multiply(…) presente in Q3Shader.cpp.

Il tcMod scale come potete ben immaginare scala la texture, tcMod scroll invece la fa muovere orizzontalmente e verticalmente della quantità specificata. Il tcMod rotate ruota la texture attorno al suo centro di X gradi al secondo. Il tcMod stretch invece accetta in input una funzione periodica qualsiasi (nel nostro caso la funzione seno) e “stira” la texture in entrambe le direzioni usando il valore di tale funzione nel tempo. Anche Il tcMod turb (turbolenza) accetta in input una funzione periodica (che però è sempre seno, quindi non si specifica come potete leggere dal Listato 7).

Iniziamo analizzando le funzioni periodiche. Ogni funzione periodica di Q3 è caratterizzata da quattro parametri: base, amplitude, phase e frequency (in italiano, base, ampiezza, fase e frequenza), gli stessi che trovate su qualsiasi libro di Analisi (“Fenomeni Periodici e vibratori” sul mio). La formula generale di queste funzioni è la seguente.

Base + Ampiezza*FUNZIONE(Tempo*Frequenza + Fase)

Al posto di FUNZIONE si intende una qualsiasi funzione periodica. Q3 ne supporta 5: sin, triangle, square, sawtooth e inverse sawtooth (seno, triangolo, quadrato, “sega” e “sega inversa”).

Nel Listato 8 (pseudocodice) possiamo vedere come sono definite e in Figura 3 il loro grafico:


x = time*frequency + phase
x = x - floor(x) //vogliamo solo la parte frazionaria





RISULTATO = Base + Ampiezza*y

Detto questo non ci rimane molto altro da dire nel metodo DXGShader::OnAnimate() concateniamo le trasformazioni per ogni layer


bool Q3Shader::OnAnimate()
{

for(int i = 0; i < transforms.size(); i++)
{

D3DXMATRIX result;
D3DXMatrixIdentity(&result);

//Per ogni layer...
std::vector& curr = transforms;

for(int j = 0; j {

//Concateniamo tutte le trasformazioni
Q3Transform* transf = curr[j];
transf->Multiply(result,GetWrapper()->GetTime());
}

//Impostiamo la matrice finale
static char buffer[10];
sprintf(buffer,"texmat%d",i);
effect->SetMatrix(buffer,&result);
}

return true;
}


E impostiamo nell’effect file una matrice di nome texmatN dove N è il numero del layer corrente. Questa matrice verrà successivamente passata in TextureTransform nel file effect. Ho usato questo sistema perché non mi sembrava il caso di scomodare i vertex shader per così poco. Il bello è che possiamo sfruttare la potenza degli effect (Vertex,PixelShader etc) e la facilità d’uso degli shaders di Q3.

Il resto del codice presente in Q3Shader.cpp non è niente di particolare, fa solo il parsing linea per linea dello shader per trovare i vari tcMod.
Voto medio articolo: 3.7 Numero Voti: 6
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 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:
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 | 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-2024
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5