次の方法で共有


エフェクトの使用 (Direct3D 9)

このページでは、エフェクトを生成および使用する方法について説明します。ここで取り上げるトピックは、次を行う方法も含みます。

  • エフェクトの作成
  • エフェクトのレンダリング
  • セマンティクスを使用したエフェクト パラメーターの検索
  • ハンドルを使用したパラメーターの効率的取得および設定
  • アノテーションによるパラメーター情報の追加
  • エフェクト パラメーターの共有
  • エフェクトのオンラインでのコンパイル
  • プレシェーダーを使用したパフォーマンスの向上
  • パラメーター ブロックを使用したエフェクト パラメーターの管理

エフェクトの作成

BasicHLSL サンプルにあるエフェクトの作成例を以下に示します。デバック シェーダーを作成するためのエフェクト作成コードは、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 ); 

この関数は次の引数をとります。

  • デバイス。
  • エフェクト ファイルのファイル名。
  • シェーダーの解析中に使用される、#defines の NULL 終端リストへのポインター。
  • ユーザー書き込みへのオプションのポインターはハンドラーを含みます。ハンドラーは、#include を解決するために必要となるときは常にプロセッサーによって呼び出されます。
  • コンパイラー にシェーダーの使用法に関するヒントを与えるシェーダー コンパイル フラグ。オプションには次のものがあります。
    • 検証のスキップ。優れたシェーダーがコンパイルされることがわかっている場合。
    • 最適化のスキップ (最適化によってデバッグが困難になる場合に使用されることがあります)。
    • デバッグできるようにシェーダーに含めるデバッグ情報の要求。
  • エフェクト プール。複数のエフェクトが同じメモリー プール ポインターを使用する場合、エフェクト内のグローバル変数は互いに共有されます。エフェクト変数を共有する必要がない場合、メモリー プールは NULL に設定できます。
  • 新しいエフェクトへのポインター。
  • 検証エラーの送信先として指定できるバッファーへのポインター。この例では、パラメーターは NLL に設定されていて、使用されていません。

注: 2006 年 12 月の SDK から、DirectX 10 HLSL コンパイラは DirectX 9 と DirectX 10 の両方で既定のコンパイラとなりました。詳細については、「エフェクト コンパイラ ツール」を参照してください。

エフェクトのレンダリング

エフェクト ステートをデバイスに適用するための呼び出しの順序を次に示します。

また、エフェクト レンダリング コードは、エフェクトを含まない対応するレンダリング コードよりも単純です。以下はエフェクトを含むレンダリング コードです。

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

レンダリング ループは、エフェクトをクエリして含まれているパスの数を確認する処理、およびテクニックに対してすべてのパスを呼び出す処理から構成されます。レンダリング ループを拡張して、それぞれ複数のパスを持つ複数のテクニックを呼び出すことができます。

セマンティクスを使用したエフェクト パラメーターの検索

セマンティクスとは、エフェクト パラメーターにアタッチされた識別子です。アプリケーションは、セマンティクスを使用してエフェクト パラメーターを検索できます。1 つのパラメーターは最大で 1 つのセマンティクスを持つことができます。セマンティクスは、パラメーター名に続くコロン (:) の後に記述されます。次に例を示します。

 float4x4 matWorldViewProj : WORLDVIEWPROJ; 

セマンティックスを使用せずにエフェクトのグローバル変数を宣言した場合、代わりに次のように表示されます。

 float4x4 matWorldViewProj; 

エフェクト インターフェイスでは、セマンティクスを使用して特定のエフェクト パラメーターへのハンドルを取得できます。たとえば、次のコードでは、行列のハンドルが返されます。

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

エフェクト インターフェイスでパラメーターを検索するための方法は、セマンティクス名を使用する方法以外にもたくさんあります。

ハンドルを使用したパラメーターの効率的取得および設定

ハンドルを使用すると、エフェクトのパラメーター、テクニック、パス、およびエフェクトを含むアノテーションを効率的に参照できます。ハンドル (D3DXHANDLE 型) は、文字列ポインターです。GetParameterxxx や GetAnnotationxxx などの関数に渡されるハンドルは、次の 3 つのうちのいずれかの形式を取ります。

  • GetParameterxxx などの関数から返されたハンドル。
  • パラメーター、テクニック、パス、またはアノテーションの名前を含む文字列。
  • NULL に設定されたハンドル。

