DirectXの話 第106回
DirectX11でテクスチャを使用する
今回は簡単にDirectX11でテクスチャを使用してみます。
凝ったものではないので簡単に進めましょう。
DirectX10からテクスチャをシェーダで利用するには ShaderResourceView というものを作成する必要が出てきました。
これはテクスチャリソースとは別で、シェーダとリソースを繋ぐインターフェイスみたいなもののようです。
ShaderResourceView を Textureオブジェクトなしで作成することもできるようですが、今回は Textureオブジェクトと ShaderResourceView の両方を作成することにします。
まずはファイルからテクスチャを読み込みます。
ID3D11Resource* pRes;
HRESULT hr = D3DX11CreateTextureFromFile( pDevice, filename, NULL, NULL, &pRes, NULL );
if( FAILED(hr) ) { return false; }
m_pTexture = static_cast<ID3D11Texture2D*>(pRes);
ファイルからテクスチャを読み込むD3DXの関数を用います。
フォーマットに対して正しいデータを用意できるなら、ID3D11Device::CreateTexture2D() メソッドを使用しても問題ないですが、面倒なのでD3DXを使っておくのが一番です。
関数に渡す出力先を ID3D11Resource にして、後で ID3D11Texture2D に変更していますが、こうしないとコンパイラに怒られました。
次に ShaderResourceView を作成します。
// テクスチャ情報を取得する
D3D11_TEXTURE2D_DESC texDesc;
m_pTexture->GetDesc( &texDesc );
// ShaderResourceViewの情報を作成する
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory( &srvDesc, sizeof(srvDesc) );
srvDesc.Format = texDesc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.MipLevels = texDesc.MipLevels;
// ShaderResourceViewを作成する
hr = pDev->GetD3DDevice()->CreateShaderResourceView( m_pTexture, &srvDesc, &m_pSResView );
return SUCCEEDED(hr);
まずは作成したテクスチャから情報を取得します。
次に ShaderResourceView を作成するための情報を設定します。
フォーマットやミップマップレベルがテクスチャ情報と違っている場合は作成に失敗します。失敗するようなら設定をチェックしてみてください。
作成は ID3D11Device::CreateShaderResourceView() メソッドを使用します。
第1引数が ShaderResource として使用するリソースのオブジェクト、第2引数が情報、第3引数に出力先を設定します。
シェーダに設定して使用するのはこの ShaderResourceView となります。
ShaderResourceView はレンダリングターゲットとして使用するテクスチャにも必要です。
// レンダリングテクスチャを作成する
D3D11_TEXTURE2D_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.Width = m_Param.width;
desc.Height = m_Param.width;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = m_Param.renderFormat;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
HRESULT hr = pDevice->CreateTexture2D( &desc, NULL, &m_pRenderTexture );
if( FAILED(hr) ) { return false; }
// ShaderResourceViewを作成する
// これを作成しておかないとテクスチャリソースとして使用できない
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory( &srvDesc, sizeof(srvDesc) );
srvDesc.Format = m_Param.renderFormat;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
hr = pDevice->CreateShaderResourceView( m_pRenderTexture, &srvDesc, &m_pSResView );
if( FAILED(hr) ) { return false; }
// レンダリングターゲットを作成する
hr = pDevice->CreateRenderTargetView( m_pRenderTexture, NULL, &m_pRenderTarget );
if( FAILED(hr) ) { return false; }
レンダリングターゲットとなるテクスチャを作成する場合、BindFlags に D3D11_BIND_RENDER_TARGET を設定する必要があります。
また、D3D11_BIND_SHADER_RESOURCE フラグがないとシェーダリソースとして使用できません。
それ以外は普通のテクスチャと作成方法に違いはありません。
テクスチャをシェーダで使用する場合、サンプラも必要になります。
// サンプラを作成する
D3D11_SAMPLER_DESC samDesc;
ZeroMemory( &samDesc, sizeof(samDesc) );
samDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
samDesc.AddressU = samDesc.AddressV = samDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
samDesc.MaxAnisotropy = 1;
samDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
samDesc.MaxLOD = D3D11_FLOAT32_MAX;
HRESULT hr = g_pDevice->GetD3DDevice()->CreateSamplerState( &samDesc, &g_pSamDiffuse );
if( FAILED(hr) ) { return false; }
サンプラパラメータを変更したい場合、新規に作成する必要があるみたいです。
これらを設定するには下記のようにします。
pContext->PSSetShaderResources( 0, 1, &pResourceViews );
pContext->PSSetSamplers( 0, 1, &g_pSamDiffuse );
シェーダプログラム側では以下のように記述します。
//! テクスチャとサンプラの設定
Texture2D texDiffuse : register( t0 );
SamplerState samDiffuse : register( s0 );
//! ピクセルシェーダ
float4 RenderPS( OutputVS inPixel ) : SV_TARGET
{
return texDiffuse.Sample( samDiffuse, inPixel.tex );
}
レジスタ t0, s0 を使用します。
DirectX9と違って、テクスチャクラスのメソッドとして存在する Sample() にサンプラとUV座標を設定してやるとピクセルのカラーが取得できます。
簡単なサンプルを用意してみました。
テクスチャ1枚を読み込み、これを利用してレンダリングテクスチャに描画、このレンダリングテクスチャを利用してフレームバッファに描画しています。
おまけ その1
DirectX9ではデバッグ情報をコンソールに出力するためコントロールパネルのDirectXの項目をちょっといじる必要がありました。
しかし、DirectX10からはデバッグレイヤーが使用できるようになっています。
デバイスを作成する際にフラグを設定しますが、D3D11_CREATE_DEVICE_DEBUG を設定するといろいろとデバッグ出力してくれるようになります。
Releaseし忘れとかもきちんと検出してくれるので重宝します。
おまけ その2
本当は DeferredContext についてやってみたかったんですが、うまくいかなかったです。
OpenGLの DisplayList のような CommandList を作成する機能ですが、描画に必要な設定等の一部を CommandList にすることは出来ないっぽいです。
つまり、ImmediateContext で一部の定数バッファを変更・設定し、そのあと CommandList でマテリアル設定+描画といったことは出来ないようです。
ImmediateContext で描画に関するすべての設定を行い、CommandList で描画キックだけしてみたらビデオドライバがハングしました。
一定時間止まってからビデオドライバが再起動しましたが、結局描画もされませんでした。
すべてのコマンドを DeferredContext で設定してみたのですが、やっぱり描画されませんでした。ハングしないだけましですが。
描画されないのは解せませんが、いろいろ制約もあるんじゃないかと思われます。