|
|
May 06 眼を開いた状態と目を閉じた状態の MeshGeometry3Dを、スライダーで線形合成してみた 左から右へ、眼を開いた状態、目を閉じた状態、合成したもの 眼をあいている状態では、目の周りにアイシャドウを引いていたりして、合成した時に汚くなることがある。結構細かいところに気をつける必要あり。 それから、六角大王で dxf ファイルにエクスポートした際に、頂点数が変わってしまう。このため、単純にマッピングできず、TriangleIndices でマッピングを取っている。このため、処理的にはかなり重い。動かない場所は処理しないなどの高速化が必要かもしれない。 それから、六角大王から通常状態と目を閉じた状態でエクスポートした場合、わずかに座標がずれている。このため、単純に動かない場所は処理しないという処理ができない。結構厄介だ・・・・・ namespace Morphing_V1 { public partial class Window1 : System.Windows.Window { ・・・ GeometryModel3D gm1 = null; MeshGeometry3D mg10 = null; Point3DCollection p3dcol10 = null; ・・・ private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { double v = this.slider1.Value / 10d; for (int c = 0; c < this.NormalOR32.Children.Count; c++) { gm1 = (GeometryModel3D)(this.NormalOR32.Children[c]); mg10 = (MeshGeometry3D)(gm1.Geometry); p3dcol10 = mg10.Positions; gm2 = (GeometryModel3D)(this.CloseEyesOR40.Children[c]); mg20 = (MeshGeometry3D)(gm2.Geometry); p3dcol20 = mg20.Positions; gmRes = (GeometryModel3D)(this.ResOR50.Children[c]); mRes = (MeshGeometry3D)(gmRes.Geometry); poRes = mRes.Positions; Point3DCollection resPoCol = poRes; for (int j = 0; j < mg10.TriangleIndices.Count; j++) { int inorm = mg10.TriangleIndices[j]; int ia = mg20.TriangleIndices[j]; resPoCol[ia] = new Point3D( p3dcol10[inorm].X * v + p3dcol20[ia].X * (1.0 - v), p3dcol10[inorm].Y * v + p3dcol20[ia].Y * (1.0 - v), p3dcol10[inorm].Z * v + p3dcol20[ia].Z * (1.0 - v)); } mRes.Positions = resPoCol; } } } } スライダーで、MeshGeometry3Dの BOX の頂点座標 Positions を直接変更してみる。このぐらいの頂点数なら問題なくスムーズに変形できるが・・・・・
using System; using System.Windows; using System.Windows.Media; using System.Windows.Media.Media3D; namespace Morphing_V2 { public partial class Window1 : System.Windows.Window { private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { double v = slider1.Value / 10f;
GeometryModel3D gm3d = (GeometryModel3D)this.BoxOR9.Children[0]; MeshGeometry3D mesh3d = (MeshGeometry3D)gm3d.Geometry; for (int i = 0; i < mesh3d.Positions.Count; i++) { mesh3d.Positions[i] = new Point3D( originalMeshGeometry3DPosition[i].X * v, originalMeshGeometry3DPosition[i].Y * v, originalMeshGeometry3DPosition[i].Z * v); } } Point3DCollection originalMeshGeometry3DPosition = null; private void Window_Loaded(object sender, RoutedEventArgs e) { GeometryModel3D gm3d = (GeometryModel3D)this.BoxOR9.Children[0]; MeshGeometry3D mesh3d = (MeshGeometry3D)gm3d.Geometry; originalMeshGeometry3DPosition = new Point3DCollection(); for (int i = 0; i < mesh3d.Positions.Count; i++) originalMeshGeometry3DPosition.Add(mesh3d.Positions[i]); } } } <Window x:Class="Morphing_V2.Window1" Title="Morphing_V2" Width="497" Height="450" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/interactivedesigner/2006" xmlns:c="http://schemas.openxmlformats.org/markup-compatibility/2006" c:Ignorable="d" Loaded="Window_Loaded"> <Grid> <Viewport3D x:Name="ZAM3DViewport3D" ClipToBounds="true" Width="400" Height="300"> <Viewport3D.Resources> <ResourceDictionary> <MaterialGroup x:Key="ER___Default_MaterialMR1" > <DiffuseMaterial> <DiffuseMaterial.Brush> <SolidColorBrush Color="#D3C8AD" Opacity="1.000000"/> </DiffuseMaterial.Brush> </DiffuseMaterial> <SpecularMaterial SpecularPower="93.8667"> <SpecularMaterial.Brush> <SolidColorBrush Color="#333333" Opacity="1.000000"/> </SpecularMaterial.Brush> </SpecularMaterial> </MaterialGroup> <Transform3DGroup x:Key="SceneTR7" > <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0"/> <ScaleTransform3D ScaleX="1" ScaleY="1" ScaleZ="1"/> <RotateTransform3D> <RotateTransform3D.Rotation> <AxisAngleRotation3D Angle="0" Axis="0 1 0"/> </RotateTransform3D.Rotation> </RotateTransform3D> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0"/> </Transform3DGroup> <Transform3DGroup x:Key="BoxOR9TR8" > <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0"/> <ScaleTransform3D ScaleX="1" ScaleY="1" ScaleZ="1"/> <RotateTransform3D> <RotateTransform3D.Rotation> <AxisAngleRotation3D Angle="47.33348273" Axis="0.5325566653 -0.8245461129 -0.1910683281"/> </RotateTransform3D.Rotation> </RotateTransform3D> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0"/> </Transform3DGroup> <MeshGeometry3D x:Key="BoxOR9GR10" TriangleIndices="0,1,2 2,3,0 4,5,6 6,7,4 8,9,10 10,11,8 12,13,14 14,15,12 16,17,18 18,19,16 20,21,22 22,23,20 " Normals="0,0,-1 0,0,-1 0,0,-1 0,0,-1 0,0,1 0,0,1 0,0,1 0,0,1 0,-1,0 0,-1,0 0,-1,0 0,-1,0 1,0,0 1,0,0 1,0,0 1,0,0 0,1,0 0,1,0 0,1,0 0,1,0 -1,0,0 -1,0,0 -1,0,0 -1,0,0 " Positions="-0.5,-0.5,-0.5 -0.5,0.5,-0.5 0.5,0.5,-0.5 0.5,-0.5,-0.5 -0.5,-0.5,0.5 0.5,-0.5,0.5 0.5,0.5,0.5 -0.5,0.5,0.5 -0.5,-0.5,-0.5 0.5,-0.5,-0.5 0.5,-0.5,0.5 -0.5,-0.5,0.5 0.5,-0.5,-0.5 0.5,0.5,-0.5 0.5,0.5,0.5 0.5,-0.5,0.5 0.5,0.5,-0.5 -0.5,0.5,-0.5 -0.5,0.5,0.5 0.5,0.5,0.5 -0.5,0.5,-0.5 -0.5,-0.5,-0.5 -0.5,-0.5,0.5 -0.5,0.5,0.5 " /> </ResourceDictionary> </Viewport3D.Resources> <Viewport3D.Camera> <PerspectiveCamera x:Name="FrontOR6" FarPlaneDistance="10" LookDirection="0,0,-1" UpDirection="0,1,0" NearPlaneDistance="1" Position="0,0,2.38423" FieldOfView="39.5978" /> </Viewport3D.Camera> <ModelVisual3D> <ModelVisual3D.Content> <Model3DGroup x:Name="Scene" Transform="{DynamicResource SceneTR7}"> <!-- Scene (XAML Path = ) --> <AmbientLight Color="#333333" /> <DirectionalLight Color="#FFFFFF" Direction="-0.612372,-0.5,-0.612372" /> <DirectionalLight Color="#FFFFFF" Direction="0.612372,-0.5,-0.612372" /> <Model3DGroup x:Name="BoxOR9" Transform="{DynamicResource BoxOR9TR8}"> <!-- Box (XAML Path = (Viewport3D.Children)[0].(ModelVisual3D.Content).(Model3DGroup.Children)[3]) --> <GeometryModel3D x:Name="BoxOR9GR10" Geometry="{DynamicResource BoxOR9GR10}" Material="{DynamicResource ER___Default_MaterialMR1}" BackMaterial="{DynamicResource ER___Default_MaterialMR1}"/> </Model3DGroup> </Model3DGroup> </ModelVisual3D.Content> </ModelVisual3D> </Viewport3D> <Slider Height="31" HorizontalAlignment="Left" Margin="37.5,0,0,8" Name="slider1" VerticalAlignment="Bottom" Width="100" ValueChanged="slider1_ValueChanged" /> </Grid> </Window> May 02 2008年4月30日(水) 第27回 Codeseek勉強会で、「宇宙仮面のZAM3D で簡単3D XAML プログラミング」というタイトルで、僭越ながら講師をやらせていただきました。
ここでは、その資料、デモサンプルなどをアップします。
|
AGENDA
|
-
自己紹介
-
WPF と XAML の概要
-
Expression Blend
-
ZAM3D とは
-
ZAM3D と Visual Studio の連携
-
XAML の基礎 Viewport3D
-
アニメーションと Storyboard
-
WPF の 3D 能力
-
3Dデータのインポート
-
回してみよう
-
ベクトルの外積とクオータニオン
-
触ってみよう Hit Test
-
おまけ 初音ミクは電気羊の夢を見るか
説明に使用した PowerPoint はこちらからどおぞ。
|
|
デモパッケージ
|
|
デモに使用したパッケージはこちらからどおぞ。
巨大(78MB)なので、ご注意ください。
また、グラフィックスアクセラレータがないと、厳しいです。
使用したなソフトウェア
-
ZAM 3D 1.0
-
Visual Studio 2008
-
六角大王 Super 5.5
注意
このデモで使用している 3D データは下記のものを使用しています。使用条件は下記 URL より確認してください。
|
April 24 第27回codeseek勉強会:ZAM3D で簡単3D XAML プログラミング のお知らせです。
--------- ここから ----------- 第27回codeseek勉強会 「宇宙仮面のZAM3D で簡単3D XAML プログラミング」 (共催:tk-engineering、こみゅぷらす、eパウダ~) 2008年4月30日(水) 19:00~21:00 場所:渋谷駅東口からすぐの貸し会議室 (ご登録後お知らせいたします) 募集締め切り:2008年4月27(日)23時59分59秒 24名まで 無料 宇宙仮面のC#プログラミング( http://uchukamen.com/)を主催されている 宇宙仮面様にご登壇いただき、XAMLでの3Dプログラミングについて解説して いただけることになりました。 この貴重な機会に、ぜひご参加ください。 ZAM3D で簡単3D XAML プログラミング - ZAM3D の機能と使い方を紹介 20分 - ZAM3D, Expression Blend, Visual Studio の連携 20分 - 3Dデータのインポート 20分 - 3Dの回転について20分 - ZAM3D の問題点、その他注意点など 20分 - Q&Aなど 20分
--------- ここまで -----------
March 30 口を動かすには、モーフィングをプログラムでやればできないことはないと思うけど、そこまでやる元気はない。とりあえず六角のデータで複数の顔の表情をインポートして、そのWPFの切り替える方法でどこまでいくか試してみた。 テスト実行用クリックワンス http://uchukamen.com/WPF/MikuClickOnce/publish.htm 操作方法 - 右クリックでドラッグ→回転
- 左シフトを押しながら右クリックでドラッグ→移動
- ホィール→拡大縮小
- 左コントロールを押して右クリック→回転、位置のリセット
- キー
A...あ、I...い、U....う、E...え、O...お 1...スマイル その1(にこっ) 2...スマイル その2(笑) 3...スマイル その3 (ウィンク) - 方向矢印で目の上下左右
実装的には、こんな感じで、いくつかの表情を遠くの空間に置いておく。表情の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 を作りたかったんだよな…と・・・ March 29 もともとの V6 の XAML データだけでも、 8MB もあるので、Visual Studio 2008 が
と 悲鳴を上げている。 やっぱりここまでくると、しゃべらせないとね・・・ということで、顔をモーフィングしたデータをいくつかあわせてエクスポートしてみた。XAML データはさらに重くなって、19MB。Visual Studio 2008 が開くには開くが、Grid を追加したら、Visual Studio 2008 がハンドルされていない例外を発生しましたということで、種類 'System.OutOfMemoryException' の例外が発生して、アウト!
少しミクをダイエットさせなければ・・・・ 呼び出しのターゲットが例外をスローしました。 場所 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() March 24 さて、クオータニオンの原理がわかったところで、Trackball.cs を組み込みます。 MSDN の Cube Animation Demo の trackball.cs は、 http://msdn2.microsoft.com/en-us/library/ms771572.aspx にありますので、ダウンロードしておきます。必要なファイルは、trackball.cs です。このファイルは、これまで書いたように、回転に関して示唆に富むソースコードです。3D が初めての人は、ぜひ一読して、理解することをお勧めします。わずか200行程度なのですが、ベクトルの外積、クオータニオンを凝縮したコードです。また、簡単に組み込むことも意識して作られており、参考になります。 - ZAM 3D から Export する。
- trackball.cs をプロジェクトに追加する。
- Form をロードした際のイベント Window_Loaded を追加する。
- 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; } - 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 操作方法 - 右クリックでドラッグ→回転
- 左シフトを押しながら右クリックでドラッグ→移動
- ホィール→拡大縮小
- 左コントロールを押して右クリック→回転、位置のリセット
注意: 原点を中心に回転するようになっていますので、ミクを原点に配置しないとうまく回転してくれません。 六角、ZAM3D、VS2008の パッケージ一式はこちら 注意: キオ式 初音ミクの XAML 化データに関しては、本家 キオ式アニキャラ3D の "当方の3Dデータ、動画の使用条件について" を参照してください。 注意: 今回使用したミクのデータは、V1.1-1.5 ぐらいで、少々古いです。また、ごみが混じっていたり、消し忘れの球が見えていたりしますが、ご愛嬌ということで w March 23 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 となる。
右項のクオータニオンの回転が0の場合は上の絵のように回転面に対して、右項Rを回転させるだけでわかりやすいが、右項のクオータニオンの回転が0ではない場合に、Rにさらに回転が加わったものに対して、左項のクオータニオンの回転が合成されるため、先の資料を読んでいても、よくわからない。
そこで、XAML でプログラムを作って、実際に動作を確かめてみた。Form 左のスライダーが、左項クオータニオンの X, Y, Z, W、右側のスライダーが 右項のクオータニオンの X, Y, Z, W。ピンクのベクトルが 左項のクオータニオン、黄色のベクトルが右項のクオータニオン、黒のベクトルが積のクオータニオン。スライダーで、左項、右項のクオータニオンを動かすと、結果としての積のクオータニオンを表示するWPFアプリケーション。
スライダーの値を読み取って、クオータニオンの積を計算し、表示する処理の一部抜粋。
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 は、このクオータニオンの積も提供してくれているので、とても処理が楽。素晴らしい。 March 19 カメラの移動方法を調べていたら、そのものずばりの MSDN キューブ アニメーションのデモ を発見。これを組み込めば簡単に視点の変更ができる。 おお、もうひとりのおやじもこれを組み込んだのね。さすがだ。 と思い、デモソフトの Trackball.cs のコードをながめてみたら・・・外積を使っている?と不思議に思い、ソースを読んでみた。 次の図のようにマウスで左から右へドラッグし、ミクを中心にカメラを右方向に動かす場合、Y軸を中心にカメラを右方向に回転させることになります。では、どのようにY軸を中心にと判断しているかというと、カメラの視線ベクトルとカメラの移動ベクトルの外積から回転軸を求めています。外積を使えば、マウスの移動角度に応じて、斜めに回転する回転軸も簡単に求めることができます。
なるほど・・・外積ってこういう利用方法があるだぁ・・・と感動。ちなみに下が外積を簡単に説明した図。なつかしい・・・
と、さらに ソースを読んでいったところ、回転軸が求まったところで、クオータニオンの計算があり、意味不明 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。 March 17 六角大王スーパーには、モーフィングという機能が付いていて、次のようにいろいろなモーフィングを登録することができる。
なんとキオ式ミクには驚くほどのモーフィングが登録されており、たとえばウィンクや、笑うなどがボタン一発でできるようになっている。恐るべし・・・。
この WPFデータを作ることはできるので、うまく使えば・・・あんなことや・・・こんなことが・・・ いろいろ試してみたが、マテリアルの色がうまく乗らない。たとえば、髪飾りのピンクと目の色が同じマテリアルにマップされており、髪飾りをピンクにすると、白眼もピンクになってしまう。しょうがないので、六角上でさらに細かく分割しました。 あまり細かくすると、ZAM 3D へのインポートが大変になるので、顔(目、上の歯、下の歯、口の中)、ヘッドセットや、髪飾りは、各1つにグループ化しました。
ようやくこれで ZAM 3D上で色が重ならずに、着色ができた。
ということで、現在のデータ(六角、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 でコンパイル&実行してあげると、そのまま踊りだします。
ZAM 3Dでは、左下にある球体でトラックボールのようにオブジェクトの方向を変更することができ、かなり簡単にアニメーションを作成することができます。ただし、ZAM 3D の制限として、1つのアニメーションしか作成できないので、ちょっと不便です。複数のアニメーションを設定できると楽なんですけど・・・ それから、ちょっとまえのデータですが、MikuMikuDance のスクリーンショットと実行ファイルをさらしておきますw。
なお、うしろのビデオは、WMV を WPF でリアルタイムに変形&再生するように、Expression Blend で追加したものです。WPFすげー。 それから、これはカメラのパン、ズーム、それから回転を行うデモ。 http://uchukamen.com/WPF/MikuData/KioVS.exe
コード的には、ズームはとても簡単。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 March 14
ちょっと前の話だが、MIX08 - Mobile Devices and Microsoft Silverlight のデモビデオがアップされている。
http://visitmix.com/blogs/2008Sessions/T12/
3Dはハードウェアアクセラレータが乗らないだろうから、厳しいだろうな・・・
でも、ビデオなど、同じXAMLでかけるのはでかい。
今の予定では、次の通り。Windows CE 6 ベースになる。
Silverlight 1.0 for mobile CTP Q2 CY2008
Silverlight 1.0 for mobile RTW Q4 CY2008
Silverlight 2 for mobile CTP Q4 CY2008
Silverlight 2 for mobile RTW Q2 CY2009
かなり気になっている。 March 13
六角→ ZAM 3D でテクスチャの喪失があるものの、ある程度のデータ移行が可能。
Shade に関してはえムナウさんからのレポートを期待。
データに関しては、勉強会後に Shareベースに切り出した XAML データが
えムナウさんから放流される見込み。
ここでめでたく、初音ミク XAML化計画は当初の目的達成。
それにしても、XAML 偉大だ・・・初音ミク がリアルタイムで踊ってくれる。 w
ここから先は、福井勉強会 ネタばらになりそうなので、自粛 w。
March 12 ようやく、すべてのパーツを ZAM 3Dにインポートするところまできました。ところが、くつかのテクスチャーがドロップしてしまいます。原因未調査です。次に、ZAM 3Dでパーツの関連を付けて、Bone も入れて、回転軸を調整したのが次の状態。
これを Expression Blend で開いてみる。
OKのように見えるが、オフセットが反映されていない。このため、そのままで頭を回転するとこんな情けないことに orz
なんでだろう・・・ やっつけで、Blend で細かく調整すればできないことはないのだけれど、毎回ZAM 3Dからインポートするたびに調整していたのではやっていられないです。でも、今日は眠いのでここまでにして、調査は先送りにします。 今の段階でコンパイルして表示できるようになったので、データをアップしておきます。
キオ式 初音ミクの XAML 化データに関しては、本家 キオ式アニキャラ3D の "当方の3Dデータ、動画の使用条件について" を参照してください。 キオ式 初音ミクの XAML 化データ Miku Kio V2.zip 約 3MB この時点での問題点を整理しておくと、 - 一部のテクスチャーがインポートできない。
- オフセットが反映されない。
- 体をパーツにばらしている単純な方法なので、パーツの接続面が汚い。
- 眼だけはパーツとして切り出しましたが、顔の表情が変えられない。
- 指先を変えられない。これは、テーマソングのときに致命的*(意味不明)
とりあえず、デモ用の元データとしては使えそうですが、いまいちです。もう一人が Shade で切り出しているので、そちらの成果を期待しています。 |