NV30, R300 und DirectX 9

DirectX 9.0 - Ein Überblick

In diesem Artikel möchten wir einen kleinen Überblick über die Neuerungen in DirectX 9.0 geben, mit Schwerpunkt auf den Vertex und Pixel Shadern, welche eindeutig die wichtigsten neuen Features sind.


Neue Texture- / Backbuffer- / Depth+Stencil- / Rendertargetformate

Textureformate
32 Bit (zwischen 2-16 Bit je Kanal) Integer Formate
  • D3DFMT_A2B10G10R10
  • D3DFMT_A2R10G10B10
  • D3DFMT_V16U16
  • D3DFMT_A2W10V10U10
  • D3DFMT_CxV8U8
Hier ist vorallem das CxV8U8 Format interessant, da es sich hier um ein komprimiertes Format für Normalen handelt.


64 Bit (zwischen 16 Bit je Kanal) Integer Formate
  • D3DFMT_A16B16G16R16
  • D3DFMT_Q16W16V16U16
  • D3DFMT_MULTI2_ARGB8
Neue hochauflösende Normalenformate, welche bereits bei ATi's Car Demo bewundert werden konnten. Das MULTI2_ARGB8 ist eine Multi-Element Texture Format. Diese kann als Rendertarget verwendet werden, wobei der Pixel Shader dort einzelne Element(mehrere RGBA Kanäle) ändern kann. Harwaresupport ist momentan noch unbekannt.


16-64 Bit Fließkomma (16 Bit je Kanal) Formate
  • D3DFMT_R16F
  • D3DFMT_G16R16F
  • D3DFMT_A16B16G16R16F
Fließkomma Formate sind auch neu. Diese Texturen werden nie auf Polygone gelegt, sondern als Lookup-Table/Zwischenspeicher für den Vertex/Pixel Shader verwendet.


32-128 Bit Fließkomma (32 Bit je Kanal) Formate
  • D3DFMT_R32F
  • D3DFMT_G32R32F
  • D3DFMT_A32B32G32R32F
Fließkomma Formate gibt es in zwei Bittiefen pro Kanal. Jedoch sollte man nicht vergessen, dass die 128 Bit Versionen 4x mehr Bandbreite als eine unkomprimierte 32 Bit Texture verschlingen.


Backbufferformate
32 Bit (2-10 Bit je Kanal) Integer Formate
  • D3DFMT_A2B10G10R10
  • D3DFMT_A2R10G10B10
Diese neuen Formate sind nicht im Vollbildmodus einsetzbar. Daher dürfte die Verwendung eher in professionellen Programmen liegen wie z.B.CAD, Bildbearbeitung oder 3D-Modellierung.


Depth+Stencilformate
32-Bit Fließkomma Formate
  • D3DFMT_D24FS8
  • D3DFMT_D32F
Fließkomma Z-Buffer Formate sind auch neu. Die GeForce FX oder eine Karte von 3Dlabs könnten die ersten sein, welche diese unterstützen.

Rendertargetformate

Als Rendertarget können fast alle neuen Textureformate verwendet werden, je nach Hardware. Rendertargets sind Backbuffer, welche jedoch nie direkt sichtbar gemacht werden. DirectX 9.0 unterstützt hier 4 Stück, man kann sie auch als temporäre Ablagen des Pixel Shaders bezeichnen. 



Mehrere Rendertargets


Bisher konnten die Pixel Shader nur auf ein Ziel schreiben. Seit DirectX 9.0 sind es 4. Diese werden vorallem für Multi-Pass Algorithmen benötigt, wie z.B. ATi's NPR-Demo. Leider sind mit DirectX 9.0 nut Z-Buffer und Stencil Operationen in den MRT möglich. Dies dürfte sich ab 2004 / DirectX 10 ändern, wenn auch andere Operationen wie z.B. Fog, Alpha-Blending, Alpha-Test und FSAA hinzukommen sollen.

2 Rendertargets
Zwei Rendertargets werden benötigt, um dieses Szene zu erstellen.  



Adaptive Tesselation


Adaptive Tesselation nennt man ein Verfahren, welches den Detailgrad von N-Patches, RT-Patches oder andere HOS (Higher Order Surfaces) in Abhängigkeit von x,y,z, oder / und w verändert. Leider ist hier noch kein Hardwaresupport bei den Herstellern zu finden. Zwar sollten die Radeon 9500/9700 und die Matrox Parhelia dazu in der Lage sein, bisher wird dieses Feature jedoch noch nicht mit den DirectX 9.0 Treibern unterstützt.



