You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebGL蒙皮矩阵正常,FBX SDK设置蒙皮矩阵遇阻求助

WebGL蒙皮矩阵导出FBX至Blender的旋转问题

我有一套模型和对应的蒙皮矩阵,在WebGL中动画与蒙皮变形均可正常工作,但尝试用FBX SDK导出为Blender可识别的骨骼时失败。曾尝试生成嵌套骨骼(考虑关节继承变换将对象空间转换为世界空间),但无效。也考虑过直接沿用WebGL的animationMatrix * inverseObjectSpaceTransformMatrix逻辑设置蒙皮矩阵,但不确定可行性,更不清楚如何导入Blender这类不支持该逻辑的软件。理想方案是将WebGL蒙皮矩阵系统导出为Blender可用的骨骼,目前仅以FBX SDK为例进行尝试。

核心参考数据

以下是各骨骼的逆对象空间变换数据:

b_pedestal:
    inverse_object_space_transform:
        r=[0.0, 0.0, 0.0, 1.0],
        t=[0.0, 0.0, 0.0]
b_pelvis:
    inverse_object_space_transform:
        r=[0.5, 0.5, 0.5, 0.5],
        t=[-1.034437, -0.0118, -0.0]
b_utility:
    inverse_object_space_transform:
        r=[0.0, 0.0, 0.0, 1.0],
        t=[0.0, 0.0, 0.0]
b_l_thigh:
    inverse_object_space_transform:
        r=[-0.540589, 0.495971, -0.505714, 0.453904],
        t=[-0.992615, 0.051638, 0.184923]
b_r_thigh:
    inverse_object_space_transform:
        r=[-0.505715, -0.453904, 0.540589, 0.495971],
        t=[0.992615, -0.051638, -0.184923]
b_spine_1:
    inverse_object_space_transform:
        r=[0.5, 0.5, 0.5, 0.5],
        t=[-1.091035, -0.0118, -0.0]
b_l_calf:
    inverse_object_space_transform:
        r=[-0.581244, 0.447637, -0.464801, 0.495718],
        t=[-0.540767, -0.041656, 0.184923]
b_r_calf:
    inverse_object_space_transform:
        r=[-0.464801, -0.495718, 0.581244, 0.447637],
        t=[0.540767, 0.041655, -0.184923]
b_spine_2:
    inverse_object_space_transform:
        r=[0.463693, 0.533844, 0.533844, 0.463693],
        t=[-1.177534, -0.178358, -0.0]

WebGL蒙皮矩阵生成逻辑

for (var nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++)
{
    var inverseObjectSpaceTransform = inverseObjectSpaceTransforms[nodeIndex];
    var translationScale = inverseObjectSpaceTransform.ts;

    var scale = translationScale[3];
    var rotation = inverseObjectSpaceTransform.r;
    var translation = [translationScale[0], translationScale[1], translationScale[2]];

    var inverseObjectSpaceTransformSRT = new TransformSRT(scale, rotation, translation);
    var inverseObjectSpaceTransformMatrix = mat4.create();
    inverseObjectSpaceTransformSRT.setMatrix(inverseObjectSpaceTransformMatrix);

    inverseObjectSpaceTransformMatrices.push(inverseObjectSpaceTransformMatrix);

    var node = nodes[nodeIndex];
    var parentNodeIndex = node.parent_node_index;
    parentNodeIndices.push(parentNodeIndex);
}

for (var nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++)
{
    var frameMatrix = frameMatrices[nodeIndex];
    var animationMatrix = animationMatrices[nodeIndex];

    var inverseObjectSpaceTransformMatrix = inverseObjectSpaceTransformMatrices[nodeIndex];
    var parentNodeIndex = parentNodeIndices[nodeIndex];

    if (parentNodeIndex >= 0)
    {
        Spasm.assert(parentNodeIndex < nodeIndex);
        var parentAnimationMatrix = animationMatrices[parentNodeIndex];
        mat4.multiply(animationMatrix, parentAnimationMatrix, frameMatrix);
    }
    else
    {
        mat4.copy(animationMatrix, frameMatrix);
    }

    mat4.multiply(tempMatrix0, animationMatrix, inverseObjectSpaceTransformMatrix);
    mat4.transpose(tempMatrix0, tempMatrix0);
    transformBuffer.set(tempMatrix0.subarray(0, 12), nodeIndex * 12); // collapsed skinning matrices
}

