DirectX一覧

DirectX vs OpenGL テクスチャ編

DirectXとOpenGLとでテクスチャに関していろいろと違うので
ここでそれらについて書いてみたいと思います。

用語

DirectXでは、テクスチャアドレッシングモードとテクスチャラッピングモードがあります。
OpenGLでは、テクスチャラッピングモード だけです。

しかも両者でそれぞれの言葉が意味しているものがずれており、
DirectX::アドレッシングモード = OpenGL::テクスチャラッピングモード
という状態となっています。
これは酷いですね。

個人的にはDirectXのアドレッシングモードという部分を廃止して、
各テクスチャの張り方を指定するラッピングモードだけに統一してほしいと思います。
円筒のUVラッピングモードってあまり使いませんし、
必要になったら通常のUVにオフセットや変換式をかませれば済むことでしょう。

設定方法について

DirectXでは、縮小フィルタ、ミップフィルタ、拡大フィルタの設定がそれぞれ設定できます。
OpenGLでは、縮小フィルタ+ミップフィルタ、拡大フィルタの2つです。

このあたり、最初はすごく困った覚えがあります。
DirextXの縮小フィルタ=D3DTEXF_POINT, ミップフィルタ=D3DTEXF_NONE
これが、
OpenGLだと GL_NEAREST_MIPMAP_NEAREST になるとか。

また、OpenGLではテクスチャフィルタの設定もきちんとやらないと
テクスチャが使用可能にならない点も注意しなくてはなりません。

RADEON HDについて

そして、RADEON HDとCatalyst 10.7の(少なくとも)組み合わせでは
glTexImage2Dをすべてやり終えた後で、テクスチャフィルタの設定を行う必要があります。

赤本の通りのコードでも実は動かないという可能性があるので、注意が必要です。
もしかしたらシェーダー使わずに、固定機能だけで完結させるならば
このトリックは必要ないかもしれません。


続・ATIドライバの謎?

Direct3D9: (INFO) :Failed to create driver indexbuffer を調べてみた。
海外のForumでは割とよくあがっている話っぽい。このメッセージが意味するところは、
IndexBufferをVRAM上に作るときに、VRAM上に作れなかった、という話になるっぽい。
あとは、ドライバがハードウェアのインデックスバッファに対応していないということもあるらしい。

考えられる原因は以下の通り

  • DrawPrimitiveUPを使用した
  • HWが対応していてもドライバで無効状態

また、INFOカテゴリに入っているメッセージなので、
操作している側には特に問題はないメッセージであるとのことで無視してもよさそう。

なお、この場合のインデックスバッファは
AGPメモリやシステムメモリに確保されるような感じです。


DirectX9のRenderStateからGL読替

DirectX9のRenderState設定がGLのどれにつながるかわからないって話を聞いた。
知っている範囲で書いてみます。

DirectX9 OpenGL
D3DFILL_WIREFRAME glPolygonMode( GL_FRONT_AND_BACK, GL_LINE )
D3DCULL_NONE glDisable( GL_CULL_FACE )
D3DCULL_CW,CCW GL_CULL_FACE有効化してGL_CW,GL_CCWらも設定
D3DRS_SCISSORTESTENABLE GL_SCISSOR_TEST
D3DRS_STENCILENABLE GL_STENCIL_TEST
D3DRS_ZENABLE GL_DEPTH_TEST
D3DRS_ZWRITEENABLE glDepthMaskで設定
D3DRS_STENCILREF glStencilFuncで設定

各種比較関数、ブレンド設定の列挙値は大変なので除外で。
調べてみるとすぐにわかりそうな感じですし。

感想

OpenGL側はDirectXとちがって、必要要素を一括に設定する気配を感じる。
このほうがデバイスに値を流すのには適しているんだろう。

○○さんへ

他にこれは?というのがあれば、コメントにでも~。
知ってる範囲で追記しようかと思います。


DirectX9でATIカードに振り回される

どうもATIのグラフィックボードおよびドライバは
DirectXを正しく使わないと描画が不正になる模様。DrawIndexedPrimitiveでnMinVertex, nMaxVertexを正しく設定しないと、
それだけでポリゴンが壊れて描画されたりすることを確認できた。
ちなみにNVIDIAではこれらの情報を参考にしていないのか、
適当な値を放り込んでもきちんと動いてくれる。

症状としては、全く描画されないか、変な部分とくっついたりしたポリゴンなどが確認できたので、
動作としては未定義動作に近いのかもしれない。

