「 DirectX10 」一覧

IntelグラボとOpenGLの苦悩 その2


シェーダーを使う状況かつVertexBufferObject(VBO)を使ってということを前提条件とします。
このとき、glEnableVertexAttribArray, glVertexAttribPointer等を使って、
頂点ストリームを設定していくのが自然です。しかしながら今回、これでトラブルに見舞われました。

GL_ARB_vertex_program の仕様をOpenGL Registryで調べると、
各頂点属性のindex値は、頂点データのセマンティクスにより決まるように書いてあります。
今までは、この値を参考にしながら glEnableVertexAttribArray( index ); を設定していました。

そのようにして実装した結果、NVIDIA, Intel のグラフィックチップによる描画では
問題が起こらなかったのですが、AMD(ATI)のチップでは、位置情報以外設定されない!という動作結果が出てしまいました。

もともとはIntelの場合を想定して、この作業を行っていたのに、
意外にもAMDでまともに動作しないという状況ではちょっと不味い…。

GLSLを使える状況、すなわち、GL_ARB_vertex_shaderの拡張でシェーダーを使う分には、
従来方式で問題はおこりません。今回~programのほうなので問題があるわけです。
この~programは、いわゆるシェーダーアセンブラへOpenGLが対応したときの拡張です。

おおまかな出現順から考えると、

  1. GL_ARB_vertex_program, fragment_program
  2. GL_ARB_vertex_buffer_object
  3. GL_ARB_vertex_shader

という順序なので、VBOとの同時使用で問題が引き起こされている感もありそうです。

  • VBOを使用せずに、頂点データを直接VertexAttribPointerで設定した
    • NG. 動作せず。
  • VBO使用して、glVertexPointer等でオフセットを指定した
    • OK. AMDで動作した。

こんな結果になりました。
もう古いAPIである glVertexPonterやglColorPointer等で、VBO内のオフセットを設定してあげることにより、
シェーダーアセンブラ使用時でも、頂点属性をシェーダー側へ流し込むことが出来ました。

※ なるべくならこんな古いAPIを使いたくはなかったんですが…。


DirectX10でテクスチャ配列を使用する例


前回の日記で書いた、DirectX10でテクスチャ配列を使用するサンプルができました。
いろいろとはまりどころはあったのですが、ようやく出来ました。
注意点はその都度説明します。今回のサンプルではテクスチャ配列を使う例ということで、
下の図のようなイメージで実装しました。

2枚のテクスチャをテクスチャ配列として、あたかも1つのテクスチャオブジェクトとして扱います。
それを、描画の際にはUVのUの値でブレンドさせて表示させてみるということを行っています。

シェーダーコード

頂点シェーダーは以前のものと変更していないので省略します。
以下にピクセルシェーダーのソースコードを貼り付けます。

前の説明にも書いたように、テクスチャ配列内でテクスチャを特定するのは取得するUV値の次の要素に配列内インデックス値を入れます。
つまり今回 xyにUVなので、z要素にそのインデックス値を入れています。

C++コード側

肝となるのはテクスチャ配列のデータの準備からデバイスに設定するまでだと思います。
そのためここではその部分に絞って説明します。

テクスチャ配列を作る

まず、2枚のテクスチャの準備を行います。
今回は2枚のddsファイルを用意してこれらを準備する段階で1つのテクスチャ配列に入れるということをします。

こんな感じでメモリ上にDDSファイルの中身を読み込んでおきます。
続いて、ID3D10Texture2Dのオブジェクトを作成します。
そのために次のように D3D10_TEXTURE2D_DESCを用意します。
ポイントとなるのは、ArraySize = 2と設定しているところですね。

また、この例ではDDSの中身を知っているため解析もせずに直接値を放り込んでいます。あくまで例ということでご注意ください。

そして、D3D10_USAGE_DEFAULT で作成する必要があったので、
データを放り込むためにはサブリソース情報を使わなくてはなりません。
また、ここがポイントですが2枚のテクスチャを1つのテクスチャ配列に入れるために、サブリソース情報は2つのサイズで作成します。

