2013/02/20

SharpDX で自分用 DirectX 11 ラッパーを作り始める

SharpDX.Toolkit のソースコードを眺めていると違和感があり、DirectX をより深く知る目的も含めて、ラッパーを自作することにしました。対象は DirectX 11 にしています。
今の所、とても実装が楽しいです。DirectX のインタフェースを知ることで今までの疑問が次々と解決される点が一つの楽しみですが、より簡素なインタフェースへ纏める作業そのものが自分の本能を突き動かすような所があります。
既存コードの修正箇所を減らしたいため、初めは XNA に近づけようとしましたが、それは大きな誤りであると感じ、今は DirectX 11 として利用しやすいか否かという観点で実装しています。
大きな所では、11 ではデバイス、デバイス コンテキスト、スワップ チェーンと、その役割と共に明確に分離されていますが、一方で XNA は 9 であるため、それらが GraphicsDevice として一体化している点などでしょうか。
SharpDX.Toolkit では、11 で分離されたこれらの要素を再び GraphicsDevice として一体化していますが、このために GraphicsDevice が複雑怪奇となっていますし、11 では意味を失っているイベントやメソッドを実装するなどの問題を引き起こしているように見えます。
後は、シェーダ ステージでしょうか。当面、自分は一部のステージしか使わないのですが、既に 11 の文書を頼りに実装しているので、これらは変にラップせずに明確なインタフェースとすべきであろうと考えたりなどしています。

しかし、XNA から応用できる考え方は多々あると感じていて、参考にしながら実装を進めています。4.0 にある RenderTarget と DepthStencilBuffer を纏めてしまう設計などは、3.0 の時と比較して便利だと感じましたし。

当面、デバイスやリソース周りの実装は先が見えているのですが、BasicEffect や SpriteBatch は面倒そうだなぁ・・・と。

で、また脱線し始めたと自分で感じてはいるものの、ラッパー作成は一度は経験した方が良いであろうとも感じ、納得できる所までやろうと思っています。一度自分で経験して理解を深めなければ、他のフレームワークを効率よく扱うこともできないでしょうし。

2013/02/13

SharpDX を触り始める

SharpDX という OSS を触ってみました。
SharpDX
http://sharpdx.org/
SharpDX は C# で DirectX を扱うためのライブラリですが、名前空間 SharpDX.Toolkit には XNA Framework に酷似した枠組みも含まれています。

XNA と比較すると、Game、Graphics、Content アセンブリに相当するクラスがあります。IEffectMatrices などが含まれているので、XNA 4.0 を意識した状態でしょうか。ただし、DirectX 実装の名前空間にあるクラスで十分な物に対してはラッパーは存在しませんし、Content Pipeline の概念などもありません。なお、SharpDX.Toolkit は DirectX 11 対応のみのようです。
追記
後に DirectX Tool Kit (DirectXTK) を知り、名称が似ているので DiectXTK の C# バージョンかとも思ったのですが、API やソースコードを見る限りはそうではないようで。
そこで、過去に作成した XNA アプリケーションの中で簡単なものを選択し、SharpDX.Toolkit を用いて移植してみてます。
追記
SharpDX のラッパー作成を通じて感じていることですが、XNA からの移行として SharpDX を考えることは妥当ではないと思います。僕が DirectX 11 を対象としている事もありますが、SharpDX は 10 以上が主であり(9 対応は後付だそうです)、XNA は 9 対応であり、9 と 10 の差異は大きいと感じます。この差異は、SharpDX.Toolkit においても見受けられます。
一応、SharpDX のフル パッケージには、SharpDX.Toolkit のサンプル アプリケーションも含まれていました。簡単な物ならば DirectX を気にせずに実装できるようです。しかし、DirectX を除いても、フォーム アプリケーションの知識が多少必要でしょうか。

ひとまず、もう少し触ってみようと思います。SharpDX.Toolkit の Game クラスから簡単にフォームへアクセスできるため、Game クラスを主体にしたままエディタを作る事を考えた場合に都合が良いかなと。簡単にボタン コントロールを画面に置いたりできるので、ちょっとしたデバッグ用の UI を作りたい場合にも便利な気がします。

まぁ、「XNA の方が極めて楽だな・・・」と思いながら弄っています。

2013/02/05

天空光

