如何在 Three.js 中获取蒙皮网格顶点的全局位置?

anq*_*qie 1 javascript three.js

在 Three.js 中,由于这个问题,我们现在能够获取非蒙皮网格体顶点的全局位置,但是如何获取具有骨骼和变形目标的蒙皮网格体顶点的全局位置?

例如,(2.5, 1.5, 0.5)以下情况如何打印? mesh.geometry.vertices[0]原来是在(0.5, 0.5, 0.5)。然后,bones[1]将顶点移动到(2.5, 0.5, 0.5)。最后,变形将顶点移动到(2.5, 1.5, 0.5)

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 200);
camera.position.z = 3;
camera.position.y = 2;
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const geometry = new THREE.BoxGeometry(1, 1, 1);
geometry.morphTargets.push({name: "morph", vertices: []});
for (const vertex of geometry.vertices) {
  geometry.skinIndices.push(new THREE.Vector4(vertex.x < 0 ? 0 : 1, 0, 0, 0));
  geometry.skinWeights.push(new THREE.Vector4(1, 0, 0, 0));
  geometry.morphTargets[0].vertices.push(vertex.clone().add(new THREE.Vector3(0, 1, 0)));
}

const material = new THREE.MeshPhongMaterial({
  skinning: true,
  emissive: 0xffffff,
  wireframe: true,
  morphTargets: true
});

const mesh = new THREE.SkinnedMesh(geometry, material);

const bones = [new THREE.Bone(), new THREE.Bone()];
for (const bone of bones) {
  mesh.add(bone);
}

const skeleton = new THREE.Skeleton(bones);
mesh.bind(skeleton);

bones[0].position.x = -2;
bones[1].position.x = 2;
mesh.morphTargetInfluences[0] = 1;
scene.add(mesh);

// This code assigns (0.5, 0.5, 0.5) to pos,
// but I want to know the code which assigns (2.5, 1.5, 0.5) to pos. 
const pos = mesh.geometry.vertices[0].clone().applyMatrix4(mesh.matrixWorld);
console.log(`(${pos.x}, ${pos.y}, ${pos.z})`);

(function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
})();
Run Code Online (Sandbox Code Playgroud)
body {
  margin: 0;
  overflow: hidden;
}
canvas {
  width: 100%;
  height: 100%;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

gma*_*man 6

所以这就是我所做的试图解决这个问题。我懒得检查它是否适用于所有情况,但是......

首先,我使用了着色器编辑器扩展,然后运行了一个蒙皮示例,并使用着色器编辑器查看了生成的着色器

在此输入图像描述

查看顶点着色器,着色器的相关部分是

#ifdef USE_SKINNING
    mat4 boneMatX = getBoneMatrix( skinIndex.x );
    mat4 boneMatY = getBoneMatrix( skinIndex.y );
    mat4 boneMatZ = getBoneMatrix( skinIndex.z );
    mat4 boneMatW = getBoneMatrix( skinIndex.w );
#endif
#ifdef USE_SKINNING
    mat4 skinMatrix = mat4( 0.0 );
    skinMatrix += skinWeight.x * boneMatX;
    skinMatrix += skinWeight.y * boneMatY;
    skinMatrix += skinWeight.z * boneMatZ;
    skinMatrix += skinWeight.w * boneMatW;
    skinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;
    objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;
#endif

vec3 transformedNormal = normalMatrix * objectNormal;
#ifdef FLIP_SIDED
    transformedNormal = - transformedNormal;
#endif


vec3 transformed = vec3( position );

#ifdef USE_MORPHTARGETS
    transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];
    transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];
    transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];
    transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];
    #ifndef USE_MORPHNORMALS
    transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];
    transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];
    transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];
    transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];
    #endif
#endif

#ifdef USE_SKINNING
    vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );
    vec4 skinned = vec4( 0.0 );
    skinned += boneMatX * skinVertex * skinWeight.x;
    skinned += boneMatY * skinVertex * skinWeight.y;
    skinned += boneMatZ * skinVertex * skinWeight.z;
    skinned += boneMatW * skinVertex * skinWeight.w;
    transformed = ( bindMatrixInverse * skinned ).xyz;
