2010年07月

2010年07月26日

ムーランを買ったのでカボチャプリンを作ってみた

待望のムーランを入手した。ムーランは裏ごしする時に使う調理器具で、自分が買ったものはこのように分解できて、3つのプレートを交換することで漉すサイズを調整できる。分解できると洗うのも楽だね。

fig01

使うときはこのように底にプレートをはめて

fig02

回転歯とハンドルをそこにセットする。プレートは中央が盛り上がって円錐状になっていて、ハンドルに付いた歯とフィットするようになっている。歯自体は緩やかな螺旋状でハンドルを回すとプレートから浮いている側から漉す対象が歯の下に取り込まれ、螺旋構造によって徐々にプレートに押し付けられて圧力でプレートの穴から出てくる仕組みだ。回転歯と横に渡した軸受けの梁の間にはバネが入っていて常に歯をプレートに押し付けるようになっている。

fig03

組み立てが終わったので(も、もちろん洗ったよ・・・)いよいよカボチャプリンを作ってみたい。

プリンに使った材料はこんな感じ。

プリン本体:
カボチャ1/4、卵3個、生クリーム(35%)100ml、牛乳250cc、バニラエッセンス、ラム酒、シナモン、砂糖50g

カラメルソース:
砂糖50g、キャラメルシロップ

まずカボチャを粗く切って蒸かし器で竹串が通るまで蒸かした。電子レンジだと温度が高過ぎてカボチャの甘みが出ないらしい。

fig05


出来たら皮だけ取ってカボチャの身の部分をムーランに投入する。一度に全部投入して構わない。

fig04

残った皮はスタッフがおいしく・・・

fig10


あとはハンドルを軽く回すと、

fig07


プレートからニョロニョロとカボチャペーストが出てくる。

fig06

これをボールに取って、ムーランの壁に張り付いてなかなか歯にひっかからないカボチャはゴムベラなどで集めて歯の裏に押し込んで搾り出す。プレートの表面に張り付いているカボチャペーストもゴムベラでこそげ落とす。

カボチャペーストが出来たらこれにラム酒を加えて混ぜ合わせる。

今度はタマゴを3つ割って泡立て器でほぐして、そこに生クリーム100mlとバニラエッセンス10滴、シナモン少々を入れて混ぜ、さらに牛乳250cc、砂糖50gを投入して混ぜる。

fig08

これをカボチャペーストに加えて混ぜる。

fig09

ある程度混ざったら裏ごし器に投入して繊維や溶け残ったものなどを漉す。網目が段々目詰まりしてくるからゴムベラなどで網の表面にプリン液を押し付けるようにして漉す。漉すとこのように繊維が残る。さすがにここまでムーランで処理することは出来ないけど、最初に押しつぶす作業と比べてペースト状になっているだけにそれほど時間もかからず漉す事が出来た。

fig11

裏ごし器を洗って、もう一度裏ごしを繰り返す。これでプリン液は完成だ。

これと平行してカラメルソースを作る。
小さい鍋に砂糖を50g投入して火にかけてゆっくり溶かす。強火だといっきに焦げるので鍋を持ち上げて火に近づけたり遠ざけたりしながら砂糖を溶かす。溶けて沸騰してきたら焦げ過ぎないように注意しながら焦げ色を付け、いい焦げ具合になったら鍋に水を大匙1杯程度入れてカラメルの温度を下げる。

次にキャラメルフレーバーシロップを小さじ1杯程度加える。これを入れるとカラメルシロップの香りが凄く良くなる。無ければ入れなくてもいい。

ソースが冷めないうちに型に流し込む。今回は8cm×25cmの四角い型にしてみた。

fig12

これが流し込んだところ。固まってうまく流れなくなったら火であぶってとこしてやればOK。

fig13

これに先に作ったプリン液を流し込んで、150度に予熱しておいたオーブンの中段に入れて、トレイにお湯を張り、型の上からアルミホイルをかぶせて蒸し焼きにする。40〜50分すると凝固して竹串を刺しても穴がふさがらないようになったら出来上がり。

fig14

回りにナイフを入れて型に平らなものをかぶせて一挙にひっくり返せば、このように綺麗に型からプリンが抜ける。

fig15

ムーランが無かったら最初の裏ごしで結構時間を取られるところだけど、物凄く楽に作業が進んだ。これなら裏ごしが面倒なスィートポテトとかも楽に出来るので、秋になったら色々作ってみようかな。



