Three.js에서 JSON 형식으로 모델을로드하고 Bone 애니메이션을 만들 때의주의 사항

조금 (대부분?) 빠져서 메모.

아니-, Three.js는 update가 격렬한 것과 원래 일본어 기사가 적기 때문에 상당히 고전했다. (라고 할까, 아직 전혀 제대로 파악할 수 없지만)

우선 모델을 읽고 애니메이션 시킨다는 WebGL에서 뭔가 할 때 피할 수없는 처리로 빠졌기 때문에 메모.

우선 결론부터 말하면, **Bone 애니메이션을 시킬 때는,Bone의 수가 문제가 된다**같다.

bone의 수는 아무래도 관계없는 같다. 다시 작성해 보면 정상적으로 애니메이션했습니다.

덧붙여서 모델 데이터는 Blender에서 Three.js의 JSON 형식으로 Export한 것을 사용.

내보내기 설정

Export設定

모델을 작성해 Bone를 몇개인가 적당히 추가하고 나서(17정도?) Export해 읽어들였는데, 모델 자체는 정상적으로 로드되고 있지만, 애니메이션하지 않는다.

시험에 Bone를 두 정도로 줄이고 Export → 읽고, 하면 움직였다.
그래서 Bone를 복수 사용해 복잡한 움직임을 시킬 경우는 모델을 나누어 Three.js측에서 합성하는 등의 처리가 필요할지도 모른다.

다만, 과연 그것은 귀찮기 때문에 좀 더 조사해 보기로 한다.

덧붙여서 애니메이션 된 소스를 일일이 올려 놓는다.



animation.js
var container;
var camera, scene, projector, renderer;
var mesh;
var animation;
var composer;

init();

function init() {

    container = document.createElement('div');
    document.body.appendChild(container);

    camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000);
    camera.position.z = 800;
    camera.target = new THREE.Vector3(0, 0, 0);

    scene = new THREE.Scene();

    var light = new THREE.DirectionalLight( 0xefefff, 2);
    light.position.set(1, 1, 1).normalize();
    scene.add(light);

    var light = new THREE.DirectionalLight(0xffefef, 2);
    light.position.set(-1, -1, -1).normalize();
    scene.add(light);

    var loader = new THREE.JSONLoader(true);
    var url = 'models/some-model.js';
    loader.load(url, function(geometry, materials) {
      var material = new THREE.MeshFaceMaterial(materials);
      mesh = new THREE.SkinnedMesh(geometry, material);
      mesh.scale.x = mesh.scale.y = mesh.scale.z = 150.0;

      //enable skinning
      mesh.material.materials.forEach(function (mat) {
          mat.skinning = true;
      });

      scene.add(mesh);

      if (mesh.geometry.animation.name) {
        THREE.AnimationHandler.add(mesh.geometry.animation);
        animation = new THREE.Animation(mesh, mesh.geometry.animation.name, THREE.AnimationHandler.CATMULLROM);
        animation.play();
      }

      animate();
    });

    renderer = new THREE.WebGLRenderer({antialias: true, preserveDrawingBuffer: true});
    renderer.sortObjects = false;
    renderer.setSize(window.innerWidth, window.innerHeight);

    container.appendChild(renderer.domElement);

    window.addEventListener('resize', onWindowResize, false);

    //ポストプロセスの設定
    composer = new THREE.EffectComposer(renderer);
    composer.addPass(new THREE.RenderPass(scene, camera));

    //オリジナルのポストプロセスを追加
    composer.addPass(new THREE.ShaderPass({
      vertexShader: document.getElementById('vshader').textContent,
      fragmentShader: document.getElementById('fshader').textContent,
      uniforms: {
        ef: { type: 't', value: 0.2 },
        tDiffuse: { type: 't', value: null }
      }
    }));

    var toScreen = new THREE.ShaderPass(THREE.CopyShader);
    toScreen.renderToScreen = true;
    composer.addPass(toScreen);
}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
    requestAnimationFrame(animate);
    animation && animation.update(0.03);
    render();
}

function render() {

    //theta += 0.1;
    //cnt++;

    //camera.position.x = radius * Math.sin(THREE.Math.degToRad(theta));
    //camera.position.z = radius * Math.cos(THREE.Math.degToRad(theta));
    //camera.lookAt(camera.target);

    //if (mesh && mesh.morphTargetInfluences) {

    //    // Alternate morph targets

    //    var time = Date.now() % duration;

    //    var keyframe = Math.floor(time / interpolation);

    //    if (keyframe != currentKeyframe) {

    //        mesh.morphTargetInfluences[lastKeyframe] = 0;
    //        mesh.morphTargetInfluences[currentKeyframe] = 1;
    //        mesh.morphTargetInfluences[keyframe] = 0;

    //        lastKeyframe = currentKeyframe;
    //        currentKeyframe = keyframe;
    //    }

    //    mesh.scale.set(100, 100, 100);
    //    mesh.morphTargetInfluences[keyframe] = (time % interpolation) / interpolation;
    //    mesh.morphTargetInfluences[lastKeyframe] = 1 - mesh.morphTargetInfluences[keyframe];
    //}

    //renderer.render(scene, camera);
    composer.render();
}

참고 링크


  • Three.js
  • Blender 공식 사이트
  • blender.jp(일본어 비공식 사이트)
  • 좋은 웹페이지 즐겨찾기