「 2010年07月 」一覧

OpenGLでのストリームアウト その5


AMD(ATI)で動くOpenGLのストリームアウト(transform_feedback拡張)が一応できました。GL_EXT_transform_feedback のエクステンションを利用します。

準備するもの

  • 頂点データのためにVBOを使えるようにすること
  • GLSLによるシェーダーを使えるようにすること
  • クエリー発行できること

シェーダーの準備

GLSLシェーダーを準備します。
glAttachでオブジェクトを設定し、glLinkProgramまで終わらせておきます。

その後そのシェーダーからストリームアウトするのに必要なvarying変数を設定します。
変数名による配列を準備して、
それをglTransformFeedbackVaryingsEXTにて設定します。
この例を下記に示します。

ストリームアウト実行部

シェーダーをセットするほか、いくつかの設定を行います。

ストリームアウトするため、ラスタライズ処理の無効化をします。
また、書き出す先のVBOをバインドしておきます(streamOutIdが対象VBOです)。
続いて、ストリームアウトの開始を宣言し、同時に書き込んだプリミティブ数を取得するためのクエリを発行しておきます。

この後、通常の描画と同じように実行します。
終了時には設定を戻します。以下に例を示します。

各種終了を宣言し、クエリの結果を取得します。
ここでプリミティブの数を取得しないとその後結果をうまく使うことができません。
OpenGLにはDrawAutoは存在しないためです*1

この後描画するのであれば、GL_ARRAY_BUFFERにバインドして通常と同じように描画できます。
ただし、インデックスバッファと併用できるかは現在未確認です。
頂点の増減がGSで行われてしまうため(行われる可能性があるため)です。
頂点を増やさなかったら、インデックスバッファ使える可能性はあるのかも。

*1 : transform_feedback2拡張らで含まれるらしいですが

NVIDIAで使えない理由

glBindBufferOffsetEXT のAPIだけが取得できません。
取得しようとするとNULLが返ってきます。
そのため、同様の機能を探すとglBindBufferOffsetNVとなるため、
それだったらNV拡張のほうを使うか、ということになるわけです。

多くの部分でEXTと共通ですが、ストリームアウトするvarying変数らの設定が異なります。
NVIDIA版では、変数のLocationIdで設定するようになります。

感想

とりあえずNVIDIAの今後のドライバでこのAPI取得ができるようになるなら、
どの環境であれEXT拡張に統一できるんですが・・・。
また、一部のAPIが欠落しているのにもかかわらず、GetString( EXTENSION )でサポートしている旨を返してきてほしくはないですね。


OpenGLでVRAM情報を得る – AMD編


最近気づいたけど、AMD(ATI)用のエクステンションで
GL_ATI_meminfo というものがある。
これを用いれば、OpenGL環境のアプリでもどのくらいVRAMを使っているかがわかる。使い方は簡単で、以下のようにして値をとってくる。

ここで GL_VBO_FREE_MEMORY_ATIの代わりに以下のものを使うこともできる。

  • GL_TEXTURE_FREE_MEMORY_ATI
  • GL_RENDERBUFFER_FREE_MEMORY_ATI

種別に応じて使用可能な領域サイズを KB単位で知ることができる。
しかしながら、手元で試してみたところどの種別にしても同じサイズが返ってきた。
多くの場合、VBOかTEXTUREで返ってくる値のみをみていればいいのかもしれない。

その一方で、搭載メモリを取得する方法も発見した。
こちらは定義されていないので自分でdefineなどをしてあげる必要がある。
なおこれもKB単位で返ってくる。

これらの結果を利用すれば、搭載メモリ、使用メモリ、空きメモリを
OpenGLの環境であっても計測することができる。


CubeMapGS


DirectXサンプルにある、CubeMapGSを動かしてみた。
Radeon5450では実用的な速度で動いているとは言い難い。

  • Sphereで 25fps程度
  • Carで 10fps程度

DX11世代のハードでこの状況だとまだ安心して
毎フレーム描画で使えないなと思う。
せいぜいシーンの切り替わりタイミングで1回だけとかになるだろうか。

でもそれくらいだったら6回パスを回してもいいんじゃないかとも思える。

このスペックのせいでOpenGL側で同じように処理する機能はどうやるかっていう
調査のやる気がずいぶん削がれてしまった・・・


STL関連リンクエラー


どこかで以下のようなエラーを見かけたので、
それについて書いておこうと思います。

メッセージ例

error LNK2019: 未解決の外部シンボル “__declspec(dllimport) class std::basic_ostream > &
__cdecl std::operator<< >(
class std::basic_ostream > &,char const *)” (__imp_??$?6DU?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z)
が関数 “hogehoge(void)” で参照されました。

原因

libを作った環境でのSTL実装と、exeを作ろうとした環境でのSTL実装が違うためにこの問題は起こります。
最近の例だとVisualStudio2005とVisualStudio2008の混在させて使うような状況で起こりえます。

この例でいえば、VCランタイムはDLL版を使っています。
かといってVCランタイムのスタティック版を使ったからといってこの問題は解決できません。
そのときには、”○○は未解決です”というリンクエラーが出ることになるでしょう。

解決策

使用するlibとexeとでコンパイラのバージョンを一致させる。
もし、一方がビルド不能なのであれば、そちらにあわせることになるでしょう。

感想

こういうことがあるからあまりSTLって使いたくないんですよね。
長い間使いたいようなlibを作る場合には、特に。


OpenGLでのストリームアウト その4


