もんしょの巣穴blog

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  1. --/--/--(--) --:--:--|
  2. スポンサー広告

OpenKinectの始め方

Windows7上で動いたので、Demoの起動までを覚え書き的に書いてみます。

まず、以下からドライバとデモ(ソース付き)をDLします。

http://openkinect.org/wiki/Getting_Started_Windows

上の方に"kinect-v??-withsource.zip"と言うのがあるので落とします。
この記事を書いているときの最新は v15 です。
自分が試した限りでは、v14 はそのまま動作しますが、v15 はハングしました。
調べてみたら頂点バッファのサイズが凄いことになってて、多分VRAMが足りなかったとかじゃないかと。

落としたら解凍して、KinectをPCに接続します。
多分、ドライバのチェックで失敗すると思います。
次にコンパネ -> ハードウェアとサウンド -> デバイスとプリンターを開くと、多分USBハブが追加されていると思います。
これがKinectなので、解凍したフォルダ内のNUI Motorドライバをインストールします。
署名がないとかで警告が出ますが、無視して先に進めましょう。
インストールが終わるとNUI CameraとNUI Audioも出てくるので、こちらもインストールします。
やっぱり警告が出ますが無視。

あとはデモを実行。
v14 は3つのウィンドウが開き、以下のような画面が出てくるはずです。

kinect01.png kinect02.png  

