morphing animation glTF
morphing
glTFのモーフィングアニメーションを読み込み、androidで描画することができた。
モデルの権利表記
https://sketchfab.com/3d-models/yard-grass-3a67e76decc849c694c228eb590a9902
ライセンス:CC Atribution
https://creativecommons.org/licenses/by/4.0/legalcode
苦労した点
なぜか、compute shader内のバッファーで、vec3で読み取ろうとすると正確な値がとってこれなかった。
バッファーがfloatであるとして読み取るとうまくいったが、なぜなのだろう。
この手のような、用意しているデータや計算方法は間違ってないけど、glslの構造上エラーとなるみたいな場合、調査が難航する。
glTFでmorphing (alpha処理後)
glTFモデルのmorphing
前回の記事
aqoole-hateena.hatenablog.com
からalphaがglTFファイルにあるalpha cutoffよりも小さい場合に、描画しない処理を追加したものがこちら。
モデルの権利表記
https://sketchfab.com/3d-models/yard-grass-3a67e76decc849c694c228eb590a9902
ライセンス:CC Atribution
https://creativecommons.org/licenses/by/4.0/legalcode
glTFでmorphing (マテリアル処理なし)
Morphing
morphingのアニメーションに挑戦してみた。
それっぽくはなっているが、、、合ってるのか?
モデル
https://sketchfab.com/3d-models/yard-grass-3a67e76decc849c694c228eb590a9902
ライセンス:CC Atribution
https://creativecommons.org/licenses/by/4.0/legalcode
glTFでのアニメーション
glTFでアニメーション
colladaに引き続き、glTFでもアニメーションを行うことができた。
モデルの権利表記
配布元リポジトリ
https://github.com/TheThinMatrix/OpenGL-AnimationUnlicense license
https://unlicense.org/
glTFでanimationを行うときの注意点
nodeとjointとの関係のようなものは別の機会にまとめたいと思うが、注意点としては以下のようなものがある。
- inverse bind matrixはjointの順番に並んでいる
- animationのtargetはnodeの番号
- inverse bind matrixは親nodeからの継承も含めて計算されている
- rotationのQuaternionからの変換はwikiにある変換を用いればよい
Rotation formalisms in three dimensions - Wikipedia
colladaでanimationを行う際には
[Animation] colladaファイルの読み取りからAnimationを行うまで - Qiita
でまとめた通り
OBJECT SPACE | -> | BONE SPACE | -> | OBJECT SPACE |
geometry | b | p |
となるが、glTFでは
OBJECT SPACE | -> | BONE SPACE | -> | OBJECT SPACE |
geometry | b | p |
となる。
glTFでのアニメーション
glTFでアニメーション
colladaに引き続き、glTFでもアニメーションを行うことができた。
モデルの権利表記
配布元リポジトリ
https://github.com/TheThinMatrix/OpenGL-AnimationUnlicense license
https://unlicense.org/
glTFでanimationを行うときの注意点
nodeとjointとの関係のようなものは別の機会にまとめたいと思うが、注意点としては以下のようなものがある。
- inverse bind matrixはjointの順番に並んでいる
- animationのtargetはnodeの番号
- inverse bind matrixは親nodeからの継承も含めて計算されている
- rotationのQuaternionからの変換はwikiにある変換を用いればよい
Rotation formalisms in three dimensions - Wikipedia
colladaでanimationを行う際には
[Animation] colladaファイルの読み取りからAnimationを行うまで - Qiita
でまとめた通り
OBJECT SPACE | -> | BONE SPACE | -> | OBJECT SPACE |
geometry | b | p |
となるが、glTFでは
OBJECT SPACE | -> | BONE SPACE | -> | OBJECT SPACE |
geometry | b | p |
となる。
glTFでのモデルの描画
glTFでモデルの描画
joint情報を読み込み、静止モデルの描画をしてみた。
モデルの権利表記
配布元リポジトリ
https://github.com/TheThinMatrix/OpenGL-AnimationUnlicense license
https://unlicense.org/
glTFフォーマットの読み込み方
tiny glTFを用いてglTFのモデルを読み込んでいる。ライセンスはMITライセンス。
https://github.com/syoyo/tinygltf
使い方は以下のgit hubにあるコードを参考にした。ライセンスはApache 2.0。
https://github.com/techlabxe/vk_raytracing_book_1/blob/master/Common/include/util/VkrModel.h
https://github.com/techlabxe/vk_raytracing_book_1/tree/master/Common/src/util
glTFの構造については仕様書にまとめられている。
glTF™ 2.0 Specification
アニメーションの読み込みと描画に成功すれば、情報を改めてまとめ直したいと思っているが、とりあえずコードは以下のようなもの。
using namespace tinygltf; std::vector<glm::uvec4> tmpJoint; std::vector<glm::vec4> tmpWeight; const uint8_t* jointSrc; const glm::vec4* weightSrc; for(auto& primitive : model.meshes[0].primitives){ //each primitive Geometry geo = {}; for(auto& attr : primitive.attributes) { std::string attName = attr.first; //positions if (std::regex_search(attName, std::regex("position", std::regex::icase))) { const auto &posAccr = model.accessors[attr.second]; const auto &posBufView = model.bufferViews[posAccr.bufferView]; size_t offsetByte = posAccr.byteOffset + posBufView.byteOffset; const auto *src = reinterpret_cast<const glm::vec3 *>(&(model.buffers[posBufView.buffer].data[offsetByte])); size_t vertexSize = posAccr.count; for (uint32_t i = 0; i < vertexSize; i++) { geo.positions.emplace_back(src[i]); } //indices const auto &indexAccr = model.accessors[primitive.indices]; const auto &indexBufView = model.bufferViews[indexAccr.bufferView]; size_t indexOffsetByte = indexAccr.byteOffset + indexBufView.byteOffset; const auto *indexSrc = reinterpret_cast<const uint16_t *>(&(model.buffers[indexBufView.buffer].data[indexOffsetByte])); for (uint32_t i = 0; i < indexAccr.count; i++) geo.indices.emplace_back((uint32_t) indexSrc[i]); } //texture coord if (std::regex_search(attName, std::regex("texcoord", std::regex::icase))) { const auto &tcAccr = model.accessors[attr.second]; const auto &tcBufView = model.bufferViews[tcAccr.bufferView]; size_t offsetByte = tcAccr.byteOffset + tcBufView.byteOffset; const auto *tcSrc = reinterpret_cast<const glm::vec2 *>(&model.buffers[tcBufView.buffer].data[offsetByte]); for (uint32_t i = 0; i < tcAccr.count; i++) geo.texCoords.emplace_back(tcSrc[i]); } //joint information if(std::regex_search(attName, std::regex("joint", std::regex::icase))){ const auto& jointAccr = model.accessors[attr.second]; const auto& jointBufView = model.bufferViews[jointAccr.bufferView]; size_t offsetByte = jointAccr.byteOffset + jointBufView.byteOffset; //in case of acc.componentType = TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE jointSrc = reinterpret_cast<const uint8_t*>(&model.buffers[jointBufView.buffer].data[offsetByte]); for(uint32_t i = 0; i < jointAccr.count; i++){ uint32_t index = i * 4; glm::uvec4 u(jointSrc[index], jointSrc[index + 1], jointSrc[index + 2], jointSrc[index + 3]); tmpJoint.emplace_back(u); } } //weight information if(std::regex_search(attName, std::regex("weight", std::regex::icase))){ const auto& weightAccr = model.accessors[attr.second]; const auto& weightBufView = model.bufferViews[weightAccr.bufferView]; size_t offsetByte = weightAccr.byteOffset + weightBufView.byteOffset; weightSrc = reinterpret_cast<const glm::vec4*>(&model.buffers[weightBufView.buffer].data[offsetByte]); for(uint32_t i = 0; i < weightAccr.count; i++) tmpWeight.emplace_back(weightSrc[i]); } } mGeometries.emplace_back(geo); }
Animationの完成
Animationの実装
今までの実装では時間の取得の仕方が間違っていた。
時間の表示を正常にするとコマ飛びすることなくアニメーションが表示された。
時間の取得方法
実装当初に色々調べた際に、あまり深く調べることなく以下のstack overflowのanswerをそのまま流用していた。
How to get the current time in native Android code? - Stack Overflow
#include
// from android samples
/* return current time in milliseconds */
static double now_ms(void) {struct timespec res;
clock_gettime(CLOCK_REALTIME, &res);
return 1000.0 * res.tv_sec + (double) res.tv_nsec / 1e6;}
しかしコメント文にあるように、このコードではミリセカンドの単位で時間が取得できる。
アニメーションを行うときにはセカンドの単位で表示していたので、1000倍の速さについていけずコマ飛びしていた。
timespec構造体
仕様は以下に書かれてある。
cpprefjp.github.io
tv_sec エポックからの経過秒。値は0以上 tv_nsec ナノ秒単位で表される秒未満の値 値の範囲は[0, 999'999'999]
tv_nsecはナノセカンドの単位で整数値で取得されるので、を掛ける必要がある。
つまり clock_gettime を実行したときの時刻tは
double t = res.tv_sec + res.tv_nsec / 1e9;
で取得できる。