webGL      2018.06.07 ( 最終更新日:2021/01/12 )

【WebGL特集】第5回:Blenderでアニメーション出力

※この記事は2013年の記事を2018年6月に加筆修正したものです。three.jsの使用バージョンはr92です。

【前記事】
【WebGL特集】第1回:WebGLって何?
【WebGL特集】第2回:回転する立方体
【WebGL特集】第3回:カメラをグリグリ
【WebGL特集】第4回:Blenderでモデル出力

前回までの内容で、Blenderからオリジナルモデルを出力できるようになりました。
ここから先はスケルトンを用いたアニメーションの解説となります。
アニメーションの出力方法はFbx、json、glTS、モーフなど様々ありますが、ここでは一番メジャーなjsonを使って出力します。さあ、ここからが本番です。

1、モデリングから.json吐き出しまでの手順

1、Blenderで人体モデルを用意します。

押し出し

Boxを取り出し一面ポリゴンを選択して、左にあるメッシュツールの「個々に押し出し」を繰り返して人体形成
【maya系ショートカット】tabでモード変更。右クリックメニューから面。alt+xで「個々に押し出し」x,y,zキーで軸固定

2、骨入れます。

オブジェクトモードに戻り>追加>アーマーチェアで、カーソルのあるところに一本骨ができる。
編集モードにしてEキー(左のアーマーチェアツール > 押し出し)を押すと1本目の先端から2本目のアーマーチェアを伸ばせる。
【maya系ショートカット】アーマーチェアの押出はalt+x。

骨入れ

3、エンベロープの設定をします。

エンベロープ

モデルを選択した後、シフト押しながらアーマーチェア選択。ctl+Pでペアレントする。自動エンベロープでウェイト設定
【maya系ショートカット】ペアレントはp

4、リギングします。今回は足だけIKにします。

リギング

リグControllerはlocatorではなく骨を使用。ロケーターにしてしまうとアクションがオブジェクトごとに分割されてしまい出力に支障をきたす。両足元にIKリグ用の骨を2個追加。
足の骨をオブジェクト選択 >ポーズモード > 先端の骨を選択 > ポーズ > インバースキネマティクス>IKをボーンに追加>ターゲットなし でIKを設定。 IKを設定した先端の骨を選択し、右メニューにあるボーンコンストレイントタブから、ターゲットにArmatureを設定。ボーンに先程追加したIKリグ用の骨を選択。IKのルートを足の付け根にするために、チェーンの長さを2で調整。

5、動かします。

アニメーション

ポーズモードにしてアーマーチェアやIKを動かしてキーを打つ。これを繰り返してアニメーションを作る。ドープシートのアクションで、どのアクションにキーが入っているかを確認

6、出力します。

出来たら、オブジェクトモードに戻し、モデルを選択。Three.jsでエキスポートします。エキスポート設定は以下の通り。デフォルトでは通りませんので必ず確認してください。

出力する時は「デフォルトのポーズ」Rest position である必要があります。スケルトンをすべて選択し、ポーズモード > ポーズ >トランスフォームをクリア >すべて Tスタンスに戻してから
メッシュを選択して出力してください。レストポーズにするときに、AutoKeyの状態だと、レストポーズのキーが打たれてしまうのでAutoKeyを解除しておくとよいかと思います。
アクションの1つ目に「0rest」という名前のアクション名でレストポーズを作り、2つ目にアニメーションを作って、1つ目をアクティブにした状態でも、出力できます。その場合はwebに表示する際には2つ目のアニメーションを指定する必要があります。

2、scriptの記述

では、実装します。パス表記のところはオリジナルのパスに変更してね。

<html>
<meta charset="utf-8">
<body>
<script src="build/three.min.js"></script>
<script src="js/controls/TrackballControls.js"></script>
<script>
var camera, scene, renderer, trackball, mixer;
var clock = new THREE.Clock();
var action ={};

init();

