Bagikan melalui


Menggunakan Efek (Direct3D 9)

Halaman ini akan menunjukkan kepada Anda cara membuat dan menggunakan efek. Topik yang dibahas meliputi cara:

Membuat Efek

Berikut adalah contoh pembuatan efek yang diambil dari Sampel BasicHLSL. Kode pembuatan efek untuk membuat shader debug berasal dari 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 );

Fungsi ini mengambil argumen ini:

  • Perangkat.
  • Nama file dari file efek.
  • Penunjuk ke daftar #defines yang dihentikan NULL, yang akan digunakan saat mengurai shader.
  • Penunjuk opsional ke handler yang disertakan oleh pengguna. Handler dipanggil oleh prosesor setiap kali perlu menyelesaikan #include.
  • Bendera kompilasi shader yang memberikan petunjuk pengkompilasi tentang bagaimana shader akan digunakan. Opsi ini meliputi:
    • Melompati validasi, jika diketahui shader baik sedang dikompilasi.
    • Melewati pengoptimalan (terkadang digunakan saat pengoptimalan membuat penelusuran kesalahan lebih sulit).
    • Meminta informasi debug untuk disertakan dalam shader sehingga dapat di-debug.
  • Kumpulan efek. Jika lebih dari satu efek menggunakan penunjuk kumpulan memori yang sama, variabel global dalam efek dibagikan satu sama lain. Jika tidak perlu berbagi variabel efek, kumpulan memori dapat diatur ke NULL.
  • Penunjuk ke efek baru.
  • Penunjuk ke buffer tempat kesalahan validasi dapat dikirim. Dalam contoh ini, parameter diatur ke NULL dan tidak digunakan.

Catatan

Dimulai dengan SDK Desember 2006, pengkompilasi DirectX 10 HLSL sekarang menjadi pengkompilasi default di DirectX 9 dan DirectX 10. Lihat Alat Pengkompilasi Efek untuk detailnya.

 

Merender Efek

Urutan panggilan untuk menerapkan status efek ke perangkat adalah:

Kode render efek juga lebih sederhana daripada kode render yang sesuai tanpa efek. Berikut adalah kode render dengan efek:

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

Perulangan render terdiri dari kueri efek untuk melihat berapa banyak pass yang dikandungnya, lalu memanggil semua pass untuk teknik. Perulangan render dapat diperluas untuk memanggil beberapa teknik, masing-masing dengan beberapa pass.

Menggunakan Semantik untuk Menemukan Parameter Efek

Semantik adalah pengidentifikasi yang dilampirkan ke parameter efek untuk memungkinkan aplikasi mencari parameter . Parameter dapat memiliki paling banyak satu semantik. Semantik terletak di titik dua (:) setelah nama parameter. Contohnya:

float4x4 matWorldViewProj : WORLDVIEWPROJ;

Jika Anda mendeklarasikan efek variabel global tanpa menggunakan semantik, itu akan terlihat seperti ini sebagai gantinya:

float4x4 matWorldViewProj;

Antarmuka efek dapat menggunakan semantik untuk mendapatkan handel ke parameter efek tertentu. Misalnya, berikut mengembalikan handel matriks:

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

Selain mencari dengan nama semantik, antarmuka efek memiliki banyak metode lain untuk mencari parameter.

Gunakan Handel untuk Mendapatkan dan Mengatur Parameter Secara Efisien

Handel memberikan cara yang efisien untuk mereferensikan parameter efek, teknik, pass, dan anotasi dengan efek. Handel (yang berjenis D3DXHANDLE) adalah penunjuk string. Handel yang diteruskan ke fungsi seperti GetParameterxxx atau GetAnnotationxxx dapat berada dalam salah satu dari tiga bentuk:

  • Handel yang dikembalikan oleh fungsi seperti GetParameterxxx.
  • String yang berisi nama parameter, teknik, lulus, atau anotasi.
  • Handel diatur ke NULL.

Contoh ini mengembalikan handel ke parameter yang memiliki semantik WORLDVIEWPROJ yang melekat padanya:

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

Menambahkan Informasi Parameter dengan Anotasi

Anotasi adalah data khusus pengguna yang dapat dilampirkan ke teknik, lulus, atau parameter apa pun. Anotasi adalah cara yang fleksibel untuk menambahkan informasi ke parameter individual. Informasi dapat dibaca kembali dan digunakan dengan cara apa pun yang dipilih aplikasi. Anotasi dapat berupa jenis data apa pun dan dapat ditambahkan secara dinamis. Deklarasi anotasi dibatasi oleh tanda kurung sudut. Anotasi berisi:

  • Jenis data.
  • Nama variabel.
  • Tanda sama dengan (=).
  • Nilai data.
  • Titik koma akhir (;).

Misalnya, kedua contoh sebelumnya dalam makalah ini berisi anotasi ini:

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

Anotasi dilampirkan ke objek tekstur dan menentukan file tekstur yang harus digunakan untuk menginisialisasi objek tekstur. Anotasi tidak menginisialisasi objek tekstur, itu hanyalah bagian dari informasi pengguna yang dilampirkan ke variabel. Aplikasi dapat membaca anotasi dengan ID3DXBaseEffect::GetAnnotation atau ID3DXBaseEffect::GetAnnotationByName untuk mengembalikan string. Anotasi juga dapat ditambahkan oleh aplikasi.

Setiap anotasi:

Parameter Efek Berbagi

Parameter efek adalah semua variabel non-statis yang dideklarasikan dalam efek. Ini dapat mencakup variabel dan anotasi global. Parameter efek dapat dibagikan di antara efek yang berbeda dengan mendeklarasikan parameter dengan kata kunci "bersama" dan kemudian membuat efek dengan kumpulan efek.

