「 2010年08月 」一覧

OpenGLでD3DFMT_G16R16F形式などの再現


OpenGLでD3DFMT_G16R16Fの形式テクスチャを作る方法について説明します。
これをマスターすれば、32bit float形式も、R16F, R32F形式にも対応できます。

必要なもの

  • GL_RGBA16F, GL_RGBA32F などフォーマットを使えること
  • OpenGL拡張の GL_ARB_texture_rg
  • OpenGL拡張の GL_ARB_texture_swizzle

確認した動作環境

  • NVIDIA GeForce9800GT
  • ATI RADEON HD 5450

どちらも入手できる最新ドライバにて。

方法説明

テクスチャの準備

ケース1 GL_ARB_texture_rg がある場合

何も考えなくてもRGテクスチャが作れます。

このような感じで作れてしまいます。
楽になりました。

ケース2 GL_ARB_texture_rg がない場合

仕方ないのでRGBA16FやRGBA32F形式のテクスチャを作り代替させます。

テクスチャの設定

ケース1,2どちらにしてもこのままではDirectX9と同じ挙動となりません。
それは、DirectXでは存在しない要素に対しては”1″を返し、
OpenGLでは存在しない要素に対しては”0″を返すためです。

これを共通の振る舞いをさせる設定を行います。
GL_ARB_texture_swizzleのおかげでこの切り替えを行うことができます。
なかなか強力な機能なので有効に使っていきましょう。

D3DFMT_G16R16Fの例

アルファとブルーの成分が常に1を返すようにスウィズル設定を行います。

これまでの適用により全く同じ見た目になるように挙動を修正することができます。

まとめ

GL_ARB_texture_swizzleはかなり便利です。
包含するようなテクスチャ形式を用意できれば、この機能を使って
OpenGL側でいろいろと出来ます。

お願い

GL_ARB_texture_swizzleとかrgとかが、どのくらいの環境で使用可能なのかがわかりません。
よろしければ、お使いの環境でどうであったかや、知っている環境でどうであったかなどの情報をお待ちしております。


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 セマンティクスは有効でないため位置と同じ失敗はしない。


OpenGLよしなし事


今日はOpenGLのメモ程度内容をつらつらと。

FBOのレンダーバッファ、レンダーテクスチャ

レンダーテクスチャはテクスチャとして利用可能なのはわかるが、
レンダーバッファの存在価値ってなんだろうか。

情報ソースはどこか忘れたけど、
レンダーバッファのほうはテクスチャとして使わない分、高速に動くように
ドライバ内で最適化の余地があるらしい。

テクスチャ

glGenTextures, glTexImage2Dだけではテクスチャは使えるようにならない。
glTexParameteriでアドレッシングモードとフィルタモードを設定しておく必要がある。

このあたり忘れるとテクスチャが張られないだけでなく、
FBOもincomplete状態となってしまうので気をつけよう。

gl○○Pointer,glEnableClientStateとか

GLSL前提のシェーダーありき環境だとこれら不要かとおもう。
頂点アトリビュートの設定やVBOつかってできるし。

OpenGL3系で廃止のマークもあるので、使わないにこしたことはないだろう。

GLSL

コンパイル

オフラインコンパイルできないのがつらいなぁ。
確かにソースコードは環境非依存で持って行けるけど。
GLSLソース内日本語の存在は確か禁止だったはず。

cgcでコンパイルしてという話も聞いたが、結局はARB規定されているアセンブリに落ちる程度。
結局のところその後ドライバ解釈が入ってシェーダーバイナリがGPUに送られる。

それでもソース解釈よりはいいだろうし、気付けばこれってDX9とやっていることはほぼ同じじゃんと気付いてしまった。
あとはARBで規定されている部分って最低限だったと思うので、
現時点でどのくらい使い物になるんだろうか。

ソース記述

頂点入力でfloat4の入力しか受け付けられない模様。
UVなど2要素でいいものでも float4すなわちvec4で受け取る必要がある。
この部分が、DirectXと違って理解に苦しむなぁ。


