チュートリアル 7:テクスチャー マッピングと定数バッファー
まとめ
前のチュートリアルでは、プロジェクトへのライティングについて説明しました。ここではそれを基にして、立方体にテクスチャーを追加します。また、定数バッファーの概念と、それを使用して帯域幅使用率を最小限に抑えることで処理を高速化する方法について説明します。
このチュートリアルでは、結果として、中央の立方体の上にテクスチャーがマッピングされるように変更します。
このチュートリアルで、Direct3D 10 の基本的概念の説明は終わります。後続のチュートリアルでは、これらの概念に基づいて、DXUT、メッシュのロード、および各シェーダーの例を紹介します。
ソース
(SDK ルート)\Samples\C++\Direct3D10\Tutorials\Tutorial07
テクスチャー マッピング
テクスチャー マッピングとは、3D ジオメトリへの 2D イメージの射影のことを指します。それは、平凡な箱の上に包装紙を配置する、プレゼントのラッピングと考えられます。これを行うには、ジオメトリのサーフェス上の点を 2D イメージに対応付ける方法を指定する必要があります。
その方法は、モデルの座標とテクスチャーを正しく合わせるというものです。複雑なモデルの場合は、手動でテクスチャー用の座標を決定することが難しくなります。そのため、一般的に、3D モデリング パッケージでは、モデルを対応するテクスチャー座標と共にエクスポートします。ここで使用する例は立方体なので、テクスチャーと一致させるために必要な座標を決定することは簡単です。テクスチャー座標は、頂点で定義されてから、サーフェス上の個々のピクセルに対して補間されます。
テクスチャーからのシェーダー リソースの作成
テクスチャーは、シェーダーでの読み取りが可能なように、ファイルから取得されシェーダー リソース ビューの作成に使用される 2D イメージです。
hr = D3DX10CreateShaderResourceViewFromFile( g_pd3dDevice, L"seafloor.dds", NULL, NULL, &g_pTextureRV, NULL );
座標の定義
イメージを立方体にマッピングできるようにするには、まず、立方体の各頂点のテクスチャー座標を定義する必要があります。任意のサイズのイメージを使用できるため、使用される座標系を [0, 1] に正規化する必要があります。テクスチャーの左上隅は (0,0) に相当し、右下隅は (1,1) にマッピングされます。
この例では、テクスチャー全体を立方体の各側面に広げています。これにより、混乱することなく座標の定義が簡素化されます。ただし、テクスチャーを 6 面すべてにわたって引き伸ばすように指定することは可能ですが、点の定義はより難しくなり、引き伸ばされて歪んで見えます。
最初に、テクスチャー座標を含むように、頂点の定義に使用する構造体を更新しました。
struct SimpleVertex{ D3DXVECTOR3 Pos; // Position D3DXVECTOR2 Tex; // Texture Coordinate};
次に、これらの座標も含むように、シェーダーの入力レイアウトを更新しました。
// Define the input layoutD3D10_INPUT_ELEMENT_DESC layout[] ={ { L"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, { L"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 }, };
入力レイアウトが変更されたため、対応する頂点シェーダー入力も、その追加部分と一致するように変更する必要があります。
struct VS_INPUT{ float4 Pos : POSITION; float2 Tex : TEXCOORD;};
ついに、チュートリアル 4 で定義した頂点にテクスチャー座標を含める準備ができました。2 つ目のパラメーター入力が、テクスチャー座標を含んだ D3DXVECTOR2 であることに注目してください。立方体の各頂点は、テクスチャーの隅と一致します。これにより、各頂点の座標が (0,0) (0,1) (1,0) または (1,1) となる単純なマッピングが作成されます。
// Create vertex bufferSimpleVertex vertices[] ={ { D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) }, { D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) }, { D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) }, { D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) }, { D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) }, { D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) }, { D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) }, { D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) }, { D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) }, { D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) }, { D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) }, { D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) }, { D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) }, { D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) }, { D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) }, { D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) }, { D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) }, { D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) }, { D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) }, { D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) }, { D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) }, { D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) }, { D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) }, { D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) },};
テクスチャーをサンプリングするときは、下にあるジオメトリのマテリアル カラーでそれを調整する必要があります。
シェーダー リソースとしてのテクスチャーのバインド
テクスチャーは、前のチュートリアルで説明した行列とベクトルのようなオブジェクトです。テクスチャーをシェーダーで使用できるようにするには、それらをエフェクトに設定する必要があります。これは、変数へのポインターを取得してから、それをシェーダー リソースとして設定することで実行できます。
g_pDiffuseVariable = g_pEffect->GetVariableByName("txDiffuse")->AsShaderResource();
リソース ポインターを取得したら、以前に初期化した 2D テクスチャー リソース ビューをフックするために使用できます。
g_pDiffuseVariable->SetResource( g_pTextureRV );
ここまでで、シェーダー内のテクスチャーを使用する準備ができました。
テクスチャー (fx) の適用
実際にジオメトリの上にテクスチャーをマッピングするには、ピクセル シェーダー内のテクスチャー ルックアップ関数を呼び出します。関数 Sample は 2D テクスチャーのテクスチャー ルックアップを実行し、サンプリングされた色を返します。次に示すピクセル シェーダーは、この関数を呼び出して、基になるメッシュ カラー (またはマテリアル カラー) で乗算してから、最終的な色を出力します。
txDiffuse は、リソース ビュー g_pTextureRV をそれにバインドしたときに、上のコードから渡したテクスチャーを格納するオブジェクトです。
samLinear は、テクスチャー ルックアップのためのサンプラー仕様であり、以下で説明されています。
input.Tex は、ソース内で指定したテクスチャーの座標です。
// Pixel Shaderfloat4 PS( PS_INPUT input) : SV_Target{ return txDiffuse.Sample( samLinear, input.Tex ) * vMeshColor;}
変数 samLinear は、指定したテクスチャーのサンプリング方法をピクセル シェーダーに 指示するための情報を含む構造体です。ここでは、線形フィルターと、ラップされる両方のアドレスが含まれます。これらの設定は、単純なテクスチャーには役立ちますが、フィルターの説明は、このチュートリアルの範囲外となります。
SamplerState samLinear{ Filter = MIN_MAG_MIP_LINEAR; AddressU = Wrap; AddressV = Wrap;};
必ず実行する必要のあるもう 1 つの操作は、頂点シェーダーを通じてテクスチャー座標を渡すことです。これを行わないと、データはピクセル シェーダーに到達するときに失われます。ここでは、単純に入力の座標を出力にコピーして、ハードウェアに残りを処理させます。
// Vertex ShaderPS_INPUT VS( VS_INPUT input ){ PS_INPUT output = (PS_INPUT)0; output.Pos = mul( input.Pos, World ); output.Pos = mul( output.Pos, View ); output.Pos = mul( output.Pos, Projection ); output.Tex = input.Tex; return output;}
定数バッファー
Direct3D 10 の使用を開始すると、アプリケーションで定数バッファーを使用してシェーダー定数 (シェーダー変数) を設定できます。定数バッファーは、C スタイルの構造体に似た構文を使用して宣言されます。定数バッファーを使用すると、個別の呼び出しを行って各定数を別々にコミットする代わりに、シェーダー定数を分類して同時にコミットできるようになることで、シェーダー定数の更新に必要な帯域幅を削減できます。
定数バッファーを効率的に使用する最も良い方法は、それらの更新頻度に基づいて、シェーダー変数を定数バッファーに編成することです。これにより、アプリケーションにおいて、シェーダー定数の更新に必要な帯域幅を最小限に抑えることができます。例としてこのチュートリアルでは、定数を、各フレームを変更する変数用、ウィンドウ サイズが変更されたときのみ変更する変数用、および一旦設定されたら変更しない変数用の 3 つの構造体に分類します。
次の定数バッファーは、このチュートリアルの .fx ファイルで定義されています。
cbuffer cbNeverChanges { matrix View; }; cbuffer cbChangeOnResize { matrix Projection; }; cbuffer cbChangesEveryFrame { matrix World; float4 vMeshColor; };
このチュートリアルでは、ID3D10Effect インスタンスを使用して、変数が変化する際の定数バッファーの更新を処理します。ID3D10Effect インターフェイスによって、定数バッファーの作成、更新、設定などの多くのタスクがより簡単になります。次のコードは、アプリケーションでの定数バッファー変数の使用方法を示しています。
定数バッファー変数をエフェクト インスタンスから取得すると、エフェクト インスタンスによって必要なすべての定数バッファーがセットアップされるようになります。
//called once after the effect is loaded and the ID3D10Effect instance is created g_pWorldVariable = g_pEffect->GetVariableByName( "World" )->AsMatrix(); g_pMeshColorVariable = g_pEffect->GetVariableByName( "vMeshColor" )->AsVector();
定数バッファー変数の更新によって、エフェクト インスタンスが適宜定数バッファーをコミットするように通知されます。
//called whenever the variables in the constant buffer are changed g_pWorldVariable->SetMatrix( ( float* )&g_World ); g_pMeshColorVariable->SetFloatVector( ( float* )g_vMeshColor );