次の方法で共有


効果の使用 (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 に設定できます。
  • 新しい効果へのポインター。
  • 検証エラーを送信できるバッファーへのポインター。 この例では、 パラメーターは NULL に設定されており、使用されていません。

注意

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

 

効果をレンダリングする

デバイスに効果の状態を適用するための呼び出しのシーケンスは次のとおりです。

効果のレンダリング コードは、効果のない対応するレンダリング コードよりも簡単です。 効果を持つレンダリング コードを次に示します。

// 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 つのセマンティックを含めることができます。 セマンティックはコロンの後にあります (:)パラメーター名の後。 たとえば、次のようになります。

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 を使用して注釈を読み取り、文字列を返すことができます。 注釈は、アプリケーションによって追加することもできます。

各注釈:

効果パラメーターを共有する

効果パラメーターは、エフェクトで宣言されたすべての非静的変数です。 これには、グローバル変数と注釈を含めることができます。 効果パラメーターは、"共有" キーワード (keyword)でパラメーターを宣言し、効果プールを使用して効果を作成することで、異なる効果間で共有できます。

効果プールには、共有効果パラメーターが含まれています。 プールは、ID3DXEffectPool インターフェイスを返す D3DXCreateEffectPool を呼び出すことによって作成されます。 インターフェイスは、効果の作成時に 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 命令は preshader 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 呼び出しで複数の状態変更を設定する便利な方法です。

エフェクト