Dela via


Använda en effekt (Direct3D 9)

Den här sidan visar hur du genererar och använder en effekt. De ämnen som beskrivs är hur du:

Skapa en effekt

Här är ett exempel på hur du skapar en effekt som tas från BasicHLSL-exempel. Koden för att skapa en felsökningsskuggning är från OnCreateDevice:

ID3DXEffect* g_pEffect = NULL;
DWORD dwShaderFlags = 0;

    dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
    dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
    dwShaderFlags |= D3DXSHADER_NO_PRESHADER;

    // Read the D3DX effect file
    WCHAR str[MAX_PATH];
    DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"BasicHLSL.fx" );

    D3DXCreateEffectFromFile( 
        pd3dDevice, 
        str, 
        NULL, // CONST D3DXMACRO* pDefines,
        NULL, // LPD3DXINCLUDE pInclude,
        dwShaderFlags, 
        NULL, // LPD3DXEFFECTPOOL pPool,
        &g_pEffect, 
        NULL );

Den här funktionen använder följande argument:

  • Enheten.
  • Filnamnet på effektfilen.
  • En pekare till en NULL-terminerad lista med #defines som ska användas vid parsing av shaderrn.
  • En valfri pekare till en användarskriven inkluderingshanterare. Hanteraren anropas av processorn när den behöver lösa en #include.
  • En skuggningskompileringsflagga som ger kompilatorn tips om hur skuggningen ska användas. Alternativen är:
    • Hoppar över valideringen, om kända bra skuggningar kompileras.
    • Hoppa över optimering (används ibland när optimeringar gör felsökning svårare).
    • Begär att felsökningsinformation inkluderas i shadern så att den kan felsökas.
  • Effektpoolen. Om fler än en effekt använder samma minnespoolpekare delas de globala variablerna i effekterna mellan dem. Om du inte behöver dela effektvariabler kan minnespoolen ställas in på NULL-.
  • En pekare till den nya effekten.
  • En pekare till en buffert som valideringsfel kan skickas till. I det här exemplet angavs parametern till NULL- och användes inte.

Anmärkning

Från och med december 2006 SDK är DirectX 10 HLSL-kompilatorn nu standardkompilatorn i både DirectX 9 och DirectX 10. Mer information finns i Effect-Compiler Tool.

 

Rendera en effekt

Sekvensen med anrop för att tillämpa effekttillstånd på en enhet är:

Effektåtergivningskoden är också enklare än motsvarande återgivningskod utan effekt. Här är återgivningskoden med en effekt:

// Apply the technique contained in the effect 
g_pEffect->Begin(&cPasses, 0);

for (iPass = 0; iPass < cPasses; iPass++)
{
    g_pEffect->BeginPass(iPass);

    // Only call CommitChanges if any state changes have happened
    // after BeginPass is called
    g_pEffect->CommitChanges();

    // Render the mesh with the applied technique
    g_pMesh->DrawSubset(0);

    g_pEffect->EndPass();
}
g_pEffect->End();

Renderingloopen består av att undersöka effekten för att avgöra hur många pass den innehåller och att sedan anropa alla pass i en teknik. Återgivningsloopen kan expanderas för att anropa flera tekniker, var och en med flera pass.

Använda semantik för att hitta effektparametrar

En semantik är en identifierare som är kopplad till en effektparameter så att ett program kan söka efter parametern. En parameter kan ha högst en semantisk. Semantiken finns efter ett kolon (:) efter parameternamnet. Till exempel:

float4x4 matWorldViewProj : WORLDVIEWPROJ;

Om du deklarerade den globala effektvariabeln utan att använda en semantisk variabel skulle den se ut så här i stället:

float4x4 matWorldViewProj;

Effektgränssnittet kan använda en semantik för att få åtkomst till en specifik effektparameter. Följande returnerar till exempel matrisens handtag:

D3DHANDLE handle = 
    m_pEffect->GetParameterBySemantic(NULL, "WORLDVIEWPROJ");

Förutom att söka efter semantiskt namn har effektgränssnittet många andra metoder för att söka efter parametrar.

Använd handtag för att hämta och ange parametrar effektivt

