Verwenden von Shadern in Direct3D 10

Die Pipeline verfügt über drei Shaderstufen, wobei jede mit einem HLSL-Shader programmiert ist. Alle Direct3D 10-Shader sind in HLSL geschrieben und richten sich an Shadermodell 4.

Unterschiede zwischen Direct3D 9 und Direct3D 10:

  • Im Gegensatz zu Direct3D 9-Shadermodellen, die in einer Zwischenassemblysprache erstellt werden könnten, werden Shadermodell 4.0-Shader nur in HLSL erstellt. Die Offlinekompilierung von Shadern in geräteverbrauchbarem Bytecode wird weiterhin unterstützt und für die meisten Szenarien empfohlen.

In diesem Beispiel wird nur ein Vertex-Shader verwendet. Da alle Shader aus dem allgemeinen Shaderkern erstellt werden, ähnelt das Erlernen der Verwendung eines Vertex-Shaders sehr stark der Verwendung eines Geometrie- oder Pixel-Shaders.

Nachdem Sie einen HLSL-Shader erstellt haben (in diesem Beispiel wird der Vertex-Shader HLSLWithoutFX.vsh verwendet), müssen Sie ihn für die jeweilige Pipelinephase vorbereiten, in der er verwendet wird. Dazu müssen Sie Folgendes tun:

Diese Schritte müssen für jeden Shader in der Pipeline wiederholt werden.

Kompilieren eines Shaders

Der erste Schritt besteht darin, den Shader zu kompilieren, um zu überprüfen, ob Sie die HLSL-Anweisungen ordnungsgemäß codiert haben. Hierzu rufen Sie D3D10CompileShader auf und stellen sie wie hier dargestellt mit mehreren Parametern bereit:

    IPD3D10Blob * pBlob;
    
        
    // Compile the vertex shader from the file
    D3D10CompileShader( strPath, strlen( strPath ), "HLSLWithoutFX.vsh", 
        NULL, NULL, "Ripple", "vs_4_0", dwShaderFlags, &pBlob, NULL );

Diese Funktion akzeptiert die folgenden Parameter:

  • Der Name der Datei ( und die Länge der Namenszeichenfolge in Bytes ), die den Shader enthält. In diesem Beispiel wird nur ein Vertex-Shader verwendet (in der Datei HLSLWithoutFX.vsh, in der die Dateierweiterung .vsh eine Abkürzung für Vertex-Shader ist).

  • Der Name der Shaderfunktion. In diesem Beispiel wird ein Vertex-Shader aus der Ripple-Funktion kompiliert, der eine einzelne Eingabe übernimmt und eine Ausgabestruktur zurückgibt (die Funktion stammt aus dem HLSLWithoutFX-Beispiel):

    VS_OUTPUT Ripple( in float2 vPosition : POSITION )
    
  • Ein Zeiger auf alle Makros, die vom Shader verwendet werden. Verwenden Sie D3D10_SHADER_MACRO, um Ihre Makros zu definieren. Erstellen Sie einfach eine Namenszeichenfolge, die alle Makronamen (wobei jeder Name durch ein Leerzeichen getrennt ist) und eine Definitionszeichenfolge (mit jedem Makrokörper durch ein Leerzeichen getrennt) enthält. Beide Zeichenfolgen müssen NULL beendet sein.

  • Ein Zeiger auf alle anderen Dateien, die Sie enthalten müssen, um Ihre Shader zu kompilieren. Dabei wird die ID3D10Include-Schnittstelle verwendet, die über zwei vom Benutzer implementierte Methoden verfügt: Open und Close. Damit dies funktioniert, müssen Sie den Textkörper der Open- und Close-Methoden implementieren. fügen Sie in der Open-Methode den Code hinzu, den Sie zum Öffnen der gewünschten Includedateien verwenden würden. Fügen Sie in der Close-Funktion den Code hinzu, um die Dateien zu schließen, wenn Sie damit fertig sind.

  • Der Name der zu kompilierenden Shaderfunktion. Dieser Shader kompiliert die Ripple-Funktion.

  • Das Shaderprofil, das beim Kompilieren als Ziel verwendet werden soll. Da Sie eine Funktion in einen Vertex-, Geometrie- oder Pixel-Shader kompilieren können, teilt das Profil dem Compiler mit, welcher Shadertyp und welches Shadermodell der Code verglichen werden soll.

  • Shadercompilerflags. Diese Flags geben dem Compiler an, welche Informationen in die kompilierte Ausgabe eingefügt werden sollen und wie der Ausgabecode optimiert werden soll: für geschwindigkeit, zum Debuggen usw. Eine Liste der verfügbaren Flags finden Sie unter Effektkonstanten (Direct3D 10). Das Beispiel enthält Code, mit dem Sie die Compilerflagswerte für Ihr Projekt festlegen können. Dies ist hauptsächlich eine Frage, ob Sie Debuginformationen generieren möchten oder nicht.

  • Ein Zeiger auf den Puffer, der den kompilierten Shadercode enthält. Der Puffer enthält auch alle eingebetteten Debug- und Symboltabelleninformationen, die von den Compilerflags angefordert werden.

  • Ein Zeiger auf einen Puffer, der eine Liste von Fehlern und Warnungen enthält, die während der Kompilierung aufgetreten sind. Dabei handelt es sich um dieselben Meldungen, die in der Debugausgabe angezeigt werden, wenn Sie den Debugger beim Kompilieren des Shaders ausführen würden. NULL ist ein akzeptabler Wert, wenn die Fehler nicht an einen Puffer zurückgegeben werden sollen.

