Vulkanでモデル読み込みに成功!やった~~~~!
glTF形式の読み込み処理はかなり面倒というか分かりにくく、データをどう持つかといった部分を考えるのも含めて結構時間を費やしてしまったのだが、書き終えてみるとかなりあっさり表示出来てしまってびびった。ほぼ一発完動。うれしい!!!
頂点バッファの読み込み方に手こずった。「座標」「テクスチャ座標」「法線ベクトル」といったアトリビュートが各頂点ごとにあるのだが、その並び方というのが各属性ごとに固まっているのである。「座標,テクスチャ座標,法線ベクトル,座標,テクスチャ座標,法線ベクトル,…」という形ではなく「座標座標座標…」「テクスチャ座標テクスチャ座標テクスチャ座標…」「法線ベクトル法線ベクトル法線ベクトル…」という並びなのだ。
Vulkanはこういう並びのデータでも問題なく読み込める。それぞれ別個のbindingとして扱えばよいだけなのだ。パイプラインにおいてVertexInputBindingDescriptionを複数用意し、複数のbindingがあることを宣言する。そしてBindVertexBuffersにおいて複数のバッファを指定すれば、それぞれが別々のbindingに接続されるのだ。
自分は少し勘違いをしていたのだが、頂点入力bindingが複数個に増えたからといって頂点シェーダの入力変数のlayoutに「binding=2」みたいなのを書かなければいけない訳ではない。bindingとlocationというまったくの別軸があって、シェーダのコードではlocationさえ指定すれば情報が手に入る。VertexInputBindingDescriptionによって「こういうbindingが存在する」という構造を定義し、VertexInputAttributeDescriptionによって「このlocationはこのbindingのこの部分に対応する」という構造を定義するようになっているのだ。location-bindingの対応付けはパイプライン定義内でやっていくようになっている。分かればそう難しいことではないのだが引っかかってしまった。
glTFのモデルの部品は階層構造になっている。ツリー構造を再帰的に描画していくような方法にはパフォーマンス的にあまりうま味が無さそうなので、とりあえず全てのメッシュプリミティブをマテリアルごとに並べてしまうという実装をした。たぶんボーン変形を実装しようとするとツリー構造にもちゃんと意味が出てくるので単純にこうはいかないのではないかという気はする。
雑に100体くらい並べてみたところ、さすがに処理速度が厳しそうな感じになった。10msかかっているので90fpsはギリ。まあただ最適化の余地は多分にあるので、GPU最適化の類の勉強をして色々改良すればもっと速くなるとは思う。
- MVP行列をCPUでちびちび計算してPushConstantで入れてる
→カメラとモデルの姿勢・座標をUniform BufferやStorage BufferとしてGPUに送ってGPU上で計算させる - 1つのマテリアルを描画するごとにDescriptorSetをバインドし直してる
→複数枚あるテクスチャ全部Sampler2DArrayとしてまとめちゃう、マテリアルの切り替えには頂点バッファなりなんなりに添え字を持たせる - 1つのメッシュを描画するごとに頂点バッファ・インデックスバッファをバインドし直してドローコール投げてる
→バインドし直さなくても全頂点アクセスできるようにデータを整え、DrawIndirectで多数のドローコールをまとめる
今思いつくのはこのあたり。GPU駆動レンダリングという概念があるらしく、とにかくバインドし直すのをやめ、少ないドローコールに全てを押しこめ、何もかもGPUにやらせるのが正義らしい。
https://vkguide.dev/docs/gpudriven/gpu_driven_engines/
そういえばhapticsとReference Space周りの研究がまだだった。OpenXRの基本を学び終わってからVulkanの勉強に行くつもりだったのだが一日Vulkan漬けになってしまった。まあ進捗出たからいいや。
Categories: 未分類