Handtag ger ett effektivt sätt att referera till effektparametrar, tekniker, pass och anteckningar med en effekt. Handtag (som är av typen D3DXHANDLE) är strängpekare. Handtagen som skickas till funktioner som GetParameterxxx eller GetAnnotationxxx kan vara i någon av tre former:

  • Ett handtag som returneras av en funktion som GetParameterxxx.
  • En sträng som innehåller namnet på parametern, tekniken, passet eller anteckningen.
  • Ett handtag inställt på NULL.

Det här exemplet returnerar ett handtag till parametern som har WORLDVIEWPROJ-semantiken kopplad till sig:

D3DHANDLE handle = 
    m_pEffect->GetParameterBySemantic(NULL, "WORLDVIEWPROJ");

Lägg till parameterinformation med anteckningar

Anteckningar är användarspecifika data som kan kopplas till valfri teknik, pass eller parameter. En anteckning är ett flexibelt sätt att lägga till information i enskilda parametrar. Informationen kan läsas tillbaka och användas på valfritt sätt som programmet väljer. En anteckning kan vara av vilken datatyp som helst och kan läggas till dynamiskt. Anteckningsdeklarationer avgränsas med vinkelparenteser. En anteckning innehåller:

  • En datatyp.
  • Ett variabelnamn.
  • Ett likhetstecken (=).
  • Datavärdet.
  • Ett avslutande semikolon (;).

Båda de föregående exemplen i det här dokumentet innehåller till exempel den här kommentaren:

texture Tex0 < string name = "tiger.bmp"; >;

Anteckningen är kopplad till texturobjektet och anger den texturfil som ska användas för att initiera texturobjektet. Anteckningen initierar inte texturobjektet. Det är bara en del av användarinformationen som är kopplad till variabeln. Ett program kan läsa anteckningen med antingen ID3DXBaseEffect::GetAnnotation eller ID3DXBaseEffect::GetAnnotationByName för att returnera strängen. Anteckningar kan också läggas till av programmet.

Varje anteckning:

Dela effektsparametrar

Effektparametrar är alla icke-statiska variabler som deklareras i en effekt. Detta kan omfatta globala variabler och anteckningar. Effektparametrar kan delas mellan olika effekter genom att deklarera parametrar med nyckelordet "delad" och sedan skapa effekten med en effektpool.

En effektpool innehåller parametrar för delad effekt. Poolen skapas genom att anropa D3DXCreateEffectPool, som returnerar ett ID3DXEffectPool-gränssnitt. Gränssnittet kan anges som indata till någon av funktionerna D3DXCreateEffectxxx när en effekt skapas. För att en parameter ska delas över flera effekter måste parametern ha samma namn, typ och semantik i var och en av de delade effekterna.

ID3DXEffectPool* g_pEffectPool = NULL;   // Effect pool for sharing parameters

    D3DXCreateEffectPool( &g_pEffectPool );

Effekter som delar parametrar måste använda samma enhet. Detta framtvingas för att förhindra delning av enhetsberoende parametrar (till exempel skuggningar eller texturer) mellan olika enheter. Parametrar tas bort från poolen när de effekter som innehåller de delade parametrarna släpps. Om delningsparametrar inte behövs anger du NULL- för effektpoolen när en effekt skapas.

Klonade effekter använder samma effektpool som den effekt som de klonas från. Att klona en effekt skapar en exakt kopia av en effekt, inklusive globala variabler, tekniker, pass och anteckningar.

Kompilera en effekt offline

Du kan kompilera en effekt under körning med D3DXCreateEffect, eller så kan du kompilera en effekt offline med hjälp av kommandoradsverktyget fxc.exe. Effekten i CompiledEffect Sample innehåller en hörnskuggning, en pixelskuggare och en teknik:

// File: CompiledEffect.fx

// Global variables
float4 g_MaterialAmbientColor;    // Material's ambient color
...

// Texture samplers
sampler RenderTargetSampler = 
   ...

// Type: Vertex shader                                      
VS_OUTPUT RenderSceneVS( float4 vPos : POSITION, 
                         float3 vNormal : NORMAL,
                         float2 vTexCoord0 : TEXCOORD0 )
{
   ...
};
// Type: Pixel shader
PS_OUTPUT RenderScenePS( VS_OUTPUT In ) 
{ 
   ...
}

// Type: Technique                                     
technique RenderScene
{
    pass P0
    {          
        ZENABLE = true;
        VertexShader = compile vs_1_1 RenderSceneVS();
        PixelShader  = compile ps_1_1 RenderScenePS();
    }
}

