初音ミク XAML化計画~キーボード入力で口と目を動かす

口を動かすには、モーフィングをプログラムでやればできないことはないと思うけど、そこまでやる元気はない。とりあえず六角のデータで複数の顔の表情をインポートして、そのWPFの切り替える方法でどこまでいくか試してみた。

テスト実行用クリックワンス

http://uchukamen.com/WPF/MikuClickOnce/publish.htm

操作方法

  • 右クリックでドラッグ→回転
  • 左シフトを押しながら右クリックでドラッグ→移動
  • ホィール→拡大縮小
  • 左コントロールを押して右クリック→回転、位置のリセット
  • キー
    A…あ、I…い、U….う、E…え、O…お
    1…スマイル その1(にこっ)
    2…スマイル その2(笑)
    3…スマイル その3 (ウィンク)
  • 方向矢印で目の上下左右

image

実装的には、こんな感じで、いくつかの表情を遠くの空間に置いておく。表情のTransform を切り替えてあげるだけで、一瞬で顔を切り替えてしまう方法。ここで、 off は、遠くの空間、trans は顔の場所の Transform。
private void Grid_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
    if (e.Key == System.Windows.Input.Key.A)
    {
        faceNow.Transform = off;
        faceNow = faceA;
        faceA.Transform = trans;
    }
  ・・・以下、同様
}

やっていることは単純だけれど、とりあえず何らかの事象を受けて、表情などを変えることができる。モーフィングに比べればかなり硬いけれど、使えないことはない。

ということで、WPF をつかった新時代にふさわしいインターフェースの名前は・・・

  • Miku Interface
  • Moe Interface
  • Vocaloid Interface
  • Humanoid Interface

そうそう、これをつくりながら、Nexus 6 を作りたかったんだよな…と・・・

初音ミク XAML化計画~初音ミクは重い

もともとの V6 の XAML データだけでも、 8MB もあるので、Visual Studio 2008 が

image

と 悲鳴を上げている。

やっぱりここまでくると、しゃべらせないとね・・・ということで、顔をモーフィングしたデータをいくつかあわせてエクスポートしてみた。XAML データはさらに重くなって、19MB。Visual Studio 2008 が開くには開くが、Grid を追加したら、Visual Studio 2008 がハンドルされていない例外を発生しましたということで、種類 ‘System.OutOfMemoryException’ の例外が発生して、アウト!

image

少しミクをダイエットさせなければ・・・・

呼び出しのターゲットが例外をスローしました。
   場所 System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
   場所 System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
   場所 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   場所 System.Delegate.DynamicInvokeImpl(Object[] args)
   場所 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
   場所 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)

種類 ‘System.OutOfMemoryException’ の例外がスローされました。
   場所 MS.Internal.Host.Isolation.Protocols.ITextReadCursor.Read(Int32 length)
   場所 MS.Internal.Host.Isolation.Adapters.ToTextReadCursor.Read(Int32 length)
   場所 MS.Internal.Host.MarkupSourceProvider.SourceReader.Read(Int64 location, Int32 length)
   場所 Microsoft.Windows.Design.SourceUpdate.TextRangeManager.TextRangeSourceReader.Read(Int64 location, Int32 length)
   場所 MS.Internal.Xaml.Parser.ScannerProviderAdapter.Read(Int32 location, Int32 length)
   場所 MS.Internal.Xaml.Scanner.ReadSource(Int32 current)
   場所 MS.Internal.Xaml.Scanner.GetNextToken(Boolean stopAtEndOfLine)
   場所 MS.Internal.Xaml.Parser.ParseXmlDocument(Int32 elements)
   場所 MS.Internal.DocumentTrees.Markup.XamlSourceDocument.ParseElementFromSkeleton(XamlParseContext context, SkeletonNode node, XamlElement parent, Boolean fullElement)
   場所 MS.Internal.DocumentTrees.Markup.XamlSourceDocument.UpdateSkeleton(IDamageListener listener, Boolean force)
   場所 Microsoft.Windows.Design.Documents.Trees.MarkupDocumentTreeManager.Update()
   場所 Microsoft.Windows.Design.Documents.MarkupDocumentManager.Update()
   場所 Microsoft.Windows.Design.Documents.MarkupDocumentManager.AutoUpdater.DoUpdate()