結局、木の生成はスルーし、天空光・・・と言うのが適切か分からないですが、天空光の処理を実装していました。描画の結果としては、以下のような影を落とすための光の処理です。


基礎的なライティングの問題点
基礎的なライティングとして、拡散色 (diffuse)、放射色 (emissive)、反射色 (specular)、環境光色 (ambient) を定め、シェーダ内で指向性光源 (directional light) の方向から最終的な色を決めるという方法があります。
この方法では、面の法線、光の方向、視点方向のみから色を決定します。つまり、光の到達は考慮されず、光が到達しない面であると我々が認識できる場合であっても光で照らします。

通常、これは大きな問題にはならないと思うのですが、洞窟のような地形の描画において期待しない結果をもたらします。洞窟の奥深くへ行く程、光は到達しなくなることを通常は期待しますが、基礎的なライティングでは、問答無用で洞窟を構成する面を照らしてしまいます。

CPU での天空光シミュレーション
そこで、Minecraft のように、ブロックの各位置への光の到達具合をシミュレートし、テクスチャの明るさを変更するという処理を実装していました。

なお、実装の大枠は自分で考えたのではなく、Terasology (Java) を参考にしています。
Terasology
http://blog.movingblocks.net/blockmania/
他にも幾つか Minecraft クローンのオープン ソースを調べたりしましたが、Terasology のコードが最も充実している、かつ、可読性が高いと感じます。
処理の概念は Terasology と大差ないと思いますが、Minecraft や Terasology とは異なり、僕の実装では上下方向についてもチャンクを無限に広げているため、若干、シミュレーションでの判定ロジックが異なります。また、それらとは元より方向性が異なることもあり、シミュレーション結果を得る前にメッシュを構築して描画してしまい、シミュレーション完了でメッシュを再構築するなどしています。
ゲームとして考える場合は、シミュレーション完了を待ってメッシュ構築すべきかと思います。
処理の流れ
前提として、太陽の位置を真上に固定させて考えます。詳しくは知らないですが、Wiki を見る限りでは Minecraft も同じであろうかと思います。太陽の方向を考慮できればベストでしょうが、恐らく、その場合は実用に耐える処理速度を得られない気がします。

大まかな流れとしては、真上からの光の到達具合を判定し、そこから光の拡散を処理するだけです。
このような処理であるため、グリッドに沿った天空光の処理なのかなと思っています。
なお、非同期処理が前提です。ここに限らず、既にほとんどの処理が非同期ですが。

第 1 フェーズ
あるチャンクの内部について、Y 軸についてどこまで光が到達するかを判定します。光レベルは 0 から 15 の値を用い、0 が最も暗い状態、15 が最も明るい状態を示しますが、このフェーズでは光レベル 15 で処理します。
15 は、すなわち 4 ビットです。つまり、byte で 2 つの位置を管理でき、メモリ節約の恩恵を得られます。
第 2 フェーズ
第 1 フェーズで定めた光レベル 15 の全ての位置から、上下前後左右に隣接するブロック位置へ、光レベルを減衰させながら、再帰的に拡散させます。減衰しながらの拡散とは、光レベルを -1 しながら、0 となるまで隣接位置へ光レベルを設定していく処理です。ただし、拡散先の位置により大きな光レベルが設定されている場合は、既により強い光で照らされているため拡散を止めます。

第 3 フェーズ
第 2 フェーズまでで、チャンクの内部で閉じた光レベルの情報が構築されるので、次に互いに隣接するチャンク同士の光の拡散を処理します。これには、あるチャンクについて、隣接チャンクの外周の光レベルを参照して自チャンクの外周の光レベルを更新し、ここから再度、光の拡散を自チャンク内部で行います。これにより、チャンクの光レベルの設定が全て完了するため、メッシュの更新を要求し、メッシュの頂点に光レベルに基づいた陰影情報を書き込むようにします。
この部分は Terasology とは異なる実装にしていると言うか、Terasology のロジックを理解できなかったので、極普通の思考のまま実装しています。もしかすると、演算回数を減らす工夫が Terasology にはあるのかもしれません。

スクリーン ショット
天空光を処理することで、以下のスクリーン ショットのように、洞窟の雰囲気を出せるようになります。

