テクスチャー リソースの作成 (Direct3D 10)
テクスチャー リソースは、構造化されたデータの集合です。通常、カラー値はテクスチャーに格納され、レンダリング中にパイプラインの各種ステージによって、入力および出力としてアクセスされます。テクスチャーの作成と使用方法の定義は、Direct3D 10 で趣向をこらしたシーンをリンダリングする際の重要な手順の 1 つです。
テクスチャーは、通常はカラー情報を持っていますが、さまざまな DXGI_FORMAT でテクスチャーを作成することにより、テクスチャーに別の種類のデータを格納できます。Direct3D 10 パイプラインでは、従来と異なる方法でこのデータを活用することが可能です。
すべてのテクスチャーには、消費できるメモリー量と格納できるテクセルの数に制限があります。これらの制限はリソース定数によって指定されます。
- ファイルからのテクスチャーの作成
- 空のテクスチャーの作成
- 複数のレンダー ターゲット
ファイルからのテクスチャーの作成
Direct3D 10 でテクスチャーを作成する際には、ビューを作成する必要があります。ビューは、レンダリング中にデバイスがテクスチャーにアクセスする方法を記述するオブジェクトです。テクスチャーにアクセスする最も一般的な方法は、シェーダーを使ってテクスチャーから読み取る方法です。シェーダー リソース ビューは、レンダリング中にシェーダーがテクスチャーから読み取りを行う方法を記述するものです。テクスチャーが使用するビューの種類は、テクスチャー作成時に指定する必要があります。
テクスチャーを作成し、初期データをロードする方法には、テクスチャーとビューを別々に作成する方法とテクスチャーとビューを同時に作成する方法の 2 つがあります。API は両方の手法を提供しているため、用途に合った方法を選択できます。
テクスチャーとビューを別々に作成
テクスチャーを作成する最も簡単な方法は、イメージ ファイルからロードすることです。テクスチャーを作成するには、単に特定の構造体に格納し、テクスチャーの名前を D3DX10CreateTextureFromFile に渡します。
ID3D10Device *pDevice = NULL; // Initialize D3D10 device... D3DX10_IMAGE_LOAD_INFO loadInfo; ZeroMemory( &loadInfo, sizeof(D3DX10_IMAGE_LOAD_INFO) ); loadInfo.BindFlags = D3D10_BIND_SHADER_RESOURCE; ID3D10Resource *pTexture = NULL; D3DX10CreateTextureFromFile( pDevice, L"sample.bmp", &loadInfo, NULL, &pTexture, NULL );
D3DX 関数 D3DX10CreateTextureFromFile は、次の 3 つのことを行います。最初に Direct3D 10 テクスチャー オブジェクトを作成し、次に入力イメージ ファイルを読み取り、最後にイメージ データをテクスチャー オブジェクトに格納します。上のサンプルでは BMP ファイルがロードされますが、この関数ではさまざまな種類のファイルをロードできます。
バインド フラグは、シェーダー リソースとしてテクスチャーが作成されることを示します。シェーダー ステージのレンダリング時には、このテクスチャーからの読み取りが可能になります。
上の例は、ロード パラメーターのすべてを指定するものではありません。実際、D3DX が入力イメージに基づいて適切な値を選択できるため、ロード パラメーターを指定しないことが有益であることが多くあります。入力イメージによって、テクスチャーを作成するためのすべてのパラメーターを決定したい場合は、次のように loadInfo パラメーターに NULL を指定します。
D3DX10CreateTextureFromFile( pDevice, L"sample.bmp", NULL, NULL, &pTexture, NULL );
ロードする情報に NULL を指定することは、単純で有効な設定方法です。
テクスチャーを作成したら、テクスチャーをシェーダーへの入力としてバインドするためのシェーダー リソース ビューを作成する必要があります。D3DX10CreateTextureFromFile はポインターをリソースに返し、テクスチャーには返さないため、ロードするリソースの正確な型を決定する必要があります。次に、ID3D10Device::CreateShaderResourceView を使ってシェーダー リソース ビューを作成します。
D3D10_SHADER_RESOURCE_VIEW_DESC srvDesc; D3D10_RESOURCE_DIMENSION type; pTexture->GetType( &type ); switch( type ) { case D3D10_RESOURCE_DIMENSION_BUFFER: //... break; case D3D10_RESOURCE_DIMENSION_TEXTURE1D: //... break; case D3D10_RESOURCE_DIMENSION_TEXTURE2D: { D3D10_TEXTURE2D_DESC desc; ID3D10Texture2D *pTexture2D = (ID3D10Texture2D*)pTexture; pTexture2D->GetDesc( &desc ); srvDesc.Format = desc.Format; srvDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = desc.MipLevels -1; } break; case D3D10_RESOURCE_DIMENSION_TEXTURE3D: //... break; default: //... break; } ID3D10ShaderResourceView *pSRView = NULL; pDevice->CreateShaderResourceView( pTexture, &srvDesc, &pSRView );
上記のサンプルでは 2D シェーダー リソース ビューを作成しましたが、他の種類のシェーダー リソース ビューを作成するコードもほとんど違いはありません。これですべてのシェーダー ステージが、このテクスチャーを入力として使用できるようになります。
D3DX10CreateTextureFromFile と ID3D10Device::CreateShaderResourceView を使ってテクスチャーおよび関連するビューを作成するのは、シェーダー ステージにバインドするテクスチャーを用意する方法の 1 つです。次のセクションでは、もう 1 つの方法であるテクスチャーおよびビューの同時作成について説明します。
テクスチャーとビューを同時に作成
Direct3D 10 で実行時にテクスチャーから読み取りを行うには、テクスチャーとシェーダー リソース ビューが必要です。このようにテクスチャーとシェーダー リソース ビューの作成は一般的なタスクであるため、D3DX にはそのための D3DX10CreateShaderResourceViewFromFile が用意されています。
D3DX10_IMAGE_LOAD_INFO loadInfo; ZeroMemory( &loadInfo, sizeof(D3DX10_IMAGE_LOAD_INFO) ); loadInfo.BindFlags = D3D10_BIND_SHADER_RESOURCE; loadInfo.Format = DXGI_FORMAT_BC1_UNORM; ID3D10ShaderResourceView *pSRView = NULL; D3DX10CreateShaderResourceViewFromFile( pDevice, L"sample.bmp", &loadInfo, NULL, &pSRView, NULL );
1 回の D3DX 呼び出しで、テクスチャーとシェーダー リソース ビューの両方が作成されます。loadInfo パラメーターに NULL を指定することで loadInfo パラメーターの機能を変えずに、テクスチャーを作成する方法のカスタマイズ、または入力ファイルからの必要パラメーターの抽出を行うことができます。
D3DX10CreateShaderResourceViewFromFile 関数によって返された ID3D10ShaderResourceView オブジェクトは、後から必要に応じて元の ID3D10Resource インターフェイスを取得するために使用することができます。これを行うには ID3D10View::GetResource メソッドを呼び出します。
D3DX では、1 つでテクスチャーとシェーダー リソース ビューを作成できる便利な関数が提供されています。ただし、どの方法でテクスチャーとビューを作成するのがアプリケーションの要求に適しているかを判断するのは開発者の責任です。
テクスチャーとシェーダー リソース ビューを作成する方法を理解したら、次のセクションでは、シェーダーを使ってテクスチャーからサンプリング (読み取り) を行う方法について紹介します。
空のテクスチャーの作成
アプリケーションでテクスチャーを作成し、テクスチャーに格納するデータを計算したり、グラフィック パイプライン を使ってテクスチャーをレンダリングし、結果を後から他のプロセスで使用することが必要になる場合があります。これらのテクスチャーは作成時に指定された使用方法の種類に応じて、グラフィックス パイプラインまたはアプリケーション自身によって更新することができます。
テクスチャーのレンダリング
空のテクスチャーを作成して、実行時にデータを格納する最も一般的なケースは、テクスチャーをレンダリングしてレンダリング処理の結果を以降のフローで使用する場合です。この目的でテクスチャーを作成するときは、既定の使用方法を指定する必要があります。
次のコード サンプルは空のテクスチャーを作成します。このテクスチャーは、パイプラインのレンダリング先として使用でき、以降はシェーダーの入力として使用できます。
// Create the render target texture D3D10_TEXTURE2D_DESC desc; ZeroMemory( &desc, sizeof(desc) ); desc.Width = 256; desc.Height = 256; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; desc.SampleDesc.Count = 1; desc.Usage = D3D10_USAGE_DEFAULT; desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; ID3D10Texture2D *pRenderTarget = NULL; pDevice->CreateTexture2D( &desc, NULL, &pRenderTarget );
テクスチャーを作成するには、テクスチャーが保持するプロパティに関する情報をアプリケーションから指定する必要があります。テクセル内のテクスチャーの幅と高さは 256 に設定します。このレンダー ターゲットは 1 つのミップマップ レベルのみを必要とします。必要なレンダー ターゲットは 1 つなので、配列のサイズは 1 に設定します。各テクセルは 4 つの 32 ビット浮動小数点値を格納するので、精度の高い情報の格納に使用できます (「DXGI_FORMAT」を参照)。必要なサンプリングは、ピクセルあたり 1 サンプリングです。使用方法には、メモリー上のレンダー ターゲットの配置が最も効率的になる、既定を設定します。最後に、テクスチャーをタイミングに応じてレンダー ターゲットおよびシェーダー リソースとしてバインドすることを指定します。
レンダリングを行うためにテクスチャーをパイプラインに直接バインドすることはできません。次のコード サンプルにあるようにレンダー ターゲット ビューを使用してください。
D3D10_RENDER_TARGET_VIEW_DESC rtDesc; rtDesc.Format = desc.Format; rtDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D; rtDesc.Texture2D.MipSlice = 0; ID3D10RenderTargetView *pRenderTargetView = NULL; pDevice->CreateRenderTargetView( pRenderTarget, &rtDesc, &pRenderTargetView );
レンダー ターゲット ビューのフォーマットは、単純に元のテクスチャーのフォーマットに設定されます。リソースの情報は 2D テクスチャーとして解釈されます。レンダー ターゲットでは、最初のミップマップ レベルのみを使用します。
レンダー ターゲットを出力としてパイプラインにバインドするためにレンダー ターゲット ビューを作成することと同様に、レンダー ターゲットを入力としてパイプラインにバインドするためにシェーダー リソース ビューを作成する必要があります。これをコードで示すと次のようになります。
// Create the shader-resource view D3D10_SHADER_RESOURCE_VIEW_DESC srDesc; srDesc.Format = desc.Format; srDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; srDesc.Texture2D.MostDetailedMip = 0; srDesc.Texture2D.MipLevels = 1; ID3D10ShaderResourceView *pShaderResView = NULL; pDevice->CreateShaderResourceView( pRenderTarget, &srDesc, &pShaderResView );
シェーダー リソース ビュー記述のパラメーターは、レンダー ターゲット ビュー記述のパラメーターと類似し、選ばれた理由も同じです。
手動によるテクスチャーへの格納
実行中にアプリケーションで値を計算し、それらを手動でテクスチャーに格納し、後からグラフィック パイプラインのレンダリング処理でこのテクスチャーを使用したい場合があります。そのためには、テクスチャーの基になるメモリーに CPU がアクセスできるように、空のテクスチャーを作成する必要があります。これを実行するには、ダイナミック テクスチャーを作成し、特殊なメソッドを呼び出して基になるメモリーへのアクセスが可能になるようにします。次のコード サンプルは、その方法を示すものです。
D3D10_TEXTURE2D_DESC desc; desc.Width = 256; desc.Height = 256; desc.MipLevels = desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.SampleDesc.Count = 1; desc.Usage = D3D10_USAGE_DYNAMIC; desc.BindFlags = D3D10_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE; ID3D10Texture2D *pTexture = NULL; pd3dDevice->CreateTexture2D( &desc, NULL, &pTexture );
このフォーマットは、ピクセルあたり 32 ビットに設定され、各成分は 8 ビットに定義されています。使用方法パラメーターは動的に設定され、テクスチャーにシェーダーがアクセスするようにバインド フラグが設定されています。残りのテクスチャー記述は、レンダー ターゲットを作成する場合と同様です。
ID3D10Texture2D::マップ を呼び出すことによりテクスチャーの基になるメモリーにアクセスできます。取得されたポインターはテクスチャーにデータを格納するために使用されます。これをコード サンプルで表すと次のようになります。
D3D10_MAPPED_TEXTURE2D mappedTex; pTexture->Map( D3D10CalcSubresource(0, 0, 1), D3D10_MAP_WRITE_DISCARD, 0, &mappedTex ); UCHAR* pTexels = (UCHAR*)mappedTex.pData; for( UINT row = 0; row < desc.Height; row++ ) { UINT rowStart = row * mappedTex.RowPitch; for( UINT col = 0; col < desc.Width; col++ ) { UINT colStart = col * 4; pTexels[rowStart + colStart + 0] = 255; // Red pTexels[rowStart + colStart + 1] = 128; // Green pTexels[rowStart + colStart + 2] = 64; // Blue pTexels[rowStart + colStart + 3] = 32; // Alpha } } pTexture->Unmap( D3D10CalcSubresource(0, 0, 1) );
複数のレンダー ターゲット
パイプラインには同時に 8 つまでのレンダー ターゲット ビューを (ID3D10Device::OMSetRenderTargets によって) バインドできます。各ピクセル (また、マルチサンプリングが有効な場合は各サンプル) に対して、ブレンディングがレンダー ターゲット ビューごとに個別に実行されます。2 つのブレンディング ステート変数 BlendEnable と RenderTargetWriteMask は 8 つの要素の配列であり、配列の各メンバーは個々のレンダー ターゲット ビューに対応します。複数のレンダー ターゲットを使用するには、各レンダー ターゲットが同じリソース タイプ (バッファー、1D テクスチャー、2D テクスチャー配列など) で、さらに同じサイズ (幅、高さ、3D テクスチャーの深度、およびテクスチャー配列の配列サイズ) を持つ必要があります。レンダー ターゲットをマルチサンプリングする場合、すべてのレンダー ターゲットが同じピクセルあたりのサンプリング数である必要があります。
アクティブなレンダー ターゲットの数にかかわらず、アクティブになる深度/ステンシル バッファーの数は 1 つだけです。テクスチャー配列をレンダー ターゲットとして使用する場合、すべてのビューのサイズを一致させる必要があります。レンダー ターゲットが同じテクスチャー フォーマットを持つ必要はありません。