[Three.js] 본 애니메이션이나 모프 애니메이션을 하는 메쉬의 정점 좌표를 취득한다

Stack Overflow에서 질문했습니다. 그런데, 훌륭한 답변을 gman 씨로부터 받았으므로 전재한다.
덧붙여서 Stack Overflow 상의 코드는 특별한 기술이 없는 한, CC BY-SA 3.0에서 라이센스
gman씨는 한층 더 자신의 코드를 CC0로 라이센스한다고 명언하고 있으므로, 이 기사에 라이센스상의 문제는 특별히 없다.

문제



예를 들면 이 동작 샘플 에 있어서의 검사 메쉬를 구성하는 각 정점의 좌표를 취득하려면 어떻게 하면 좋을까?

본 애니메이션이나 모프 애니메이션을 하지 않는 메쉬의 정점 좌표



우선 전제 지식으로서.
이쪽은 간단하게 취득할 수 있다.

mesh의 vertexIndex 번째 정점의 로컬 좌표를 가져옵니다.
const localPosition = mesh.geometry.vertices[vertexIndex];

mesh의 vertexIndex 번째 정점의 전역 좌표를 가져옵니다.
mesh.updateMatrixWorld();
const globalPosition = mesh.geometry.vertices[vertexIndex].clone();
globalPosition.applyMatrix4(mesh.matrixWorld);

앞의 예의 검사 메쉬는 본 애니메이션, 모프 애니메이션을 실시하고 있으므로 이 방법에서는 정점 좌표를 취득할 수 없다.
본 애니메이션이나 모프 애니메이션의 효과가 미치기 전의 정점 좌표를 취득할 수 있을 뿐이다.

본 애니메이션이나 모프 애니메이션을 하는 메쉬의 정점 좌표



원래 제작으로 Three.js는 JavaScript 코드에서 GLSL 프로그램을 자동 생성하고 그리기 처리의 대부분을 자동 생성한 GLSL에 맡기고 있다.
본 애니메이션이나 모프 애니메이션을 하는 메쉬의 정점 좌표를 계산하는 처리도 GLSL 안에서 행해지고 있어 JavaScript측에서 계산 결과를 취득하는 것은 원칙할 수 없다.

그래서 gman씨는 GLSL중의 정점 좌표 계산 처리를 JavaScript로 번역했다.
덧붙여 모든 케이스에서 완벽하게 움직이는 보증까지는 하지 않기 때문에, 잘 움직이지 않는 케이스에 닿으면 노력할 수 있다고 하는 것이다.

mesh의 정점의 좌표를 취득한다
// 各種パラメータを最新の状態にしておく
mesh.updateMatrixWorld();
mesh.skeleton.update();

// バッファ
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();

// 頂点座標計算処理
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(mesh.bindMatrix);
  tempSkinned.set(0, 0, 0);

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

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

  transformed.copy(tempSkinned).applyMatrix4(mesh.bindMatrixInverse);

  // この時点でのtransformedがmeshのvndx番目の頂点のローカル座標

  transformed.applyMatrix4(mesh.matrixWorld);

  // この時点でのtransformedがmeshのvndx番目の頂点のグローバル座標
}

동작 샘플





검사 메쉬를 구성하는 각 정점의 좌표에 빨간 상자를 설치하고 있다.
올바르게 정점 좌표를 취득할 수 있는 것을 알 수 있다.
다만, 본래 GPU로 하는 처리를 CPU로 하고 있기 때문에 북극이 되어 버린다.

덤: 드로잉에 사용되는 GLSL 코드를 얻는 방법


material 에서 그리기에 사용되고 있는 정점 쉐이더의 소스는 renderer.context.getShaderSource(material.program.vertexShader) , 픽셀 셰이더의 소스는 renderer.context.getShaderSource(material.program.fragmentShader) 로 볼 수 있는 모양.

좋은 웹페이지 즐겨찾기