今までのものは GL_transform_feedback_NVのやつでやっていた。
しかし、今回AMDのRadeon 5450環境を手に入れられたのでこちらについても
ちょっと試してみた。GL_transform_feedback_EXT をサポートしているようなのでこちらを使うことになりそう。

APIの置き換え(その1)

NVIDIA AMD
glBeginTransformFeedbackNV glBeginTransformFeedbackEXT
glEndTransformFeedbackNV glEndTransformFeedbackEXT

こんな感じで単純に置き換えておけば、NV->EXT使用になる

APIの置き換え(その2)

glTransformFeedbackVaryingsNV は glTransformFeedbackVaryingsEXT を使用することに。
中身が違うので単純に置き換えてはいけない。

以前 ”GLchar**を受け取る!?ヘッダ正しいですか?”と書いていた部分が間違っていたことに気づいた。

  • EXT版ではGLchar**を受け取る。
  • NV版ではGLintを受け取るべき

という差異があった。これが仕様。

EXT版では、varying変数名を設定できるが、
NV版ではvarying変数のロケーションIdを受け取る
という仕様の違いがあったためであった。

NVIDIAとAMDと両方のデバイスをさわってみて初めて納得がいった。

感想

やっぱりOpenGLのTransform Feedback(StreamOut機能相当)は情報が少ない。
使っている人が少ないのかもしれないので、これからがんばる人のために
ここに情報を書いておきます。

近いうちにNVIDIAとAMDと両方で動くtransform_feedbackのサンプルを作って公開する予定です。


OpenGLでのストリームアウト その3


ようやく動作自体は出来たのでメモしておきたいと思います。
サンプルプログラムはまた後日に。

準備

  1. GLSLシェーダーをコンパイル
  2. シェーダープログラムをリンク処理
  3. glTransformFeedbackVaryingsNV でストリームアウトとして書き込むデータの設定
  4. 格納先のバッファオブジェクトを生成

描画フェーズ

  1. ラスタライズ処理の無効化(GL_RASTERIZER_DISCARD_NV)
  2. glBindBufferOffsetNV, glBeginTransformFeedbackNV らで書き込み先のバッファオブジェクトを設定
  3. 描画処理.
  4. glEndTransformFeedbackNVで終了処理.
  5. GL_RASTERIZER_DISCARD_NVのステートを戻す

描画の前後でクエリー発行しておくことで書き込まれたデータ(プリミティブ数)を取得することが出来ます。

ここまでくるのに色々と試行錯誤しましたがようやく動かせる状態にきました。
注意のポイントは以下の点だと思います。

  • シェーダープログラムのリンク後の処理
  • GLSLなのか、固定機能による処理なのか

OpenGLでのストリームアウト その2


GL_NV_transformfeedbackのspecテキスト

こう書いてあった。

なるほど、1つ疑問が解けた。
今までTransformFeedbackAttribsNVを実行していたが、
これは固定機能での描画時に使うもののようだ。
(VSもGSも存在しないときと書いてある)。

つまりGLSLでやるならば、TransformFeedbackVaryingsNVを使って設定することが必要ということ。

こちらで試してみる。
ネット探してのサンプルだけを頼りに試行錯誤していましたが、
本家ドキュメントもしっかり読まないとダメですね。反省。

その他

昨日のGLchar*とint*の差違問題はどうやらNVIDIAからglext.hを持ってくると、int*となっている。
OpenGLのサイトから持ってくるとGLchar*となっている。

とりあえずNVIDIAのほうからヘッダ持ってくるとしよう。


OpenGLでのストリームアウト その1


OpenGLを用いてもDirectX10のストリームアウト相当の機能を使うことが出来ます。この機能のためにまず最初に拡張されたのが GL_NV_transform_feedback というエクステンションです。
最近ではこれがEXTになったようですが、単純にNVから格が上がっただけとはいえないようです。
APIが変更されているからです。

EXT版で試行錯誤しましたが、うまくいかず、NVのほうでまず動作を試してみようと思います。

目標

これらの点から次のことを目標にしています。

  • Cgを用いずにGLSLを使う
  • ひとまずNV拡張のほうを使ってみる
  • ストリームアウトの結果を見るために、ジオメトリシェーダーでは入力頂点を増やして出力する
  • 出力頂点を確認してみて、動作を確認する

OpenGLでのストリームアウト

OpenGLではVertexBufferObject(VBO)に対して結果を書き込むことができます。*1

他にも、tranform_feedback2, 3とかあったりしますが、
これらはOpenGL4系で追加だそうです。

*1 : バッファオブジェクトであれば書き込み先に出来るようです

GL_EXT_transform_feedbackの不審点

glBindBufferOffsetが関数実体を持っていなさそう。
glBindBufferOffsetNVだと関数実体は取れる。
こんな状態なのに、GetStringでエクステンション名は取れてしまう。
不思議です。

GL_NV_transform_feedback 拡張の罠

今のところいくつかの怪しい点で動作せず状態です。

  • glext.h内でglTransformFeedbackAttribsNVが const GLchar* *varyings の引数になっている。
    • ExtensionRegistryでは、ここはGLint*なはずなのに。

現時点での動作不良点

  • 書き込んだプリミティブ数が0のままで取れない…。
    • 書き込み先バッファは何かデータがかかれているようだが。
    • (glMapBufferのREAD_ONLYにて値を確認)

完全に動作するサンプルを検索しても見つからないので苦労しています。
NVIDIAではCgつかったサンプル例となっているし、そのほかもCg使ってしまっているし。
GLSLでやった例ってホント見あたらず。


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個までを一度に使うことが出来るようになります。