次の例では、WORLDVIEWPROJ セマンティクスがアタッチされているパラメーターへのハンドルが返されます。

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

アノテーションによるパラメーター情報の追加

アノテーションは、任意のテクニック、パス、またはパラメーターにアタッチできるユーザー固有のデータです。アノテーションを使うと、個々のパラメーターに情報を自由に追加できます。この情報は、アプリケーションで選択した方法で、読み返したり使うことができます。アノテーションには任意のデータ型を使うことができます。アノテーションは動的に追加できます。複数のアノテーションを宣言する場合は、山型かっこを使ってそれぞれの宣言を区切ります。アノテーションは、次の情報を格納します。

  • データ型。
  • 変数名。
  • 等号 (=)。
  • データ値。
  • 終了を示すセミコロン (;)。

たとえば、前述の例はどちらもこのアノテーションを含みます。

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

テクスチャー オブジェクトにアタッチされたアノテーションは、そのテクスチャー オブジェクトを初期化するために使用する必要があるテクスチャー ファイルを指定します。アノテーションは、テクスチャー オブジェクトを初期化しません。アノテーションは、単に変数にアタッチされたユーザー情報の一部です。アプリケーションでは、ID3DXBaseEffect::GetAnnotation または ID3DXBaseEffect::GetAnnotationByName によって文字列を取得することで、アノテーションを読み出すことができます。アノテーションは、アプリケーションによって追加することもできます。

アノテーションについては、次の点に注意してください。

  • 数値または文字列のどちらかでなければなりません。
  • 必ずデフォルト値で初期化しなければなりません。
  • テクニックとパス (Direct3D 9)、および最上位レベルのエフェクト パラメーターと関連付けることができます。
  • ID3DXEffect または ID3DXEffectCompiler を使って読み出しおよび書き込みを行えます。
  • ID3DXEffect を使って追加できます。
  • エフェクト内では参照できません。
  • サブセマンティクスやサブアノテーションを持つことはできません。

エフェクト パラメーターの共有

エフェクト パラメーターは、エフェクト内で宣言された非静的変数です。これには、グローバル変数とアノテーションが含まれます。エフェクト パラメーターは、"共有" キーワードを使用してパラメーターを宣言し、エフェクト プールでエフェクトを作成することで、異なるエフェクト間で共有できます。

エフェクト プールは、共有エフェクト パラメーターを格納します。プールを作成するには、D3DXCreateEffectPool インターフェイスを返す ID3DXEffectPool を呼び出します。このインターフェイスは、エフェクトの作成時に任意の D3DXCreateEffectxxx 関数の入力として使用できます。複数のエフェクト間でパラメーターを共有する場合、パラメーターはそれぞれの共有エフェクトで同じ名前、型、およびセマンティクスを持つ必要があります。

 ID3DXEffectPool* g_pEffectPool = NULL;   // Effect pool for sharing parameters      D3DXCreateEffectPool( &g_pEffectPool ); 

パラメーターを共有するエフェクトは、同じデバイスを使用する必要があります。これにより、デバイスに依存するパラメーター (シェーダーやテクスチャーなど) が異なるデバイス間で共有されることを回避できます。共有パラメーターを含むエフェクトが解放されると、パラメーターはプールから削除されます。パラメーターを共有する必要がない場合は、エフェクトを作成するときにエフェクト プールに NULL を設定します。

エフェクトのクローンは、元のエフェクトと同じエフェクト プールを使用します。エフェクトのクローン化を行うと、グローバル変数、テクニック、パス、およびアノテーションを含めて、エフェクトの完全なコピーが作成されます。

エフェクトのオンラインでのコンパイル

エフェクトは D3DXCreateEffect を使用して実行時にコンパイルできます。また、コマンド ライン コンパイラ ツール fxc.exe を使用してオフラインでコンパイルすることもできます。CompiledEffect サンプルのエフェクトでは、頂点シェーダー、ピクセル シェーダー、および 1 つのテクニックを扱っています。

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

