Texture 2067 Visite) DirectX 11
Una texture è una risorsa che rappresenta un’ immagine. Il suo utilizzo è solitamente quello di essere avvolta sul modello in modo da coprire i poligoni come se fosse una carta da parati e dare dei dettagli che richiederebbero anche migliaia di poligoni. In Direct3D una texture viene gestita tramite una zona di memoria che contiene i colori dei singoli pixel da passare poi allo shader in modo pressoché identico ad un constant buffer. Nello shader esiste un oggetto apposito, la texture, che legge i pixel sottoforma di vettore float4, nell’ordine i 4 valori sono rosso, verde, blu ed alpha usato per mantenere l’informazione riguardo la trasparenza del colore. Il colore viene usato negli algoritmi per il calcolo del colore finale. Il suo utilizzo oggi non si limita più ad essere una semplice immagine ma è ormai normale usare una texture per dati come le normali (RGB usati come vettori xyz) o addirittura parametri per equazioni. Visti questi utilizzi è dalla versione 9 di Direct3D che è possibile leggere le texture non solo nei pixel shader ma anche negli altri (usare il colore della texture per deformare un paesaggio ad esempio).
Le texture possono essere create dinamicamente in memoria o caricate da file. Oltre alla gestione delle comuni texture bidimensionali Direct3D11 gestisce strutture speciali come texture ad una dimensione o a più dimensioni (texture 3D, cubiche o array di texture) oltre a gestire decine di formati di colore diverso (immagini a 64 o 128bit, formate da un solo colore o che usano un colore come esponenziale per gli altri 3).
Tutte le texture vengono caricate in interfacce che derivano dall’interfaccia base Resource. Queste sono, a seconda del tipo di texture, Texture1D, Texture2D, Texture3D e così via. Gli shader tuttavia non utilizzano direttamente queste ma uno speciale oggetto ShaderResourceView che rappresenta una vista su una risorsa.
Per utilizzare una texture è quindi necessario creare una texture delle giuste dimensioni e formato, caricare l’immagine da file caricando i dati all’interno della texture e quindi creare da questa la vista. Direct3D ci viene incontro con metodi che inglobano tutti questi passaggi restituendo direttamente la ID3D11ShaderResourceView.
ShaderResourceView texture = ShaderResourceView.FromFile(device, "path.bmp");
La vista diventa qualcosa di indipendente dal tipo di texture.
Sampling
La lettura delle texture viene chiamata Sampling. Indipendentemente dalla dimensione in pixel le coordinate vengono contate dall’angolo in alto a sinistra pari a (0,0) a quello in basso a destra (1,1). I vertici di ogni triangolo passeranno queste coordinate al Pixel Shader che per ogni pixel leggerà il corrispondente valore. La dimensione del triangolo però sarà maggiore o minore della dimensione in pixel della texture che di conseguenza sarà deformata. La lettura quindi subirà un filtro che farà in modo che la qualità non degradi eccessivamente. Direct3D incorpora diversi tipi di filtri, ma migliore sarà la qualità e più sarà alto lo sforzo per la scheda video. Le proprietà per questa operazione sono descritte attraverso l’oggetto Sampler.
SamplerStateDescription description = SamplerStateDescription.Default();
description.Filter = Filter.MinMagMipLinear;
description.AddressU = TextureAddressMode.Wrap;
description.AddressV = TextureAddressMode.Wrap;
_samplerState = new SamplerState(Device, description);
Le proprietà principali da considerare sono l’address ed il filter. Il primo indica cosa succede lungo la coordinata se si superano i valori 0-1. Nel caso di Clamp i valori saranno limitati ai 2 valori, Wrap invece eseguirà una sorta di ripetizione facendo ripetere le coordinate. Ad esempio la coordinata nel punto 0.5 sarà uguale a quella in 1.5, 2.5 e così via, utile per ripetere la stessa texture più volte su una superficie. Ci sono poi altre forme con la Mirror che ripete la texture invertendola ad ogni ripetizione o la border che pone il pixel pari al colore di bordo (altra proprietà del sampler non obbligatoria) nei punti 0 ed 1.
La seconda proprietà principale è il filter che indica la qualità del filtro per la deformazione. Questo viene applicato in 3 casi: diminuizione (MIN), ingrandimento (MAG), e MIP per la lettura in mip level (una delle strutture texture prevede la possibilità di creare immagini che contengono copie di dimensioni minori per evitare al device le operazioni di filtro). Le varie opzioni descrivono il filtro in base alle 3 tipologie e vanno dal POINT che è la qualità minore, per passare poi alla LINEAR ed infine alla ANISOTROPIC.
Shader
All’interno dello shader occorre definire 2 oggetti:
Texture2D diffuseMap;
SamplerState textureSampler;
Il primo è la texture, il secondo invece è il sampler. Il metodo sample permette di leggere il colore generato dalla lettura.
float4 color = diffuseMap.Sample(textureSampler, coordinateTexture);
Il metodo Sample prende in input il sampler da utilizzare e le coordinate texture che per una texture2D sono un float2. Per texture di dimensioni diverse si utilizzeranno oggetti diversi (Texture1D, Texture2DArray etc) e diversi vettori per la lettura.
Indipendentemente dal formato usato i valori restituiti sono in genere compresi tra 0 ed 1 per i colori ad 8 bit. Se una texture sarà con un formato ad 1 solo colore allora i valori yzw saranno pari a 0. Gli oggetti Sampler e Texture saranno passati in modo identico ai constant buffer
DeviceContext.PixelShader.SetSampler(0, _samplerState);
Il metodo Sample funziona solo nel Pixel Shader, per leggere la texture in altri shader potete utilizzare altri metodi come il Load che legge il valore delle coordinate in pixel e non effettua sampling. Altri metodi come il GetDimension restituiranno invece la dimensione in pixel della texture.
Inoltre è possibile utilizzare anche un altro tipo di definizione per le texture
Texture2D<float4> texture;
In questo modo saremo noi a decidere il formato (potremmo quindi utilizzare in modo più semplice formati interi, contenenti un solo float e così via).
L’SDK contiene l’elenco di tutte le istruzioni supportate. Se utilizzate la compilazione in shader 5.0 avrete ancora più metodi per leggere.
Vi rimando al tutorial numero 5 della serie (Github o in fondo dal primo tutorial)