顶点着色器蒙皮逻辑

mat4 get_bone_transform(int bone_index) {
    int stride_bone_index = bone_index * 3;
    vec4 i0 = u_skinning_matrices[stride_bone_index + 0];
    vec4 i1 = u_skinning_matrices[stride_bone_index + 1];
    vec4 i2 = u_skinning_matrices[stride_bone_index + 2];
    vec4 i3 = vec4(0.0, 0.0, 0.0, 1.0);
    mat4 bone_transform = mat4(
        vec4(i0.x, i1.x, i2.x, i3.x),
        vec4(i0.y, i1.y, i2.y, i3.y),
        vec4(i0.z, i1.z, i2.z, i3.z),
        vec4(i0.w, i1.w, i2.w, i3.w)
    );
    return bone_transform;
}
mat4 skinning_transform = (get_bone_transform(blend_indices[0]) * a_blendweight[0]);
skinning_transform += (get_bone_transform(blend_indices[1]) * a_blendweight[1]);
skinning_transform += (get_bone_transform(blend_indices[2]) * a_blendweight[2]);
skinning_transform += (get_bone_transform(blend_indices[3]) * a_blendweight[3]);
mat4 model_view_matrix = u_view_matrix;// * u_model_matrix;
mat4 camera_matrix = u_projection_matrix * model_view_matrix;
vec4 position_transformed = vec4(
    (a_position.x * u_position_scale.x) + u_position_offset.x,
    (a_position.y * u_position_scale.y) + u_position_offset.y,
    (a_position.z * u_position_scale.z) + u_position_offset.z,
    1.0
);
vec4 position_skinned = vec4((skinning_transform * position_transformed).xyz, 1.0);

FBX SDK导出尝试代码(Python)

for nodeIndex in range(self.node_count):
    nodeatt = fbx.FbxSkeleton.Create(model.scene, f"skeleton_{nodeIndex}")
    nodeatt.SetSkeletonType(fbx.FbxSkeleton.eLimbNode)
    joint = fbx.FbxNode.Create(model.scene, f"{self.names[nodeIndex]}_{nodeIndex}")
    joint.SetNodeAttribute(nodeatt)

    bone_nodes.append(joint)
    parentNodeIndex = self.mParents[nodeIndex]
    if parentNodeIndex >= 0:
        bone_nodes[parentNodeIndex].AddChild(bone_nodes[nodeIndex])
    else:
        # root is first bone always
        rootnodeatt = fbx.FbxSkeleton.Create(model.scene, 'rootSkeleton')
        rootnodeatt.SetSkeletonType(fbx.FbxSkeleton.eRoot)
        root = fbx.FbxNode.Create(model.scene, 'rootNode')
        root.AddChild(bone_nodes[0])
        root.SetNodeAttribute(rootnodeatt)
        model.scene.GetRootNode().AddChild(root)

    egt = joint.EvaluateGlobalTransform()
    fbx_transform = fbx.FbxAMatrix()
    fbx_transform.SetIdentity()
    transform = self.mTransforms[nodeIndex]
    rotation = transform.get_euler_rotation()  # already inverted to be non-inverted object space transform
    translation = transform.get_translation()
    fbx_transform.SetR(fbx.FbxVector4(rotation[0], rotation[1], rotation[2]))
    fbx_transform.SetT(fbx.FbxVector4(translation[0], translation[1], translation[2]))

    fix = egt.Inverse() * fbx_transform
    joint.LclTranslation.Set(fbx.FbxDouble3(fix.GetT()[0], fix.GetT()[1], fix.GetT()[2]))
    joint.LclRotation.Set(fbx.FbxDouble3(fix.GetR()[0], fix.GetR()[1], fix.GetR()[2]))

当前问题现象

导入Blender后,骨骼位置正确,但旋转错误。


内容的提问来源于stack exchange,提问作者Monteven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 17:45:36