take_z_ultima at 11:29|この記事のURLComments(0)TrackBack(0)料理 | Goods

2010年07月23日

modoからFLASHへ書き出してみた その15 modo 401 SP4 ActionScript 3.0

前回はモーフマップをモデルに追加するテストスクリプトをアップしただけで終わっちゃったけど、今回はそれの進展バージョンと、その中身の話だ。

まず最初になるべく全ての物体は一般化して使いたいって言うのが前提だ。PaperVision3D上で球体も立方体もmodoから出力した様々なモデルも、大雑把に考えると、全てはTriangleMesh3Dクラスのインスタンスと言える。この事はそれらのインスタンスをすべてTriangleMesh3D型の変数に代入して管理できる事を意味する。そしてその変数に代入されたインスタンスはどれでもTriangleMesh3Dクラスとその上位クラスが実装しているメソッドやメンバー変数を持っている事が期待できるから、

var obj:TriangleMesh3D;
obj=new Morph01( );
obj.rotationX=30.0;

としてやれば、modoから出力したMorph01クラスのインスタンスの角度をX軸回りに30度回転させられる。ちなみにrotationXはTriangleMesh3Dクラスの親の親のDisplayObject3Dのものだ。これが出来るのも、Morph01クラスがTriangleMesh3Dクラスを継承しているお陰で、クラス定義の冒頭で、

public class Morph01 extends TriangleMesh3D

となっているわけだ。ただ、Morph01クラスのインスタンスをTriangleMesh3D型の変数に入れてしまえば扱いがTriangleMesh3Dとしてのものになってしまうので、それを継承して新たにMorph01で追加したモーフ変形の機能なんかがまるきり無いものとして扱われてしまう。そこで、Morph01の追加機能を活かそうと思ったら、

var obj:Morph01;
obj=new Morph01( );
obj.update( );

のように、変数の型をMorph01にする必要が出てくる。でもこうしちゃうと、変数objにはSphereとかPlaneとか、他のTriangleMesh3Dクラスを継承したクラスのインスタンスは代入出来なくなってしまう(電化製品というカテゴリなら電子レンジも冷蔵庫も電気スタンドも含む事が出来るけど、冷蔵庫というカテゴリでは電子レンジも電気スタンドも含める事が出来ないってことね)。

これを解消するためは型キャストを使えばよくって、例えばこんな感じでOKだ。

var obj:TriangleMesh3D;
obj=new Morph01( );
Morph01(obj).update( );

Morph01(obj)の部分が型キャストをしている部分で、変数objに入っているインスタンスはMorph01のインスタンスと見なすよっていう意味だ。objがMorph01のインスタンスならばそのクラスに定義されているupdate( )メソッドが呼び出せるわけだね(電化製品というカテゴリで冷蔵庫を扱ったとしても、そのもの自体は冷蔵庫なんだから、取り扱うときに冷蔵庫だと思って使えば使えるってことね)。

でもこうしちゃうとせっかくモーフィングと言う一般的な機能を作ったのに、モーフィングの機能がMorph01クラスの特別な機能になってしまい、モーフィングをサポートしたいろんなクラスを作った時にそれらのインスタンスはそれを生成する元となったクラスにいちいちキャストしなくちゃならなくなって、変数に入れたインスタンスがどのクラスのインスタンスだったか覚えておかないと、モーフィングも簡単に出来なくなってしまう。

var obj1:TriangleMesh3D;
var obj2:TriangleMesh3D;
obj1=new Morph01( );
obj2=new Tube( );
Morph01(obj1).update( );
Tube(obj2).update( );

そこで新たにTriangleMesh3Dクラスを継承したModoModelというクラスを作ってモーフィングのための仕組みを埋め込み、modoからエクスポートするクラスはこのクラスのサブクラスにしてみた。

package  {
 import org.papervision3d.core.geom.*;
 import org.papervision3d.core.proto.*;
 public class ModoModel extends TriangleMesh3D
 {
  public var morphvalues:Vector.<Number>;
  public function ModoModel( material:MaterialObject3D, vertices:Array, faces:Array, name:String=null )
  {
   super( material, vertices, faces, name);
  }
  public function update():void {}
 }
}