具体的には以下に示すような感じになります。
なお、DDSは128バイト目からが実データなので直接設定してしまっています。

これらの情報からテクスチャを作成します。

リソースビュー

このままではシェーダーにセットできないので、
ここからシェーダーリソースビューを作成します。
シェーダーからもテクスチャ配列として扱いたいので、それように作ります。

ポイントは、ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2DARRAY; と設定しているところと、
それにあわせてテクスチャの構造を記載している部分です。
(ベースレベルミップマップの番号とテクスチャそのものがもつミップマップ数とか)
これらの情報を元に、シェーダーリソースビューを作成します。

ここまで出来ればあとはデバイスに設定するだけです。

セットおよび描画

通常の1枚テクスチャを設定するのと変わらないコードとなります。

まとめ

特に注意が必要だったのはデータを作成する部分でした。

  • サブリソースはテクスチャ配列の数だけ必要
  • シェーダーリソースビューの作成の ViewDimensionには
  • D3D10_SRV_DIMENSION_TEXTURE2DARRAYを設定すること。
    • RTVやDSVのTEXTURE2DARRAYではない点に注意です。

この部分さえクリアすれば何も難しいことはありませんでした。

今回テクスチャ配列を使った例って見かけないので、
とっかかりのコードとして作成してみました。
使い方、やり方わからない!って人達の助けになればいいなと思います。

ダウンロード

今回のソースコード(cppのみ)をおいておきます。

HelloDX10_4.cpp テクスチャ配列を使う例


DirectX10以降のテクスチャ配列について


今回気になることもあって、改めて調べてみました。

はじめに

聞いていた話ではテクスチャ配列の利便性っていうものは、
複数のテクスチャを1つの変数(名)で扱えて便利だよって感じでした。
また大抵の場合、使用されるテクスチャの集合(セット)はユニークであるし、
そういった場合にはそれら集合をまとめて変更できるから良いって話でした。

あるモデルに付いているテクスチャは複数の種類存在します。

  • ディフューズマップ
  • スペキュラマップ
  • ノーマルマップ

だけどもモデルのデータにおいて、その中身はすべて異なるわけです。
よってそのテクスチャ配列を1つ交換するだけで、上記の例だと3つのテクスチャが交換でき、便利。

という話を聞いていたわけです。

疑問点はどこか

テクスチャ配列は1つの変数名となっています。
これのインデックス値を変えることで、参照先テクスチャを変えてサンプリングしてくるわけです。
しかし、サンプリングしてくるのにサンプラーユニットは共通しているわけで、
そうなると、サンプラーのほうに負担がかかってしまうのではないかと思ったのです。

よって、サンプラーがボトルネックになってしまい、
この機能のうまみはないのでは?と疑問に思いました。

DirectX10でのアクセス方法

DirectX10ではシェーダーファイル内で次のようにして、
指定された配列番号のテクスチャを参照することができます。

※ 今までにテクスチャ配列から値を読み出すコードって見たことがないので、書いてみました。
配列内での序数は、UV値設定の最後の要素に入れる、というのさえ分かってしまえば、
他何も難しいことはなさそうです。

まとめ

このコード例を書いていて、気付いてしまいました。
そう、DirectX10以降ではサンプラーはテクスチャと別に設定できるようになったのでした。
だから、テクスチャ配列内序数ごとに使用するサンプラーを切り替えてあげれば、
危惧していた問題点も無くなるというわけです。

このように、サンプラーの設定は N個必要だけど、
テクスチャについては、テクスチャ配列を用いるようにした場合1つの切り替えだけで済むということになりそうです。

補足

とりあえずテクスチャ配列のサンプルを作成中です。
サンプラー使い回しの例で、軽くやってみる予定です。


D3D10_TEXTURE2D_DESCのArraySize。テクスチャ配列の罠?


DirectX10でテクスチャ配列使ってみようと思い、
ソースコードを書いて試そうとした矢先に躓きました。D3D10_TEXTURE2D_DESCのArraySize = 2; として、
テクスチャを作成するも失敗。

