1

我一直在努力寻找一种方法来向现有骨架添加/移除衣服(衣服网格和身体网格应该共享骨架),但我总是得到有趣/奇怪的结果。

我期待附加到共享骨架的所有衣服都已在 Blender 上以相同的骨架导出,它们都共享相同的骨骼名称和结构,我认为这会让它变得非常简单。

我在 Native iOS SceneKit 上使用过这些相同的 3D 文件,实际上我只是做了类似的事情clothing.skeleton = body.skeleton

但正如我所说,在 Three.JS 上似乎并不那么直截了当。

我试过像

clothingMesh.bind(bodySkeleton,clothingMesh.worldMatrix);

也试过:

clothingMesh.skeleton = bodySkeleton;
bodySkeleton.update();

但总是以奇怪的结果结束。我见过一些使用“ratargeting”函数的代码,但我相信这些仅在骨架骨骼名称不匹配时使用,这不是我的情况。

JSFiddle游乐场

我一直在通过 JSFiddle https://jsfiddle.net/cabada/sxv4kbnm/玩弄它,那里有一个完整的代码,我正在尝试构建这个概念。

我修复骨架绑定方式的另一个目标是能够从其他来源复制骨架动画,这样我就可以动态下载它们并将它们动态应用到我的角色中。但同样,得到奇怪的结果,动画骨骼也与目标骨骼共享相同的骨骼名称和结构以进行动画处理。

我在网上找到的与该主题相关的资源和示例

https://raw.githack.com/funwithtriangles/three.js/dev/examples/webgl_animation_sharedskeleton.html

https://rawcdn.githack.com/mrdoob/three.js/r105/examples/webgl_loader_sea3d_bvh_retarget.html

https://jsfiddle.net/satori99/pay0oqcd/

https://github.com/mrdoob/three.js/pull/16608

插图

目标是能够向身体添加/移除服装,使每个角色的所有网格都使用一个骨架(以实现良好的性能),并且能够添加/移除来自其他来源的动画。

4

1 回答 1

0

嗨,我有同样的问题,我试图用我在下面写的函数解决你的问题,但没有成功。你能解决问题吗?

在更改角色的几何蒙皮网格后,我尝试使用 bakeSkeleton(MyCharacterName)。该功能不会给我控制台错误,而是显示变形为爆炸的角色?

    function bakeSkeleton ( target ) {
      var v1 = new THREE.Vector3();

      target.traverse( function ( object ) {
            if ( !object.isSkinnedMesh ) return;
            if ( object.geometry.isBufferGeometry !== true ) throw new Error( 'Only BufferGeometry supported.' );

            var positionAttribute = object.geometry.getAttribute( 'position' );
            var normalAttribute = object.geometry.getAttribute( 'normal' );

            for ( var j = 0; j < positionAttribute.count; j ++ ) {
                  object.boneTransform( j, v1 );
                  positionAttribute.setXYZ( j, v1.x, v1.y, v1.z);

                  getBoneNormalTransform.call( object, j, v1 );
                  normalAttribute.setXYZ( j, v1.x, v1.y, v1.z );
            }

            positionAttribute.needsUpdate = true;
            normalAttribute.needsUpdate = true;

            object.skeleton.bones.forEach(bone => bone.rotation.set(0,0,0));
      } );
}

const getBoneNormalTransform = (function () {

      var baseNormal = new THREE.Vector3();

      var skinIndex = new THREE.Vector4();
      var skinWeight = new THREE.Vector4();

      var vector = new THREE.Vector3();
      var matrix = new THREE.Matrix4();
      var matrix3 = new THREE.Matrix3();

      return function ( index, target ) {

            var skeleton = this.skeleton;
            var geometry = this.geometry;

            skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index );
            skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index );

            baseNormal.fromBufferAttribute( geometry.attributes.normal, index ).applyNormalMatrix( matrix3.getNormalMatrix(this.bindMatrix) );

            target.set( 0, 0, 0 );

            for ( var i = 0; i < 4; i ++ ) {

                  var weight = skinWeight.getComponent( i );

                  if ( weight !== 0 ) {

                        var boneIndex = skinIndex.getComponent( i );

                        matrix.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] );

                        target.addScaledVector( vector.copy( baseNormal ).applyNormalMatrix( matrix3.getNormalMatrix(matrix) ), weight );

                  }

            }
            
            matrix3.getNormalMatrix(this.bindMatrixInverse);
            
            return target.applyNormalMatrix( matrix3 );

      }; 
      
}());
于 2022-02-22T22:50:17.757 回答