とは言っても見ての通り今のところ唯一のメソッドであるupdateは空で、本体は継承側のクラスでオーバーライドしている。そういう仕様だったらインターフェースで作れと突っ込みが入りそうな感じだけど、まだモーフィングの機能をこっちに移し途中なので、辛抱してねw。最終的にはモーフィングのための変数やメソッドの中身はこっちに持って来て、サブクラスの方にはモーフィングのためのデータだけ置いておくつもり。モーフィングのデータだけはサブクラスでそれぞれ特有だからね。

これでmodoからエクスポートしたクラスはどれでもModoModelクラスでキャストできるし、TriangleMesh3Dとして扱う事も出来るようになり、使い方をより共通化できたわけだ。

var obj1:TriangleMesh3D;
var obj2:TriangleMesh3D;
obj1=new Morph01( );
obj2=new Tube( );
ModoModel(obj1).update( );
ModoModel(obj2).update( );

さて、そこで今度はmodoが書き出すクラスの方だ。まず大きく変わったのがデータの構造だ。モーフィングすると形状が変わるけど、変わった形状のデータだけ保持していては変形させるたびに計算誤差が累積して元に戻せなくなるから、いつも元の形状のデータは保持しておいて、そこから変形したデータを表示用に使う方法にする事にした。先に出てきたupdate()メソッドはその変換をするためのものだ。そしてこの元の形状データと言うのは同じクラスのインスタンスどうしは全く同じで構わないはずだ。だから元の形状データである_vertsはクラスで共通のクラスのメンバー変数にしてみた(staticで宣言)。

