LoginSignup
22
24

More than 5 years have passed since last update.

Three.jsでJSON形式でモデルを読み込んでBoneアニメーションさせるときの注意点

Last updated at Posted at 2013-06-29

ちょっと(だいぶ?)ハマったのでメモ。

いやー、Three.jsはupdateが激しいのと、そもそも日本語記事が少ないのでだいぶ苦戦した。(というか、まだ全然しっかり把握できてないけど)

とりあえず モデルを読み込んでアニメーションさせる 、というWebGLでなにかやるときに避けて通れない処理でハマったのでメモ。

まず結論から言うと、**Boneアニメーションをさせる際は、Boneの数が問題になる**ぽい。 boneの数はどうやら関係ないぽい。改めて書きだしてみたら正常にアニメーションしました。

ちなみにモデルデータはBlenderでThree.jsのJSON形式にExportしたものを使用。

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();
}

参考リンク

22
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
24