エフェクト コンパイラ ツールを使用して vs_1_1 のシェーダーをコンパイルすると、次のアセンブリ シェーダー命令が生成されました。

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

プレシェーダーを使用したパフォーマンスの向上

プレシェーダーは一定のシェーダー式を事前に計算することによって、シェーダーの効率を増やすためのテクニックです。エフェクト コンパイラはシェーダーの本体からシェーダー計算を自動的に引き出し、シェーダーの実行前に CPU で実行します。結果として、プレシェーダーはエフェクトのみで動作します。たとえば、これらの2 の式はシェーダーの実行前にシェーダーの外側で評価できます。

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

削除できるシェーダー計算とは、均一パラメーターに関連付けられているものです。つまり計算は、頂点やピクセルごとに変化しません。エフェクトを使用する場合、エフェクト コンパイラがプレシェーダーを自動的に生成して実行してくれます。フラグを有効にする必要はありません。プレシェーダーは、シェーダーごとの命令の数を減らすことができ、シェーダーが消費する定数レジスタの数も減らすことができます。

エフェクト コンパイラは、CPU と GPU という 2 種類のプロセッサに対してシェーダー コードをコンパイルするため、ある種のマルチプロセッサ コンパイラとして考えることができます。さらに、エフェクト コンパイラは GPU から CPU にコードを移動するように設計されているため、シェーダーのパフォーマンスが向上します。これは、ループから静的な式を引き出すことに非常に似ています。位置をワールド空間から射影空間にトランスフォームして、テクスチャー座標をコピーするシェーダーは、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();     } } 

エフェクト コンパイラ ツールを使用して vs_1_1 のシェーダーをコンパイルすると、次のアセンブリ命令が生成されます。

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

これは、約 14 のスロットが使用し、9 つの定数レジスタを消費します。プレシェーダーがあると、コンパイラは静的な式をシェーダーからプレシェーダーに移動します。プレシェーダーがある場合同じシェーダーは次のように表示されます。

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

エフェクトは、シェーダーを実行する直前にプレシェーダーを実行します。結果は、実行が必要な命令の数 (シェーダーの対応に依存している頂点またはピクセルごと) が減少しているため、増やされたシェーダーのパフォーマンスと同じ能力となります。加えて、静的な式がプレシェーダーに移された結果、シェーダーによって消費される定数レジスタが少なくなります。これは、必要な定数レジスタが少なくなったために、必要とする定数レジスタの数によって以前は制限されていたシェーダーがコンパイルできるようになったことを意味します。プレシェーダーによって、5 % から 20 % のパフォーマンスの向上を期待できます。

プレシェーダーの入力定数は出力定数とは異なることに注意してください。出力 c1 は 入力 c1 レジスタと同じではありません。プレシェーダーの定数レジスタへの書き込みでは、実際には対応するシェーダーの入力 (定数) スロットに書き込まれます。

 // 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 命令は、プレシェーダー c1 の値を読み取りますが、mul 命令は頂点シェーダーが使用するハードウェア シェーダー レジスタに書き込みます。

パラメーター ブロックを使用したエフェクト パラメーターの管理

パラメーター ブロックとは、エフェクトのステート変更のブロックです。パラメーター ブロックは、1 回の呼び出しでステート変更を適用できるように、ステート変更を記録します。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(); 

API 呼び出しによって適用される 4 つの変更がパラメーター ブロックに保存されます。ID3DXEffect::BeginParameterBlock を呼び出すと、ステート変更の記録が開始されます。ID3DXEffect::EndParameterBlock はパラメーター ブロックに変更を追加する操作を停止し、ハンドルを返します。このハンドルは ID3DXEffect::ApplyParameterBlock を呼び出すとき使用されます。

EffectParam サンプルでは、レンダリング シーケンスでパラメーター ブロックが適用されます。

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

パラメーター ブロックは、ID3DXEffect::Begin 呼び出しの直前に、4 つのすべてのステート変更の値を設定します。パラメーター ブロックを使用すると、1 回の API 呼び出しで複数のステート変更を簡単に設定できます。