static private var _verts:Vector.<Number>=new <Number> [

これでクラスのインスタンスがいくら増えても元形状のデータは1つきりしか存在しないはず・・・。

package  {
 import org.papervision3d.Papervision3D;
 import org.papervision3d.core.geom.*;
 import org.papervision3d.core.geom.renderables.Triangle3D;
 import org.papervision3d.core.geom.renderables.Vertex3D;
 import org.papervision3d.core.math.NumberUV;
 import org.papervision3d.core.proto.*;
 public class Morph03 extends ModoModel {
  static private var _verts:Vector.<Number>=new <Number> [
   0.5,-0.5,-0.22833301127,
   0.5,-0.5,0.215000003576,

       :

   -0.5,0.166666671634,0.5,
   -0.5,0.5,-0.5,
  ];

同様にモーフィングデータ(名前の配列と変位量の配列)も、同じクラスの中では共通のはずなので、これもクラス共通のメンバー変数として宣言して見た。

  static public var _morphnames:Array = new Array(
  "LTwist",

  :

  "open");
  static public var vert_count:int = _verts.length;
  static public var morph_count:int = _morphnames.length;
  static private var _morphs:Vector.<Number>=new <Number> [
   -0.132722616196,-0.104241132736,0.0,
   -0.395433247089,-0.199332416058,0.0,

       :

   -0.0,0.208333328366,0.0,
   0,0,0,
  ];

そしてクラスのコンストラクタはそのスーパークラスのコンストラクタを呼んで、次にbuildObject()メソッドを呼ぶだけのものだ。もちろんスーパークラスはModoModelクラスで、そのコンストラクタはさらに上位のTriangleMesh3Dのコンストラクタを呼ぶようになっている。今まではスーパークラスがTriangleMesh3Dだったので、直に呼べたものが、間にModoModelクラスが入っちゃったためにコンストラクタが間接的に呼び出されるようになっただけだ。

  public function Morph03(material:MaterialObject3D=null) : void
  {
   super( material, new Array(), new Array(), null );
   buildObject();
  }

こっちがModoModel側のコンストラクタね。ここで書かれているsuperがTriangleMesh3Dのコンストラクタにあたる。

  public function ModoModel( material:MaterialObject3D, vertices:Array, faces:Array, name:String=null )
  {
   super( material, vertices, faces, name);
  }

次にデータを生成するbuildObject()メソッド。この部分は以前にも説明したので変更した部分だけ説明すると、冒頭の部分で、各モーフマップの適用量を格納するmorphvaluesという配列をモーフマップの数だけ用意して0で初期化している。そして、以前は変換後にnullを代入して消去していた変数_vertsはそのまま残し、_uvと_facesだけ消去するようにした。

  private function buildObject() : void
  {
   var i:int;
   morphvalues = new Vector.<Number>;
   for(i=0;i<morph_count;i++){
    morphvalues.push(0.0);
   }

   var _uvs:Vector.<Number> = new <Number> [
    0.619708836079,0.681360900402,
    0.619708836079,0.798565328121,

          :

    0.451220542192,0.0299999993294,
    0.366976410151,0.365445375443,
   ];
   var _faces:Vector.<int> = new <int> [
    0,1,2,
    3,2,1,

   :

    36,66,38,
    50,38,66,
   ];
   var vertices :Array  = this.geometry.vertices;
   var faces    :Array  = this.geometry.faces;
   // Vertices
   for( i = 0; i < _verts.length/3; i++ )
   {
   vertices.push( new Vertex3D( _verts[i*3],_verts[i*3+1],_verts[i*3+2]) );
   }
   // Faces
   var uvA :NumberUV;
   var uvC :NumberUV;
   var uvB :NumberUV;
   var ptr1:int;
   var ptr2:int;
   var ptr3:int;
   for( i = 0; i < _faces.length/3; i++ )
   {
    ptr1=_faces[i*3];
    ptr2=_faces[i*3+1];
    ptr3=_faces[i*3+2];
    var a:Vertex3D = vertices[ptr1];
    var b:Vertex3D = vertices[ptr2];
    var c:Vertex3D = vertices[ptr3];
    uvA =  new NumberUV( _uvs[ptr1*2], 1.0-_uvs[ptr1*2+1] );
    uvB =  new NumberUV( _uvs[ptr2*2], 1.0-_uvs[ptr2*2+1] );
    uvC =  new NumberUV( _uvs[ptr3*2], 1.0-_uvs[ptr3*2+1] );
    faces.push(new Triangle3D(this, [ a, b, c ], material, [ uvA, uvB, uvC ] ) );
   }
   _faces=null;
   _uvs=null;

   this.geometry.ready = true;
   if(Papervision3D.useRIGHTHANDED)
     this.geometry.flipFaces();
  }

ところでポリゴンを定義するfaces配列も、ポイントの接続と組み合わせはクラスの中で共通なんだからクラスのメンバー変数として定義して良さそうな感じだけど、Triangle3Dのコンストラクタを呼び出している部分をよく見ると、

faces.push(new Triangle3D(this, [ a, b, c ], material, [ uvA, uvB, uvC ] ) );

渡しているのはVertex3DのインスタンスだったりNumberUVのインスタンスだったりして、クラス内で共通のものじゃなくて各インスタンスごとのものだ。だから生成されるTriangle3Dのインスタンスもインスタンス特有であってクラス共有じゃないから、faceはstaticで定義しなかったわけだ。これがもしポイントの番号みたいに直接インスタンスに関係しないデータだったらクラス共通に出来たんだけどね。

そして最後にモーファーを実現するupdate()メソッドだ。modoの相対モーフマップのデータは各ポイントのXYZ変位量が並んでいるだけなので、そのモーフマップを100%適用したければ、元の座標値データにモーフマップデータの変位量を足して新しい座標値を生成するだけだ。適用量が100%じゃなければ、変位量にそのパーセンテージを賭けて値を出して足せばいいし、複数のモーフマップが組み合わさっているなら各ポイントごとに加算してやればいいだけだ。

それをやっているのが下のメソッドで、各モーフマップの適用量がmorphvalues配列に入っていて、ポイントごとの全てのモーフマップの変位量が_morphs配列に入っているので、ポイントごとに全てのモーフマップの変位量と適用量をかけあわせたものの合計を変数tx、ty、tzに蓄積し、それを形状データのおおもとが入っている_vertsからオリジナルの座標値データを取り出してきて足し合わせ、変形後の座標値をPaperVision3Dが実際の表示に使うgeometry.vertics配列に設定している。こうすればこの後Papervision3Dにこのオブジェクトのレンダリングを頼むだけで変形された形状をレンダリングしてくれるわけだ。

  public override function update():void {
   var vertices :Array  = this.geometry.vertices;
   var tmp :Vertex3D;
   var tx:Number=0.0;
   var ty:Number=0.0;
   var tz:Number=0.0;
   for( var i:int = 0; i < _verts.length/3; i++ )
   {
    tx=ty=tz=0.0;
    for ( var j:int = 0;j<morph_count;j++)
    {
     tx+=_morphs[j*vert_count+i*3]*morphvalues[j];
     ty+=_morphs[j*vert_count+i*3+1]*morphvalues[j];
     tz+=_morphs[j*vert_count+i*3+2]*morphvalues[j];
    }
    tmp=vertices[i] as Vertex3D;
    tmp.x=_verts[i*3] + tx;
    tmp.y=_verts[i*3+1] + ty;
    tmp.z=_verts[i*3+2] + tz;
   }
  }
 }
}

これを呼び出すMainクラスはこんな感じで、Morph03クラスをTriangleMesh3D型変数で取り扱って、モーファーを使う時だけModoModel型にキャストして使うようになっている。

package  {
 import org.papervision3d.view.BasicView;
 import org.papervision3d.objects.primitives.Sphere;
 import flash.events.Event;
 import org.papervision3d.objects.primitives.Plane;
 import org.papervision3d.core.geom.renderables.Vertex3D;
 import flash.display.DisplayObject;
 import org.papervision3d.objects.DisplayObject3D;
 import org.papervision3d.core.geom.TriangleMesh3D;
 
 public class Main extends BasicView{
  var obj:TriangleMesh3D;
  private var offset:Number=0.0;
  public function Main() {
   obj=new Morph03();
   scene.addChild(obj);
   var ct:DisplayObject3D=new DisplayObject3D();
   camera.x=1.2;
   camera.y=0.2;
   camera.z=3;
   startRendering();
   addEventListener(Event.ENTER_FRAME,loop);
  }
  
  private function loop(e:Event){
   var v:Number=Math.sin(offset);
   var vs = ModoModel(obj).morphvalues; 
   if (v<0.0) {
    vs[2]=0.0;
    vs[3]=-v;
   } else {
    vs[2]=v;
    vs[3]=0.0;
   }
   offset+=0.2;
   ModoModel(obj).update();
  }
 }
}

それではまた来週。

modoカテゴリー別ページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)modo | FLASH

