Uso di Shader in Direct3D 10
La pipeline ha tre fasi shader e ognuna è programmata con un shader HLSL. Tutti gli shader Direct3D 10 sono scritti in HLSL, destinati al modello shader 4.
Differenze tra Direct3D 9 e Direct3D 10:
- A differenza dei modelli shader Direct3D 9 che potrebbero essere creati in un linguaggio di assembly intermedio, i shader modello 4.0 vengono creati solo in HLSL. La compilazione offline di shader in bytecode di consumo del dispositivo è ancora supportata e consigliata per la maggior parte degli scenari.
In questo esempio viene usato solo un vertex shader. Poiché tutti gli shader vengono compilati dal core di shader comune, imparare a usare un vertex shader è molto simile all'uso di una geometria o di pixel shader.
Dopo aver creato uno shader HLSL (questo esempio usa il vertex shader HLSLWithoutFX.vsh), sarà necessario prepararlo per la fase della pipeline specifica che lo userà. A tale scopo, è necessario:
- Compilare un shader
- Creare un oggetto Shader
- Impostare l'oggetto Shader
- Ripetere per tutte le 3 fasi shader
Questi passaggi devono essere ripetuti per ogni shader nella pipeline.
Compilare un shader
Il primo passaggio consiste nel compilare lo shader, per verificare di aver codificato correttamente le istruzioni HLSL. Questa operazione viene eseguita chiamando D3D10CompileShader e fornendolo con diversi parametri, come illustrato di seguito:
IPD3D10Blob * pBlob;
// Compile the vertex shader from the file
D3D10CompileShader( strPath, strlen( strPath ), "HLSLWithoutFX.vsh",
NULL, NULL, "Ripple", "vs_4_0", dwShaderFlags, &pBlob, NULL );
Questa funzione accetta i parametri seguenti:
Nome del file ( e lunghezza della stringa del nome in byte ) contenente lo shader. In questo esempio viene usato un vertex shader solo (nel file HLSLWithoutFX.vsh file in cui l'estensione file vsh è un'abbreviazione per vertex shader).
Nome della funzione shader. In questo esempio viene compilato un vertex shader dalla funzione Ripple che accetta un singolo input e restituisce uno struct di output (la funzione proviene dall'esempio HLSLWithoutFX):
VS_OUTPUT Ripple( in float2 vPosition : POSITION )
Puntatore a tutte le macro usate dallo shader. Usare D3D10_SHADER_MACRO per definire le macro; creare semplicemente una stringa di nome che contiene tutti i nomi di macro (con ogni nome separato da uno spazio) e una stringa di definizione (con ogni corpo di macro separato da uno spazio). Entrambe le stringhe devono essere terminate null.
Puntatore a qualsiasi altro file che è necessario includere per ottenere la compilazione degli shader. In questo modo viene usata l'interfaccia ID3D10Include che include due metodi implementati dall'utente: Apri e Chiudi. Per eseguire questo lavoro, è necessario implementare il corpo dei metodi Open e Close; nel metodo Open aggiungere il codice che si userebbe per aprire qualsiasi file che si desidera includere, nella funzione Close aggiungere il codice per chiudere i file al termine dell'operazione.
Nome della funzione shader da compilare. Questo shader compila la funzione Ripple.
Profilo shader da destinazione durante la compilazione. Poiché è possibile compilare una funzione in un vertice, geometria o pixel shader, il profilo indica al compilatore quale tipo di shader e quale modello shader confrontare il codice.
Flag del compilatore shader. Questi flag indicano al compilatore quali informazioni inserire nell'output compilato e come si vuole ottimizzare il codice di output: per velocità, per il debug e così via. Per un elenco dei flag disponibili, vedere Costanti effetto (Direct3D 10). L'esempio contiene un codice che è possibile usare per impostare i valori del flag del compilatore per il progetto: si tratta principalmente di una questione che indica se si desidera generare informazioni di debug.
Puntatore al buffer contenente il codice shader compilato. Il buffer contiene anche tutte le informazioni sulla tabella dei simboli e di debug incorporate richieste dai flag del compilatore.
Puntatore a un buffer contenente un elenco di errori e avvisi rilevati durante la compilazione, ovvero gli stessi messaggi visualizzati nell'output di debug se si esegue il debugger durante la compilazione. NULL è un valore accettabile quando non si desidera che gli errori vengano restituiti a un buffer.
Se lo shader viene compilato correttamente, viene restituito un puntatore al codice shader come interfaccia ID3D10Blob. Viene chiamata interfaccia BLOB perché il puntatore si trova in una posizione in memoria costituita da una matrice di DWORD. L'interfaccia viene fornita in modo che sia possibile ottenere un puntatore al shader compilato che sarà necessario nel passaggio successivo.
A partire da dicembre 2006 SDK, il compilatore DirectX 10 HLSL è ora il compilatore predefinito in DirectX 9 e DirectX 10. Per informazioni dettagliate, vedere Strumento del compilatore effetto .
Ottenere un puntatore a uno shader compilato
Diversi metodi API richiedono un puntatore a uno shader compilato. Questo argomento viene in genere chiamato pShaderBytecode perché punta a un shader compilato rappresentato come sequenza di codici byte. Per ottenere un puntatore a uno shader compilato, compilare prima lo shader chiamando D3D10CompileShader o una funzione simile. Se la compilazione ha esito positivo, lo shader compilato viene restituito in un'interfaccia ID3D10Blob . Infine, usare il metodo GetBufferPointer per restituire il puntatore.
Creare un oggetto Shader
Dopo aver compilato lo shader, chiamare CreateVertexShader per creare l'oggetto shader:
ID3D10VertexShader ** ppVertexShader
ID3D10Blob pBlob;
// Create the vertex shader
hr = pd3dDevice->CreateVertexShader( (DWORD*)pBlob->GetBufferPointer(),
pBlob->GetBufferSize(), &ppVertexShader );
// Release the pointer to the compiled shader once you are done with it
pBlob->Release();
Per creare l'oggetto shader, passare il puntatore al shader compilato in CreateVertexShader. Poiché è stato necessario compilare correttamente lo shader per primo, questa chiamata passerà quasi certamente, a meno che non si abbia un problema di memoria nel computer.
È possibile creare tutti gli oggetti shader desiderati e semplicemente mantenere i puntatori a loro. Questo stesso meccanismo funziona per gli shader geometry e pixel presupponendo che corrispondano ai profili shader (quando si chiama il metodo di compilazione) ai nomi dell'interfaccia (quando si chiama il metodo create).
Impostare l'oggetto Shader
L'ultimo passaggio è impostato sullo shader sulla fase della pipeline. Poiché nella pipeline sono presenti tre fasi dello shader, è necessario eseguire tre chiamate API, una per ogni fase.
// Set a vertex shader
pd3dDevice->VSSetShader( g_pVS10 );
La chiamata a VSSetShader accetta il puntatore al vertex shader creato nel passaggio 1. Questo imposta lo shader nel dispositivo. La fase vertex shader è ora inizializzata con il relativo codice vertex shader, tutto ciò che rimane inizializza le variabili shader.
Ripetere per tutte le 3 fasi shader
Ripetere questi stessi set di passaggi per compilare qualsiasi vertice o pixel shader o anche uno shader geometry che restituisce l'output dello shader pixel.