2005年03月06日

ソフトウェア3D

Widgetのことで、Layerのメソッドを見てみると、図形描画系のメソッドはFillRectぐらいしかない。
ライン描画と円の描画は昔書いたので、そのコードを久しぶりに見てみる。
あんまり変更しなくても、動くことは動きそう。
とりあえず、Widgetならライン描画ぐらいあれば事足りるわけだが、なんとなくポリラインやポリゴン、三角形なども描画出来たらなぁ・・・ テクスチャマッピングとか。
そんなことを考えていてふと、ソフトウェア3Dライブラリを組かな? と思った。
昔ターゲット用に遊びで組んだものは、フラットシェーディングまででライティングは全然だった。
基礎の学習とコードの追える3Dライブラリとして作るのもいいかも。
以前、途中まで書いた3Dのドキュメントも見直して公開し始めても良いかも。
3Dの基礎的な部分って意外とドキュメントや書籍が少なくてかなり困った記憶があるが、今は整備されているだろうか?
なんか、すごい脱線していっているかも・・・

投稿者 Takenori : 19:50 | トラックバック

2005年03月16日

Direct3Dを使えるようにしようとする

LAMPばかり触っていて疲れてきたので、Direct3Dを直接使い、吉里吉里上で表示させてみようと考え、作り始めた。
Direct3Dに設定したWindowに対して、GDIで描画したらどうなるか試してみる。
両方描画できているっぽい。
これならオーバーレイのような感じで使えそう。(オーバードローと言うような表現が良いかも)
まずはとにかく表示できるようにすることとし、チュートリアルのメッシュをベースに、各機能をクラス化した。
ただし、VectorやMatrixのクラスも追加するのは面倒なので、引数にx,y,zを渡すなどして少し省力化。
各クラスが出来たので、クラスプラグインとして利用できるように、インターフェイスとなるクラスと登録用の関数を書く。
激しく面倒くさい。
SWIGを拡張してTJSに対応させるか、簡単なスクリプトからクラスと関数を生成してくれるようなツールが欲しいと切に願う。
でも、今回はすべて手書き。
そもそもどうやって書くかまだあまり知らないし。

クラスを返したり、受け取ったりする部分はどのように書くのだろう?
よくわからないので、勘で次のように書いてみる。
tTJSVariant result;
iTJSDispatch2 *tjsclass = Create_NC_Transform3D();
tTJSVariant val = (tjs_int)m_XObject->getTransform();
tjsclass->PropSet( TJS_MEMBERMUSTEXIST, TJS_W("transform3d"), NULL, &val, tjsclass );
result = tTJSVariant(tjsclass);
tjsclass->Release();
val.Clear();
return result;

実行させてみるが、どうもうまくいっていない様子。
Transform3Dと言うクラスにネイティブにクラスのポインタを持たせ、transform3dというプロパティでそのメンバへ直接値を入れられるようにして、PropSetで渡せば何とかなるかと思ったのだが・・・
吉里吉里の内部のコードを見てみると、コンストラクタの引数で値を渡している物があった。
そうか! コンストラクタで実態を渡せるようにした方が良いか。
よく考えるとそれではまずいこともありそうな気がするが、とりあえずそうする。
で、次のように書く。

tTJSVariant result;
iTJSDispatch2 *tjsclass = Create_NC_Scene();
iTJSDispatch2 *out;
tTJSVariant param((tjs_int)m_Graphics.getRootScene());
tTJSVariant *pparam = &param;
tjsclass->CreateNew(0, NULL, NULL, &out, 1, &pparam, tjsclass);
result = tTJSVariant(out,out);
tjsclass->Release();
//out->Release();
return result;

このソースは上のソースとは違う場所。
コメントアウトしている場所は、これをやると開放しすぎてしまうので、コメントアウト。
ここは、なんかわからないけど、初期化されてねー、アクセス違反が出るなどなどかなり悩んだ。
なので、ここではRelease()をコールするのは間違いのよう。
とりあえず、これで吉里吉里からこのクラスプラグインが使えるようになった。
いろいろとはまったけど。