なんていうエラーメッセージが出てくる始末。

いろいろと試行錯誤した結果、
D3D10_USAGE_DEFAULT で作成したもののみ、テクスチャ配列使えるみたい。
CPUから書き込める USAGE_DYNAMICでは使えなかった。

これらのことから、テクスチャ配列は作成時に値を確定させておく、
Immutableな使い方しか出来ない、ということなんだろうか。
D3D10CalcSubresourceなんていう位置計算してくれる補助関数あるのだから、
CPUからも書き込みできるようになっていると期待するのは間違いなんだろうか。


シェーダーリフレクション [DirectX10]


シェーダーの中身がどのようなものが詰まっているかを取得するための方法です。
なかなか情報がなくて大変でした。
やりたいこととしては、シェーダーのバイナリファイルから
どのような変数およびデータ型がどの位置に存在するのかと知る方法となります。D3DX9には存在していたID3DXConstantTableのようなものを
D3D10ではどうやるかという感じになるでしょうか。

サンプルソース

各種リフレクションのインターフェースを取得して、中身を調査します。

実行結果

この結果は、BasicHLSL10のシェーダーコードを元にしています。
どうやら、グローバルにおいた無名の定数バッファは$Globalsという名前で配置されるようです。

また、関数の引数で uniform と宣言したものについては、
$Paramsという名前の定数バッファとして処理されるようです。

さらに、Descでの値は$Globalsの値を考慮していないっぽいです。

まとめ

いろいろと注意点はありますが、これでコンパイル済みシェーダーファイルを利用する目処が立ちそうです。
また、このリフレクションはCore APIに搭載されているため、
別途のd3dx10_**.dllに依存しないのもいい点だと思います。


D3D10始めました -第3回 Texture使用-


今回はテクスチャを読み込んで表示させてみます。

シェーダーファイル

まずはシェーダーのソースを示します。

従来のシェーダーと変わったところと言えば、ピクセルシェーダー内の
サンプラーおよびテクスチャのアクセス部分です。

対象とするテクスチャからのサンプルを、どのように行うか(サンプラ)、どの位置からとるか(UV)という意味のコードになっているように読み取れます。

入力レイアウトおよびデータの変更

テクスチャアクセス用にUVの設定が入りました。

テクスチャの準備

テクスチャは D3DX10CreateShaderResourceViewFromFile 関数を使い、
ビューを取得します。
このビューの中から対象となるテクスチャを取り出すことが出来ます。

  1. ファイルを元にシェーダーリソースビューを作成&取得
  2. リソース(ID3D10Resource)を取得
  3. リソースがTexture2DのデータならID3D10Texture2Dとしてそのリソースを使用できる。

という手順をたどる必要があります。

コードにすると次のようになります。

サンプラーの設定オブジェクト

テクスチャをシェーダーから扱うときにサンプラーを経由するため、
そのサンプラーの設定するためのオブジェクトを準備します。

基本的に設定を構造体に詰めて、生成関数を呼ぶだけなので簡単です。

描画処理

シェーダー等をセットした後に、
サンプラーオブジェクトとテクスチャのビューをデバイスに設定します。

まとめ

デバイス生成時にデバッグフラグをつけていると
デバイスへの設定情報が怪しいときにいろいろなメッセージを出してくれます。
今回のことでもサンプラー設定デフォルトでいいやと判断して、
設定を怠ったところ、警告メッセージが大量に出てきました。

以前のコードではリソースを解放するコードを書いておらず、
内部的にはリソースリークが発生している状況でした。
使ったデータはきちんと解放しましょう。

次回くらいには、DirectX10でのリソースリーク検知について書いてみようと思います。

ソースコード

今回作成した分のソースコードです。
HelloDX10_3.cpp のダウンロード

このバージョンからリソースはきちんと解放しています。


DirectX10 始めました -第2回-


前回は初期化およびクリア処理までだったので、
今回は最初の三角形をだす部分までをやってみます。

