In questo tutorial approfondirò ciò che è stato spiegato nel precedente. Abbiamo creato i nostri oggetti principali: il device, responsabile delle operazioni, lo swap chain, che è la destinazione su cui vedremo i nostri oggetti, ed il RenderTargetView che è la zona di memoria su cui il device andrà a lavorare. Il procedimento dovrebbe essere chiaro. Il device legge i poligoni, li elabora negli shader e scrive i pixel sul RenderTargetView. Al termine il target view sarà copiato sullo schermo tramite il swap chain.
Esiste anche un'altra superficie: il Depth Stencil Buffer.
Questo buffer serve per mantenere informazioni sulla profondità e le maschere. La proiezione dei vertici va da 0 ad 1 (è spietato nel primo tutorial). Ogni volta che DirectX genera un pixel non lo inserisce automaticamente nella sua zona. Confronta infatti il valore della sua profondità con quella precedente in modo da verificare se quel pixel ne ha altri davanti o dietro. Questa informazione è memorizzata all’interno del depth buffer. E’ quindi importante creare questo buffer per le operazioni con oggetti tridimensionali, altrimenti un oggetto disegnato per ultimo sovrascriverà tutti i precedenti anche se si trova dietro di loro. Lo stencil è una superficie libera, fusa insieme al depth, che permette di creare delle maschere. Una maschera è una zona che fa da filtro per delle operazioni. Disegnando una figura si può imprimere la sua posizione nello stencil e fare in modo che i prossimi oggetti vengano disegnati solo dove è stata disegnata la precedente. In questo modo si possono creare dei tagli, utili per effetti avanzati.
ID3D10DepthStencilView* renderTargetDepth;
D3D10_TEXTURE2D_DESC descDepth;
descDepth.Width = width;
descDepth.Height = height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = DXGI_FORMAT_D32_FLOAT;
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D10_USAGE_DEFAULT;
descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
ID3D10Texture2D *pDepthBuffer;
device->CreateTexture2D( &descDepth, NULL, &pDepthBuffer );
D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
descDSV.Format = descDepth.Format;
descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;
device->CreateDepthStencilView( pDepthBuffer, &descDSV, &renderTargetDepth );
pDepthBuffer->Release();
Questa scrittura crea una texture (una zona di memoria rettangolare che contiene immagini o dati). Il depth stencil è una texture speciale creata con BindFlags uguale a stencil. Importante è la dimensione (width ed height) che deve essere uguale al device, ed il formato. Nell’esempio ho usato D32 ossia 8 byte dedicati esclusivamente al depth. Un formato uguale a DXGI_FORMAT_D24_UNORM_S8_UINT significa 24 bit per il depth e 8 per lo stencil. Nell’help trovate tutti i formati. Viene naturale che più bit occupa un formato e più lente saranno le operazioni su di esso.
device->OMSetRenderTargets( 1, &this->renderTargetView, this->renderTargetDepth);
La stessa istruzione SetRenderTargets imposta anche lo stencil. Ora il device è completo. Bisogna gestire però un’altra cosa. Il resize. Per resize si intende la modifica della dimensione della finestra. Quando capita bisogna ricordare a DirectX di aggiornarsi. La procedura è semplice:
nell’evento resize del form (WM_SIZE) distruggete il render target ed il depth buffer, usate l’istruzione
this->swapChain->ResizeBuffers(1,width,height,DXGI_FORMAT_R8G8B8A8_UNORM,2);
e poi ricreate di nuovo target e depth e reinseriteli. Cambiate anche il viewport aggiornando i valori.
Enumerare le schede video
Alcuni computer possiedono più di una scheda video. Esiste un modo per analizzare il sistema e trovare le schede esistenti.
IDXGIFactory* factory;
CreateDXGIFactory(__uuidof(IDXGIFactory) ,(void**)&factory);
UINT i = 0;
IDXGIAdapter * pAdapter;
while(factory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND)
{
i++;
}
factory->Release();
La classe DXGIFactory permette di leggere informazioni dal sistema. Si crea senza il device (proprio per poter interrogare la scheda per capire come meglio impostare il device). Nello stesso modo è possibile enumerare le risoluzioni supportate utilizzando le classi IDXGILayout restituita dall’adapter.
IDXGIOutput* output;
adapter->EnumOutputs(0,&output);
DXGI_OUTPUT_DESC outputDescription;
output->GetDesc(&outputDescription);
UINT numModes;
output-> GetDisplayModeList( DXGI_FORMAT_R8G8B8A8_UNORM,DXGI_ENUM_MODES_INTERLACED , &numModes,0);
numModes ora contiene il numero di display validi
DXGI_MODE_DESC* mode=new DXGI_MODE_DESC[numModes];
Output->GetDisplayModeList( DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED,&numModes,mode);
Si alloca memoria per l’array che conterrà tutte le modalità supportate. Iterate le strutture ad una ad una per ottenere le informazioni sulle risoluzioni supportate.
Con questo tutorial si completa la creazione del device. In futuro ci saranno occasioni per approfondire ulteriormente il device. Nel prossimo inizieremo a descrivere le prime funzionalità di base: il testo e gli sprite per poi passare finalmente al 3D.
Vi lascio il codice del tutorial.
Demo
I commenti sono disabilitati.