投稿者 Takenori : 01:28 | トラックバック

描画されない

プラグインを最初にロードし、変数にクラスを持たせ、ベースレイヤーでonPaintをオーバーライドし動作させてみる。
が、真っ黒。
そういえば、作ったクラス単体で動くかどうか試していない。
テスト用のプログラムを作り、動作確認。
少し間違っていたので修正しきちんと表示されるように。
修正したのでプラグインの方も再度ビルド。
動作させてみるがやはり真っ黒。
うーん、なんかDirect3Dで描画した後、上書きされているような・・・
吉里吉里の描画部分を追うが深いので途中で断念。
レンダーターゲットをサーフェイスにして、そこから吉里吉里のレイヤーへ描くようにすれば描画できると思うが、VRAM->システムメモリ->VRAMとなんか無駄な転送しまくりなのがちょっと嫌。
完全に3D化し効率的な動作を目指すのであれば、吉里吉里本体からレイヤー周りをはずして、Direct3Dに描かせるのが良い。
でもそしたら亜種になってしまう。
吉里吉里3Dとかいう名前にするのも楽しいかもしれないが・・・
そしたら、#ifdefとプロジェクトに含めるソースで切り分けられるようし、できるだけ共通のソースにしたほうがメンテナンスは楽。
まずはレイヤーに描いてみるかな。
吉里吉里3Dはその後に考えよう。

投稿者 Takenori : 01:59 | トラックバック

2005年03月17日

重ひ・・・

レイヤーのバッファへのポインタを設定して、レンダリング後イメージをそこへコピーするようにした。
バックバッファのサーフェイスをロックしコピーするのはやはり無理だったので、レンダーターゲットを指定して、そこからコピーするようにした。

tiger20050317.png

表示はされたけど・・・ 重ひ・・・
Athlon XP 1600 + GeForce2 MX400で、5FPSぐらい。
ほとんどMeshサンプルそのままだけど・・・
この方式で普通に使えるぐらいの速度は出るかなぁ。
うーん・・・ どうするか。

投稿者 Takenori : 14:15 | トラックバック

2005年03月19日

直接描画が出来ない

BaseLayerのDrawが最終的に描画を行っているみたいなので、何もせずにreturnするようにしてメイク。
実行させると何も描かれなくなった。
で、プラグインの方をレンダーターゲットを設定せずに、普通にPresentするように戻し、実行。
何も描画されません。
あれー?
よくわからない。

レイヤーへ描画する方もいろいろといじってみるが、速度はいっこうに改善されず。
速いマシンでも同じぐらいの重さ。
何か根本的な問題があるような・・・

スクリプトから3Dを触れると言うのは思いのほか楽しかった。
VCでビルド&実行というのも作業量的には同じなのだが、スクリプトの方がかなりお手軽。
まあ、プロジェクトを作ったりしなくてもいいし、面倒なことはクラスDLL内に行っているので、お手軽な感じがする。
初めから大部分をスクリプト側に提供するようにしておき、プロトタイプをスクリプトで組んでいくのが楽でよいかも。
で、速度が必要な部分や汎用的な部分をネイティブに実装する。
そんな感じがいいかな。

吉里吉里の3D拡張は、メリットが薄くなってしまったかもしれない。
Teaspireの方へノベル/ADVシステムをのせる方が良いかも。
初めはRPGのシステムをTeaspireへ、ノベルは吉里吉里へと考えていたけど。

投稿者 Takenori : 11:26 | コメント (2) | トラックバック

子ウィンドウへ描画

CreateDeviceに渡すhFocusWindowは最上位ウィンドウかNULLでなければ失敗する。
ここに子ウィンドウを渡してみたのだが、失敗したので出来ないなーと思ってやめていた。
でも、EffectEditサンプルでは子ウィンドウに表示できている様子。
EffectEditサンプルはMFC+DXUTだったので、コード追うのを途中でやめた。
と、ここまでが少し前の状態。

