テクスチャ
前2つのパートで何度か名前だけ出てきたテクスチャですが、このパートで簡単に見ていきます。 “簡単に”というのはテクスチャはID3D11Bufferとは違い特殊な機能があるのですが、すべて見ていくと長くなるので後で別パートと分けて見ていきたいと思います。
ここでは2次元配列のようなものである2次元テクスチャ(英訳:Texture2D)を使って、 ファイルからのデータの読み込みや、シェーダ内でのアクセス方法について説明していきます。
概要
このパートではテクスチャ(英訳:Texture)を使って画面をクリアする方法について見ていきます。 対応するプロジェクトはPart03_ClearScreenWithTextureになります。
-
シェーダ内での使い方
- Texture2Dキーワード
- SamperStateキーワード
- GPUへの設定
-
ID3D11Texture2D
ID3D11Device::CreateTexture2D関数 -
ID3D11SamplerState
ID3D11Device::CreateDevice関数 - まとめ
- 補足
- DirectXTKを使ったテクスチャの読み込み
- テクスチャの種類
- DXGI_FORMAT
- シェーダ内での分岐命令
シェーダ内での使い方
Texture2Dキーワード
Texture2Dを使ったコードは下のようなものになります。
Texture2Dは今まで出てきたRWTexture2Dとかなり似ています。 違いはデータの読み込みだけが可能なことと、読み込みのためにいくつかの関数が用意されていることです。 もちろん、上のコードのように添え字アクセス可能です。
ちなみにテクスチャの要素のことをテクセル(英訳:texel)と呼ばれたりしています。
読み込み関数にはサンプラステート(英訳:SamplerState)と呼ばれるものを使うものがあります。 サンプラステートについては次の項目で見ていきます。
ドキュメント:
テクスチャー オブジェクト(日本語)
Texture2D(日本語)
(英語)
SamperStateキーワード
サンプラステートはテクスチャ読み込みの時に使うものになります。 上のサンプルの次の部分で実際に読み込んでいます。
Texture2D::SampleLevel関数でテクセルを読み込んでいます。
ドキュメント:SampleLevel
(日本語)
(英語)
SampleLevelの引数
- 第1引数:使用するサンプラステート
- 第2引数:サンプリングする場所を表すUV値
- 第3引数:サンプリングするミップマップのレベル
ミップマップについてはあとのパートで説明します。
サンプラステートを使ってテクスチャの要素を読み込むときはUV座標と呼ばれる座標系を使って読み込みます。 UV座標系はテクスチャのサイズを0~1の範囲で表したものになります。
例えば、横幅が200で縦幅が100のテクスチャの中央にあるテクセル(100,50)にアクセスしたい場合、 UV座標系では(0.5, 0.5)と表します。
UV座標系を使う際の利点はテクスチャのサイズが変わっても同じUVで同じテクセルの場所を指定できる点でしょうか。 これはTexture2Dキーワードのコードと上のコードを比較してもらえばお分かりいただけるかと思います。
始めに登場した添字を使ったコードの場合はテクスチャの範囲外にアクセスしないように条件分岐しています。 シェーダでは条件分岐はコストがCPUと比べてコストが高い処理になります。 現在ではそこまで問題にはなりませんが、昔だとシェーダ内では条件分岐自体が出来ないことが当たり前でした。 また、特に工夫をしない限り画面に合うようにテクスチャでクリアすることはできません。 工夫をしたとしてもUV座標の計算と同じ処理を書くことになり、テクスチャのサイズが小さければドットが目立ってしまいます。 この問題も隣接するテクセルを補間することで対応できますが、サンプラを使えばGPUのハードウェア機能を利用して隣接するテクセルを補間することが出来ます。 ただ、サンプラを使った場合はコストが添え字アクセスよりもかかりますので、添え字アクセスで十分な場合は添え字を使うなど場合によって使い分けてください。
また、UVの値が0~1の範囲を超えてしまった場合の動作はサンプラの作成時に決めることが可能です。 サンプルのデフォルトでは0~1範囲を超えてしまったらまた0~1の範囲で繰り返すようにサンプリングします。 これは実際に見てもらった方がわかりやすいと思いますので、サンプルコードを書き換えてみてください。
後、Texture2DとSamplerStateのスロットの設定は定数バッファ、アンオーダードアクセスビューと同じです。 Texture2Dでは”register(t0)”と”t”を使い、 SamplerStateでは”register(s0)”と”s”を使ってください。
シェーダ側でのテクスチャの使い方は以上になります。 テクスチャを使うときはサンプラも一緒に使うことが多いで、セットで覚えていきましょう。
次はCPU側でのテクスチャとサンプラの設定の仕方を見ていきます。
GPUへの設定
シェーダ内でTexture2Dを使うときはID3D11ShaderResourceViewとして扱い、 SamplerStateの場合はID3D11SamplerStateとして扱います。
GPUへの設定の仕方も今まで出てきた定数バッファやアンオーダードアクセスビューと同じです。 設定するものがID3D11ShaderResourceViewやID3D11SamplerStateになっただけです。
ドキュメント:
ID3D11DeviceContext::CSSetShaderResources(日本語)
ID3D11DeviceContext::CSSetShaderResources(英語)
ID3D11DeviceContext::CSSetSamplers(日本語)
ID3D11DeviceContext::CSSetSamplers(英語)
シェーダへ設定できるものまとめ
シェーダへ設定できるものは今回出たもので一通り出てきました。
- 定数バッファ (ID3D11Buffer)
- シェーダリソースビュー (ID3D11ShaderResourceView)
- サンプラステート (ID3D11SamplerState)
- アンオーダードアクセスビュー (ID3D11UnorderedAccessView)
定数バッファはビューを使わずに設定できますが、Direct3D12ではビューを持つように変わっています。 サンプラステートもリソースではないのでビューは必要ないです。
GPUに設定できるもので残っているものはグラフィックスパイプライン関連の設定やクエリ関連のものになりますので随時説明していきます。
ID3D11Texture2D
ここまでの内容でシェーダ内での使い方と設定の仕方がわかりました。 最後にTexture2Dを表すID3D11Texture2DとID3D11ShaderResourceViewの作成は以下のコードになります。
コードは長くなっていますが、ID3D11Bufferの作成と似ていますので理解はしやすいと思います。
まず、ID3D11Texture2DはID3D11Device::CreateTexture2D関数で作成しています。
ドキュメント:ID3D11Device::CreateTexture2D (日本語) (英語)
ID3D11Device::CreateTexture2D
- 第1引数:D3D11_TEXTURE2D_DESC
作成するID3D11Texture2Dの情報を表すD3D11_TEXTURE2D_DESCを渡します。 上のコードで設定しているメンバが最低限ひつようとなるものです。
ドキュメント: D3D11_TEXTURE2D_DESC(日本語) D3D11_TEXTURE2D_DESC(英語)D3D11_TEXTURE2D_DESCのメンバ一部
- Width
作成するテクスチャの横幅
- Height
作成するテクスチャの縦幅
- BindFlags
作成したいビューの指定。ここでは読み取りだけ行いたいのでD3D11_BIND_SHADER_RESOURCEを指定しています。
ドキュメント: D3D11_BIND_FLAG(日本語) D3D11_BIND_FLAG(英語) - Format
テクセルあたりのデータ形式。ここでは赤、緑、青、透明度を8bitで表すDXGI_FORMAT_R8G8B8A8_UNORMを指定しています。 末尾のUNORMについては補足を参照してください。
ドキュメント: DXGI_FORMAT(日本語) DXGI_FORMAT(日本語) - MipLevels
テクスチャの専用機能のうちの一つであるミップマップの数を指定します。 ミップマップが必要なくとも必ず、1以上を指定してください。 ミップマップについては別パートで説明します。
- ArraySize
テクスチャの専用機能のうちの一つである配列の数を指定します。 配列にしない場合は1を指定してください
- SampleDesc.Count
テクスチャの専用機能のうちの一つであるマルチサンプリングを使う際の1テクセル当たりのサブピクセル数を指定します。 マルチサンプリングを使わない場合は1を指定してください マルチサンプリングについては別パートで説明します。
- Width
- 第2引数:D3D11_SUBRESOURCE_DATA
初期データを表すものです。 定数バッファとの違いは1行当たりのデータ長をSysMemPitchに設定しているところと、 テクスチャ全体のデータ長をSysMemSlicePitchに設定している所です。
- 第3引数:ID3D11Texture2D
作成したID3D11Texture2Dを受け取る変数
ID3D11Texture2Dを作成した後はシェーダから読み込むためにID3D11ShaderResourceViewを作成します。
ドキュメント:ID3D11Device::CreateShaderResourceView (日本語) (英語)
ID3D11Device::CreateShaderResourceView
- 第1引数:ID3D11Resource
ビューを作成したいID3D11Resourceを渡します。 ID3D11ResourceはID3D11Texture2DやID3D11Bufferの親クラスになりますので、直接ID3D11Texture2Dを渡すことが出来ます。
- 第2引数:D3D11_SHADER_RESOURCE_VIEW_DESC
作成するビューの情報を設定したD3D11_SHADER_RESOURCE_VIEW_DESCを渡します。 設定できる項目はリソースによって変わりますので、ここでは共通部分だけ説明します。
ドキュメント: D3D11_SHADER_RESOURCE_VIEW_DESC(日本語) D3D11_SHADER_RESOURCE_VIEW_DESC(英語)D3D11_SHADER_RESOURCE_VIEW_DESCのメンバ一部
- Format: GPU上のデータのフォーマット
基本的には作成したリソースと同じDXGI_FORMATを設定します。 いくつかのDXGI_FORMATは互換性を持っているのでその時は使いたいフォーマットをここで設定します。
- ViewDimension: リソースタイプの指定
ここで設定したタイプによって設定する項目が変わります。 サンプルではD3D11_SRV_DIMENSION_TEXTURE2Dを指定しているのでTexture2Dメンバを設定しています。
- Format: GPU上のデータのフォーマット
- 第3引数:ID3D11ShaderResourceView
作成したID3D11ShaderResourceViewを受け取る変数
以上、長くなりましたがID3D11Texture2DとID3D11ShaderResourceViewの作成は以上になります。 設定する項目が多いだけで行っていることは簡単ですので、すぐ慣れることでしょう。
ID3D11SamplerState
SamplerStateを表すID3D11SamplerStateの作成は以下のコードになります。
上のコードのID3D11Device::CreateSamplerState関数でID3D11SamplerStateを作成しています。
ドキュメント:ID3D11Device::CreateSamplerState (日本語) (英語)
ID3D11Device::CreateSamplerState
- 第1引数:D3D11_SAMPLER_DESC
作成するID3D11SamplerStateの情報を表すD3D11_SAMPLER_DESCを渡します。 重要となるのは上のコードで使っているメンバになります。
ドキュメント: D3D11_SAMPLER_DESC(日本語) D3D11_SAMPLER_DESC(英語)D3D11_SAMPLER_DESCのメンバ一部
- Filter: D3D11_FILTER
サンプリングするときの補間の仕方を決めるためのものです。 テクスチャを拡大か縮小するときとミップマップをサンプリングするときの3パターンの補間のやり方を指定します。
- AddressU, AddressV, AddressW: D3D11_TEXTURE_ADDRESS_MODE
サンプリングの時UVの値が0~1の範囲外だった時どうするかを指定するためのものです。 Uは横、Vは縦、Wは奥行きに対応しています。 画像の横方向は繰り返すけど、縦方向は範囲外になったら指定した色で塗りつぶすといった設定が可能です。
- Filter: D3D11_FILTER
- 第2引数:ID3D11SamplerState
作成したID3D11SamplerStateを受け取る変数
以上でサンプラステートの作成を終わります。
まとめ
このパートではテクスチャとそれからデータを読み取るためのサンプラについて見てきました。
この3パートでDX11で使う主要なものは大体触ってきました。 C++でいう関数的な役割のシェーダと変数であるID3D11Bufferとテクスチャの使い方がわかればDX11を使う上でもう悩むこともないでしょう。
あとここまでのパートでいくつかのリソースなどを作成してきましたが、大まかにパターンが決まっているのに気づきましたか? DX11では何か作るときはD3D11_XXX_DESCみたいな構造体を使うことが多いです。 その構造体について調べれば何ができるかが大体わかりますので是非ドキュメントを読んでいってください。
補足
DirectXTKを使ったテクスチャの読み込み
上ではID3D11Texture2Dをプログラム上で直接生成していましたが、ペイントなど他のツール上で生成した画像ファイルから読み込むことが多いです。 DX11だけだとファイルから画像データを読み込むといった関数は用意されていませんが、Microsoftが提供しているDirectXTKを使用することで読み込むことが出来ます。
DirectX::CreateWICTextureFromFile関数はpngなど様々な画像ファイルを読み込みID3D11ResourceとID3D11ShaderResourceViewを生成してくれます。 ID3D11ResourceはID3D11Texture2Dの親クラスになります。 ID3D11ResourceからID3D11Texture2Dに変換するには上のコードのようにQueryInterface関数を使用してください。
テクスチャの種類
今回はID3D11Texture2Dを使いましたが、
テクスチャには他にID3D11Texture1DとID3D11Texture3Dがあります。
簡単に言うと1次元配列と3次元配列みたいなものです。
これらもテクスチャなのでID3D11Texture2Dと同じことができます。
ドキュメント:
ID3D11Texture1D
(日本語)
(英語)
ID3D11Texture3D
(日本語)
(英語)
DXGI_FORMAT
ID3D11Texture2Dを作成するときフォーマットを指定する必要がありました。 DX11ではDXGI_FORMATを使ってフォーマットを指定します。 種類がたくさんありますが、1要素の成分数とそのビット幅をR,G,B,Aとその隣の数値で表し、末尾に型を指定します。 末尾の型にはFLOAT,UINT,INTがありシェーダ内では文字通りfloat,uint,intを表します。 TYPELESSはビット幅だけを指定するものになり、その際ビューで改めて型を指定します。当然互換性のあるフォーマットしか指定できません。 SNORMとUNORMはそれぞれ符号付と符号なしの整数を表し、シェーダ内で使うときは数値は-1~1と0~1に正規化されます。
その他、圧縮形式を表すものがあります。
圧縮形式についてはこちらを参考にしてください。
ドキュメント
DXGI_FORMAT(日本語)
DXGI_FORMAT(英語)
シェーダ内での分岐命令
シェーダ内でif文を使う際、ifキーワードの前にいくつかの属性を設定できます。
今パートではbranch属性を使いました。この属性はif文に渡された条件に応じて片方のコードしか実行しないように指定するものです。
詳しくはドキュメントを読んでください。
ドキュメント:
if ステートメント(日本語)
if Statement(英語)
また同じようにforやwhileも属性を指定できます。
こちらも詳細はドキュメントをご覧ください
ドキュメント:
フロー制御(日本語)
Flow Control(英語)
<前 | トップ | 次> |