明るくなっている部分は洞窟の入り口であり、そこから奥へ向かうにつれ光が届かなくなり、ブロックは黒くなります。
実際には、環境光遮蔽 (ambient occlustion) を別途シミュレートしているため、ブロック同士で遮蔽する部分は、更に黒くなっています。
以下のスクリーン ショットのように、強引に地形の内部へカメラを置けば、完全に閉じている洞窟などでは、完全に影で覆われてしまう状態であったりします。

とまぁ、こんな事をしていました。ただ、非同期処理とは言え、随分と CPU が苦しそうにしているので、もう少しコードを改善しなければならないと感じています。

2013/02/03

XNA 開発終了だそうで

XNA の開発が 2014/4/1 で終了との記事を見ました。
XNA Today: XNAの開発が2014年4月1日で終了
http://www.xna-today.jpn.org/archives/5359
ここ数ヶ月、XNA の利用に疑問を抱いていたので、終了報告は良いタイミングかもしれないなどと考えている所があります。

自分が XNA を利用し始めた理由は、概ね以下のような感じです。
  • 偶然 XNA の記事を目にした。
  • Java 以外の言語で存分に実装してみたかった。
  • XNA は機能が充実していない (より深く学ぶためのキッカケに適している)。

しかし、最後の「機能が充実していない」という点が、「ゲームを作りたい!」という人には大きなデメリットであっただろうと思います。僕は、これをキッカケに色々とネットで調べ、解釈し、実装するという学習の過程を楽しめていますが、ゲームの完成を目的とする人には、この過程の大半がストレスであると想像します。

まぁ、日本では、PC ゲームも Xbox 360 ゲームも盛んではなく、その時点で厳しかったでしょう。現状、Windows Phone は論外ですし。

当面、XNA で今の実装を続けるのですが、並行して次のプラットフォームを考えています。久しぶりに C++ にしてみる、あるいは、細かい事に頭を使わずに実装できる Java にしてみる、などなど。

2013/01/28

ブロック地形進捗 #2

最大頂点数をとるチャンクを空間全体に敷き詰める前提を立てていましたが、「それは流石にハードルが高すぎる」と考えるに至り、頂点数がそこまで膨大とならないようにノイズを調整し、その他実装を修正していました。

16^3 チャンクで可視範囲を 20 チャンク (20 * 16 = 320 ブロック) としたキャプチャが下図です。

他のオブジェクトの配置を考えると、16 チャンク程度で済ませる気がしないでもありません。

なお、地形の背面に存在する洞窟の状態をキャプチャしたものは下図となります。

視点から見えるチャンクの状態に依存しますが、概ね、描画の負荷が多大な状態です。原因の一つは、描画対象とするチャンクの探索にかかる負荷です。チャンクを含め、シーンに描画するオブジェクトは八分木を用いて管理していますが、満足できる結果ではないと感じています。

これから、地形だけではなく、木を表現するブロックをチャンクへ配置しようと思います。木の配置まで終えたら、節目として動画を上げてみようと思います・・・たぶん。まぁそろそろ、積んだまま放置している Borderlands 2 をやりたいな、と。

2013/01/17

ブロック地形実装進捗

「心が折れそうだ」

煮詰まってきたため、ブログを書いて気持ちを誤魔化すことにしました。納得できるパフォーマンスを出せていないので、まだ動画公開はしませんが、下図が現時点のスクリーン ショットです。

 

僕の GitHub ページでコードは公開されていますが、動画公開まではタグを貼りません。そもそも、自分の環境でもメモリ不足によりアプリケーションが落ちる状態です。

今は、チャンク サイズ 16x16x16 で作成しています。チャンクは、上下左右に int とディスク スペースが許す限り無限に配置できる設計です。
チャンク
立方体ブロックを一塊としてメッシュとする単位。Minecraft では 16x256x16 や 16x128x16 なども用いる模様。
追記
今はチャンク サイズ 32x32x32 としています(チャンクのメッシュは 16x16x16 へ分割した状態)。チャンクの粒度を細かくするとチャンク総数が増えることから、チャンク探索処理に大きな負荷がかかるため、大きなサイズとしました。メッシュはインデックス バッファの制約から 16x16x16 (ushort で収まる範囲) が妥当かと思います。Minecraft 形式の場合ならば、高さ固定から二次元でのチャンク探索となり、比較すると遥かに高速かと思います。
地形は、ノイズを用いて自動生成しています。過去の一般的な地形生成ではノイズをハイトマップ生成に利用しましたが、今回はハイトマップを更に密度へ変換して利用しています。
密度を用いる利点は、密度生成を工夫することで、内部に洞窟を自動生成させたり、ハングしている崖などの、ハイトマップのみでは表現できない地形データを表現できる事。Minecraft でも密度を利用しているとの話。スクリーン ショット上で見える地形の内部には洞窟が存在。
で、満足できていない点ですが、チャンクをあまり多くロードするとメモリ不足に陥る所です (頂点バッファを確保できなくなる)。特に、洞窟を含むチャンクでは頂点数が一気に跳ね上がり、それに応じてロードできるチャンク数が減ります。