Med hjälp av Effect-Compiler Tool för att kompilera shadern för vs_1_1 genererades följande assemblershaderinstruktioner:

//
// Generated by Microsoft (R) D3DX9 Shader Compiler 4.09.02.1188
//
//   fxc /T vs_1_1 /E RenderSceneVS /Fc CompiledEffect.txt CompiledEffect.fx
//
//
// Parameters:
//
//   float4 g_LightAmbient;
//   float4 g_LightDiffuse;
//   float3 g_LightDir;
//   float4 g_MaterialAmbientColor;
//   float4 g_MaterialDiffuseColor;
//   float g_fTime;
//   float4x4 g_mWorld;
//   float4x4 g_mWorldViewProjection;
//
//
// Registers:
//
//   Name                   Reg   Size
//   ---------------------- ----- ----
//   g_mWorldViewProjection c0       4
//   g_mWorld               c4       3
//   g_MaterialAmbientColor c7       1
//   g_MaterialDiffuseColor c8       1
//   g_LightDir             c9       1
//   g_LightAmbient         c10      1
//   g_LightDiffuse         c11      1
//   g_fTime                c12      1
//
//
// Default values:
//
//   g_LightDir
//     c9   = { 0.57735, 0.57735, 0.57735, 0 };
//
//   g_LightAmbient
//     c10  = { 1, 1, 1, 1 };
//
//   g_LightDiffuse
//     c11  = { 1, 1, 1, 1 };
//

    vs_1_1
    def c13, 0.159154937, 0.25, 6.28318548, -3.14159274
    def c14, -2.52398507e-007, 2.47609005e-005, -0.00138883968, 0.0416666418
    def c15, -0.5, 1, 0.5, 0
    dcl_position v0
    dcl_normal v1
    dcl_texcoord v2
    mov r0.w, c12.x
    mad r0.w, r0.w, c13.x, c13.y
    expp r3.y, r0.w
    mov r0.w, r3.y
    mad r0.w, r0.w, c13.z, c13.w
    mul r0.w, r0.w, r0.w
    mad r1.w, r0.w, c14.x, c14.y
    mad r1.w, r0.w, r1.w, c14.z
    mad r1.w, r0.w, r1.w, c14.w
    mad r1.w, r0.w, r1.w, c15.x
    mad r0.w, r0.w, r1.w, c15.y
    mul r0.w, r0.w, v0.x
    mul r0.x, r0.w, c15.z
    dp3 r1.x, v1, c4
    dp3 r1.y, v1, c5
    dp3 r1.z, v1, c6
    mov r0.yzw, c15.w
    dp3 r2.x, r1, r1
    add r0, r0, v0
    rsq r1.w, r2.x
    dp4 oPos.x, r0, c0
    mul r1.xyz, r1, r1.w
    dp4 oPos.y, r0, c1
    dp3 r1.x, r1, c9
    dp4 oPos.z, r0, c2
    max r1.w, r1.x, c15.w
    mov r1.xyz, c8
    mul r1.xyz, r1, c11
    mov r2.xyz, c7
    mul r2.xyz, r2, c10
    dp4 oPos.w, r0, c3
    mad oD0.xyz, r1, r1.w, r2
    mov oD0.w, c15.y
    mov oT0.xy, v2

// approximately 34 instruction slots used

Förbättra prestanda med preshaders

En preshader är en teknik för att öka skuggningseffektiviteten genom att förberäkna konstanta skuggningsuttryck. Effektkompileraren hämtar automatiskt skuggningsberäkningar från en skuggnings brödtext och kör dem på processorn innan skuggningen körs. Därför fungerar preshaders bara med effekter. Dessa två uttryck kan till exempel utvärderas utanför en shader innan den körs.

mul(World,mul(View, Projection));
sin(time)

Shader-beräkningar som kan flyttas är de som är associerade med enhetliga parametrar. Beräkningen ändras alltså inte för varje hörn eller pixel. Om du använder effekter genererar och kör kompilatorn automatiskt en preshader åt dig. det finns inga flaggor att aktivera. Preshaders kan minska antalet instruktioner per skuggning och kan också minska antalet konstanta register som en skuggning förbrukar.

