Le environment map sono una classe evoluta di texture che presentano caratteristiche di tridimensionalità. Tra di esse questo tutorial ne illustrerà una: la cube texture. Le texture cubiche sono texture 3D che a differenza delle normali texture contengono immagini che si sviluppano lungo un cubo virtuale come mostra questa figura.
![Cube Mapping](http://www.notjustcode.it/public/CubeMapping_13136/enviroMap_thumb7.gif)
Una cube texture contiene quindi 6 texture tutte unite insieme. Comprendere il significato delle texture cubiche può essere difficile senza un esempio. In effetti a cosa servirebbe una texture strutturata in questo modo, basterebbero 6 texture ben piazzate! In realtà le texture cubiche non vengono mai utilizzate per coprire banalmente gli oggetti. Guardate la foto qui sotto
![Esempio Cube Map](http://www.notjustcode.it/public/CubeMapping_13136/envTex_thumb2.jpg)
Osservate la teiera, potrete notare che questa rifletta su di sè l'ambiente circostante. Le environment map sono proprio questo: texture ottimizzate per contenere immagini che circondano oggetti.
Ma come ottenere texture come queste. E' possibile creare un texture così? Ovviamente sì, esistono dei tools che creano e salvano su file queste texture ma in realtà non è questo il modo di procedere. La cosa migliore infatti non è caricarle da file ma generarle automaticamente tramite il render to surface.
Se avete letto il tutorial sul rendering to surface (traduzione: leggetevelo) potete quindi già immaginare il procedimento: inquadrare l'intera zona (nel mio caso questa specie di stanza) da sei punti di vista corrispondenti ai sei lati di un cubo virtuale. Ognuna di queste immagini andrà posizionata sulla texture cubica e sarà DirectX che automaticamente la posizionerà attorno all'oggetto in modo da creare l'effetto.
Processo macchinoso? Neanche tanto per un effetto così professionale. Ora lo vado ad illustrare.
Dim texCubica As CubeTexture
texCubica = New CubeTexture(device, 256, 1, Usage.RenderTarget, Format.X8R8G8B8, Pool.Default)
Ho dichiarato e creato una texture cubica. Nelle opzioni potete vedere il valore 256 che rappresenta la grandezza in pixel dello spigolo del cubo, l'uso impostato su RenderTarget ed il formato. Le altre opzioni lasciatele così.
Se avete letto il tutorial sul rendering su surface avete imparato ad utilizzare l'oggetto RendetToSurface: novità in DirectX 9 esiste l'oggetto
Dim rToCube As RenderToEnvironmentMap
rToCube = New RenderToEnvironmentMap(device, 256, 1, Format.X8R8G8B8, True, DepthFormat.D16)
Il RTEM (come è lungo questo nome!!!!!) è un oggetto che semplifica ed accellera il processo di rendering sulle texture volumetriche e quindi anche sul cubo. Dovete impostare anche questa ovviamente con la dimensione dello spigolo ed il formato (uguali a quelle della texture), un booleano che indica se vogliamo usare i depth buffer (Zbuffer o Stencil) ed il formato del buffer (formato uguale a quello usato per il device).
L'oggetto RTEM è ora pronto. Similmente al rendering su surface dobbiamo creare ora un ciclo che renderizzi sui sei lati la scena che circonda un oggetto (ma ovviamente senza l'oggetto su cui va messa la texture volumetrica). Il ciclo deve trovarsi fuori da altri cicli begin - endScene (e quindi prima di iniziare il rendering della scena finale o altri rendering).
Iniziamo quindi il ciclo
render2Cube.BeginCube(texCubica)
Questa istruzione chiede una texture cubica ed inizia il processo. Potete notare che ci sono anche altre istruzioni su altre forme (sferiche, iperboloidi) ma ora trattiamo questa. Notate infine che non serve viewport a differenza del rendering su superficie. Tramite l'istruzione
render2Cube.Face(i, 1)
Specificate al device il numero della faccia da utilizzare (parametro "i" mentre 1 è il livello di mipmapping che al momento non ci interessa). Il valore i deve variare da 0 a 5 e corrispondono ai valori delle costanti di DirectX.
CubeMapFace.PositiveX
CubeMapFace.NegativeX
CubeMapFace.PositiveY
CubeMapFace.NegativeY
CubeMapFace.PositiveZ
CubeMapFace.NegativeZ
Rifatevi all'immagine in cima per capirci.
Ora renderizzate normalmente la scena e questa invece che sullo schermo sarà stampata sulla texture scelta (ricordatevi quindi di fare tutte le faccie). Potete usare tutto ad eccezione delle istruzioni device.beginScene e endScene e della istruzione present.
Quando avete finito di renderizzare la scena per tutte e sei le facce uscite con
render2Cube.End(1)
Dove 1 indica il tipo di filtro usato: lasciatelo così. Da qui potete cominciare il normale rendering in cui utilizzare la cube texture su un modello (ma non come sprite) o effettuare un altro rendering su un'altra cube texture. La texture va inserita come una normale texture ma per far in modo che sia disposta a riflesso dovete impostare le coordinate texture come segue
device.TextureState(0).TextureCoordinateIndex =_
TextureCoordinateIndex.CameraSpaceReflectionVector
Questo ovviamente se inserite la texture nell'indice 0. Per tornare alla normalità
device.TextureState(0).TextureCoordinateIndex = TextureCoordinateIndex.PassThru
Riflessione
Per ogni faccia dovete naturalmente impostare la camera in modo che i lati contengano la giusta immagine con il giusto orientamento. Ecco un select che mostra come settare i valori.
Select Case i
Case CubeMapFace.PositiveX
v = New Vector3(1.0F, 0.0F, 0.0F)
upV = New Vector3(0.0F, 1.0F, 0.0F)
Case CubeMapFace.NegativeX
v = New Vector3(-1.0F, 0.0F, 0.0F)
upV = New Vector3(0.0F, 1.0F, 0.0F)
Case CubeMapFace.PositiveY
v = New Vector3(0.0F, 1.0F, 0.0F)
upV = New Vector3(0.0F, 0.0F, -1.0F)
Case CubeMapFace.NegativeY
v = New Vector3(0.0F, -1.0F, 0.0F)
upV = New Vector3(0.0F, 0.0F, 1.0F)
Case CubeMapFace.PositiveZ
v = New Vector3(0.0F, 0.0F, 1.0F)
upV = New Vector3(0.0F, 1.0F, 0.0F)
Case CubeMapFace.NegativeZ
v = New Vector3(0.0F, 0.0F, -1.0F)
upV = New Vector3(0.0F, 1.0F, 0.0F)
End Select
Dove v e upV sono due vector3. Settate la view matrix così
device.Transform.View = Matrix.LookAtLH(p, Vector3.Add(p, v), upV)
Dove p è un vector3 che contiene la posizione del centro del cubo.
Dovete anche impostare la projection matrix in questo modo
device.Transform.Projection = Matrix.PerspectiveFovLH(CSng(Math.PI / 2), 1, 1, 1000)
Ho impostato l'angolo a 90 gradi ed il rapporto a 1 (la texture è quadrata in ogni faccia). Potete cambiare come volete il valore 1000 che è la distanza del clip plane che non ci interessa nel rendering.
Ricordate infine di rimettere a posto la camera e la projection matrix dopo aver fatto il rendering sulla cube texture.
Per l'effetto di trasparenza invertite i lati.
Ricordate sempre di non effettuare cicli begin e end (sia del device che della cube texture che di ogni altra forma di rendering) all'interno di altri. Dovrete quindi effettuare il rendering sulla cube texture prima del rendering vero e proprio. Ultimi avvisi:
1)La tecnica può risultare molto dispendiosa se fatta in ogni frame o se si usano formati e dimensione elevate, regolatevi di conseguenza.
2)Le cube texture non sono supportate da schede vecchie, controllate la compatibilità
3)Se usate più texture con ugual formato e dimensione è sufficiente lo stesso oggetto RTEM altrimenti lo dovrete cambiare.
4)In alcune situazioni (punti fissi) può bastare fare il rendering solo una volta ed usare sempre la texture generata.
5)Viene generata una texture, seppur particolare è sempre una texture. Usate tutte le altre tecniche per le texture per ottenere i migliori effetti.
6)L'oggetto riflette tutto quello che viene renderizzato sulle facce ma se usate 2 oggetti a specchio non crediate di riuscire a generare l'effetto di riflessione infinito con un rendering: ne serviranno in teoria infiniti (o comunque finchè si riesce a distinguere l'oggetto).
Vi lascio l'esempio della foto.
Esempio VB.Net
Esempio C#