今は、可視範囲で 10 チャンク距離 (10 * 16 = 160 ブロック距離)、予備範囲で 12 チャンク距離 (12 * 16 = 192 ブロック距離) をロードしています。やはり、より遠方まで地形を表示したいわけで、せめて可視範囲で 12 チャンク距離程度はロードしたい所です。
Minecraft では 200 ブロック距離までも描画可能との話 (これを知ると、まだまだやれる事があると考えざるを得ません)。
理屈としては、恐らく、チャンク内で交互にブロックが配置 (立方体の角のみ隣接する配置) される構成が、チャンク頂点数を最大とする構成であろうと思います。そこで、この最大頂点数のチャンクのみをロードする前提で考えている所です。
最大頂点数のチャンクは、サイズ 16x16x16 において、頂点数 36864、インデックス数 36864。
チャンクの外部から不可視となる閉塞空間を検出し、閉塞空間に隣接する面を刈り取るなどしてみましたが、最大頂点構成では閉塞空間がなく、あえなく敗れ去った所です。とは言え、閉塞空間を含むチャンクでは大幅に頂点を削減できています。

そこで、今は、カメラとチャンクの位置関係から背面を除去しようとしている所です。チャンクのメッシュは実行中に非同期に更新しているため、カメラとチャンクの位置関係が変化した場合にメッシュ再構築を要求し、背面を刈り取るだけですが、現実装では即座にメッシュ再構築が完了するわけではないため、どうアプローチしようかと悩んでいる所です。

2012/11/28

近況

先週より、ようやく実装中心の生活に戻った所です。『とびだせ どうぶつの森』をわずかにプレイしながら。どうぶつの森は、自分でプレイ方針を決めるスタイルのゲームであるため、本当に面白いですね。

DQX については、先の近況報告の時点で利用券購入を止めていましたが、その後、『デモンズソウル』のトロコン、および、『ダークソウル』のトロコンと DLC 攻略に狂っていました。デモンズソウルでは、トロコン以外にも縛りプレイなどを続けていました。
僕は、デモンズソウルやダークソウルの動画と生放送をニコニコ動画で頻繁に視聴しているのですが、観ているとどうしても自分でやりたいという衝動にかられ、その欲望のままに再プレイを始めていました。

最近は、ほぼ毎日のように、夜はダークソウル RTA に挑戦する某氏二人 (落ち着いた口調の方と煽り口調の方) の生放送を視聴しています。正直、僕は、ダークソウルはデモンズソウルと比べると、その面白さがはるかに劣る (比較にすらならない) と感じていますが、RTA の視聴となると、タイムを縮めるための実況者の試行錯誤を垣間見ることができ、非常に面白いです。

実装の近況ですが、色々と悩んだ挙句、今は Minecraft クローンに近いものを実装しています。

僕は、Minecraft のようなゲームを作りたいわけではないですが (そもそもゲームを作りたいわけでもないですが)、ブロックで表現される世界のエディットを考えると、それは Minecraft でブロックを生成および消滅させることと同じであり、ならば Minecraft のような設計がベストであろうと考えるに至りました。

今月中にデータ定義と描画までは終わらせたかったのですが、エディタを考慮したオブジェクトの扱いや、Web 上でのデータの公開を同時に検討していると、一筋縄ではいかず、年内に終われば良いかなと考えている所です。できれば、数年前に上げた動画のように、物理エンジンへの統合とエフェクトの実装までは行いたいですが、あわてず進めていこうと思っています。

libgdx いじり

Google が提供している Java 版の Tango Examples は Rajawali をベースにしているため、自分が仕事で開発する Tango アプリも Rajawali ベースとしていましたが、最近は libGDX への移行を進めています。一応、要点については移行が...