Texture 3569 Visite) DirectX 12
Le texture sono uno degli elementi ormai standard in campo 3D, utilizzate ormai non solo per ricoprire il modello come una carta da parati, ma anche per contenere tante informazioni per i nostri shader come normali, materiali, addirittura come il sudore deve scivolare su un modello.
In Direct3D12 le texture sono state gestite come fossero comuni risorse tanto che la loro creazione usa gli stessi metodi.
Se eravate abituati alle vecchie DirectX che caricavano da file le immagini allora resterete delusi: in Direct3D12 si carica tutto a mano.
ResourceDescription textureDesc = ResourceDescription.Texture2D(Format.R8G8B8A8_UNorm, 256, 256);
Resource texture = device.CreateCommittedResource(new HeapProperties(HeapType.Default), HeapFlags.None, textureDesc, ResourceStates.CopyDestination);
Nella descrizione indichiamo formato e dimensioni (esempio 32bit 256x256) quindi creiamo la risorsa.
Notate pero una differenza: l'heap utilizzato é il default e lo stato a copy destination.
Una texture a differenza di un constant buffer non deve essere essere scritta continuamente da CPU, ma solo letta da Gpu. Impostando la risorsa come upload non sarebbe corretto in termini di ottimizzazione. Tuttavia così non potete scriverci sopra, ma non è un problema, creiamo una seconda texture.
Resource textureUploadHeap = device.CreateCommittedResource(new HeapProperties(HeapType.Upload), HeapFlags.None, textureDesc, ResourceStates.GenericRead);
Ora generiamo un array di byte da caricare
byte[] textureData = GenerateTextureData();
Immaginiamo per il momento di caricare i file da una bitmap o di generarli dinamicamente.
GCHandle handle = GCHandle.Alloc(textureData, GCHandleType.Pinned);
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(textureData, 0); textureUploadHeap.WriteToSubresource(0, null, ptr, TexturePixelSize * TextureWidth, textureData.Length);
handle.Free();
Indipendentemente da dove abbiamo caricato useremo i metodi di dotNet per bloccare l'array in memoria e prenderne il puntatore (zona di memoria in ram del nostro array).
Quindi usando il metodo WriteToSubresource e andremo a copiare i dati nel primo blocco (subresource).
Approfondiremo in seguito la struttura di un buffer.
Ora la nostra texture é carica: il suo scopo è quello di essere travasata nella nostra texture finale.
commandList.CopyTextureRegion(new TextureCopyLocation(texture, 0), 0, 0, 0, new TextureCopyLocation(textureUploadHeap, 0), null); commandList.ResourceBarrierTransition(texture, ResourceStates.CopyDestination, ResourceStates.PixelShaderResource);
commandList.Close(); commandQueue.ExecuteCommandList(commandList);
Usando la command list (assicuratevi sia aperta) chiederete a DirectX di copiare i dati da una texture all'altra.
Ricordate che l'operazione è asincrona, quindi usate il fence per essere sicuri che il travaso é andato a buon fine.
Come per i constant buffer registriamo anche la texture in un Heap.
ShaderResourceViewDescription srvDesc = new ShaderResourceViewDescription()
{
Shader4ComponentMapping = D3DXUtilities.DefaultComponentMapping(),
Format = textureDesc.Format,
Dimension = ShaderResourceViewDimension.Texture2D,
};
srvDesc.Texture2D.MipLevels = 1;
device.CreateShaderResourceView(texture, srvDesc, heap.CPUDescriptorHandleForHeapStart);
Per prima cosa descriviamo il nostro Shader con la struttura ShaderResourceViewDescription quindi come per i constant buffer registriamo l'oggetto nell' Heap tramite CreateShaderResourceView. Il metodo DefaultComponentMapping è una utility che ho creato in sostituzione di quelle create da Microsoft.
L'heap da utilizzare sarà lo stesso usato per i constant buffer. Ricordate, potete passare un solo Heap per constant buffer e texture, quindi dovrete inserirli tutti in un unico heap.
Ora potete passare la texture tramite root parameter
commandList.SetGraphicsRootDescriptorTable(0, Heap.GPUDescriptorHandleForHeapStart);
Ovviamente dovrete posizionare correttamente l'heap, non semplicemente porlo all'inizio.
Prima di renderizzare un oggetto con texture occorre creare un Sampler, cosa che vedremo nel prossimo tutorial.
Vi rimando al tutorial HelloTexture del mio repository se volete avvantaggiarvi e seguire il codice