概要
本記事はWebGPU及びwgpuの解説記事ではない。 そして、FPSゲームの制作方法の解説記事でもない。 wgpuを用いてFPSゲームを作ったときの所感や苦労を書き連ねた記事である。
この頃、3DCGを用いそうなゲームの開発環境について悩むことが多かった。 経験したことのあるそれぞれの環境について、次のように考えていた。
- Unreal Engine:綺麗だが重い。ブループリント。GUIベース。ライセンス問題。
- Unity:GUIベース。ライセンス問題。Linuxでは作れないっぽい?
- OpenSiv3D:C++。
- LWJGL:悪くないがなんだかな(Java良い言語なんだけどJVMを挟むからパフォーマンスが……)。
- Direct3D11以前:Windowsのみ対応。他OSで動かすつもりがないなら至高の選択肢。
- Direct3D12:Windowsのみ対応。実装が重い。ならVulkanで良い。
- OpenGL:もう古い。Windowsのサポートが古いはず。
- Vulkan:マルチプラットフォームにするなら至高の選択肢。実装が重い。
- Metal:macOSのみ対応。実装が重いらしい。
- WebGL2:Webブラウザ向けのAPI。それで良いなら(どうせパフォーマンスは求めないし)至高の選択肢。JavaScript系。
WebGPUに対しては次のように考えていた。
- 実装が重い
- Webブラウザしか動かないならWebGL2で良い
- JavaScript系
- 抽象レイヤーを介すから重い
しかし、実際に使ってみて、次のように考えが変わった。 他にも色々思ったことがあるため、以降の章で随時書く。
- wgpuというWebGPU実装をそのまま使える
- スタンドアロンで使える
- 実装が重過ぎない
- マルチプラットフォームなら至高の選択肢
Rust及びwinit所感
そもそもwgpuはRust製であるため、Rustを主要使用言語に選べる。 私はRustが好きであるため、ありがたくRustを使った。 しかし、OOPをサポートしないRustでのゲーム開発には無理があるなと感じた。 次の点である。
- ポリモーフィズムがないのでmatchで静的ディスパッチする必要がある
- 所有権の問題で構造体のメンバを更新して再代入することができない
- オブジェクト指向っぽくモジュールを分けるとファイル構造が汚くなる
ウィンドウを作成するためにwinit 0.30を用いた。 winitは0.29から0.30に変わるときに、かなり大きく仕様を変更した。 しかし、ほぼ殆どすべての参考記事ではwinit 0.29が用いられており、winit 0.30の仕様も手伝ってかなり苦労した。 次の点である。
- 非同期にウィンドウ生成するため初めにウィンドウハンドルを得ることができない
- 非同期にウィンドウ生成するためライフタイム管理が面倒くさい
- 正常に動作しないメソッドがある(WindowAttributes::with_enabled_buttons, WindowAttributes::with_cursor)
WebGPU(wgpu)所感
まず、相変わらずサンプルプログラムが貧弱だなとも思った。 一つのポリゴンしか描かないとか、一つのシェーダしか使わないとか、カメラもポリゴンも動かないとか、そんなんじゃまともなアプリケーションは作れまい。 ただ、Web系の技術であるからか、Vulkanよりは多いようにも感じた。 このまま振興して、より丁寧で実践的な解説が増えてくれるとありがたい。
左手座標系であることに驚いた。 XxYxZが[-1,1]x[-1,1]x[0,1]である。 WindowsでのデフォルトバックエンドがVulkanであるのに、Direct3Dと同じ座標系を採用したのは何のためであろうか。
また、頂点入力バッファにインスタンシングのためのバッファを設けられるようだが、最大float32x4単位で渡さなければならないことに驚いた。 座標変形行列を渡したい場合は4個に分けて渡さなければならない。 メリットがないように感じたたため、ユニフォームバッファとして渡すのが良いだろう。
BindGroupの意義がわからなかった。 どうせすべてのグループのすべてのバインディングに値が入っている必要があるので、すべて同じグループでも良いはずである。 カメラとサンプラを分けるといった、役割の異なるバインディングを異なるグループに分けるコードも見かけたが、メリットがないように感じた。
また、少なくともGPUを積んでいないノートPC(Windows)のバックエンドをVulkanにすると垂直同期が取られなかった。 少なくともUbuntu 24.04のマシン(NVIDIA RTX 3060)では非常に重くなった。
球の作成
素材を使っても良かったが、球体モデルを自作した。 ほぼ殆どこの参考文献から流用したが、次のようなアルゴリズムである。
- 頂点位置
- 緯度方向・経度方向に領域を分割する
- 原点からの角度・距離に従って位置をプロットする
- ただし、UV展開の都合上赤道上の頂点は二重にプロットする
- 法線ベクトルは頂点位置ベクトルの単位ベクトル
- UV座標は北半球・南半球に分割して年輪のようにプロットする
- インデックス
- 北極点・南極点を含むインデックスをよしなに結ぶ
- 上の辺・下の辺それぞれの上の頂点をよしなに結ぶ
- ただし、一周するとインデックスが循環することに注意する
- また、赤道上の頂点は結ばないようにする(UV座標が飛ぶため意図しない色が乗る)
次のように期待通りのモデルが生成できた。
壁の当たり判定
WebGPUとは全く関係ないが、FPSゲームを作るにあたって、壁の当たり判定の実装に苦労した。 次のように解決した。
- 壁(直方体)のx-y平面上の4点の座標を得る
- その4点を外側に1ずつ移動する
- その4点に直方体の回転を適応する
- その4点におけるプレイヤーの座標の内外判定(ベクトルの回転方向を見る)を行う
参考文献
- WebGPU Bind Group best practices
- クォータニオンによるキャラクターの制御メモ
- 【Unity】プログラミングで球メッシュを作る方法
- 2線分の交点座標(2次元)
- ゲームつくろー 衝突判定編 3D衝突編 その16 直線と移動する球の衝突場所と時刻を得る
■