Kumpulan efek berisi parameter efek bersama. Kumpulan dibuat dengan memanggil D3DXCreateEffectPool, yang mengembalikan antarmuka ID3DXEffectPool . Antarmuka dapat disediakan sebagai input ke salah satu fungsi D3DXCreateEffectxxx saat efek dibuat. Agar parameter dibagikan di beberapa efek, parameter harus memiliki nama, jenis, dan semantik yang sama di setiap efek bersama.

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

    D3DXCreateEffectPool( &g_pEffectPool );

Efek yang berbagi parameter harus menggunakan perangkat yang sama. Ini diberlakukan untuk mencegah berbagi parameter yang bergantung pada perangkat (seperti shader atau tekstur) di berbagai perangkat. Parameter dihapus dari kumpulan setiap kali efek yang berisi parameter bersama dirilis. Jika parameter berbagi tidak diperlukan, berikan NULL untuk kumpulan efek saat efek dibuat.

Efek kloning menggunakan kumpulan efek yang sama dengan efek dari mana mereka dikloning. Mengkloning efek membuat salinan efek yang tepat, termasuk variabel global, teknik, lulus, dan anotasi.

Mengkompilasi Efek Offline

Anda dapat mengkompilasi efek pada durasi dengan D3DXCreateEffect, atau Anda dapat mengkompilasi efek secara offline menggunakan alat pengkompilasi baris perintah fxc.exe. Efek dalam Sampel CompiledEffect berisi shader vertex, shader piksel, dan satu 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();
    }
}

Menggunakan Alat Pengkompilasi Efek untuk mengkompilasi shader untuk vs_1_1 menghasilkan instruksi shader rakitan berikut:

//
// 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

Meningkatkan Performa dengan Preshader

Preshader adalah teknik untuk meningkatkan efisiensi shader dengan menghitung ekspresi shader konstanta sebelumnya. Pengkompilasi efek secara otomatis menarik komputasi shader dari tubuh shader dan mengeksekusinya pada CPU sebelum shader berjalan. Akibatnya, preshader hanya bekerja dengan efek. Misalnya, kedua ekspresi ini dapat dievaluasi di luar shader sebelum dijalankan.

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

Komputasi shader yang dapat dipindahkan adalah yang terkait dengan parameter seragam; artinya, komputasi tidak berubah untuk setiap puncak atau piksel. Jika Anda menggunakan efek, pengkompilasi efek secara otomatis menghasilkan dan menjalankan preshader untuk Anda; tidak ada bendera untuk diaktifkan. Preshader dapat mengurangi jumlah instruksi per shader dan juga dapat mengurangi jumlah register konstan yang dikonsumsi shader.

Anggap pengkompilasi efek sebagai semacam pengkompilasi multi-prosesor karena mengkompilasi kode shader untuk dua jenis prosesor: CPU dan GPU. Selain itu, pengkompilasi efek dirancang untuk memindahkan kode dari GPU ke CPU dan karenanya meningkatkan performa shader. Ini sangat mirip dengan menarik ekspresi statis dari perulangan. Shader yang mengubah posisi dari ruang dunia ke ruang proyeksi, dan menyalin koordinat tekstur akan terlihat seperti ini di 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();
    }
}

Menggunakan Alat Pengkompilasi Efek untuk mengkompilasi shader untuk vs_1_1 menghasilkan instruksi perakitan berikut:

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
    }
}

Ini menggunakan sekitar 14 slot dan mengonsumsi 9 register konstanta. Dengan preshader, pengkompilasi memindahkan ekspresi statis keluar dari shader dan ke preshader. Shader yang sama akan terlihat seperti ini dengan 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
    }
}

Efek mengeksekusi preshader tepat sebelum mengeksekusi shader. Hasilnya adalah fungsionalitas yang sama dengan peningkatan performa shader karena jumlah instruksi yang perlu dijalankan (untuk setiap puncak atau piksel tergantung pada jenis shader) telah berkurang. Selain itu, lebih sedikit register konstan yang dikonsumsi oleh shader sebagai hasil dari ekspresi statis yang dipindahkan ke preshader. Ini berarti bahwa shader yang sebelumnya dibatasi oleh jumlah register konstan yang mereka butuhkan sekarang dapat dikompilasi karena membutuhkan lebih sedikit register konstanta. Sangat masuk akal untuk mengharapkan 5 persen dan peningkatan performa 20 persen dari preshader.

Perlu diingat bahwa konstanta input berbeda dari konstanta output dalam preshader. Output c1 tidak sama dengan input c1 register. Menulis ke register konstan di preshader benar-benar menulis ke slot input shader (konstanta) yang sesuai.

// 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)

Instruksi cmp di atas akan membaca nilai preshader c1, sementara instruksi mul akan menulis ke shader perangkat keras mendaftar untuk digunakan oleh shader vertex.

Gunakan Blok Parameter untuk Mengelola Parameter Efek

Blok parameter adalah blok perubahan status efek. Blok parameter dapat merekam perubahan status, sehingga dapat diterapkan nanti dengan satu panggilan. Buat blok parameter dengan memanggil 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();

Blok parameter menyimpan empat perubahan yang diterapkan oleh panggilan API. Memanggil ID3DXEffect::BeginParameterBlock mulai merekam perubahan status. ID3DXEffect::EndParameterBlock berhenti menambahkan perubahan ke blok parameter dan mengembalikan handel. Handel akan digunakan saat memanggil ID3DXEffect::ApplyParameterBlock.

Dalam Sampel EffectParam, blok parameter diterapkan dalam urutan render:

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

Blok parameter menetapkan nilai keempat perubahan status tepat sebelum ID3DXEffect::Begin dipanggil. Blok parameter adalah cara yang berguna untuk mengatur beberapa perubahan status dengan satu panggilan API.

Efek