2010年07月22日

Compositeのトラッカーを調べてみた その2 Composite 2011

暑いってレベルじゃねーよ・・・orz

さて、前回は画面上の特定パターンを追跡してその軌跡を得る方法について調べてみた。今回はそこで得られた軌跡をもとに、他のフッテージを追従させることをやってみたい。

ところで前回は1つのポイントを追跡したけど、1つのポイントの軌跡から得られるものはそのポイントの各フレームでの位置だけだ。大きさや向きの変化まで捉えたかったら追跡するパターンの異なる位置で2つあるいは4つのポイントを追跡する必要がある。そこでトラッカーには追跡するポイントを追加する「Add」ボタンがある。このボタンを押すと、ToolUIの左側にあるトラッカーのリストに1つ項目が追加され、ビューにもリファレンスボックスとトラッカーボックスが追加される。

fig01

あとの作業は前回やった1つのポイントを追跡する手順を繰り返し行うだけで、1つのトラッカーツールで複数のポイントを追跡する事が出来る(ただし追跡作業は1回につき1つだけ)。

で、実際にいくつのポイントがあるとどういう事が出来るかと言うと、次のようになる。

1ポイント:位置

2ポイント:位置・サイズ・平面回転

4ポイント:四角形の4頂点の移動変形による擬似的なパース変形

あくまで各ポイントが持つデータは2次元のデータなので、3次元的な効果はあくまで擬似的なものだ。

