2014年09月25日
Metalでボーンアニメーション!!
3Dアプリの開発にはまずモデルの読み込みが必要となります。
Metalにはレンダリング機能はあるものの、ローダは備わっていません。
そこで今回はボーンアニメーションに対応したモデルローダ作成のノウハウ(特にMetalの特性にどう対応したか)を紹介したいと思います。
ソースコードはこちらです: http://log.blog.klab.org/support/20140925/Blacksmith-bone_animation_sample.zip
メインとなるファイルはBSModelとBSRendererです。
BSModelはモデルデータの管理やボーン計算を担当し、BSRendererはBSModelから頂点データやテクスチャ、ボーンマトリックス等を受け取ってレンダリングを行います。
まずはじめにモデルファイルのパースが必要となります。
3Dモデルのファイルフォーマットは世の中にいくつも存在するので、それら全ての仕様を把握して対応するのは大変です。
ですが、代表的な複数のフォーマットに対応したAssimpというオープンソースのライブラリがあります。
http://assimp.sourceforge.net/
Assimpは様々なフォーマットのファイルを読み込んで統一の形式に変換してくれるので、BSModelの中でパーサとしてこのライブラリを使わせていただきました。
Assimpの開発に関わっている方々に感謝いたします。
さて、ボーンアニメーションを再生するための頂点の位置計算にはいくつか方法があります。
1)ボーンマトリックスをパラメータとして渡して頂点シェーダで計算する
2)レンダリングパスを分けながら頂点シェーダで計算する
3)頂点テクスチャにボーンマトリックスを格納して頂点シェーダで計算する
4)CPUで計算する
まずはOpenGL(ES)を使う場合を考えてみましょう。
GPUを利用してハードウェアアクセラレーションを効かせられるのが1〜3の方法です。
もし1の方法で問題なく実行できる環境にあればそれがベストです。
しかし、OpenGL(ES)にはシェーダに渡せるパラメータの大きさに制限があり、ボーンマトリックスの数がそれを超えてしまうと正しく計算できません。
そこでそのような場合には2の方法を取りますが、レンダリングパスが増えてしまうのでパフォーマンスは落ちます。
3の方法はボーンマトリックスの情報をテクスチャに格納してシェーダに渡すというテクニカルな方法です。
テクスチャは先程のパラメータの大きさ制限を受けることはないので、大量のデータを格納してシェーダに渡すことが出来ます。
ただし、頂点シェーダでテクスチャをフェッチできるようになったのはOpenGLESでは3.0からなので、古い環境をサポートしなければならない場合は3の方法を取ることは出来ません。
4は環境に依存することのない方法で、ボーンが何本あろうとも有効な方法ですが、ハードウェアアクセラレーションが効かないので、頂点数に比例して処理時間がかかります。
VBOを使わない方法なので毎フレームメインメモリからの転送が必要で、その分のコストもかかってしまいます。
一方、Metalではシェーダに渡せるデータ容量の制限が大幅に緩和されています。
ですのでボーンアニメーションを実装する場合には特に気にすることなく1の方法を採用出来、実装が非常に簡単になります。
これはOpenGL(ES)と比べ、大きな強みと言えるでしょう。
シェーダはGPUProgram.metalに書かれており、vertexShaderWithBoneAnimation(…)がボーンマトリックスを元に頂点の位置計算を行う頂点シェーダです。
頂点の位置を計算する仕組みは整ったので、後はボーンマトリックスを準備しなくてはいけません。
ボーンマトリックスの計算自体はOpenGL使用時と特に変わりありませんが、Metal使用時のポイントが一つあります。
それはボーンマトリックスのバッファ(CPU/GPU shared memory buffer)を複数用意するということです。
CPUとGPUは非同期で動いています。
CPUでレンダリングコマンドを作成してコマンドキューにプッシュした後は、GPUが処理を終えて終了通知のコールバックが返ってくるまではGPUがボーンマトリックスのバッファにアクセスしています。
その間にCPUからボーンマトリックスのバッファに対して書き込みを行うと不整合が起きます。
しかしながらレンダリングコマンドを投げた後にGPUが非同期で働いている間、CPUに何もさせずに待っているのももったいない話です。
ですのでボーンマトリックスのバッファを複数用意しておき、GPUがある一つのバッファにアクセスしている間、その他のバッファに対してCPUで計算した結果を書き込むようにします。
こうすることでCPUとGPUが非同期で動いていることを活かしたパフォーマンスの引き出し方が可能になります。
以上で紹介したように、MetalではOpenGLよりも簡単にボーンアニメーションに対応することが可能で、よりパフォーマンスを上げる仕組みを採用することが出来ます。
OpenGL(ES)に対応した従来のミドルウェアは上記1〜4の方法をハードウェアスペックとボーンの数により切り替えることによってボーンアニメーションを実現していると私は予想しています。
全体的なパフォーマンスアップに埋もれて気づき難いかもしれませんが、ミドルウェアがMetalへの対応を行えばボーンアニメーションのパフォーマンスが向上することが予想されます。
ミドルウェアを使用する側もボーンの数を気にせずにモデリングできる様になるかと思います。
これからはiOSのゲームにヤマタノオロチの様な沢山のボーンを持ったキャラクターがガンガン登場するようになるかもしれません。
Metalにはレンダリング機能はあるものの、ローダは備わっていません。
そこで今回はボーンアニメーションに対応したモデルローダ作成のノウハウ(特にMetalの特性にどう対応したか)を紹介したいと思います。
ソースコードはこちらです: http://log.blog.klab.org/support/20140925/Blacksmith-bone_animation_sample.zip
メインとなるファイルはBSModelとBSRendererです。
BSModelはモデルデータの管理やボーン計算を担当し、BSRendererはBSModelから頂点データやテクスチャ、ボーンマトリックス等を受け取ってレンダリングを行います。
まずはじめにモデルファイルのパースが必要となります。
3Dモデルのファイルフォーマットは世の中にいくつも存在するので、それら全ての仕様を把握して対応するのは大変です。
ですが、代表的な複数のフォーマットに対応したAssimpというオープンソースのライブラリがあります。
http://assimp.sourceforge.net/
Assimpは様々なフォーマットのファイルを読み込んで統一の形式に変換してくれるので、BSModelの中でパーサとしてこのライブラリを使わせていただきました。
Assimpの開発に関わっている方々に感謝いたします。
さて、ボーンアニメーションを再生するための頂点の位置計算にはいくつか方法があります。
1)ボーンマトリックスをパラメータとして渡して頂点シェーダで計算する
2)レンダリングパスを分けながら頂点シェーダで計算する
3)頂点テクスチャにボーンマトリックスを格納して頂点シェーダで計算する
4)CPUで計算する
まずはOpenGL(ES)を使う場合を考えてみましょう。
GPUを利用してハードウェアアクセラレーションを効かせられるのが1〜3の方法です。
もし1の方法で問題なく実行できる環境にあればそれがベストです。
しかし、OpenGL(ES)にはシェーダに渡せるパラメータの大きさに制限があり、ボーンマトリックスの数がそれを超えてしまうと正しく計算できません。
そこでそのような場合には2の方法を取りますが、レンダリングパスが増えてしまうのでパフォーマンスは落ちます。
3の方法はボーンマトリックスの情報をテクスチャに格納してシェーダに渡すというテクニカルな方法です。
テクスチャは先程のパラメータの大きさ制限を受けることはないので、大量のデータを格納してシェーダに渡すことが出来ます。
ただし、頂点シェーダでテクスチャをフェッチできるようになったのはOpenGLESでは3.0からなので、古い環境をサポートしなければならない場合は3の方法を取ることは出来ません。
4は環境に依存することのない方法で、ボーンが何本あろうとも有効な方法ですが、ハードウェアアクセラレーションが効かないので、頂点数に比例して処理時間がかかります。
VBOを使わない方法なので毎フレームメインメモリからの転送が必要で、その分のコストもかかってしまいます。
一方、Metalではシェーダに渡せるデータ容量の制限が大幅に緩和されています。
ですのでボーンアニメーションを実装する場合には特に気にすることなく1の方法を採用出来、実装が非常に簡単になります。
これはOpenGL(ES)と比べ、大きな強みと言えるでしょう。
シェーダはGPUProgram.metalに書かれており、vertexShaderWithBoneAnimation(…)がボーンマトリックスを元に頂点の位置計算を行う頂点シェーダです。
頂点の位置を計算する仕組みは整ったので、後はボーンマトリックスを準備しなくてはいけません。
ボーンマトリックスの計算自体はOpenGL使用時と特に変わりありませんが、Metal使用時のポイントが一つあります。
それはボーンマトリックスのバッファ(CPU/GPU shared memory buffer)を複数用意するということです。
CPUとGPUは非同期で動いています。
CPUでレンダリングコマンドを作成してコマンドキューにプッシュした後は、GPUが処理を終えて終了通知のコールバックが返ってくるまではGPUがボーンマトリックスのバッファにアクセスしています。
その間にCPUからボーンマトリックスのバッファに対して書き込みを行うと不整合が起きます。
しかしながらレンダリングコマンドを投げた後にGPUが非同期で働いている間、CPUに何もさせずに待っているのももったいない話です。
ですのでボーンマトリックスのバッファを複数用意しておき、GPUがある一つのバッファにアクセスしている間、その他のバッファに対してCPUで計算した結果を書き込むようにします。
こうすることでCPUとGPUが非同期で動いていることを活かしたパフォーマンスの引き出し方が可能になります。
以上で紹介したように、MetalではOpenGLよりも簡単にボーンアニメーションに対応することが可能で、よりパフォーマンスを上げる仕組みを採用することが出来ます。
OpenGL(ES)に対応した従来のミドルウェアは上記1〜4の方法をハードウェアスペックとボーンの数により切り替えることによってボーンアニメーションを実現していると私は予想しています。
全体的なパフォーマンスアップに埋もれて気づき難いかもしれませんが、ミドルウェアがMetalへの対応を行えばボーンアニメーションのパフォーマンスが向上することが予想されます。
ミドルウェアを使用する側もボーンの数を気にせずにモデリングできる様になるかと思います。
これからはiOSのゲームにヤマタノオロチの様な沢山のボーンを持ったキャラクターがガンガン登場するようになるかもしれません。