で、今日再びコードを追ってみた。
CreateDeviceに渡す、D3DPRESENT_PARAMETERS の hDeviceWindow へ子ウィンドウのハンドルを入れれば、そこへ描画してくれるようだ。
で、ビルド。
うまくいった。
描画速度も問題なさげ。

もう少し機能を追加すれば、いろいろと遊べそうだ。
楽しくなってきた。

投稿者 Takenori : 22:41 | トラックバック

2005年03月20日

やりたかったこと

ちょっとプログラムの問題に傾倒しすぎて、当初の目的を忘れていた。
創りたい物は、3D RPGのイベントシーンのようなものがずっとつながってストーリーが展開されていくような作品。
ノベルやADVとは違うし、ムービーとも少し違う。
リアルタイム3Dアニメーションノベル? 既に名前があるのかどうか知らないが、そういったものを作りたい。
3Dであれば、モデルデータやアニメーションなどが再利用しやすいので、蓄積されていくごとに作りやすくなっていき、ドラマ的なものへなっていくのではないかと思っている。
データの蓄積もクリエイティブコモンズ ( Japan.internet.comの記事 ) によって、促進されればなぁと思う。
ただ、クリエイティブコモンズは使用時のライセンス料などの商用関連のライセンス項目がないのが残念。また、現状そんなに普及していないのもちょっと・・・と言うところだが、オープンソースが普及してきたように、クリエイティブコモンズも成長していけばと思う。
とにかく、そういった作品を作ること、また普及することが目的。
なので、3D拡張のシステムはそういったものが作りやすい性質のものにするつもり。
クラス構成などはJava3DやLAMPを参考にしようと考え中。(どちらかと言えば、Java3Dを基本とするようなもの)
システムは思いっきりDirectX依存になる予定。
リアルタイム3Dの性質上ある程度仕方ないし、少し依存するなら思いっきりしちゃえって感じだ。
後、バージョン1は期待しない方が吉だろうな。

投稿者 Takenori : 13:54 | コメント (2) | トラックバック

2007年03月14日

カテゴリ名変更

3D拡張となっていたカテゴリをDirectX拡張に変更。
DirectXでの描画に対応されていくようなので、その辺りに関連することを書こうと思います。
当面は、過去に得た知識などをまとめていくことにします。

投稿者 Takenori : 21:30 | トラックバック

2007年03月19日

どうやって2Dを描画するか

Direct3Dで2Dを描画する場合、次のような方法が思いつく。

1. 四角いポリゴンにテクスチャとして貼って表示。頂点はT&L済みにする。
2. 四角いポリゴンにテクスチャとして貼って表示。頂点はトランスフォーム前のにする。
3. ソフトで合成して、IDirect3DDevice9::UpdateSurfaceでコピー。
4. IDirect3DDevice9::StretchRectだけでコピー。

2D画像を描画する場合、当たり前だけど、元画像の 1pixel を画面の 1pixel に対応させる必要がある。
これが案外面倒。
テクスチャ座標は、そのまま表示するのなら 0.0 ~ 1.0 で表すことになると思うが、テクスチャが2の累乗値しか許可されていない場合、それ以外の値の幅の画像を描画しようとしたら、1ドットだけ拡大されてしまい見難い絵になってしまうこともありえる。
また、ポリゴンのどの部分が実際に描画され、どの部分は描画されないのかも気にかける必要がある。
以上のようなことを考えると、トランスフォーム前の頂点座標を与えて綺麗に表示する ( 2番 ) のは、変換時の誤差まで考えなくてはならないので、難しいのではないかと推測できる。
そんな関係で、以前は1番で実装していた。
ただ、3D的な演出 (ガラスが割れて崩れ落ちるようなものなど) への切り替えを考えると、2番も考慮しておいたほうが良いと思われる。(普段は必要ないので、1番でいいと思うが)
3番は、DirectX使うメリットが少ない。何でも出来るとも考えられるが。
4番は、拡大縮小のコピーしか出来ないのと、コピーできるサーフェイスのタイプにいろいろと制限がある。でも、ほとんどの場合、これで事足りるのではないだろうか。
ちなみに、動画のVMRモードではStretchRectでバックバッファへコピーしています。コピー時のYUV から RGBA への色空間の変換はサポートされている ( サポートされないデバイスもある模様 ) ので、色空間変換が必要な場合は、CPU負荷が軽くなって助かります。画像の色空間変換はかなり重いので。