Tänk på effektkompilatorn som en slags kompilator med flera processorer eftersom den kompilerar skuggningskod för två typer av processorer: en PROCESSOR och en GPU. Dessutom är effektkompilatorn utformad för att flytta kod från GPU:n till processorn och därmed förbättra skuggningsprestanda. Det här liknar att dra ut ett statiskt uttryck ur en loop. En skuggning som omvandlar position från världsrymd till projektionsutrymme och kopierar strukturkoordinater skulle se ut så här i HLSL:

float4x4 g_mWorldViewProjection;    // World * View * Projection matrix
float4x4 g_mWorldInverse;           // Inverse World matrix
float3 g_LightDir;                  // Light direction in world space
float4 g_LightDiffuse;              // Diffuse color of the light

struct VS_OUTPUT
{
    float4 Position   : POSITION;   // vertex position 
    float2 TextureUV  : TEXCOORD0;  // vertex texture coords 
    float4 Diffuse    : COLOR0;     // vertex diffuse color
};

VS_OUTPUT RenderSceneVS( float4 vPos : POSITION, 
                         float3 vNormal : NORMAL,
                         float2 vTexCoord0 : TEXCOORD0)
{
    VS_OUTPUT Output;
    
    // Transform the position from object space to projection space
    Output.Position = mul(vPos, g_mWorldViewProjection);

    // Transform the light from world space to object space    
    float3 vLightObjectSpace = normalize(mul(g_LightDir, (float3x3)g_mWorldInverse)); 

    // N dot L lighting
    Output.Diffuse = max(0,dot(vNormal, vLightObjectSpace));
    
    // Copy the texture coordinate
    Output.TextureUV = vTexCoord0; 
    
    return Output;    
}
technique RenderVS
{
    pass P0
    {          
        VertexShader = compile vs_1_1 RenderSceneVS();
    }
}

Om du använder Effect-Compiler Tool för att kompilera skuggningen för vs_1_1 genereras följande sammansättningsinstruktioner:

technique RenderVS
{
    pass P0
    {
        vertexshader = 
            asm {
            //
            // Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
            //
            // Parameters:
            //
            //   float3 g_LightDir;
            //   float4x4 g_mWorldInverse;
            //   float4x4 g_mWorldViewProjection;
            //
            //
            // Registers:
            //
            //   Name                   Reg   Size
            //   ---------------------- ----- ----
            //   g_mWorldViewProjection c0       4
            //   g_mWorldInverse        c4       3
            //   g_LightDir             c7       1
            //
            
                vs_1_1
                def c8, 0, 0, 0, 0
                dcl_position v0
                dcl_normal v1
                dcl_texcoord v2
                mov r1.xyz, c7
                dp3 r0.x, r1, c4
                dp3 r0.y, r1, c5
                dp3 r0.z, r1, c6
                dp4 oPos.x, v0, c0
                dp3 r1.x, r0, r0
                dp4 oPos.y, v0, c1
                rsq r0.w, r1.x
                dp4 oPos.z, v0, c2
                mul r0.xyz, r0, r0.w
                dp4 oPos.w, v0, c3
                dp3 r0.x, v1, r0
                max oD0, r0.x, c8.x
                mov oT0.xy, v2
            
            // approximately 14 instruction slots used
            };

        //No embedded pixel shader
    }
}

Detta använder cirka 14 platser och förbrukar 9 konstanta register. Med en preshader flyttar kompilatorn de statiska uttrycken från skuggningen och in i preshadern. Samma skuggning skulle se ut så här med en preshader:

technique RenderVS
{
    pass P0
    {
        vertexshader = 
            asm {
            //
            // Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
            //
            // Parameters:
            //
            //   float3 g_LightDir;
            //   float4x4 g_mWorldInverse;
            //
            //
            // Registers:
            //
            //   Name            Reg   Size
            //   --------------- ----- ----
            //   g_mWorldInverse c0       3
            //   g_LightDir      c3       1
            //
            
                preshader
                dot r0.x, c3.xyz, c0.xyz
                dot r0.y, c3.xyz, c1.xyz
                dot r0.z, c3.xyz, c2.xyz
                dot r1.w, r0.xyz, r0.xyz
                rsq r0.w, r1.w
                mul c4.xyz, r0.w, r0.xyz
            
            // approximately 6 instructions used
            //
            // Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
            //
            // Parameters:
            //
            //   float4x4 g_mWorldViewProjection;
            //
            //
            // Registers:
            //
            //   Name                   Reg   Size
            //   ---------------------- ----- ----
            //   g_mWorldViewProjection c0       4
            //
            
                vs_1_1
                def c5, 0, 0, 0, 0
                dcl_position v0
                dcl_normal v1
                dcl_texcoord v2
                dp4 oPos.x, v0, c0
                dp4 oPos.y, v0, c1
                dp4 oPos.z, v0, c2
                dp4 oPos.w, v0, c3
                dp3 r0.x, v1, c4
                max oD0, r0.x, c5.x
                mov oT0.xy, v2
            
            // approximately 7 instruction slots used
            };

        //No embedded pixel shader
    }
}

En effekt kör en preshader precis innan du kör en skuggning. Resultatet är samma funktion med ökad skuggningsprestanda eftersom antalet instruktioner som måste köras (för varje hörn eller pixel beroende på typen av skuggning) har minskat. Dessutom förbrukas färre konstanta register av skuggningen som ett resultat av att statiska uttryck flyttas till preshadern. Det innebär att skuggningar som tidigare begränsats av antalet konstanta register som de behövde nu kan kompileras eftersom de kräver färre konstanta register. Det är rimligt att förvänta sig en prestandaförbättring på 5 procent och 20 procent från preshaders.

Tänk på att indatakonstanterna skiljer sig från utdatakonstanterna i en preshader. Utdata c1 är inte samma som indata c1-registret. Att skriva till ett konstant register i en preshader skriver faktiskt till motsvarande shaders indata (konstant) plats.

// BaseDelta c0 1
// Refinements c1 1
preshader
mul c1.x, c0.x, (-2)
add c0.x, c0.x, c0.x
cmp c5.x, c1.x, (1), (0)

Cmp-instruktionen ovan läser värdet för preshader c1, medan mul-instruktionen skriver till hårdvaruskuggregistre som ska användas av vertex-shadern.

Använda parameterblock för att hantera effektparametrar

Parameterblock är block med effekttillståndsändringar. Ett parameterblock kan registrera tillståndsändringar så att de kan tillämpas senare med ett enda anrop. Skapa ett parameterblock genom att anropa ID3DXEffect::BeginParameterBlock:

    m_pEffect->SetTechnique( "RenderScene" );

    m_pEffect->BeginParameterBlock();
    D3DXVECTOR4 v4( Diffuse.r, Diffuse.g, Diffuse.b, Diffuse.a );
    m_pEffect->SetVector( "g_vDiffuse", &v4 );
    m_pEffect->SetFloat( "g_fReflectivity", fReflectivity );
    m_pEffect->SetFloat( "g_fAnimSpeed", fAnimSpeed );
    m_pEffect->SetFloat( "g_fSizeMul", fSize );
    m_hParameters = m_pEffect->EndParameterBlock();

Parameterblocket sparar fyra ändringar som tillämpas av API-anropen. När man anropar ID3DXEffect::BeginParameterBlock, börjar inspelningen av ändringar i tillståndet. ID3DXEffect::EndParameterBlock slutar lägga till ändringarna i parameterblocket och returnerar en referens. Handtaget används när du anropar ID3DXEffect::ApplyParameterBlock.

I EffectParam-exempelanvänds parameterblocket i återgivningssekvensen:

CObj g_aObj[NUM_OBJS];       // Object instances

    if( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {
        // Set the shared parameters using the first mesh's effect.

        // Render the mesh objects
        for( int i = 0; i < NUM_OBJS; ++i )
        {
            ID3DXEffect *pEffect = g_aObj[i].m_pEffect;

            // Apply the parameters
            pEffect->ApplyParameterBlock( g_aObj[i].m_hParameters );

            ...

            pEffect->Begin( &cPasses, 0 );
            for( iPass = 0; iPass < cPasses; iPass++ )
            {
              ...
            }
            pEffect->End();
        }

        ...
        pd3dDevice->EndScene();
    }

Parameterblocket anger värdet för alla fyra tillståndsändringarna precis innan ID3DXEffect::Begin anropas. Parameterblock är ett praktiskt sätt att ange flera tillståndsändringar med ett enda API-anrop.

effekter