環境

Core i7 870, 4GB
RADEON 5450 1GB(Catalyst 10.7)
Windows7 Ultimate(x64)

まとめ

開発環境では NVIDIAのカードがおすすめ。
ドライバで振り回されることも比較的少なめ。
でもテスト環境にATIのカードを準備しておくとよさそう。
なぜならATIの環境では、正しく命令(API)を使えているかの確認に使えそうだからである。

むしろ、NVIDIAが緩いせいか、NVIDIAのカードでしか動作確認していないと、
ATIの環境に持って行ったときにうまく動かないということが発覚しそうな気がする。

いやはや、勉強になった。


続・CubeMapGS

以前の7/29日記で、CubeMapGSの動作を書いたので、
NVIDIAのGeforce9800GTではどうだったんだろうと思ってチェックしてみた。

球体モデルの結果

カード Instancing fps値
RADEON 5450 TRUE 28.2
RADEON 5450 FALSE 38.2
GeForce9800GT TRUE 119.2
GeForce9800GT FALSE 61.2

これだけ見ると世代的には前の9800GTのほうが性能がいい。
またインスタンシングフラグの影響がそれぞれ逆転しているのも不思議。

車体モデルの結果

カード Instancing fps値
RADEON 5450 TRUE 8.8
RADEON 5450 FALSE 9.7
GeForce9800GT TRUE 44.8
GeForce9800GT FALSE 32.6

製品のレンジが違うとはいえ、ここまで差があるのか。
GSはNVIDIAの圧勝です。

まとめ

この状況を見るとGSで6面のキューブマップについては
リアルタイムに毎フレーム処理できるものではなさそうです。
せいぜいシーン初回で描いてしまうとかその程度かなと思います。
他のメリットは、レンダーパスの処理がシンプルになるというのもあるけど、
ローエンドでもこれくらいの形状ならある程度(60fps維持)の性能が出せないと、
安心して使えないですね。


Scissorによる挙動違い(DX vs GL)

DirectXとOpenGLとで、シザーの設定時のクリア挙動が異なるようです。座標系がそれぞれ異なるってのもあるけど、
そこは上下を逆転して対処したとしても、それだけでは互換動作は不可能。

フレームバッファの縦横と、ビューポートの縦横と、シザー設定の縦横
それぞれ違う設定にしてみて、双方のAPIで実行してみるとわかりやすいかと。

DirectXではビューポートとシザー設定のお互い被っている領域でクリア
GLではシザー設定に従ってクリア。

同じような振る舞いにさせるならシザー領域とビューポートの領域のAND集合を取るようにして、
できあがった矩形でシザーテストするようにしてクリア処理を入れるようにする。

GL面倒だけど、こういうった細かいことをきちんと実装できるだけ
柔軟性はあると思う。


Vertex Texture Fetchによるスキニング

Vertex Texture Fetchを利用して、スキニングメッシュのサンプルを動かしてみました。
ひにけにxnaさんのところでは、XNAを用いての解説があったので、
ここでは、普通のDirectX SDKとC++による組み合わせでこれをやってみたいと思います。

使用するもの

  • SkinnedMeshのサンプルプログラム
  • ShaderModel 3.0に対応したグラフィックボード
    • Radeon X1000シリーズを除く。これはVTFに対応していないため。

ソースコード

各準備

まずは、ボーンのマトリックスを格納するためのテクスチャを準備します。
ここではグローバルにおいて、手抜きしました。
また、毎回書き換えるので、ダイナミックテクスチャにしています。

ボーン用テクスチャの概要

ボーン用のテクスチャは、横方向に使用したいボーンの数、縦方向に4というサイズで作成しました。
というのも、行列は4x4のfloat要素で、今回のテクスチャはRGBA32Fという形式のため、1テクセルに4要素格納できます。
そのため、4テクセル使えば、1つの行列が生成できるということを目的にしています。

横方向(U方向)はボーンのインデックスを示すことにして、
V方向には行列の行を格納することにしました。
このほうが配列っぽく見えるかなと思っています。

マトリックスパレット数調整のための変換部分

またついでに、ConvertToIndexedBlendedMesh を呼び出している部分も
これらの設定のボーン数をみるように修正が必要です。

マトリックスパレットの設定部分の修正

マトリックスパレット設定部分を次のように変更します。