Displacement Mapping


Hier unterstüzt DirectX 9.0 zwei Verfahren.

Displacement Mapping
  • Sampled Displacement Mapping
    • Ein einfaches Polygon wird mit eine Displacement Map belegt, wodurch es eine Struktur erhält und sich die Vertexanzahl erhöht. Adaptive Tesselation ist hierbei möglich. Diese Methode ist ideal für größere Objekte oder Landschaften. Hardwaresupport ist momentan nur beim Matrox Parhelia zu finden.
  • Pre-Sampled Displacement Mapping
    • Wie der Name schon sagt, muss hierbei dass zu verändernte Polygon bereits seinen fertigen Polygondetailgrad besitzen. Anhand der vorhandenen Vertexdaten wird dann mittels der Displacement Map die Position der Vertexdaten modifiziert, nicht jedoch die Anzahl. Adaptive Tesselation ist hierbei nicht möglich. Diese Mehtode eignet sich für kleine Objekte. Hardwaresupport ist momentan nur beim ATi's Radeon 9500/9700 zu finden, jedenfalls nach deren Ankündigung.

NVIDIA's GeForce FX unterstützt keines dieser Formate. Laut NVIDIA möchte man einen eigenen Weg gehen und Displacement Mapping über den Vertex Shader ermöglichen -> "Render to Vertex" nennt sich die Technik, über die bisher jedoch nichts bekannt ist.



Two-Sided Stencil


Bis DirectX 8.1 konnte man immer nur eine Seite eines Polygons mittels Stencil-Operationen rendern. Für Schattenvolumen benötigt man jedoch die Vorderseite und die Rückseite, welche jeweils mit verschiedenen Stenciloperationen zu behandeln sind. Also muss man die Geometriedaten zweimal durch die VPU schicken. Eben dieses Problem wird durch Two-Sided Stencil behoben, da man für Vorder- und Rückseite eines Polygons verschiedene Stenciloperationen festlegen kann und somit nur noch einen Durchlauf benötigt. Einen guten Artikel dazu gibts hier . Die Implementierung in bestehenden Code ist dabei sehr einfach, wie im Beispiel deutlich wird:

alt:
technique ShadowVolumes
{
    pass P0    // Durchlauf 1
    {       
        vertexshader = <vShd>;   

        VertexShaderConstant[16] = <mWVP>;
        VertexShaderConstant[20] = <mWV>;
        VertexShaderConstant[24] = <mP>;
        VertexShaderConstant[28] = <mWVt>;
        VertexShaderConstant[90] = <cShd>;
        VertexShaderConstant[91] = <pVL>;

        ColorWriteEnable = 0;
        ZFunc            = Less;
        ZWriteEnable     = False;
        StencilEnable    = True;
        StencilFunc      = Always;
        StencilMask      = 0xffffffff;
        StencilWriteMask = 0xffffffff;

        CullMode = CCW;                  // rendere nur Vorderseiten
        StencilZFail      = IncrSat;    // Stenciloperation - erhöhe Stencilbufferwert und fange Überlauf ab
    }

    pass P1    // Durchlauf 2
    {
        CullMode = CW;                  // rendere nur Rückseiten
        StencilZFail      = DecrSat;    // Stenciloperation - verringere Stencilbufferwert und fange Überlauf ab
    }
}

neu:
technique ShadowVolumes_TwoSidedStencil
{
    pass P0    // einziger Durchlauf
    {       
        vertexshader = <vShd>;   

        VertexShaderConstant[16] = <mWVP>;
        VertexShaderConstant[20] = <mWV>;
        VertexShaderConstant[24] = <mP>;
        VertexShaderConstant[28] = <mWVt>;
        VertexShaderConstant[90] = <cShd>;
        VertexShaderConstant[91] = <pVL>;

        ColorWriteEnable = 0;
        ZFunc            = Less;
        ZWriteEnable     = False;
        StencilEnable    = True;
        StencilFunc      = Always;
        StencilMask      = 0xffffffff;
        StencilWriteMask = 0xffffffff;

        CullMode = NONE;                // rendere Vorder- und Rückseiten
        StencilZFail      = IncrSat;    // Stenciloperation - erhöhe Stencilbufferwert und fange Überlauf ab
        CCW_StencilZFail   = DecrSat;   // Stenciloperation - verringere Stencilbufferwert und fange Überlauf ab
        TwoSidedStencilMode = True;
    }
}



Scissor-Test Rechteck