それではまず1ポイントでやってみる。前回ハートマークをトラッキングしたシーンを用意して、

  1. File→ImportでArrow.tifを読み込み、パネルを閉じる
  2. マウスの中ボタンを押してゲートメニューを右に出てToolsメニューからCompositionを選んで、下のToolからBlend&Compをスケマティックビューへドラッグ&ドロップする

    fig02


  3. そしてArrowの出力をBlend&CompのFrontへ、fig01をBackへ繋いで、Blend&Compの出力をOutputへ繋ぐ。繋ぐところを間違わないようにね。間違っちゃったらCTRLキーを押しながら接続している線をドラッグすれば繋ぎなおせるよ。

    fig03

    これで2つの画像が合成される。

    fig04

  4. 再びマウスの中ボタンを押してゲートメニューを出し、右に抜けてToolsメニューを出し、Transformを選んでToolの2DTransformをArrowとBlend&Compを繋ぐ線の上にドラッグ&ドロップする。

    fig05

    これでArrowの位置や大きさをコントロールできるようになる

    fig06

  5. 挿入した2DTransformを選択した状態で、ToolUIの2DTransformタブの「TransformType」をSRTから1Pointに切り替える

    fig07

  6. SourceのX、Y値を0にする

    fig08

  7. Destinationのラベルを右クリックして、ポップアップしてくるメニューの「SetTracker」をクリックする

    fig09

  8. TrackerSelectorパネルが出たら、Trackerの左横の三角をクリックして展開し、出てきたTrackerAnalyzer(1)を選択し、「UseOffset」をONにして「Copy」を押し、このパネルを閉じる

    fig10

    ここで展開したTrackerはツールとして挿入したTrackerツールで、選択したTrackerAnalyzer(1)はそのTrackerツール内でハートマークを追跡したアナライザの1つだ。1つのツールでいくつもの点をトラッキングすればここに複数の項目が並ぶ事になるし、Trackerツールをいくつも追加していれば、展開する前の項目がツールの数だけ並ぶ事になる。
    UseOffsetをONにすると、リファレンスボックスのXY座標値がShiftのXY座標値に加算された座標値が2DTransformに渡される。その結果、リファレンスボックスが設置された位置から2DTransformが開始され、SourceのXYを0にしてある(Arrowは100X100の画像で基準点はその中心にある)のでArrowはハートマークに重なる。OFFにした場合はShift値のみになるので、Arrowは(0,0)の位置(Shiftのフレーム1の値)に配置される。
    Copyはキーを2DTransformにコピーするからその後にTrackerが変化しても影響を受けないのに対してLinkの場合はTrackerの値を参照する形になるのでTrackerが変化すればそのまま影響を受ける。

    fig12

    これでハートマークに重ねてArrowが動くようになった。
  9. 次にArrowをハートマークからちょっと放してハートの方を向くようにしてみる。2DTransformのToolUIの一番左にあるリストの追加ボタンを押す

    fig13

    これでTransformが1つ追加される。このように1つの2DTransformの中で複数回の移動・回転・スケーリングが行える。適用する順番も下にある矢印のボタンで変えられるよ(今回はこのままでOK)。

    fig14

  10. 新しくTransformが追加されるとSRTモードなので下のようなギズモが出てくる。これらをドラッグして矢印の位置や角度を適当に設定する。

    fig15


    こんな感じにしてみた。

    名称未設定 16

  11. これで再生すると、この姿勢を保ちながら矢印がハートに追従して動く

    fig17

それではまた次回。

maxまとめページ 



take_z_ultima at 11:39|この記事のURLComments(0)TrackBack(0)Composite | CG

2010年07月21日

modoからFLASHへ書き出してみた その14 modo 401 SP4 ActionScript 3.0

modo401SP5(英語版)がリリースされたみたいだな。

http://www.luxology.com/support/modo401_SP5_details.aspx

さて、modoからUVマップ付きで三角ポリゴンメッシュがエクスポートできるようになったので、今度はモーフマップも書き出させてみたい。

Adobe Flash Player を取得

そこでプログラムは作ったんだけど、解説を書く時間が無くなっちゃったので、とりあえず使い方だけ書いておくと、モーフマップ付きのオブジェクトを利用するには、何番目のモーフマップの値をどのくらいの適用量にするかを設定し、update()メソッドを実行するだけ。あとは他のオブジェクトと同じ扱いになる。下のプログラムの

obj.morphvalue[2]=0.0;

なんてやっている部分がそれで、morphvalueプロパティがモーフマップの適用量を格納する配列になっている。ただ、実際何番目のモーフマップがmodoで作ったモームマップのどれに当たるかがこれじゃあわからないので、モーフマップの名前を_morphnamesプロパティに配列で入れてあるよ。これを調べるか、またはmodoから吐き出したオブジェクトの定義ファイルを見れば対応がわかる。