頂点バッファの準備

DirectX10では頂点およびインデックス格納には、バッファオブジェクトが必須です。
以前のDrawPrimitiveUPとかが消えてしまったので。
今回はインデックスを使わずに頂点バッファだけを準備します。

バッファの作成時に、データを設定することが出来ます。
D3D10_USAGE_IMMUTABLEでバッファを作成すると、この初期化タイミングでしかデータを設定することが出来ません。

他に使いそうなフラグとしては、D3D10_USAGE_DYNAMIC, D3D10_USAGE_STAGING があります。
後者の設定は、GPUからCPUにデータを書き戻すときに使用します。

概念自体は従来と変更無く、頂点バッファを作成して、
その中身をサブリソースという設定に従って書き込むという感じでしょうか。

シェーダーの準備(C++側)

ピクセルシェーダー側もほとんど同じなので、ここでは頂点シェーダーだけ説明します。
DX9の頃と変わりなく、シェーダーファイルを読み込んでコンパイルさせ、
そのデータからシェーダーを作成します。

注意点は、頂点シェーダーのシェーダーバイナリである pBlobVSというデータは、
後段で使用するため破棄してはいけません。このあたりがDX9のころと大きく変わっています。

シェーダーの準備(C++) その2

頂点シェーダーのコンパイル結果から、入力レイアウトを作成します。
これは、従来のVertexDeclarationに相当します。

シェーダーファイル

頂点シェーダー、ピクセルシェーダーはこんな感じで作成しました。
今回は最低限で作成するため、定数レジスタは未使用です。

描画処理

今まで準備した各データを設定して描画コールするだけです。

結果

HelloDX10_2 ソースコード

今回の結果プレビューはこんな感じ

苦労したポイント、ひっかかったポイント

今回はいろいろと罠に引っかかりました。
同じように誰かが引っかからないように、また自分の防備録としてここにポイントを記載します。

頂点入力は正しい?

シェーダー内で float4 Col : COLOR などと受け取っていると忘れてしまいがち。
この中には正規化した 0.0-1.0の値が格納されてくること、
頂点データ内にあるときには、各要素8ビットのRGBA値が入っていること期待している。
そのため、頂点カラーは DXGI_FORMAT_R8G8B8A8_UNORMにして入力レイアウトを作成しなくてはならない。

従来 DWORDで送っていたために、つい頂点カラーは DXGI_FORMAT_R8G8B8A8_UINTにしてしまいそうだが、
これをやってしまうと、シェーダーとレイアウトの不一致が起こり描画されない結果となる。

シェーダーはあってる?

いろいろと出力セマンティクスが変わっている。
ここに注意しないと、絵が描画されない結果につながる。

頂点シェーダーからの位置出力

SV_POSITION というセマンティクスで出力する。
従来の POSITION セマンティクスを使ってもコンパイル失敗しないため要注意である。

また色情報については、SV_TARGET というものになっており、
従来の COLOR セマンティクスは有効でないため位置と同じ失敗はしない。


DirectX10を始めました


はじめに

今はもうDirectX11が出て使われ始めていますが、
今更DirectX10の勉強を始めてみました。

今回初ということで、画面のクリアをするプログラムを作ってみました。
これをベースにしていろいろと機能を触ってみようかと思います。

DXGIのセットアップ

DXGIとはスワップチェインとか管理しているレイヤーになります。
ハードウェアデバイス列挙やフルスクリーンへの切り替えなど担当してくれます。
まずはこの初期化を行います。
初期化すると、スワップチェインとD3D10デバイスが取得できます。

やり方は以下の通りです。

初期化に必要なパラメータを構造体に詰めて、D3D10CreateDeviceAndSwapChainを呼び出します。
D3D9の頃と比べるとこの部分は楽になったような気がします。

バックバッファの取得とメインとなるカラーバッファの取得

デバイス生成するとバックバッファが1つ付いてきます。
これを通常の描画先とするために、ビュー(RenderTargetView)を取得します。
その取得している部分は以下のようになります。