上の画像は2つですが、もう1つはカメラそのままの画像です。
そっちは勘弁してください(笑

カメラの画像と深度は取得できるので、閾値で切り抜くとかは簡単にできそうです。
いやぁ…凄い世の中になったもんだねぇ。
スポンサーサイト
  1. 2010/11/21(日) 14:05:09|
  2. プログラミング
  3. | トラックバック:0
  4. | コメント:1

ddx, ddyの挙動を調べてみた

微妙に謎がある勾配命令 ddx, ddy(アセンブリ命令では dsx, dsy) について調べてみました。
これはGPUごとに動作の違いがあるかもしれないので私が調べたものがすべてのGPUに適用されるわけではないでしょうからお気を付けください。
ちなみに、うちのGPUはGeForce8600GTSです。安物ですね。

調査方法は4x4のレンダリングターゲットにポリゴン等を書いてみて、その過程をPIXで追うというだけ。
頂点のテクスチャ座標として適当な数列を与えています。わかりやすいものからランダムなものまで。
泥臭いけど確実な方法ですね。
まず、ポイントリストで16の点を各ピクセルに描画してみましたが、ddx, ddy 命令での出力は 0 でした。
やはりこの命令は1つのプリミティブ(点1つ、三角ポリゴン1つ)の間で有効な命令のようです。
では、ポリゴンを描画した場合どうなるか?
頂点に与えた数列をそのまま引数にすると隣接ピクセル(ddx なら右、ddy なら下)の同じ引数との差分が出力されました。
単純に隣接ピクセルとの間で差分をとるだけっぽいのかと思ったのですが、いろいろ計算してみるとそうでもないらしいことがわかります。
例えば2つの適当な数列で内積して2乗とかしてみると隣接ピクセルとの差になってない場合が出てきます。
よく調べてみると、どうやらいくつかのピクセルにおける勾配関数の戻り値は同一です。
GPUによって挙動は違うかもしれませんが、8600GTSでは同一の戻り値になるのは2x2の領域です。
つまり、2x2の領域において左上のピクセルと右上のピクセルの差分が ddx で、左上のピクセルと左下のピクセルの差分が ddy で取得できる形になるようです。
あるポリゴンの描画範囲が2x2の右下のみを描画する場合があったとしても左上を中心として計算しているようです。
GPUは2x2の領域の1ピクセルでも描画される状況であれば領域すべてのピクセルを計算するものと思われます。
この際、ddx や ddy が来たタイミングの同一レジスタの値の差分を返すというものなのでしょう。
『ShaderX5』には勾配関数を使用してタンジェントスペースなしでバンプマップをやる手段について記載されていますが、完全なピクセル単位というわけにはいかないようですね。
まあ、2x2なら誤差範囲と言っていいと思いますし、人間の目にはそれらの違いを理解することは難しいと思いますが。

  1. 2009/06/28(日) 17:32:16|
  2. プログラミング
  3. | トラックバック:0
  4. | コメント:0

Dynamic Shader Linkage vs Uber Shader

ビックカメラで安かった『Far Cry 2』を買って遊んだんですが、リアルにすればそれで良いとか考えてるゲーム開発者は死ねばいいんだ、とちょっと思った。
基本的にJRPGと同じでお使い、作業の連続。それでいて移動には時間がかかるし、行ったり来たりも多い。
こちらはリアルであることを強制され、とにかく敵の位置も把握しにくいジャングルの中で四苦八苦。
それに比べて敵は下生えの草に隠れててもこっちを見つけてくる。こっちからは相手が見えなくても相手にはこっちが見えるのは理不尽すぎる。
スニーキングができればまだ楽しめたかもしれませんが、敵の背後から近づいて殺しても次の瞬間には他の敵にこちらの位置がばれる仕様。MGSのように簡単に警戒態勢を解いてはくれないしね。
しかも酔いそうになった。『KILLZONE2』なんて目じゃないほどに。最後には慣れたけど。
リアルはリアルでもCoD4はうまくバランスとってたのにねぇ…。遊びやすくないゲームは駄目だと思う。

ここから本題。
GameDev.netのフォーラムでタイトルのようなスレッドを見つけました。
Dynamic Shader LinkageはDX11で追加される、シェーダプログラムをリンクさせる機能です。要はシェーダジェネレータとかでやってることをやってくれるやつ…だと思う。
このスレッドでは多分自前のシェーダジェネレータのことを言ってるのでしょう。DX11はまだ出てませんので。
Uber Shaderは究極のシェーダというような意味。何でも屋のようにすべての機能を持っているシェーダのようです。
つまり、if文、for文といったフロー制御をバンバン使って1つのシェーダプログラムで複数の機能を持たせているやつですね。
前者の利点はデザイナが自分でいろいろなシェーダを作れるようになることです。トライエースもそうやってますね。
欠点は管理が難しいところと、最適化が難しいところ。単純に動的生成していたら最適化は無理ですね。
後者の利点はシェーダプログラムの管理がしやすいところでしょうね。シェーダを増やすもの、最適化するのもプログラマです。
欠点はなんと言っても速度。フロー制御はとにかく遅い。
…ほんとに遅いのでしょうか?
まことしやかに囁かれていることですが、実際にはどの程度重いのでしょう?
Dynamic Shader Linkageの場合、シェーダをほぼ常に切り替えながら使っていく必要がありますが、Uber Shaderではレジスタの設定だけで済みます。
フロー制御が重い、と言うのと同じくらいに、シェーダやレジスタ、レンダーステートの設定は重いと言われています。
わからなかったら人に聞く!とはいうものの、こういうのは自分でパフォーマンス計測した方が速い。
そこで計測してみました。前回と同じような方法で。

前回も今回も自宅マシンでテストしています。
CPUがCore2Duo E6750、メモリが2G、GPUがGeforce8600GTSです。
描画するモデルは前回も使用した1900ポリゴン、2マテリアルのモデル。
このモデルを点光源1?4、それぞれについてディフューズのみとディフューズ+スペキュラで描画します。
その、計8回の描画を100回繰り返します。合計800体のモデルが描画されます。
描画位置はすべて同じですが、Zテストあり、Z書き込みなしで描画するので、常に描画されます。
Uber Shaderはピクセルシェーダ側でのみフロー制御を利用します。頂点シェーダはどちらも共通です。
シェーダプログラム以外はUber Shader有利に設定してあります。マテリアル情報はどちらの描画でも常に設定しますが、シェーダ自体の設定はUber Shaderが800体描画するうちに1回だけ、Dynamic Shader Linkageはマテリアル1つにつき1回設定、つまり1600回設定します。
Dynamic Shader Linkageは実際にはシェーダを自動生成していません。8つのシェーダを予め作成しています。シェーダ生成の時間やキャッシュのチェック等は行っていません。
上記のプログラムを600回繰り返し、かかった時間を計測した結果が以下です。

 かかった時間平均時間FPS
Uber Shader840981407
Dynamic Shader Linkage354535916

圧倒的にDynamic Shader Linkageの方が速いですね。2倍以上速いです。
やはりフロー制御は重い。SM3.0でないと使えないし、シェーダジェネレータで対処できるなら使うべきではないでしょうね。

おまけ
ShaderXの編集者であるWolfgang Engel氏は、我々はシェーダジェネレータは使用しない、と言ってます。
やはり最適化が難しく、品質の高い映像が作れないからだそうです。
技術的に独立できる部分はヘッダファイルのようにして保存しておき、これを各プログラマ間で共有すればいいとか。
シェーダジェネレータはインディーズタイトルや非商用のものならいいけど、パッケージソフトで使うべきではないそうで。
しかし、ロックスターは金あるからいいけど、こちとらそんなこと言ってられねぇんだ!と言いたくなったりもしますね。
とはいえ、個人でゲーム作るならシェーダジェネレータっていらないんですよね。そんなにシェーダプログラム使わないでしょうし。
多くても200はないんじゃないでしょうかね。個人で作るゲームでは。

PS
表が間違ってたので修正しました。

  1. 2009/04/12(日) 19:44:14|
  2. プログラミング
  3. | トラックバック:0
  4. | コメント:2

パフォーマンス計測

昨日、SetRenderState()とかをキャッシュするStateManagerを作成したので、今日はそのパフォーマンステストプログラムを作成してみました。
SetRenderState()の発行回数は当然減らせるだけ減らすべきなので、無駄な発行を行わないようにキャッシュしておくのStateManagerです。
と言うのも、会社の先輩にSetRenderState()の速度について聞かれて、そういえば試したことなかったなと思ったので。
で、ついでに他のテストもかねてやってみたわけで。
2000ポリゴン弱のモデルを読み込み、以下の3種類の頂点バッファ、インデックスバッファを作成します。

1.メッシュごとに頂点バッファとインデックスバッファを作成する
2.頂点バッファをモデル全体で統合し、インデックスバッファはメッシュごとに作成する
3.頂点バッファもインデックスバッファも1つに統合する

これでそれぞれテストを行ったのですが、速度はどれもほぼ変わりません。
やはり、3が若干速かったですが、FPSにして1も違いませんでした(Z書き込みなしで1000体表示して22FPSくらい)。
次に問題のStateManagerですが、やはり1000体表示しました。
当然、SetRenderState()、SetTextureStageState()、SetSamplerState()をそれぞれキャッシュを通した場合と通していない場合でチェック。
結果はほぼ変わらず。というか、先のテスト以上に差がない。
これはどういうことでしょう?
考えられるのは、そもそもDirectX、もしくはドライバの方でキャッシュを行っている可能性です。
調べてみたのですが、”Pure Device”ならキャッシュは効かない、と言うような話があるフォーラムにありました。
つまり、”Pure Device”ではないと言うこと?っていうか、”Pure Device”ってなに?
ちなみに、std::map を使ったのですが、Debugモードだとかなり重いです。Releaseモードなら問題なし。

と言うわけで、このStateManagerは失敗かも。
std::map を std::vector あたりで実装するならもう少し速くなるかも。
無駄な項目が出てきてしまいますが、O(1)でアクセスできるし。
まあ、そっちも試してみます。

追記
std::map を std::vector に変えてみましたが、ほぼ変わりません。
誤差程度の差なので、意味がないですね。
とりあえず std::map で行こうと思いますが、使うかどうかは微妙なところか。

  1. 2009/03/29(日) 19:31:34|
  2. プログラミング
  3. | トラックバック:0
  4. | コメント:0

OpenGL ES 2.0 その04

前回はDirectXでいうところのDrawPrimitiveUP()と同じ描画方法をやりましたが、今回は頂点バッファインデックスバッファを使用した場合の描画方法をやります。
OpenGLにはディスプレイリストが存在するのですが(2.0でもあるかどうかは知らない)、GLES2.0には存在しません。
頂点バッファもインデックスバッファも作成方法はほぼ同じです。データの種類と、一部の関数での引数が違うだけです。
では、まず頂点バッファの作成方法から見ていきます。

glGenBuffers( 1, &g_hVbo );

まずはバッファを作成します。複数のバッファを作成することも可能で、DirectXのストリームのように複数のバッファを設定してもかまいません。
次にこのバッファに頂点情報を書き込みます。

glBindBuffer( GL_ARRAY_BUFFER, g_hVbo );
glBufferData( GL_ARRAY_BUFFER, size, pData, GL_STATIC_DRAW );
glBindBuffer( GL_ARRAY_BUFFER, 0 );

まず、作成したバッファをバインドします。これが1行目です。
2行目で情報を書き込みます。ターゲット、データサイズ、データの先頭アドレス、usage となります。
usage は通常、GL_STATIC_DRAW を選択します。これは基本的に変更しないデータを書き込む場合の設定です。
GL_DYNAMIC_DRAW を選択するのはデータを頻繁に変更する場合です。多分、速度は遅くなるけど、アクセスがしやすいような状態になっているんではないかと思います。
3行目でバッファのバインドを解除しています。OpenGLではバインドは引数に 0 を渡すとデフォルト設定に戻ります。DirectXもテクスチャやシェーダはそんな感じですね。
GL_ARRAY_BUFFER は頂点バッファのターゲットです。
このターゲットを GL_ELEMENT_ARRAY_BUFFER に変更するとインデックスバッファを指定したことになります。

実際の描画は以下のように行います。

glBindBuffer( GL_ARRAY_BUFFER, g_hVbo );
// ここで頂点とシェーダプログラム内の属性をバインド
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, g_hIbo );