package  {
 import org.papervision3d.view.BasicView;
 import org.papervision3d.objects.primitives.Sphere;
 import flash.events.Event;
 import org.papervision3d.objects.primitives.Plane;
 import org.papervision3d.core.geom.renderables.Vertex3D;
 import flash.display.DisplayObject;
 import org.papervision3d.objects.DisplayObject3D;
 import org.papervision3d.core.geom.TriangleMesh3D;
 
 public class Main extends BasicView{
  var obj:Morph01;
  private var offset:Number=0.0;
  public function Main() {
   obj=new Morph01();
   scene.addChild(obj);
   var ct:DisplayObject3D=new DisplayObject3D();
   camera.x=1.2;
   camera.y=0.2;
   camera.z=3;
   startRendering();
   addEventListener(Event.ENTER_FRAME,loop);
  }
  
  private function loop(e:Event){
   var v:Number=Math.sin(offset);
   if (v<0.0) {
    obj.morphvalue[2]=0.0;
    obj.morphvalue[3]=-v;

   } else {
    obj.morphvalue[2]=v;
    obj.morphvalue[3]=0.0;

   }
   offset+=0.2;
   obj.update();
  }
 }
}

今回のプログラムはまだ実験段階で、整理が付いていないので、モーフマップが付いていないモデルを出力しようとすると、変数定義が無くてエラーになったりすると思うので気をつけてね。

それではまた次回。

modoカテゴリー別ページ



take_z_ultima at 11:59|この記事のURLComments(0)TrackBack(0)modo | FLASH

2010年07月20日

Compositeのトラッカーを調べてみた その1 Composite 2011

すでに夏バテ・・・orz

さて、前回ちょっと触れたCompositeのトラッカーをちょっとやってみたい。

一応最初に説明しておくと、トラッカーはビデオストリーム中から指定した形状のものを追跡して連続した座標値として記録する機構だ。

例えばこんなムービーで、ハートの形状の動きを追跡しようと思ったら、

fig01

Compositeを起動して、File→Newを選んでどこか適当なフォルダに新規のコンポジションファイルをCreateする。スケマティックビューがどこかのビューポートに表示されていれば、Outputのノードが表示される。

fig02

次にFile→Importでトラッキングしたいフッテージをインポートし、インポートパネルを閉じる。

スケマティックビューにムービーのフッテージが現れたら、フッテージのノードの右側の部分をドラッグして、出てきたラバーバンドを引っ張ってOutputの右側に繋げる。

fig03

これでフッテージが出力ノードに繋がった。

次に中ボタンをクリックしてゲートメニューを出し、右側にカーソルを移動させて

fig04

Toolsメニューが出たらToolsのタブからTrackerを探してクリックし、下に出てきたToolメニューの中からTrackerをドラッグして、

fig05

先に繋いだ線の上にドラッグ&ドロップする。

fig06

これでトラッカーにフッテージを流し込めるようになった。今回は間に挿入したけど別にフッテージからOutputとTrackerに別々に接続しても構わない。

fig07

Trackerのノードを選択すると、プレイヤーに実線と点線で描かれた四角形が表示される。

fig08

たまたまこの例では赤い線で表示されているけど、この四角形の色は下のToolUIのTrackerタブにあるColorの項目で任意の色に変えられる。背景の色と混ざらないような色を選べばいいだろう(以後は水色に変えて作業をする)。

fig09

このボックスの実線の方がリファレンスボックスで、このボックスによって追跡するパターンを指定する。点線の方はトラッカーボックスで、トラッカーはこのボックスの範囲でリファレンスのパターンと似たものを探す。

fig10

どちらのボックスも縁をドラッグするとサイズも縦横比も自由に変えられ、内側をドラッグすれば位置が変えられる。また、リファレンスボックスの方はドラッグ中はズームが効くようになっていて、正確な位置決めが出来るようになっている。

fig11

このズーム倍率を決めているのがTool UIのTrackerタブのDisplayの下にあるZoomで、デフォルトで3倍になっている。その他Reference、Track、Shiftなどの項目の下の数値を入力(ドラッグして値を変化させることも出来る)して、直接位置や大きさを設定する事も出来る。

fig12

さて、準備が出来たらいよいよハートマークの追跡をしてみたい。現在のフレームをフレーム1にあわせてからリファレンスボックスをハートマークの中心にあわせて位置を決め、ハートマークが入りきる大きさにサイズを調整する。そしてトラッカーボックスも適当に大きさを変える。

fig13

デフォルトの状態ではリファレンスボックスが参照する画像はフレーム1の画像だ。もし異なるフレームの画像を参照したければ、そのフレームでリファレンスボックスを設定した後で「Snap」ボタンを押す必要がある。そしてそこから前後に辿るなら、そのフレームより前のトラッキングは「|<」と「<」を使う事になる。