D3DX を使うのなら、ID3DXSprite を使えば深く考えなくても良さそう。ID3DXSprite は使ったことがないので、その内試してみたい。
でも、ヘルプを見たところ少し凝ったことをしようとすると、ID3DXSprite では対応出来なさそうな気もする。

そんな感じなので、1~4をすべてサポートして、うまくやってくれるようなのがいいな。
とりあえずは、そこを目指そう。

投稿者 Takenori : 01:08 | トラックバック

作り始めようとするが……

なんだかんだ言いながら、結局遊び始めてしまう。
とりあえず、いろいろと実験してみたいだけなので、実用的なものが出来るかどうかは不明。
で、昔作ったものを参考にしつつ作ろうと思ったけど、参考にしないほうが良さげ。
いろいろな制限から変な構造だったり、知識不足からイマイチな構造だったり。
ってことで、新たに考えて作ることに。

実験用だけど、基本的なところはそれなりにしたいので、基本構造はシーングラフにする予定。
さっくり出来るかと思いきや、結構時間がかかりそう。
早くいろいろと試してみたい。

投稿者 Takenori : 01:23 | トラックバック

描画のソート

前後関係を持ったものを描画する場合、どのように描画するのが得策か。
Direct3Dを使って描画するのであれば、次の2つが考えられる。

1. 一番後ろのものから順に描画していく
2. Zバッファを使う

一番後ろのものから順に描画していった場合、当然だが重なり面が大きければ大きいほど無駄に描かれる面が出来てしまう。
しかし、2Dに限るのであれば、そんなに重なることはないように思う。
それに、ハードウェアで重ねるのであれば、多少はオーバーヘッドがあっても、今のビデオカードなら難なくこなしてくれるだろう。
ただ、3Dの場合は、あるポリゴンが他のポリゴンに刺さっているような場合うまく描画されない。

Zバッファを使った場合、半透明のものを描画しないのであれば、ソートなど気にせずZ値を与えて、適当に描画すればとりあえずは前後関係に従って描画される。
半透明のものを描画しようとした場合は、描画順を気にしないと期待した結果が得られない。
描画したオブジェクトが半透明であっても、そのオブジェクトのZ値はZバッファに書かれてしまう。Zバッファに書かれるということは、それ以後、それより後ろのオブジェクトは描画されないことになる。
その結果、後ろの絵と半透明のオブジェクトの合成を期待したにもかかわらず、背景色と合成された絵が出てきてしまうこともありえる。
これを避けるためには、オブジェクトを後ろから前へ描画する必要がある。
しかし、ただ単純にそのようにすると明らかに無駄が多い。
半透明のオブジェクトの前に不透明のオブジェクトがあったら、半透明のオブジェクトは描かなくていいはずだ。
このことから、まず不透明のオブジェクトを手前から後ろへ描画し、次に半透明のオブジェクトを後ろから手前へ描画するのが効率的だろう。
そのようにすれば、Zテストによって見えないところは描画されなくなる。
たぶん、この方法が一般的。

で、仮にZバッファを使うことにしたとする。
では、Z値はどのように決めればよいのだろう?
とりあえず、1/(2^8) ぐらいずつずらしておけばいいだろうか?
それとも、適当な間隔をあけて値を入れおけばいいだろうか?
適当な間隔をあけて値を入れるのもありだが、そんな面倒なことはやりたくない。
単純に順序のみ保持しておき、描画の直前にZ値の精度から重ならないように少しずつ値をずらして設定し、半透明、不透明に分けてソートしてから描画するのが良さそうだ。
でも、Zバッファを使うことにした場合、3Dと同時に描画したくなった場合、どうするか考えないといけない。
3Dのオブジェクトに、2Dの絵が突き刺さると格好悪い。
では、やはりZバッファを使うのではなく、描画順で描いた方が良いのだろうか?
どうやって3Dと2Dを混ぜるかへつづく