ATIドライバでのGLSL


GLSLの記述について、ATIは結構シビア。
ドライバのバージョンによってはシェーダーのコンパイルが通ったり通らなかったりする。しかし、通らないのは厳密には正しく、そのようなシェーダーを書いていない開発者のほうが悪いのはわかっているんだけど、なんとも開発しづらい。

以前は警告が出ていたのものが、警告が出なくなったりして、
チェックが緩くなったものもあれば、逆にきつくなってコンパイルを通さないものが出てきたりとか。

修正の指針が曖昧なのも疑問である。

出遭ったこと

  • sampler2Dにもuniform修飾が必要である。
    • 以前は修飾が無い場合はuniformで扱われた。
    • HLSLにあわせられていたが、これの挙動が変わった。
  • float精度について
    • 以前は警告が出ていたが、現在は警告を発しなくなった

ドライバ

  • 以前っていっているのは Catalyst 9.0くらいのころ
  • 今っていってるのは Catalyst 10.7

ATIでのOpenGLの罠


NVIDIAカード(Geforce)ではうまく動いていたのに、
ATIカード(RADEON)でうまく動かなくなったというネタです。今回の使用APIはOpenGLです。

症状

ミップマップありのテクスチャで、ミップマップが使用されない。
ベースレベルの画像しか使われない(ことがある)、という現象が発生

原因および対処法

glTexImage2DとglTexParameteriの呼び出し順序が関係する模様です。

glTexParameteriでミップマップを使わない設定(GL_NEAREST)とかに設定して、
ミップマップありのテクスチャを作成&転送した場合、
それはミップマップなしのテクスチャとして作成されてしまう。
ミップマップありのテクスチャを作成したい場合にはミップマップを使用する設定のパラメータを設定しておき、
glTexImage2Dでテクスチャデータを転送する必要がある。

GL_NEAREST設定後、glTexImage2Dでのミップマップ設定は無視されるのか、
その後 GL_NEAREST_MIPMAP_NEAREST に設定を変更しても
テクスチャ自身がミップなし状態でリソース生成されてしまっているため、有効にならない。

まとめ

まさかそんな罠が潜んでいるとは…という感じでした。
確かに設定から考えてみると、ミップマップなしの設定された後テクスチャ転送なので、
最適化の観点から「使わないから余分なものを生成しない」というのは
合っているような気はします。
ちょっと強引な最適化ではありますが。

先日のDirectXの挙動といい、ATIはドライバ内で強引な最適化を施す傾向にあるのかもしれません。


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の環境に持って行ったときにうまく動かないということが発覚しそうな気がする。

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


ATIのドライバ


catalyst 10.7の挙動が不満だったので、以前の状態にしようとしてかなり大変だった。
結論として、バージョンをあげてしまったらもう後戻りできないということみたい。DriverSweeperとか使ってみてもだめだったのでどうしようもない。

この結論に至る前にはいろいろとありました。
発生した症状を書いてみます。

  • glTexImage2Dで、アクセス違反により落ちることがある。
    • デバッグで止めながら実行していると発生しにくい。
  • コンパネからのアンインストール中でBSoD発生
    • この時点ですでにいろいろと破壊された可能性は高い。
  • ChoosePixelFormat呼び出しでドライバ内部でアクセス違反発生。

まとめ

やっとまだマシになったのでそのときのメモを。
ドライババージョンをあげるときでもDriverSweeperできれいにしてからあげる。

これによりCatalyst10.7使っても、glTexImage2Dで落ちないし*1
ChoosePixelFormatで落ちることも無くなりました。

しかし、こんな挙動示すなんて…ATIのボード&ドライバは使いにくくて仕方ないです。
NVIDIAのほうがこの部分に関しては優れている気がします。

*1 : ダメでした。動かしていると落ちることが出てきた…


シェーダーのコンパイル失敗時挙動


