いろいろとはまりどころはあったのですが、ようやく出来ました。
注意点はその都度説明します。今回のサンプルではテクスチャ配列を使う例ということで、
下の図のようなイメージで実装しました。
2枚のテクスチャをテクスチャ配列として、あたかも1つのテクスチャオブジェクトとして扱います。
それを、描画の際にはUVのUの値でブレンドさせて表示させてみるということを行っています。
■シェーダーコード
頂点シェーダーは以前のものと変更していないので省略します。
以下にピクセルシェーダーのソースコードを貼り付けます。
[cpp]
struct VS_OUTPUT {
float4 Pos : SV_POSITION;
float4 Col : COLOR;
float2 Tex : TEXCOORD0;
};
SamplerState gSampler : register(s0);
Texture2DArray
float4 mainPS( VS_OUTPUT _In ) : SV_TARGET {
float3 uv1 = float3( _In.Tex.xy, 0 );
float3 uv2 = float3( _In.Tex.xy, 1 );
float4 col1 = gTexture.Sample( gSampler, uv1 );
float4 col2 = gTexture.Sample( gSampler, uv2 );
return lerp( col1, col2, _In.Tex.x );
}
[/cpp]
前の説明にも書いたように、テクスチャ配列内でテクスチャを特定するのは取得するUV値の次の要素に配列内インデックス値を入れます。
つまり今回 xyにUVなので、z要素にそのインデックス値を入れています。
■C++コード側
肝となるのはテクスチャ配列のデータの準備からデバイスに設定するまでだと思います。
そのためここではその部分に絞って説明します。
* テクスチャ配列を作る
まず、2枚のテクスチャの準備を行います。
今回は2枚のddsファイルを用意してこれらを準備する段階で1つのテクスチャ配列に入れるということをします。
[cpp]
HANDLE hFile;
DWORD dwFileSize[2] = { 0 };
BYTE* pFileData[2] = { 0 };
const char* TextureName[2] = { “check_1.dds”, “check_2.dds” };
for( UINT i = 0; i < 2; ++i ) {
hFile = ::CreateFileA( TextureName[i], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE ) {
OutputDebugStringA( "Not found texture file.\n" );
continue;
}
dwFileSize[i] = ::GetFileSize( hFile, NULL );
pFileData[i] = new BYTE[ dwFileSize[i] ];
DWORD dwSize = 0;
::ReadFile( hFile, pFileData[i], dwFileSize[i], &dwSize, NULL );
CloseHandle( hFile );
}
[/cpp]
こんな感じでメモリ上にDDSファイルの中身を読み込んでおきます。
続いて、ID3D10Texture2Dのオブジェクトを作成します。
そのために次のように D3D10_TEXTURE2D_DESCを用意します。
ポイントとなるのは、ArraySize = 2と設定しているところですね。
また、この例ではDDSの中身を知っているため解析もせずに直接値を放り込んでいます。あくまで例ということでご注意ください。
[cpp]
ID3D10ShaderResourceView* pTextureRV = 0;
ID3D10Texture2D* pTexture = 0;
D3D10_TEXTURE2D_DESC descTex2D;
ZeroMemory( &descTex2D, sizeof(descTex2D) );
descTex2D.BindFlags = D3D10_BIND_SHADER_RESOURCE;
descTex2D.CPUAccessFlags = 0;
descTex2D.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
descTex2D.Width = 32;
descTex2D.Height = 32;
descTex2D.Usage = D3D10_USAGE_DEFAULT;
descTex2D.SampleDesc.Count = 1;
descTex2D.MipLevels = 1;
descTex2D.ArraySize = 2; // テクスチャ配列のために"2"
[/cpp]
そして、D3D10_USAGE_DEFAULT で作成する必要があったので、
データを放り込むためにはサブリソース情報を使わなくてはなりません。
また、ここがポイントですが2枚のテクスチャを1つのテクスチャ配列に入れるために、サブリソース情報は2つのサイズで作成します。
具体的には以下に示すような感じになります。
なお、DDSは128バイト目からが実データなので直接設定してしまっています。
[cpp]
D3D10_SUBRESOURCE_DATA subres[2];
ZeroMemory( &subres, sizeof(subres) );
subres[0].pSysMem = pFileData[0] + 0x80u;
subres[0].SysMemPitch = sizeof(DWORD) * 32;
subres[1].pSysMem = pFileData[1] + 0x80u;
subres[1].SysMemPitch = sizeof(DWORD) * 32;
[/cpp]
これらの情報からテクスチャを作成します。
pDevice->CreateTexture2D( &descTex2D, subres, &pTexture );
*リソースビュー
このままではシェーダーにセットできないので、
ここからシェーダーリソースビューを作成します。
シェーダーからもテクスチャ配列として扱いたいので、それように作ります。
[cpp]
D3D10_SHADER_RESOURCE_VIEW_DESC descRV;
ZeroMemory( &descRV, sizeof(descRV) );
descRV.Format = DXGI_FORMAT_UNKNOWN;
descRV.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2DARRAY;
descRV.Texture2DArray.ArraySize = 2;
descRV.Texture2DArray.MostDetailedMip = 0;
descRV.Texture2DArray.MipLevels = 1;
hr = pDevice->CreateShaderResourceView( pTexture, &descRV, &pTextureRV );
[/cpp]
ポイントは、ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2DARRAY; と設定しているところと、
それにあわせてテクスチャの構造を記載している部分です。
(ベースレベルミップマップの番号とテクスチャそのものがもつミップマップ数とか)
これらの情報を元に、シェーダーリソースビューを作成します。
ここまで出来ればあとはデバイスに設定するだけです。
* セットおよび描画
[cpp]
// シェーダーのセット
pDevice->VSSetShader( pVertexShader );
pDevice->PSSetShader( pPixelShader );
// テクスチャのセット
pDevice->PSSetShaderResources( 0, 1, &pTextureRV );
// サンプラーのセット
pDevice->PSSetSamplers( 0, 1, &pSamplerState );
// 描画
pDevice->Draw( 4, 0 );
[/cpp]
通常の1枚テクスチャを設定するのと変わらないコードとなります。
■まとめ
特に注意が必要だったのはデータを作成する部分でした。
- サブリソースはテクスチャ配列の数だけ必要
- シェーダーリソースビューの作成の ViewDimensionには
- D3D10_SRV_DIMENSION_TEXTURE2DARRAYを設定すること。
- RTVやDSVのTEXTURE2DARRAYではない点に注意です。
この部分さえクリアすれば何も難しいことはありませんでした。
今回テクスチャ配列を使った例って見かけないので、
とっかかりのコードとして作成してみました。
使い方、やり方わからない!って人達の助けになればいいなと思います。
コメント