投稿者 Takenori : 22:15 | トラックバック

2007年03月21日

どうやって3Dと2Dを混ぜるか

描画のソートのつづき。

Zバッファを使って、前後に2Dを置き、中間に3Dを描くのであれば、ViewportでMinZとMaxZを設定し、トランスフォーム後のZ値の範囲を2Dで使う範囲とずらすことで可能。
つまり、トランスフォーム後の頂点で描画する2Dには、0.0~0.1、0.9~1.0などの範囲を使うようにして、3Dはその間の0.1~0.9を使う。
ただ、その場合は3Dで使えるZ値が狭まってしまうのが少しやっかい。

Viewportで範囲を狭める以外に、レンダリングターゲットにテクスチャを設定し、2Dはそこにレンダリングしておき、最後にそのテクスチャを貼り付けた板状のポリゴンを後ろと手前に描く事でも混ぜることは出来る。
他には、途中でZ値をクリアしてしまうという手もある(うまく行くのかどうかは知らないが)。

でも、本当にそこまで必要だろうか?
やはり、混ぜて使うことを考えると2Dは描画順で制御したほうが良いようにも思う。
ただ、2Dのみで使われる場合に少しだけ不利なのと、フォグやパーティクルなどの間に2Dを置きたい場合に困る。
ならば、間の3Dの中に2Dを置けるようにしてしまえば、両方の問題が解決するのでは?

つまり、以下のような構成にする。

1. 最前面2D層 ( 描画順で制御される )
2. 3D層 (T&L済み、T&L前頂点の両方あり)
3. 最背面2D層 ( 描画順で制御される )

1と2の2D層は、描画順で制御される。2はZ値で重なりが制御される。
2の中に2Dを描画する場合、T&L済み頂点を与えるか、カメラからの距離を与えて内部で適切なZ値に計算する。
重なり順だけを与えたなら、内部でT&L済み頂点にして、意図した重なりで描画されるようにする。
3Dと混ぜたい時は、距離を与えてうまく混ざるようにする。
2Dしか使わないのであれば、1と3は使わずに、2だけで描画すれば、無駄な描画が減って効率的。

内部的には、3を描画する必要がある場合はZテストとZバッファの書き込みをOFFにして、3を描画。
次にZテストとZバッファの書き込みをONにして、2を描画。
最後に、1を描画する必要がある場合は、再びZテストとZバッファの書き込みをOFFにして、1を描画するようにすれば、期待した通りに動くはず。
もっとうまいやり方があるかもしれないが、とりあえずこれで行こう。

投稿者 Takenori : 20:33 | トラックバック

テクスチャ ブレンディング

色やアルファ値を指定した方法で合成して、最終的な出力色を得る仕組み。
合成の各段階はステージと呼ばれ、各ステージでどの色とどの色を使用し、どの演算で出力するかを指定する。
引数は2個かHWサポートされていれば3個まで。
引数には、ディフューズ色、スペキュラ色、テクスチャ色、定数値、前のステージの出力色などが指定可能で、これらの色を乗算や加算などの演算を指定して最終出力色を作る。

つまり、
ステージ 1、色1 : テクスチャ、色2 : ディフューズ、OP : 色1を使用
ステージ 2、色1 : 前ステージ出力色、色2 : テクスチャ、OP : 色1 - 色2
ステージ 3、色1 : 前ステージ出力色、色2 : テクスチャ、OP : 色1 + 色2
のように指定すれば、画像1から画像2を引いて、その結果に画像3を加算と言うことが出来る。

これを使えば、いろいろなブレンド処理が可能になる。