どうもglGetShaderInfoLogの挙動がおかしい。
シェーダーのコンパイル失敗という挙動を示すようになったのも問題だったりするけど、
この関数の挙動が赤本のサンプルプログラムでは正常動作しない。

原因の推測

このおかしな挙動を始めた前後で変えたことはCatalystのバージョンを変えたこと。
現在は 10.7 を使っている。

そもそも以前の環境ではGLSLのリンクまでうまく動作していた。
ドライバが持つコンパイラ&リンカの挙動が変わったとしか思えない。

症状

あるプログラムのGLSLのコンパイルが失敗するようになった。
そしてエラー処理に突入し、赤本に書いてあるようなコードでエラーログを出しているが、
関数を抜けるとアクセス違反で終了する。

デバッグ

  • NVIDIA環境では旧コードで問題なく動いていた。
  • ATIのドライバ更新する前までは問題なく動いていた。

これらの点から考えると、プログラムの大筋は間違ってはいない。
今回のドライバによるバグが原因と考えられる。

エラー発生ポイントから、glGetShaderInfoLogのログを取得して、解放する部分で何かあるとしか考えられない。

  1. glGetShaderivでログ長取得
  2. ログ格納バッファ確保
  3. glGetShaderInfoLogでログを取得
  4. ログ表示
  5. 格納バッファ解放
  6. 呼び出し元に失敗コードを返却し、処理抜ける

という感じにくんでいるが、ログ格納バッファを解放する段階でアクセス違反していることが判明した。

CRTの中を追いかけていってわかったのは、格納バッファにログを取得した時点で、
メモリ領域を一部破壊しているということだった。
どうもログ長で返された値よりも大きなサイズを書き込みしている模様。
また、glGetShaderInfoLogで取得できるログバッファ書き込みサイズもまたglGetShaderivで返されるログ長と同じなので当てにならない。

末尾にヌル文字でも余計に書き込んでいるのでは?と考え、
取得できるログ長にその分を加算して確保するように変更した。
その結果、このアクセス違反問題は解決した。

結論

OpenGLはドライバ依存になる部分がおおい。
値を信用せず、若干の余裕を持たせてバッファは確保しておく方が
多くの環境で動かすポイントになりそう。

また、NVIDIAだけでなくATIのカードでも動作を試してみることが
OpenGLに至っては必須なのかもしれない。
DirectXだったら両者でそこまで深刻な違いは出ないのかもしれないが。


OpenGLで座標系をいじる


よくある右手系、左手系の変換の話ではありません。DirectXとOpenGLではビューポートの原点位置が異なるのですが、
これがOpenGL拡張を使うと共通化できるのでは!と。

その対応をやってくれそうな拡張が

  • GL_ARB_fragment_coord_conventions

というやつです。
ドキュメントを読むと座標系の原点をどこに設定するか、
ピクセルのサンプリング位置をどうするか、という部分を変更できるようです。

この内容だけ見るとDirectX9の半テクセルずらすという部分とも対応をとることができそうです。
あと、この拡張はOpenGL 3.2に標準として取り込まれたらしいです。

問題はこれはGLSLのシェーダー内で記述があって有効となるようです。

左上を原点としてみる、のテスト

ピクセル位置のほうはひとまず置いておいて、
ビューポートでの原点を変更することを試してみます。

DirectX9では、左上が原点だったので、
OpenGLでもこれにあわせることができるかを確認してみます。

GLSLのプログラム内に記述を書くも、挙動かわらず。。。

まとめ

そもそも変換するという点で間違っていました。
よく見ると”fragment_coord_conventions”であり、”convertion”じゃありませんでした。

というわけでOpenGLのピクセル座標系をいじるものではありませんでした。
この機能は、ピクセルに関する取り決めをするようなそんな程度のものっぽいです。

でもこれきちんと使えば、DirectXの半テクセルずれている系を
OpenGLで再現させることもできるため、エミュレーション的動作には役立たせることが可能かもしれません。