これでAnalyzeの一番右側にある「 >| 」ボタンを1回クリックしてみる。

fig14

これがその結果。ハートマークの移動が速いので、1フレームですでにトラッカーボックスから殆ど出てしまっている。

fig15

このまま連続して「>|」ボタンを押して行くと、下のGIFアニメのように、途中から追えなくなってしまった。

fig16

実はこれでも結果はかなりいい方で、それは追跡しているフッテージにノイズ成分が殆どない事と、追跡しているものが途中までほぼ直線的に動いている事に起因している。トラッカーは直前の動きから次の動きを予想するように出来ているので、ハートマークが急カーブして折り返すまでは何とか追う事が出来ている。

そして追跡するもの以外に殆ど何も無い場合には、かなりアバウトな比較が可能になる。そのアバウトさを決定しているのがAnalyzeのToleranceで、この数値が小さいほど厳密な比較が行われ、該当するものが無い場合はトラッキングポイントが記録されないようになる。逆にこの値が100なら見つからなくても予想ををつけて適当にトラッキングポイントを作ってしまう。今回は100にしてあったので、最初の1フレームで方向を決められて、なんとか途中までは追う事が出来たわけだ。今回の設定でToreranceを85以下にしてみたら、まるきりトラッキングされなくなったよ。

トラッキングされると基本的にはShiftのXYパラメータにキーが打たれる。キーがある場所はタイムスライダの部分にも黄色い線が入る。

fig19

もし全部これらをクリアしたいなら、Shiftの下にある「Reset」を押せばいい。ひとつずつ消したいならプレイヤー中の小さな四角形をクリックして選択し、BackSpaceを押すか、ToolUIのAnimationタブでグラフ内のコントロールポイントを選択してDELキーで削除することも出来る。

今度はトラッキングボックスを充分な大きさにとって、Toleranceを80にしてみた。前回は1コマずつトラッキングしたけど、今度は「>」ボタンで現在フレームからコンポジットの終了フレームまでを一挙にトラッキングしてみた(その場合は終了フレームをちゃんと設定して、スタートする前に現在フレームを1フレーム目に持って行く必要があるよ)。

fig17

これが結果。

fig18

うまい具合にトラッキングできている感じだ。

これをToleranceを60にしてやってみると、今度は3フレーム目のキーが抜けた。

fig20

それは3フレーム目でハートが他の絵と重なったためにパターンマッチが出来なかったためだ。

fig21

この例のように単純な場合でもこんな感じになっちゃう位だから、実写映像の場合はあまり厳密にマッチングを望むと、殆ど反応しなくなる事も考えられる。

また、現実の映像では追跡対象は3次元的に動くので、回転したりして、縦横のサイズが変わったり向きが変わったりする。

fig22

そんな場合はパターンが全然マッチングしなくなってしまうので追跡が不可能になってしまう。

これはToleranceを80にしてReferenceをFixedにしてトラッキングしてみたものだ。3フレーム目から先はハートマークよりそこに固定してあるパターンの方がリファレンス画像とマッチングすると判断して、トラッキングポイントがそこに留まってしまっている。

fig22

そこでこのように追跡対象が変形して追跡不能になる現象を軽減するために、パターンマッチングの参照画像をフレーム1や「Snap」ボタンを押したフレームに固定(Fixed)するのではなく、直前のトラッキングの結果得られた位置を基準にしてそこから画像をピックアップして比較画像にしてしまおうと言うのが「Roaming」の設定だ。1フレーム前の画像なら変化は少ないだろうからね。RoamingはAnalyzeのReferenceのところにあるよ(デフォルトではFixedになってるからクリックして切り替えてね)。

fig24

そしてこれが同じトラッキングをRoamingで行ったもの。

fig23

他のパターンと重なる3フレーム目を除いてちゃんと追跡できている。

まあ単純な例ではこうなるけど、実写だとなかなか難しいところもあって、最後は1フレームずつチクチク修正なんて事もあるわけだね。とにかくマーカーはコントラストを出来るだけ高くしないとね。

あとはこうやって追跡して出来たトラッキングポイントを他のフッテージに割り当てて行けば動いているものに何か貼り付けたり物体自体を置き換えたり出来るわけだね。

それについてはまた次回。

maxまとめページ 



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)Composite | CG
Archives