頂点バッファを設定した後に頂点属性のバインドを行います。
頂点属性のバインドは前回やった glVertexAttribPointer() のことです。
これらの設定は必ず頂点バッファの設定後に行う必要があります。
また、全く同じ頂点属性を持つ複数の頂点バッファを入れ替える場合も、頂点バッファのバインド後に属性のバインドを行う必要があります。
DirectXではVertexDeclarationが同じなら設定し直す必要はないのですが、OpenGLではやらないと駄目っぽいです。
どうも、頂点バッファの実体に対してバインドしているようなのです。
つまり、glBindBuffer() は属性をバインドするターゲットはこのオブジェクトですよ、と教えてやるだけのものなんじゃないかと思います。
その後、ターゲットを変更してインデックスバッファも設定します。もちろん、インデックスバッファを使用しないなら設定の必要はありません。
OpenGLはDirectXのようにバッファを使用する描画関数と使用しない描画関数が分かれていません。

glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, NULL );

インデックスを使用する場合、glDrawElements() 関数の第4引数で指定していましたが、インデックスバッファを使用する場合は NULL を指定します。
インデックスバッファを使用しているのに NULL を使用しないと描画がおかしくなるので注意してください。
また、DirectXと異なり、頂点バッファは使うけどインデックスバッファは使わずにインデックス配列を使って描画、とかも可能です。もちろん、その逆もOKです。
この辺の設定とかは頂点バッファやインデックスバッファをクラス化しておくといろいろ楽できるでしょう。

  1. 2009/03/15(日) 16:40:51|
  2. プログラミング
  3. | トラックバック:0
  4. | コメント:0
次のページ

プロフィール

monsho

Author:monsho
ゲームプログラマ?

最近の記事

最近のコメント

最近のトラックバック

月別アーカイブ

カテゴリー

ブロとも申請フォーム

この人とブロともになる

ブログ内検索

RSSフィード

リンク

このブログをリンクに追加する

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。