#endif
Run Code Online (Sandbox Code Playgroud)

所以将其翻译成 JavaScript 这就是我的想法

首先,您需要场景更新其所有世界矩阵和骨架。

scene.updateMatrixWorld();
skeleton.update();
Run Code Online (Sandbox Code Playgroud)

然后

  // These are so we can avoid doing allocations
  // in the inner loop.
  const position = new THREE.Vector3();
  const transformed = new THREE.Vector3();
  const temp1 = new THREE.Vector3();
  const tempBoneMatrix = new THREE.Matrix4();
  const tempSkinnedVertex = new THREE.Vector3();
  const tempSkinned = new THREE.Vector3();

  const bindMatrix = mesh.bindMatrix;
  const bindMatrixInverse = mesh.bindMatrixInverse;

  for (let vndx = 0; vndx < mesh.geometry.vertices.length; ++vndx) {
    position.copy(mesh.geometry.vertices[vndx]);
    transformed.copy(position);

    for (let i = 0; i < mesh.geometry.morphTargets.length; ++i) {
      temp1.copy(mesh.geometry.morphTargets[i].vertices[vndx]);
      transformed.add(temp1.sub(position).multiplyScalar(mesh.morphTargetInfluences[i]));
    }

    tempSkinnedVertex.copy(transformed).applyMatrix4(bindMatrix);
    tempSkinned.set(0, 0, 0);

    const skinIndices = geometry.skinIndices[vndx];
    const skinWeights = geometry.skinWeights[vndx];

    for (let i = 0; i < 4; ++i) {
      const boneNdx = skinIndices.getComponent(i);
      const weight = skinWeights.getComponent(i);
      tempBoneMatrix.fromArray(skeleton.boneMatrices, boneNdx * 16);
      temp1.copy(tempSkinnedVertex);
      tempSkinned.add(temp1.applyMatrix4(tempBoneMatrix).multiplyScalar(weight));
    }

    transformed.copy(tempSkinned).applyMatrix4(bindMatrixInverse);
    transformed.applyMatrix4(mesh.matrixWorld);
Run Code Online (Sandbox Code Playgroud)

例子:

#ifdef USE_SKINNING
    mat4 boneMatX = getBoneMatrix( skinIndex.x );
    mat4 boneMatY = getBoneMatrix( skinIndex.y );
    mat4 boneMatZ = getBoneMatrix( skinIndex.z );
    mat4 boneMatW = getBoneMatrix( skinIndex.w );
#endif
#ifdef USE_SKINNING
    mat4 skinMatrix = mat4( 0.0 );
    skinMatrix += skinWeight.x * boneMatX;
    skinMatrix += skinWeight.y * boneMatY;
    skinMatrix += skinWeight.z * boneMatZ;
    skinMatrix += skinWeight.w * boneMatW;
    skinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;
    objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;
#endif

vec3 transformedNormal = normalMatrix * objectNormal;
#ifdef FLIP_SIDED
    transformedNormal = - transformedNormal;
#endif


vec3 transformed = vec3( position );

#ifdef USE_MORPHTARGETS
    transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];
    transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];
    transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];
    transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];
    #ifndef USE_MORPHNORMALS
    transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];
    transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];
    transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];
    transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];
    #endif
#endif

#ifdef USE_SKINNING
    vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );
    vec4 skinned = vec4( 0.0 );
    skinned += boneMatX * skinVertex * skinWeight.x;
    skinned += boneMatY * skinVertex * skinWeight.y;
    skinned += boneMatZ * skinVertex * skinWeight.z;
    skinned += boneMatW * skinVertex * skinWeight.w;
    transformed = ( bindMatrixInverse * skinned ).xyz;
#endif
Run Code Online (Sandbox Code Playgroud)
scene.updateMatrixWorld();
skeleton.update();
Run Code Online (Sandbox Code Playgroud)

我确实用这个样本进行了测试,它似乎有效。

在此输入图像描述