Wenn der Shader erfolgreich kompiliert wird, wird ein Zeiger auf den Shadercode als ID3D10Blob-Schnittstelle zurückgegeben. Es wird als Blob-Schnittstelle bezeichnet, da der Zeiger auf eine Position im Arbeitsspeicher ist, die aus einem Array von DWORD besteht. Die Schnittstelle wird bereitgestellt, damit Sie einen Zeiger auf den kompilierten Shader abrufen können, den Sie im nächsten Schritt benötigen.

Ab dem Dezember 2006 SDK ist der DirectX 10 HLSL-Compiler jetzt sowohl in DirectX 9 als auch in DirectX 10 der Standardcompiler. Weitere Informationen finden Sie unter Effect-Compiler Tool .

Abrufen eines Zeigers auf einen kompilierten Shader

Mehrere API-Methoden erfordern einen Zeiger auf einen kompilierten Shader. Dieses Argument wird normalerweise als pShaderBytecode bezeichnet, da es auf einen kompilierten Shader verweist, der als Sequenz von Bytecodes dargestellt wird. Um einen Zeiger auf einen kompilierten Shader abzurufen, kompilieren Sie zuerst den Shader, indem Sie D3D10CompileShader oder eine ähnliche Funktion aufrufen. Wenn die Kompilierung erfolgreich ist, wird der kompilierte Shader in einer ID3D10Blob-Schnittstelle zurückgegeben. Verwenden Sie schließlich die GetBufferPointer-Methode , um den Zeiger zurückzugeben.

Erstellen eines Shaderobjekts

Nachdem der Shader kompiliert wurde, rufen Sie CreateVertexShader auf, um das Shaderobjekt zu erstellen:

    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();

Um das Shaderobjekt zu erstellen, übergeben Sie den Zeiger an den kompilierten Shader an CreateVertexShader. Da Sie den Shader zuerst erfolgreich kompilieren mussten, wird dieser Aufruf mit ziemlicher Sicherheit erfolgreich ausgeführt, es sei denn, Sie haben ein Speicherproblem auf Ihrem Computer.

Sie können beliebig viele Shaderobjekte erstellen und einfach nur Zeiger darauf beibehalten. Dieser Mechanismus funktioniert auch für Geometrie- und Pixel-Shader, wenn Sie die Shaderprofile (beim Aufrufen der Kompilierungsmethode) mit den Schnittstellennamen (beim Aufrufen der Create-Methode) übereinstimmen.

Festlegen des Shaderobjekts

Im letzten Schritt wird der Shader auf die Pipelinephase festgelegt. Da es drei Shaderphasen in der Pipeline gibt, müssen Sie drei API-Aufrufe ausführen, einen für jede Phase.

    // Set a vertex shader
    pd3dDevice->VSSetShader( g_pVS10 );

Der Aufruf von VSSetShader verwendet den Zeiger auf den in Schritt 1 erstellten Scheitelpunktshader. Dadurch wird der Shader im Gerät festgelegt. Die Vertex-Shaderphase wird jetzt mit ihrem Vertex-Shadercode initialisiert. Es bleibt nur noch die Initialisierung aller Shadervariablen.

Wiederholen für alle 3 Shaderphasen

Wiederholen Sie die gleichen Schritte, um einen Vertex- oder Pixel-Shader oder sogar einen Geometrie-Shader zu erstellen, der an den Pixel-Shader ausgegeben wird.

Kompilieren von Shadern

Programmieranleitung für HLSL