初音ミク XAML化計画~ Trackball.cs を組み込んで、初音ミクをグリングリン回す

さて、クオータニオンの原理がわかったところで、Trackball.cs を組み込みます。MSDN の Cube Animation Demo の trackball.cs は、http://msdn2.microsoft.com/en-us/library/ms771572.aspxにありますので、ダウンロードしておきます。必要なファイルは、trackball.cs です。このファイルは、これまで書いたように、回転に関して示唆に富むソースコードです。3D が初めての人は、ぜひ一読して、理解することをお勧めします。わずか200行程度なのですが、ベクトルの外積、クオータニオンを凝縮したコードです。また、簡単に組み込むことも意識して作られており、参考になります。

  1. ZAM 3D から Export する。
  2. trackball.cs をプロジェクトに追加する。
  3. Form をロードした際のイベント Window_Loaded を追加する。
  4. Windows1.xaml.cs に以下の trackball をコールするコードを追加する。
    ——– ここから ——–
    Trackball _trackball;
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // setup trackball for moving the model around
        _trackball = new Trackball();
        _trackball.Attach(this);
        _trackball.Slaves.Add(this.ZAM3DViewport3D);
        _trackball.Enabled = true;
    }
  5. trackball.cs の private void UpdateSlaves(Quaternion q, double s, Vector3D t)  を修正
    オリジナルコードだと、ZAM3Dが生成するコードとTransform3Dの相性が悪く、クラッシュします。
    また、オリジナルの trackball.cs では、カメラではなく ModelVisual3D を動かすコードになっています。オブジェクトを動かすのでも問題ありませんが、カメラを動かしたほうがスマートです。そこで、このメソッドを次のようにカメラを動かすように修正します。