投稿者 Takenori : 21:21 | トラックバック

2007年03月29日

テクスチャの分割・結合

HWのサポートしているテクスチャの最大サイズが1024*1024であれば、640*480を1枚で表示できるが、512*512の場合だと2枚のテクスチャが必要になる。
最近のビデオカードなら1024*1024は余裕だと思うが、古いビデオカードでは無理だろう。
その場合、テクスチャを分割して表示することを考えなくてはならない。
単純に表示するだけであれば、それほど苦労しないが、拡大縮小、回転を考慮するとなかなか面倒になる。
では、古いビデオカードは切り捨てるか?
もし、切り捨てたとしても、2の累乗制限の問題がある。
制限のないビデオカードもあるが制限のあるビデオカードもある。
32ビットカラーの場合、1024*512で2MB、640*480で1.17MBとなり、2倍近く無駄遣いしていることになる。
無駄遣いを軽減するアプローチとしては、2の累乗制限下では2つ考えられる。

1. 複数の画像を1つのテクスチャにまとめて、そこから切り取って貼る。
2. 1つの画像を複数のテクスチャに分けて、描画時に複数のポリゴンを使って描く。

1.の場合は、事前にまとめておくか、実行時にまとめる必要がある。
事前にまとめる場合、何らかのツールで切り出さないと扱いづらい。と言うか、手打ちなどはあまり現実的でない。
実行時に自動的にまとめる場合は、切り出しなどは考えなくていいが、あらかじめ人がまとめるよりは無駄が出やすい。デザイナーさんにくっつけてもらう場合は、それぞれの大きさなどを調整しながら、ギリギリまでつめることも出来るだろうけど、自動ではそれは難しい。
ただ、そういう職人的な部分がなくなる分、効率は上がると思う。

2.の場合は、1に比べて実行時の効率が悪い。
大量に出さない限りそう困ることはないと思うが、気持ち的には効率が良い方法を使いたい。
でも、最大サイズの制限を考えると、やはり2.の分割かな。
それと、1.の方法の手動でくっつけたものも使えるようになってた方が良さそうだ。

投稿者 Takenori : 21:51 | トラックバック

文字列の表示

ID3DXFont でとりあえず表示して見た。
text20070322.png

文字列描画で検索したらこんなページが見つかった。
でも、何か書いていることが少しおかしい気がする。

それはともかく、ID3DXFont の話。
DirectXで文字を表示する場合、ID3DXFont を使うのがお手軽。
ID3DXFont では、最終的に ID3DXSprite を使って描画している。
ID3DXFont::GetGlyphData を使えば、内部的に作られているテクスチャを得ることが出来る。
ID3DXFont::PreloadText で"0123456789ABCDEFGHIJKLMN"を渡して得たテクスチャを描画して見たら次のようになった。
tex20070323.png
また、適当な文字列を渡すと、同じ文字は複数表れないようになっていた。
テクスチャのサイズは徐々に大きくなっていくようだ。
PIX を使って、ID3DXSprite がどのように描画しているか見てみると、1文字ずつ描画するのではなく、同じテクスチャの場合はトライアングルリストを使って、一気に描いているようだ。
以上のことから、ID3DXFont はそれなりに効率的に文字を描画してくれそうだ。
ただ、テクスチャのフォーマットはA8R8G8B8となっていた。
文字はそんなに階調なくてもかまわないから、圧縮テクスチャのDXTのどれかを使ってくれたほうが、容量的にも効率的にも良い気がするのだが……
まあ、初めは ID3DXFont を使っておいて、後で自分で作るのが楽かな。

投稿者 Takenori : 22:12 | トラックバック

2007年03月30日

圧縮テクスチャ

DirectX の圧縮テクスチャは、よく考えられたフォーマットだと思う。
簡単な方法で、ある程度圧縮でき、それなりの画質を提供してくれる。

フォーマットの詳細はSDKのヘルプに詳しく書かれているので、ここではその特徴などをまとめておく。

