「 OpenGL 」一覧

テクスチャオブジェクトとサンプラ設定


いつのまにか、OpenGLのテクスチャ挙動もDirectXに合わせる方向になっているようだ。OpenGL 3.0では、glTexParameteriで GL_TEXTURE_BORDER_COLOR を設定することが非推奨となっている。
他にも テクスチャラッピングモードで GL_CLAMP_TO_BORDER を指定することは推奨されていないとなっている。
さらにglTexImage2Dでボーダー指定で 0 を設定することを要求しているようです。

従来のボーダーの処理に関して、実はドライバ内部のソフトウェア処理にて実現し、
ハードウェアとしてはサポートしていない!ということになっているのでは?と疑いを持ったのですが、
そういうわけではないようです。

DirectX10のAPIを調べてみると、ボーダーの色設定はできるのでボーダー自身のサポートがなくなるわけではないようです。
では、OpenGLではどうなるのか調べてみたところ、サンプラーオブジェクトなるものが存在していました。
そのエクステンションの名前は、”GL_ARB_sampler_objects”。
ただし、OpenGL 3.3での拡張となるようです。

サンプラオブジェクトを作成し、テクスチャのバインド前にセットすることで、
拡大縮小フィルタ設定、テクスチャラッピングモード、ボーダーカラーの設定らが可能になるとのこと。
まさにこの挙動はDirectX10のサンプラーオブジェクトと同じだといえます。

残念ながら手元のRADEON環境ではOpenGL3.3対応していないのか、
GL_ARB_sampler_objects の文字列が見当たらなかったので試せていません。
Catalystの更新((10.5で含まれるらしい。また以前のtransform_feedback2,3もここで入るらしい)でサポートされるとのことなので、そのうち試してみようと思ってはいます。


GL拡張のリスト


うちのRADEON 5450のGL extensionのリスト

OpenGL 4.1サポートされるドライバが公開されたら更新したい。
さっさとでてくれないかなと期待してます。

catalystのバージョン: 8.681
Direct3Dバージョン: 8.14.10.0716
OpenGLバージョン: 6.14.10.9232

GL_AMD_texture_compression_dxt6は BC6形式のサポートで、
GL_AMD_texture_compression_dxt7は BC7形式のサポートってことだろうか。

一方、よくわからないのがキューブマップの部分。

  • GL_AMD_seamless_cubemap_per_texture
  • GL_ARB_seamless_cube_map

どちらもキューブマップのつなぎ目をきれいにする拡張の模様。
単純に標準化したときに(ARBに加わったときに)名前が変わっただけなのかな…。


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の環境であっても計測することができる。


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面倒だけど、こういうった細かいことをきちんと実装できるだけ
柔軟性はあると思う。


GLSLスキニング


GLSLでスキニングシェーダー書いてみた

わりとGLSLでスキニングシェーダーをやったという記事を見かけないので
ここで書いてみます。

大変だった点はGLSLでは、Cのようなキャストを受け付けない点。
そのためブレンドインデックスをどうやってint型にするかで悩みました。
実際には、下記のソースコードに示すように int( )で囲ってやる程度で済む話なのですが、
知らないと結構悩みます。

備考

マトリックスを使わずにfloat4 (すなわちvec4)でやっているのは
単に自分のスタイルなだけです。
先輩のシェーダースタイルに影響された可能性が大です。
matrix型のほうが変換がmulで書ける分だけソースコードはすっきりします。
float4の4つという表現だと、シェーダー定数設定をするコードがすっきりします。
またMatrixをfloat4x3型として使っているような場合で効率よくシェーダー定数を使うことが出来ます。

他にはライティングが適当です。
平行光源と若干のアンビエント成分いれたつもりのコードです。

ソースコード(VertexProgram)

1頂点あたり4ボーンのスキニングシェーダーです。
1回のマトリックスパレットは36個です。

36個にした理由は、tiny.xがこの個数で描けるからです。