バックバッファは2次元テクスチャの扱いで、
この中身にアクセスするためのビューと作成しています。
普段のクリアで使うのは、ビューを用いて行うので、
作成が終わったらpBackBufferは使わないため解放しています。

デプスバッファとステンシルバッファ

カラーのバッファについては標準で付いてきたのですが、
デプスは自分で作らないといけないようです。

デプスとステンシルについては扱い的に2Dテクスチャとなっているようで
まずはこれの作成から開始します。
このテクスチャを作った後はバックバッファと同じようにビューを作成します。

ビューポートの設定と標準の描画先の設定

若干構造体が変わっていますがD3D9のころと中身は変わりません。
ここで先ほど作成した2つのビューをデバイスに設定しておきます。

画面のクリア処理

クリア処理は、デバイスに対して行うという感じではなく、
デバイスに対して、クリアするビューを設定して行う、という感じになっています。

メインのループでこの記述をすれば画面のクリアが行われ、
今回赤い色が表示されます。

後始末

今まで取得した&作成したインターフェースに対して、
Releaseを呼び出していきます。

感想

画面のクリアまでならば、D3D9の頃と比べて遙かに楽でした。
若干概念が変わっている部分については最初こそ戸惑いましたが、
そういうものだと理解してしまえば引っかかるような場所でもありません。

ビューとは、バイナリデータを解釈するものである、と自分は理解しました。
このバイナリデータというものがテクスチャであり、リソース群を示します。
よって、解釈対象であるビューに対してクリアなどの操作指示を行うのだと。

この2段構えな感じがD3D10の特徴なのかもしれないと思います。
そしてこのせいでD3D9ほど素直な感じがしないのだとも感じます。

ソースコード

適当に書いた初回D3D10のソースコードです。
自動生成されるコードに今まで説明した部分付け加えて、
そして、描画ループがまわるようにした程度の簡単なものです。

HelloDX10ソースコード

何かの役に立つかな・・・


DirectX10 (D3D10)


Tutorial04

今回はTutorial04を見てました。このサンプルは立方体(キューブ)の表示サンプルでした。
前回のTutorial02の部分に比べて増えたところと言えば、IndexBufferを作っているところです。
ほかには、シェーダーで透視変換させているところ。大きく変わった部分はないです。

IndexBufferもVertexBufferと同じくBufferDescriptionとSubresourceDescriptionの2つを設定して、デバイスのメソッドで作成するだけです。

変更になってる部分はデータをどう使うか指示するところ、
「D3D10_BIND_INDEX_BUFFER」「D3D10_BIND_VERTEX_BUFFER」の違いくらいです

描画する前には以下のように

各バッファをデバイスに設定し、プリミティブタイプを指定しておきます。この辺が若干以前と変わっています。
前はDrawPrimitive系でプリミティブタイプを指定していました。

また、インデックス用のフォーマット指定(D3DFMT_INDEX16/32)がありましたが、
どうやら今回からは統一されたようです。

気になったのはこのサンプル、インデックスに32ビット指定してます。
従来は16ビット(WORD型)を基本的に使っていたのに、このように変更となるとデバイスの最適化もこれからは32ビットインデックスが主流となっていくんですかねぇ…。(※ 現状のデバイス(当時)は16ビットインデックスに最適化されています)

シェーダーに対して、各マトリックスをセットして、パスを回しています。
見てわかるように描画のDrawIndexedの引数が減っています。すっきりしましたね〜。あとDX9世代でBegin/BeginPassで行っていたパスを回す処理が変更になっています。テクニックのDescから値を取得して、回数と使用指示を設定しているようです。

この辺は使う側としてはあまり変更がないといってもいいでしょう。
あまり大きな変更ではないように思います。

気になったのは今までシェーダー定数を書き換えたときには、CommitChangesなどのメソッドを呼んで確定させていた気がしますが、それが消えています。

シェーダーコード

HLSLの中身も若干変わっています。

今までfloat4x4とかやっていた部分がmatrixになっています!