圧縮率は、一定で、1pixel 当り 4bitか8bit。
非可逆圧縮 (フォーマットに適した画像なら可逆)。
幅と高さは4の倍数である必要がある。
色深度は16ビット ( R5G6B5 )
アルファはなしか、深度 1bit、4bit、8bit。
DXT1 ~ DXT5の5種類あるが、基本的にアルファの持ち方が異なるのみ。

画像は、4 * 4のブロックで圧縮される。
そのブロック内で2色を16ビットカラーで保持し、2ビットでどちらかの色、もしくはその中間色かを表すようになっている (16*2 + 2*4*4 = 64 bit、64 / (4*4) = 4 bit/pixel )
つまり、そのままの色で保持されるのはブロック当り2色となる。
中間色は、ブロックごとに次のどちらかで保持される。
① 2/3 色1 + 1/3 色2
② 1/3 色1 + 2/3 色2
の2色の中間色。

1/2 色1 + 1/2 色2
の1色の中間色 (この場合残りの1色は透過色となる)

つまり、4 * 4のブロック内に2色しかない場合は、可逆となる。
2色とその中間値の4色もしくは3色の場合も、可逆となる。
現実的にみて可逆となるケースは少ないが、なくはない。
ただ、実際に変換して見るとわかるが、意外と綺麗。
DirectX の SDK には、DxTex.exe と言う変換ツールが付いているので、簡単に試せる。

アルファは、上述の中間色にある不透明/透明の1bit相当のもの以外に2種類ある。
① 単純に 1 pixel に 4bit 当てて、16階調のアルファを持つ。
② 2種類の8bit アルファ値 + 0と255のアルファ値4種とその中間値を使う。
このどちらかを使う場合、色を含めると 1pixel 当り 8bit になる。


2Dのみの場合、そんなに一度に画像を重ねないだろうから使わなくてもいいかもしれないが、ビデオメモリが少ない場合などを考えて使えるのなら使ったほうがいい。
画像をJPEGで持っているような場合、比較して見るとそれほど違わない時も。
後はテクスチャ ブレンディングで使うこと前提で利用するという手も。

投稿者 Takenori : 22:40 | トラックバック

2008年02月28日

VRAM 容量を考える

炎エフェクトを実装しつつ、これぐらいなら何とかなるけど、これ以上になると CPU 処理は厳しいなぁという印象で、Direct3D ベースで描画するのを本気で考え中。
もう少し複雑なエフェクトを入れたいのと、拡大縮小を綺麗にしたいというのもある。

それで、ビデオメモリの最低容量はどれぐらいになるか少し考えてみた。
画面は、最低でも 1024 x 768 x 32bit color はあるだろうから、これで 3MB はとられている。
で、ゲーム画面は 800 x 600 は欲しい。
という事で、バックバッファは 800 x 600 x 32bit color = 1875 KB 。
バックバッファとフロントバッファのフリップがあると、この倍になってしまうけど、そこはコピーなどで我慢するとして1枚分と考える。
背景画像も 800 x 600 とするけど、二の累乗制限を考えると、1024 x 1024 x 32bit = 4MB か。
文字用のバッファには、800 x 600 いっぱい出る可能性があるとして、1024 x 512 は必要。
ただ、文字は 3階調 + 抜き でも十分だろうから、 DXT1 として 4bit color 相当。
という事で、1024 x 512 x 4bit(DXT1) = 256KB 。

単純に、背景出しただけで、9MBぐらい使ってしまう。
背景の1枚絵を分割すれば、もう少し削れるか。
それでも、1024 x 512 + 1024 x 128 = 2.5MB か。
つまり、7.5MB 。
フルスクリーンで、解像度落とせばもう少し削れるかな。
画面の 3MB が 1875 KB になるとして、6.5 MB ぐらい。
ただ、コマンドバッファや頂点データ用にもいくらか VRAM は削られているはず。
こういうことを考えると、VRAM 8 MB で作るのは結構厳しい。
背景以外の絵を 1MB ぐらいに入れないといけない。
と言う事は、サイズは 512 x 512 まで。
他に色数を落とすなどすれば、もっと減るけど……
24 bit color で フロント、バック、背景 で 800 x 600 + 800 x 600 + 800 x 600 = 約 4 MB
とすれば、32 bit color で 512 x 512 が 3枚ぐらい使えるのか。
ああ、Z バッファ考慮していないか。まあ、自分でソートしてもいいんだけど。