Mittels des Scissor(Schere)-Tests kann man einen Bereich im Rendertarget festlegen, welcher verändert werden darf. Der Scissor-Test kommt direkt nach dem Pixel Shader, noch vor dem Z-Test.





Async-Notification


Mittels Async-Notification können Anfragen an die Hardware gestellt werden, welche dann asynchron ausgeführt werden, sprich sie blockieren die Grafikkarte nicht und kosten somit kaum Leistung. Die wohl wichtigste Anfrage lautet D3DQUERYTYPE_OCCLUSION , welche die gezeichneten Pixel(in einem frei zu wählendem Bereich von Zeichenanweisungen) zurückgibt. Somit kann geprüft werden, ob bestimmte Objekte überhaupt sichtbar sind, z.B. Portale. Der einzige Nachteil(bei falscher Anwendung!) besteht in ihrer asynchronen Arbeitsweise, so muss man von Zeit zu Zeit prüfen, ob ein brauchbares Resultat vorliegt.



Gammakorrektur


Seit DirectX 9.0 besteht die Möglichkeit in den Pixel Shadern in einem linearem Gammaraum zu arbeiten. Dies hat vorallem den Vorteil, dass ein deutlicher Dynamikgewinn entsteht.


Hier ein simpler additiver Blendtest (Texture0 + Texture1*Blendfaktor):



Deutlich zu sehen, im linearen Gammaraum macht sich bereits ein Blendfaktor von 0.01 bemerkbar, ab 2.0 sieht man im nichtlinearen einen deutlichen Verlust des grünen Rings, erst bei 7.0 tritt dies auch im linearen Gammaraum ein. Ein negativer Blendfaktor zeigt ein ähnliches Verhalten.



High-Level Shading Language


Die DirectX 9.0 High-Level Shading Language ermöglicht es, wesentlich schneller Vertex und Pixel Shader zu schreiben, ohne sich mit dem Befehlssatz der Shader auseinanderzusetzen. Hier ein kleines Beispiel:

//-----------------------------------------------------------------------------
// Simple vertex shader for scene rendering
// c0-3 - WVP x-form
// c10 - light pos in obj. space
// c11 - light diffuse
// c12 - ambient
//-----------------------------------------------------------------------------

vs.1.1

dcl_position v0
dcl_normal   v3
dcl_texcoord v7

// Transform position
m4x4 r0, v0, c0
mov oPos, r0

// Output depth
mov oT0.x, r0.z

// Output texture coords
mov oT1.xy, v7

// Light direction to the vertex
sub r1, c10, v0

// Mormalized light direction
dp3 r1.w, r1, r1
rsq r1.w, r1.w
mul r1.xyz, r1, r1.w

// Normalize normal
dp3 r2.w, v3, v3
rsq r2.w, r2.w
mul r2.xyz, v3, r2.w

// Diffuse * material color + ambient
dp3 r3, r1, r2
mul r3, r3, c11
add oD0, r3, c12


//-----------------------------------------------------------------------------
// HLSL version

struct VS_INPUT
{
    float4 vPosition: POSITION;
    float3 vNormal: NORMAL;
    float2 vTexCoord: TEXCOORD0;
};

struct VS_OUTPUT_NOTEX
{
    float4 vPosition: POSITION;
    float fDepth: TEXCOORD0;
    float3 vNormal: TEXCOORD1;
    float3 vL: TEXCOORD2;
    float3 vH: TEXCOORD3;
};

// Simple non-textures object vertex shader
VS_OUTPUT_NOTEX obj_no_tex_vs(VS_INPUT v)
{
    VS_OUTPUT_NOTEX o;
    float4 vPosWVP;
    vPosWVP = mul(v.vPosition, matWorldViewProj);
    o.vPosition = vPosWVP;
    o.fDepth = vPosWVP.z;

    o.vNormal = v.vNormal;
    o.vL = normalize(lightPos - v.vPosition);
    // Compute halfway vector
    o.vH = normalize(normalize(viewPos - v.vPosition) + o.vL);
    return o;
}

technique ObjNoTex
{
    pass P0
    {
        VertexShader = compile vs_2_0 obj_no_tex_vs();

        CullMode = NONE;
    }
}

Bisher werden jedoch nur Shader der Version 1.1-2.0 unterstützt. Somit müssen Entwickler, welche die Version 2.x oder 3.0 nutzen wollen den assemblerartigen Weg gehen, oder NVIDIA's Cg nutzen.


<< vorherige Seite nächste Seite >>