テクスチャにボーンのマトリックスを格納していきます。
注意点としてはテクスチャのY方向へずらすときは、ロックしたときのピッチバイト数でずらさないといけない点です。
それと、シェーダー内ではマトリックスとしては取れず、
float4の4つの組で取れる感じになります。
そのためシェーダーに送る際に転置しておきます。
こうすることで、内積を利用して計算が可能になります。

プロジェクション用行列の設定修正

サンプルのSkinnedMeshのサンプルではmViewProjというシェーダー定数に
実はプロジェクション用の行列しか送っていないです。
ビュー行列はマトリックスパレットに対して適用していました。

この部分を変更します。
mViewProjにg_matProjを設定している部分で、g_matViewとの演算結果を格納するようにしておきます。

シェーダーコード

ほとんどの部分はサンプルのそのままです。
変更点は以下の通り

  • tex2Dlodにて該当するマトリックスパレット相当のテクセルから値を取得
  • 浮動小数用にサンプラを設定
  • VTF使うために、vs_3_0を設定。あわせてピクセルシェーダーも

実行結果

こんな感じになります。
理由はピクセルシェーダーでディフューズ用のテクスチャ等を処理せず、
単純にライトの強度を出しているからです。

しかし、これがVTF利用して動いています。

今は、横方向128でやりましたが、これを256にすることで
ボーンの上限256個までを一度に使うことが出来るようになります。


発想転換、再実装

新・ConvertToIndexedBlendedMesh相当品

アルゴリズムを見直して再作成しました。
前回は4つのマトリックスパレットで分割しようとした際に、
デバッグビルドだとうちの環境で5分くらいかかって、
かつ、描画バッチ数も200弱という悲惨なことになりましたが、
新しい方はそんな状況にはならず動くようになりました。

実行速度はデバッグで計っています。

パレット数 頂点数 描画バッチ数 変換時間
4 4881 24個 4.2秒
13 4578 4個 3.3秒
26 4474 2個 2.9秒

リリースビルドだと、2桁くらいは高速に動いているので
ようやく実用的になったと思います(13個のパレットのRelease時は15msで変換を完了)。
従来方式が複雑かつ長時間かかるという点で使い物にならなかっただけという話ですが。

プログラムコードは400行ほどに(従来コードは700行)。
STLのデータ構造とアルゴリズムを使いまくってます。
そのせいでデバッグでこの速度を出すのが難しかったです。
ただ気を配って書けばSTL使った場合デバッグで重くて使い物にならない!という状況は避けられると感じました。

ポイント

考えをあらためたポイントは、以下の点です

  • 頂点を共有することにがんばらない
  • 変換アルゴリズムはシンプルに

前回の足りなくなったら頂点を複製という点から、頂点は最初から複製前提で考える
という考えに切り替えたのが大きいです。


アルファブレンディングについて【教えてほしい】

アルファブレンドで下記の合成はわかった

  • 通常合成(1*srcColor + 0*destColor)
  • 加算合成(1*srcColor + 1*destColor)
  • 半加算合成(srcA * srcColor + 1*destColor)
  • 半透明合成(srcA * srcColor + (1-srcA) * destColor)

しかしこれらがアルファ値だけ別の計算が出来るという仕組みが用意されていたりする。
これの有効な利用が思いつかない。

OpenGLやDirectXではアルファブレンドセパレートとして機能があったりするが。

D3DRS_SEPARATEALPHABLENDENABLE を有効化して
なにかやっているサンプルもまた見つからなかったし。
有効に使えている例、使える例を知っている人は是非教えてください。


テクスチャアドレッシングモード

テクスチャアドレッシングモード

テクスチャを張ったときに、1.0以上の値をどう扱って、
テクスチャを張るかのモード設定です。

出来ることの代表的なものとして、

  • 繰り返し貼る(リピート)
  • 反転しながら貼る(ミラー)
  • 繰り返さずに残った部分は引き延ばし

というのがあります。

OpenGL vs DirectX

で今回感じたのが、OpenGLとDirectXでこれらの機能の差違。
同じ挙動をする設定でも、画面の描画結果が違うということになりました。
もっとも大まかな挙動という点では一緒で、
今回の違いというのは、描画ピクセルが完全一致しないということを示しています。

どうも境界あたりの処理でそれぞれ差違が出ていました。
GL_CLAMP_TO_EDGEやGL_CLAMP_TO_BORDERなど標準機能ではなく
拡張機能で試したのですがうまくいきませんでした。

どうでもいいこと

テクスチャラッピングモードというとまた別のことを指すようです。
少しわかりにくい気がするのは自分だけでしょうか…