function init() {
  //シーン
 scene = new THREE.Scene();

 //カメラ
 camera = new THREE.PerspectiveCamera( 70, 640/480, 1, 2000 );
 camera.position.set( 0, 50, 500 );
 trackball = new THREE.TrackballControls( camera );

 //ライティング
 var directionalLight = new THREE.DirectionalLight( 0xffffff, 2);
 directionalLight.position.set(0,0,3);
 scene.add( directionalLight );

  //Blenderで出力したjsonオブジェクト
  var loader = new THREE.JSONLoader();
  loader.load( 'charaAni.json', function ( geometry, materials ) {
      materials.forEach(function (material) { material.skinning = true; } ); 

      var character = new THREE.SkinnedMesh( geometry, materials);
      character.position.set(0, 0, 0);
       character.scale.set(20, 20, 20);

       //ANIMATION
       mixer = new THREE.AnimationMixer(character);
       action.dance = mixer.clipAction(geometry.animations[ 0 ]);
       scene.add( character );
       animate();
       action.dance.play();
 } );

 //グリッド
 var grid = new THREE.GridHelper( 1000, 10, 0x808080, 0x808080 );
 grid.position.set( 0, 0, 0 );
 scene.add( grid );

 //レンダラー
 renderer = new THREE.WebGLRenderer();
 renderer.setSize( 640, 480 );
 renderer.setClearColor(new THREE.Color('white'));//背景色の設定
 document.body.appendChild( renderer.domElement );
}

function animate() {
    requestAnimationFrame( animate );
    render();
}

function render() {
    var delta = clock.getDelta();
    mixer.update( delta );
    renderer.render( scene, camera );
    trackball.update();
}
</script>
</body>
</html>

See the Pen WebGLGreenMan by 住岡義和 (@mox-motion) on CodePen.

3、ソースの解説

出力したモデルの読み込み部分と、animate関数の中身が減り、render関数が追加されています。
どうやら、アニメーション専用の記述が必要そうです。

変数宣言

var camera, scene, renderer, trackball, mixer;
var clock = new THREE.Clock();
var action ={};

グローバルとして使われる変数を先に宣言しておきます。変数clockに、three.jsの時間にかかわるオブジェクトを取得し、変数actionは、配列として変数宣言します。二つとも後に使います。

オブジェクトとマテリアルを読み込む

//Blenderで出力したjsonオブジェクト
var loader = new THREE.JSONLoader();
loader.load( 'charaAni.json', function ( geometry, materials ) {
materials.forEach(function (material) { material.skinning = true; } ); 

このwebGLはスケルトン(ボーン)アニメーションです。29行目でjsonファイルを読み込みます。どうもボーンとアニメーションの情報はgeometryに入り、スキンの情報はmaterialsのほうに入るようです。30行目ですが、これがないとボーンアニメーションは動きません。ボーン一つ一つにスキンを割り当てている作業です。

オブジェクトとマテリアルを結合させてキャラクターを作る

var character = new THREE.SkinnedMesh( geometry, materials);
 character.position.set(0, 0, 0);
 character.scale.set(20, 20, 20);

jsonによって出力されたすべてのマテリアルを取得し、ジオメトリーとくっつけて、アニメーション入のキャラクターを生成しています。scale部分は調整してください。

アニメーションを再生させる

//ANIMATION
mixer = new THREE.AnimationMixer(character);
action.dance = mixer.clipAction(geometry.animations[ 0 ]);
scene.add( character );
animate();
action.dance.play();

キャラクターからアニメーションデータを取り出します。mixerという、AnimationMixerの器を用意して、その中に入ったアニメーションのクリップデータを一つ指定。シーンに追加し、後のanimate()関数を実行させて、アニメーションを再生させます。

render関数の中身

var delta = clock.getDelta();
mixer.update( delta );
renderer.render( scene, camera );
trackball.update()

clock.getDeltaで経過した秒数を取得します。64行目で、アニメーションを更新せよ(経過時間のポーズ)という感じです。あとはレンダリングとトラックボールの処理です。

以上です。次回は、各種CGソフトからBlenderへのアニメーション移植方法を解説します。

【WebGL特集】第6回:他のCGツールからBlenderへの移植

AUTHOR

sumioka