private void UpdateSlaves(Quaternion q, double s, Vector3D t)
{   
    ////// カメラバージョン
    if (_slaves != null)
    {
        foreach (Viewport3D i in _slaves)
        {
            Transform3D t3dg = i.Camera.Transform;            ScaleTransform3D scaleTransform = new ScaleTransform3D();
            scaleTransform.ScaleX = s;
            scaleTransform.ScaleY = s;
            scaleTransform.ScaleZ = s;            Rotation3D rotation = new AxisAngleRotation3D(q.Axis, q.Angle);
            TranslateTransform3D trtf = new TranslateTransform3D(-t.X * 10, -t.Y * 10, -t.Z * 10);
            Transform3DGroup tg = new Transform3DGroup();
            tg.Children.Add(scaleTransform);
            tg.Children.Add(new RotateTransform3D(rotation));
            tg.Children.Add(trtf);            i.Camera.Transform = tg;
        }
    }
}

  • あと、trackball.cs のもともとのコードが、F1 や、Spaceキーを使っていますが、使いにくいので適当にキーを修正します。
  • コンパイル&実行
  • これで、次のように ミクをグリングリン回すことができるようになります。テスト用 ClickOnce操作方法

    • 右クリックでドラッグ→回転
    • 左シフトを押しながら右クリックでドラッグ→移動
    • ホィール→拡大縮小
    • 左コントロールを押して右クリック→回転、位置のリセット

    image 注意: 原点を中心に回転するようになっていますので、ミクを原点に配置しないとうまく回転してくれません。六角、ZAM3D、VS2008の パッケージ一式はこちら注意: キオ式 初音ミクの XAML 化データに関しては、本家 キオ式アニキャラ3D の “当方の3Dデータ、動画の使用条件について” を参照してください。注意: 今回使用したミクのデータは、V1.1-1.5 ぐらいで、少々古いです。また、ごみが混じっていたり、消し忘れの球が見えていたりしますが、ご愛嬌ということで w

    初音ミク XAML化計画~初音ミクをグリングリンするには、ベクトルの外積とクオータニオン

    http://www-sens.sys.es.osaka-u.ac.jp/users/kanaya/Documents/VCQ/kanaya-handai-quaternion.pdf

    を勉強してみたところ、クオータニオンの積は、2つの回転を含むベクトルの合成であると書いてある。クオータニオン L, R, Res を適当に取り、Res = L * R を図解すると、下の図のように、クオータニオン L の軸に対して、クオータニオン Rののベクトルを、クオータニオン Lの回転角度だけ回転したものが クオータニオン Res となる。

    image 

    右項のクオータニオンの回転が0の場合は上の絵のように回転面に対して、右項Rを回転させるだけでわかりやすいが、右項のクオータニオンの回転が0ではない場合に、Rにさらに回転が加わったものに対して、左項のクオータニオンの回転が合成されるため、先の資料を読んでいても、よくわからない。

    そこで、XAML でプログラムを作って、実際に動作を確かめてみた。Form 左のスライダーが、左項クオータニオンの X, Y, Z, W、右側のスライダーが 右項のクオータニオンの X, Y, Z, W。ピンクのベクトルが 左項のクオータニオン、黄色のベクトルが右項のクオータニオン、黒のベクトルが積のクオータニオン。スライダーで、左項、右項のクオータニオンを動かすと、結果としての積のクオータニオンを表示するWPFアプリケーション。

    image

    スライダーの値を読み取って、クオータニオンの積を計算し、表示する処理の一部抜粋。

    private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        try
        {
            // 左項
            Vector3D leftAxis = new Vector3D(lx.Value, ly.Value, lz.Value);
            Quaternion leftQuaternion = new Quaternion(leftAxis, lrot.Value);
            QuaternionRotation3D qrot3d = new QuaternionRotation3D(leftQuaternion);
            RotateTransform3D leftRot3D = new RotateTransform3D(qrot3d);

            Transform3DGroup leftT3DG = (Transform3DGroup)(this.LeftOR23.Transform);
            leftT3DG.Children[2] = leftRot3D;
            this.LeftOR23.Transform = leftT3DG;

            this.textBlock1.Text = “Left:”
                + lx.Value.ToString(“f2”) + “:”
                + ly.Value.ToString(“f2”) + “:”
                + lz.Value.ToString(“f2”) + “:”
                + lrot.Value.ToString(“f2”);

            // 右項
            Vector3D rightAxis = new Vector3D(rx.Value, ry.Value, rz.Value);
            Quaternion rightQuaternion = new Quaternion(rightAxis, rrot.Value);
            QuaternionRotation3D rightQRot3D = new QuaternionRotation3D(rightQuaternion);
            RotateTransform3D rightRot3D = new RotateTransform3D(rightQRot3D);

            Transform3DGroup rightT3DG = (Transform3DGroup)(this.RightOR26.Transform);
            rightT3DG.Children[2] = rightRot3D;
            this.RightOR26.Transform = rightT3DG;

            this.textBlock1.Text += “\nRight:”
                + rx.Value.ToString(“f2”) + “:”
                + ry.Value.ToString(“f2”) + “:”
                + rz.Value.ToString(“f2”) + “:”
                + rrot.Value.ToString(“f2”);

            // クオータニオンの積
            Quaternion resultQuaternion = leftQuaternion * rightQuaternion;
            QuaternionRotation3D resultQR3D = new QuaternionRotation3D(resultQuaternion);
            RotateTransform3D resultRot3D = new RotateTransform3D(resultQR3D);

            Transform3DGroup resultTr = (Transform3DGroup)(this.ResultOR29.Transform);
            resultTr.Children[2] = resultRot3D;
            this.ResultOR29.Transform = resultTr;

            this.textBlock1.Text += “\nResult:”
                + resultQuaternion.X.ToString(“f2”) + “:”
                + resultQuaternion.Y.ToString(“f2”) + “:”
                + resultQuaternion.Z.ToString(“f2”) + “:”
                + resultQuaternion.W.ToString(“f2”);
        }
        catch
        {
        }
    }

    これで、スライダーを動かして確かめてみると・・・なるほど・・・こうなるのか・・・。

    これを数式から読み取るのは不可能だな・・・・・

    こんな3次元のモデルも XAML で簡単にできてしまうあたりに感動してしまった。

    ということで、回転させたい対象のクオータニオンをR、回転させる軸と回転角度を表わすクオータニオンLとすると、回転後のクオータニオン RES = L*R でとても簡単に計算ができる。ここで求められた回転後のクオータニオンRESから、RotateTransform3D を回転させたい対象に適用してあげれば、回転してくれるということだ。

    クオータニオンがなければ、マトリックス計算の地獄になるところだけれど、クオータニオンの積一発。おまけに、WPF は、このクオータニオンの積も提供してくれているので、とても処理が楽。素晴らしい。

    初音ミク XAML化計画~クオータニオンって何?

    カメラの移動方法を調べていたら、そのものずばりの MSDN キューブ アニメーションのデモ を発見。これを組み込めば簡単に視点の変更ができる。

    おお、もうひとりのおやじもこれを組み込んだのね。さすがだ。

    と思い、デモソフトの Trackball.cs のコードをながめてみたら・・・外積を使っている?と不思議に思い、ソースを読んでみた。

    次の図のようにマウスで左から右へドラッグし、ミクを中心にカメラを右方向に動かす場合、Y軸を中心にカメラを右方向に回転させることになります。では、どのようにY軸を中心にと判断しているかというと、カメラの視線ベクトルとカメラの移動ベクトルの外積から回転軸を求めています。外積を使えば、マウスの移動角度に応じて、斜めに回転する回転軸も簡単に求めることができます。

    image

    なるほど・・・外積ってこういう利用方法があるだぁ・・・と感動。ちなみに下が外積を簡単に説明した図。なつかしい・・・

    image

    と、さらに ソースを読んでいったところ、回転軸が求まったところで、クオータニオンの計算があり、意味不明 orz。クオータニオン自体初めて耳にするが、3D-CG をする人たちには常識らしい。ところが、にわか ミク3D野郎には難しすぎる・・・

    ということで、次の資料があったので、一から勉強中。

    http://www-sens.sys.es.osaka-u.ac.jp/users/kanaya/Documents/VCQ/kanaya-handai-quaternion.pdf

    まさか、この歳でエルミート行列、オイラー角、テンソル、などなどと、どこかで聞いたことのあることをもう一度ひっくり返すことになるとは・・・・。一通り読んでみたのだが、外積、内積なら、物理的な意味が直感的にわかるのだが、クオータニオン同士の積の物理的な意味が直感的にわからない。ミクをグリングリン回すのにも、修行が足りない orz。

    ちなみにクオータニオン(超複素数 or 四元数)は、大学では習わなかった。当時、3Dグラフィックスはなかったので w。

    初音ミク XAML化計画

    六角大王スーパーには、モーフィングという機能が付いていて、次のようにいろいろなモーフィングを登録することができる。

    image

    なんとキオ式ミクには驚くほどのモーフィングが登録されており、たとえばウィンクや、笑うなどがボタン一発でできるようになっている。恐るべし・・・。

    image

    この WPFデータを作ることはできるので、うまく使えば・・・あんなことや・・・こんなことが・・・

    初音ミク XAML化計画

    いろいろ試してみたが、マテリアルの色がうまく乗らない。たとえば、髪飾りのピンクと目の色が同じマテリアルにマップされており、髪飾りをピンクにすると、白眼もピンクになってしまう。しょうがないので、六角上でさらに細かく分割しました。あまり細かくすると、ZAM 3D へのインポートが大変になるので、顔(目、上の歯、下の歯、口の中)、ヘッドセットや、髪飾りは、各1つにグループ化しました。imageようやくこれで ZAM 3D上で色が重ならずに、着色ができた。imageということで、現在のデータ(六角、ZAM 3D、Visual Studio 2005) をまとめてアップしておきます。http://uchukamen.com/WPF/MikuData/Miku%20Kio%20V5.zipあとは、ZAM 3D でアニメーション機能があるので、下の図のように時間ごとにパーツを動かして、アニメーションを作成する。アニメーションが作成できたところで、Export to XAML で Visual Studio 20005 のプロジェクトに書き出す。このときに、アニメーションは、OnLoaded の ストーリーボードとして作成されます。したがって、このままVS 2005 でコンパイル&実行してあげると、そのまま踊りだします。imageZAM 3Dでは、左下にある球体でトラックボールのようにオブジェクトの方向を変更することができ、かなり簡単にアニメーションを作成することができます。ただし、ZAM 3D の制限として、1つのアニメーションしか作成できないので、ちょっと不便です。複数のアニメーションを設定できると楽なんですけど・・・それから、ちょっとまえのデータですが、MikuMikuDance のスクリーンショットと実行ファイルをさらしておきますw。imageなお、うしろのビデオは、WMV を WPF でリアルタイムに変形&再生するように、Expression Blend で追加したものです。WPFすげー。それから、これはカメラのパン、ズーム、それから回転を行うデモ。http://uchukamen.com/WPF/MikuData/KioVS.exeimageimageimageコード的には、ズームはとても簡単。FieldOfView の値を変更してあげればよい。private void sliderZoom_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        this.Target_CameraOR20.FieldOfView = this.slider.Value;
    }

    マウスホィールを使うのであれば、マイナスとか大きすぎる場合のリミッターを入れて、たとえば次のようにすればよい。

    private void ZAM3DViewport3D_MouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
    {
        this.Target_CameraOR63.FieldOfView += e.Delta / 100;
        if (this.Target_CameraOR63.FieldOfView < 10)
            this.Target_CameraOR63.FieldOfView = 10;
        else if (this.Target_CameraOR63.FieldOfView > 100)
            this.Target_CameraOR63.FieldOfView = 100;
    }

    カメラの方向を変更するのは、こんな感じ。

    private void SliderH_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        Transform3DGroup tg = new Transform3DGroup();
        tg.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), this.SliderH.Value)));
        tg.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), this.SliderV.Value)));    this.Target_CameraOR20.Transform = tg;
    }
    private void SliderV_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        Transform3DGroup tg = new Transform3DGroup();
        tg.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), this.SliderH.Value)));
        tg.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), this.SliderV.Value)));    this.Target_CameraOR20.Transform = tg;
    }もう少しスマートにするには、ZAM 3D のトラックボールのようなインターフェースがよいでしょう。これはもう一人のオヤジから展開されると思いますw

    Silverlight for mobile

     

    ちょっと前の話だが、MIX08 – Mobile Devices and Microsoft Silverlight のデモビデオがアップされている。

    http://visitmix.com/blogs/2008Sessions/T12/

     

    3Dはハードウェアアクセラレータが乗らないだろうから、厳しいだろうな・・・

    でも、ビデオなど、同じXAMLでかけるのはでかい。

     

    今の予定では、次の通り。Windows CE 6 ベースになる。 

     

    初音ミク XAML化計画

     
    六角→ ZAM 3D でテクスチャの喪失があるものの、ある程度のデータ移行が可能。
     
    Shade に関してはえムナウさんからのレポートを期待。
    データに関しては、勉強会後に Shareベースに切り出した XAML データが
    えムナウさんから放流される見込み。
     
    ここでめでたく、初音ミク XAML化計画は当初の目的達成。
    それにしても、XAML 偉大だ・・・初音ミク がリアルタイムで踊ってくれる。 w
     
    ここから先は、福井勉強会 ネタばらになりそうなので、自粛 w。