Qt Quick 3D - シンプルなスキニングの例
Qt Quick 3Dで簡単なスキニングアニメーションをレンダリングする方法を説明します。
一般的に、ほとんどのスキンアニメーションはモデリングツールでデザインされますが、Quick3DはBalsamインポーターと Qt Design Studioを通してglTFフォーマットもサポートしています。この例では、Quick3Dのスキンアニメーションで各プロパティがどのように使用されるかを示します。
注意: この例のデータはすべてgfTF-Tutorial Skinsから来ています。
スキニングジオメトリを作成します。
カスタムジオメトリデータを使用するために、ポジション、ジョイント、ウェイト、インデックスを持つジオメトリを定義します。
Q_OBJECT QML_NAMED_ELEMENT(SkinGeometry) Q_PROPERTY(QList<QVector3D> positions READ positions WRITE setPositions NOTIFY positionsChanged) Q_PROPERTY(QList<qint32> joints READ joints WRITE setJoints NOTIFY jointsChanged) Q_PROPERTY(QList<float> weights READ weights WRITE setWeights NOTIFY weightsChanged) Q_PROPERTY(QList<quint32> indexes READ indexes WRITE setIndexes NOTIFY indexesChanged)
各ポジションは頂点の位置で、各頂点は4つのジョイントのインデックスと対応するウェイトを持っています。
QMLでスキンデータを設定する
位置データとインデックス
10個の頂点で8個の三角形を描きます。下の表はQMLコードと頂点の視覚化です。
QMLコード | 可視化 |
---|---|
positions: [ Qt.vector3d(0.0, 0.0, 0.0), // vertex 0 Qt.vector3d(1.0, 0.0, 0.0), // vertex 1 Qt.vector3d(0.0, 0.5, 0.0), // vertex 2 Qt.vector3d(1.0, 0.5, 0.0), // vertex 3 Qt.vector3d(0.0, 1.0, 0.0), // vertex 4 Qt.vector3d(1.0, 1.0, 0.0), // vertex 5 Qt.vector3d(0.0, 1.5, 0.0), // vertex 6 Qt.vector3d(1.0, 1.5, 0.0), // vertex 7 Qt.vector3d(0.0, 2.0, 0.0), // vertex 8 Qt.vector3d(1.0, 2.0, 0.0) // vertex 9 ] indexes: [ 0, 1, 3, // triangle 0 0, 3, 2, // triangle 1 2, 3, 5, // triangle 2 2, 5, 4, // triangle 3 4, 5, 7, // triangle 4 4, 7, 6, // triangle 5 6, 7, 9, // triangle 6 6, 9, 8 // triangle 7 ] |
関節と重みのデータ
各頂点には、スキニング処理中に影響を与えるジョイントのインデックスを指定する必要があります。各頂点について、これらのインデックスを4Dベクトルとして保存します(Qtは頂点に影響を与えるジョイントの数を4つに制限しています)。私たちのジオメトリにはジョイントノードが2つ(0と1)しかありませんが、4Dベクトルを使用するため、残りの2つのジョイントインデックスとその重みを0に設定します。
joints: [ 0, 1, 0, 0, // vertex 0 0, 1, 0, 0, // vertex 1 0, 1, 0, 0, // vertex 2 0, 1, 0, 0, // vertex 3 0, 1, 0, 0, // vertex 4 0, 1, 0, 0, // vertex 5 0, 1, 0, 0, // vertex 6 0, 1, 0, 0, // vertex 7 0, 1, 0, 0, // vertex 8 0, 1, 0, 0 // vertex 9 ]
対応するウェイト値は以下のとおりです。
weights: [ 1.00, 0.00, 0.0, 0.0, // vertex 0 1.00, 0.00, 0.0, 0.0, // vertex 1 0.75, 0.25, 0.0, 0.0, // vertex 2 0.75, 0.25, 0.0, 0.0, // vertex 3 0.50, 0.50, 0.0, 0.0, // vertex 4 0.50, 0.50, 0.0, 0.0, // vertex 5 0.25, 0.75, 0.0, 0.0, // vertex 6 0.25, 0.75, 0.0, 0.0, // vertex 7 0.00, 1.00, 0.0, 0.0, // vertex 8 0.00, 1.00, 0.0, 0.0 // vertex 9 ]
スケルトンとジョイントの階層
スキニングのために、Model にスケルトンプロパティを追加します:
skeleton: qmlskeleton Skeleton { id: qmlskeleton Joint { id: joint0 index: 0 skeletonRoot: qmlskeleton Joint { id: joint1 index: 1 skeletonRoot: qmlskeleton eulerRotation.z: 45 } } }
つのJointはSkeleton でつながっています。joint1
をz軸を中心に45度回転させます。以下の画像は、ジョイントがジオメトリにどのように配置され、初期スケルトンがどのように配向されているかを示しています。
ジオメトリ内のジョイント | 初期スケルトン |
---|---|
inverseBindPosesを使ったモデルの配置
モデルが有効なskeleton を持ったら、スケルトンの初期ポーズを定義する必要があります。これはスケルタルアニメーションのベースラインを定義します。ジョイントを初期位置から動かすと、joints
とweights
テーブルに従ってモデルの頂点が動きます。各ノードのジオメトリは特殊な方法で指定されます。Model.inverseBindPoses は、ジョイントを初期位置に変換するマトリックスの逆数に設定されます。中央に移動させるには、単純に両方のジョイントに同じ変換を設定します:X軸に沿って-0.5、Y軸に沿って-1.0変換する行列です。
QMLコード | 初期位置 | 結果 |
---|---|---|
inverseBindPoses: [ Qt.matrix4x4(1, 0, 0, -0.5, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1), Qt.matrix4x4(1, 0, 0, -0.5, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1) ] |
ジョイントノードを使ったアニメーション
スキニングされたオブジェクトを準備したので、Jointのプロパティ、特にeulerRotation を変更してアニメーションさせることができます。
Timeline { id: timeline0 startFrame: 0 endFrame: 1000 currentFrame: 0 enabled: true animations: [ TimelineAnimation { duration: 5000 from: 0 to: 1000 running: true } ] KeyframeGroup { target: joint1 property: "eulerRotation.z" Keyframe { frame: 0 value: 0 } Keyframe { frame: 250 value: 90 } Keyframe { frame: 750 value: -90 } Keyframe { frame: 1000 value: 0 } } }
スキニングへの、より完全なアプローチ
スケルトンはリソースですが、その階層と位置はModelのトランスフォームに使用されます。
Skeleton ノードの代わりに、リソースタイプSkin を使うことができます。Skin タイプはシーン内の空間ノードではないので、その位置はモデルに影響しません。最小限の作業用Skinノードは通常、ノードリスト、ジョイント、オプションの逆バインドマトリックスinverseBindPosesで構成されます。
Skin 項目を使用すると、前述の例は次のように記述できます:
skin: Skin { id: skin0 joints: [ joint0, joint1 ] inverseBindPoses: [ Qt.matrix4x4(1, 0, 0, -0.5, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1), Qt.matrix4x4(1, 0, 0, -0.5, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1) ] }
コード・スニペットから、Skin は joints と inverseBindPoses の2つのリストしか持っていないことがわかります。Skeleton のアプローチとは異なり、階層構造を持たず、既存のノードの階層構造を利用するだけです。
©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。