VRAM 16 MB 以上とかにしないと辛そうというか、面倒だな。
8MB だと結構気を遣わないと足りなくなりそう。

自分が持っているこの辺りが下限かなぁというノートPCを見てみると VRAM は 16MB のようだ。
CPU は、Pen III 866 MHz 。2002年製?
とりあえず VRAM 16 MB 以上と考えておくかな。

投稿者 Takenori : 01:59 | トラックバック

パフォーマンスに関すること

だいぶ前に他のことを調べていて知ったのだが、DirectX では出来るだけ API 呼び出しを抑えるようにしないと重いようだ。
どうも、DirectX の API 体系ではドライバ内でいろいろとやることがあるらしく、API を呼び出すとそれなりの CPU 時間を食われるんだとか。
そのため見た目はたいしたことないのに、やたら重いゲームも存在するとか。
で、そのゲームでは GPU よりも CPU 側が先に頭打ちになって重くなっているとか。

なるほど。
だから、昔作ったのは単に板ポリで 2D の絵を出していただけだったのに重かったのか。
確かに、その時は GPU よりも CPU が先に頭打ちになっていた。

で、結局どうすればいいのかだけど、まずマテリアルの変更は出来るだけ抑えて、頂点に入れられる情報は出来るだけ頂点に入れる。
テクスチャは出来るだけ分割せず、まとめて1枚に収め、同じテクスチャを使うものは一度に描く。
ということのようだ。
他にも、呼び出し回数が抑えられるように出来るだけいろいろと工夫した方がよいようだ。

他にポリゴン数云々よりも、フィルレートの方が重要らしく、半透明のポリゴンは出来るだけ避けた方がよいようだ。
半透明のポリゴンは、後ろから描くようにソートして重ね塗りされるので、塗る面積が大きくなる。
また、不透明のポリゴンは、手前から描くことで塗られる面積を抑えられる。

後、テクスチャの二の累乗制限だけど、この制限がなくても二の累乗にしておいた方がパフォーマンス上有利なようだ。
まあ、昔は二の累乗制限があったことや、二の累乗であればテクスチャマッピングは楽になることを考えるとこれは普通に理解できる。
また、二の累乗制限がないからといって、VRAM 上でもちゃんと指定したサイズで確保されているとは限らない。
以前、640x480 でテクスチャを確保してビデオ再生をしていた時、ストライドのサイズを見ると、幅が 768 になっていた。
つまり、512 + 256?
このことから指定したサイズよりも余分にメモリが確保されている可能性がある。
という事で、テクスチャは二の累乗にしておいた方がいい。

以上を踏まえた上での基本戦略を考える。
まず、テクスチャは確保できる最大サイズで確保しておく。
同じシーンで描画する画像は、そのテクスチャに詰め込む。
半透明かどうかで取り扱いが変わるので、半透明と不透明のものは分けておく。
シーングラフを走査した後、半透明の有無やテクスチャの相違などでソートしてから描画コマンドを発行する。
こんなところかな。
まあ、実際はいろいろと試してみてパフォーマンスが出る方法にして行こうと思う。
それと、いくつか Direct3D を使うオープンソースのライブラリがあるので、それらのソースも読んでおく。

後、同じシーンで描画する画像かどうかを事前に判別するために、シーンのはじめに使用する画像を教えてやるようにしないといけない。
教えなくても出来るけど、教えた方がより効率的に配分できるはず。

投稿者 Takenori : 17:44 | トラックバック

 
Total : Today : Yesterday :