私は WebGL で小さなゲームを書いて、3D ゲーム プログラミングのコア コンセプトのいくつかを理解できるようにしています (既存のエンジンを使用せずに)。このプロジェクトは私が余暇に行っているものにすぎません。必要に応じて、WebGL バージョンはプロトタイプであり、次にネイティブ プログラムが追加されます。
私は現在、キャラクターモデルをアニメーション化しようとしている段階にあり、それをどのように行うべきかを理解しようと何日も苦労しています. 簡単なチュートリアルをオンラインで見つけるのが難しい (または別のプログラミング言語から翻訳することさえ) ことがわかったので、このサイトにアクセスしてガイダンスを求めました。ここで問題を解決できれば、将来、コンセプトについて同様の質問をする他の人にも役立つことを願っています.
私の現在の解決策はこれです(モデルの各ボーンについて):
- ボーンの元の位置に移動
- 新しく要求された位置に回転します
- (現在計算されている) 親ボーン マトリックスによる変換
- 元の位置行列の逆数で変換
これまでのところ、モデルの行列を計算する関連コードは次のとおりです。フレームごとに各ボーンに多数の新しいマトリックスを作成するため、コードが正確に効率的ではないことはわかっていますが、理解できるようにこのように記述しました。最適化は後で行います。回転するように指示されている唯一のボーンは、「Bip01 L UpperArm」というラベルの付いた、キャラクターの右肩です。
var temp = 0.0;
ModelInstance.prototype.Tick = function(step, name, hasArmature, bones) {
temp -= 0.0001*step;
if(temp>1.0)
temp -= 2.0;
else if(temp<-1.0)
temp += 2.0;
// Calculate world matrix for overall model
var rotation = quat.fromValues(this.rX, this.rY, this.rZ, 1.0);
quat.calculateW(rotation, rotation);
mat4.fromRotationTranslation(this.worldMatrix, rotation, [this.x, this.y, this.z]);
mat4.scale(this.worldMatrix, this.worldMatrix, [this.sX, this.sY, this.sZ]);
mat4.transpose(this.worldMatrix, this.worldMatrix);
// Calculate bone matrices
if(hasArmature) {
for(var i=0; i<bones.length && i<40; i++) {
// Rotate just the right shoulder for this example
var boneRotate = bones[i].name == "Bip01 L UpperArm" ? quat.fromValues(0.0, 0.0, temp, 1.0) : quat.fromValues(0.0, 0.0, 0.0, 1.0);
quat.calculateW(boneRotate, boneRotate);
// Original Position
// Translate to the position of the bone
var translationMatrix = mat4.create();
mat4.translate(translationMatrix, translationMatrix, [bones[i].pos[0], bones[i].pos[1], bones[i].pos[2]]);
// Rotation Matrix
// Amount to rotate the bone by
var rotationMatrix = mat4.create();
mat4.fromQuat(rotationMatrix, boneRotate);
// New Position
// Rotate the bone around the bones origin by the requested amount
var boneMatrix = mat4.create();
mat4.multiply(boneMatrix, translationMatrix, rotationMatrix);
// Apply parent transformations
if(bones[i].parent>=0) {
var parentID = bones[i].parent;
while(parentID>=0) {
mat4.multiply(boneMatrix, boneMatrix, this.boneMatrices[parentID]);
parentID = bones[parentID].parent;
}
}
// Remove original translation
var inverseMatrix = mat4.create();
mat4.invert(inverseMatrix, translationMatrix);
mat4.multiply(boneMatrix, boneMatrix, inverseMatrix);
// Save matrix
this.boneMatrices[i] = boneMatrix;
}
}
}
関連するボーン データの一部を以下に示します。
{
"name":"Bip01",
"parent":-1,
"pos":[0.00000000,2.48177004,0.03703270]
},
/*....*/
{
"name":"Bip01 Pelvis",
"parent":0,
"pos":[0.00000000,2.48177004,0.03141975]
},
{
"name":"Bip01 Spine",
"parent":2,
"pos":[0.00000000,2.73970008,0.03122885]
},
{
"name":"Bip01 Spine1",
"parent":3,
"pos":[0.00000000,2.97957993,0.03618195]
},
{
"name":"Bip01 Neck",
"parent":4,
"pos":[0.00000000,3.83638000,0.00980067]
}
/*....*/
{
"name":"Bip01 L Clavicle",
"parent":5,
"pos":[0.07849900,3.83639002,0.00993846]
},
{
"name":"Bip01 L UpperArm",
"parent":10,
"pos":[0.47621104,3.63395000,-0.04814709]
},
{
"name":"Bip01 L Forearm",
"parent":11,
"pos":[0.93108791,3.14579010,-0.13482325]
},
{
"name":"Bip01 L Hand",
"parent":12,
"pos":[1.32678998,2.72037005,-0.01493155]
}
/*....*/
最後に、頂点シェーダーの関連部分を以下に示します。
mat4 skinMatrix;
skinMatrix = uBoneMatrices[int(aBoneIndex.x)] * aBoneWeight.x +
uBoneMatrices[int(aBoneIndex.y)] * aBoneWeight.y +
uBoneMatrices[int(aBoneIndex.z)] * aBoneWeight.z +
uBoneMatrices[int(aBoneIndex.w)] * aBoneWeight.w;
gl_Position = (((vec4(aPosition, 1.0) * skinMatrix) * uWMatrix) * uVMatrix) * uPMatrix;
vLightViewPosition = (((vec4(aPosition, 1.0) * skinMatrix) * uWMatrix) * uLVMatrix) * uLPMatrix;
上記のコードに基づいて、どのように解決する必要があるか、および現在の結果が何であるかについての私の現在の考えのイメージを次に示します。ご覧のとおり、手はモデルの足の周りに伸びています (肩だけが実際に回転していることに注意してください)。
どんな助けでも大歓迎です:)
使用ユーティリティ: glMatrix