Passの部分も

となっており、ぱっとみてわかる程度の変更です。

ここにジオメトリシェーダーの記述があって、そのうちいじってみたいなとわくわくです。


DirectX10 (D3D10)


Tutorial02解説

今回は、DX10での三角形(ポリゴン)描画までのサンプルを読み解いてみました。
前回までの初期化にプラスして、頂点データの作成、シェーダーの作成と要素が詰まってます。

シェーダーの作成は以下のようになってました。若干自分なりのコメントをつけてみました。

間違っている場合もあると思いますが、気づいた人はコメントでご指摘ください。(^^;

[code lang=”cpp”]
// Create the effect
// このフラグは新しいHLSLのセマンティクスを有効にするため?
// 従来のHLSLコードを通過させないためのフラグである模様
DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif
hr = D3DX10CreateEffectFromFile( L"Tutorial02.fx", NULL, NULL, "fx_4_0", dwShaderFlags, 0,
g_pd3dDevice, NULL, NULL, &g_pEffect, NULL, NULL );
[/code]

どうやらDX9時代と変更がない感じです。プロファイルが fx_4_0になったくらいでしょうか。
シェーダーのフラグでDX10用のHLSLを要求しているように見えます。

fxファイルを覗くとわかるのですが、technique10や今までに見慣れないセマンティクスが使われています。
これらを要求するためのフラグみたいです。

[code lang=”cpp”]
// Define the input layout
D3D10_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = sizeof(layout)/sizeof(layout[0]);
// Create the input layout
D3D10_PASS_DESC PassDesc;
g_pTechnique->GetPassByIndex( 0 )->GetDesc( &PassDesc );
// 上記で設定したinput elementと、シェーダーのバイトコードからinputレイアウトの作成.
hr = g_pd3dDevice->CreateInputLayout( layout, numElements, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &g_pVertexLayout );
if( FAILED( hr ) ) return hr;
// Set the input layout
g_pd3dDevice->IASetInputLayout( g_pVertexLayout );
// Create vertex buffer
SimpleVertex vertices[] =
{
D3DXVECTOR3( 0.0f, 0.8f, 0.5f ),
D3DXVECTOR3( 0.5f, -0.5f, 0.5f ),
D3DXVECTOR3( -0.5f, -0.5f, 0.5f ),
};
D3D10_BUFFER_DESC bd;
bd.Usage = D3D10_USAGE_DEFAULT;
bd.ByteWidth = sizeof( SimpleVertex ) * 3;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = vertices;
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
if( FAILED( hr ) ) return hr;
// Set vertex buffer
UINT stride = sizeof( SimpleVertex );
UINT offset = 0;
g_pd3dDevice->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
// Set primitive topology
g_pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
[/code]

シェーダーのバイトコードと、頂点宣言二つからinputlayoutを作成します。
そしてこのレイアウトはIA(InputAssembler)にセット。
この次に、ようやく頂点バッファの作成に取りかかります。

DX9時代とかなり変更になっているのがこの部分。
BufferDescriptionとSubresourceDescriptionの二つから頂点バッファを作成しています。

各DESCに値をセットして、初期化時にデータを転送してしまうっぽいです。

このD3D10_USAGE_DEFAULTフラグでは、GPUからのアクセスはできて、CPUからのアクセスは禁止という意味だそうです。
DX10からはすべてのバッファがMap(従来のLock)ができるわけではなくなりました。

つまり上記の例ではMapすら禁止ということで、初期化時にシステムメモリ上のデータを設定することで、初回1度だけデータ転送しているということなんでしょう。

SubResourceとはなんぞや?

よくわからなかったので調べてみました。

上記でもSubResourceDescription を設定してます。
このdescではバッファの初期データを与えるためにセットしています。

SubresourceDescriptionは実際のリソースデータを示し、データのサイズとレイアウトに関する情報を保持します。

どこかにも記述があったのですが、Subresourceっていわゆる実データなんでしょうか。
Surfaceに相当とか書いてあった気が…。