2011年6月28日火曜日

LinuxでC#.NET + OpenGL+GLSLでMMD描画してみた

アニメーションするならやっぱりシェーダーも入れた方がいいのでGLSLに手を伸ばしてみました。以前やったcgfxはHLSLにかなり近い構造になってました。GLSLも似た構造だと効いてたので安心してましたがとんでもない!まさか頂点シェーダー部分とピクセルシェーダー部分のソースを別けないといけないとは思いませんでした。

とりあえずXNAのBasicEffectクラスを意識してこのようなクラスを用意しました。
using System;
using System.IO;
using System.Collections.Generic;
using Tao.OpenGl;

namespace WindowsForm
{
 public class BasicEffect
 {
  /// <summary>
  /// シェーダープログラム
  /// </summary>
  public int Program{get;private set;}
  
  /// <summary>
  /// 開放確認
  /// </summary>
  public bool IsDisposed{get; private set;}
  
  /// <summary>
  /// コンストラクタ
  /// </summary>
  public BasicEffect ()
  {
   // シェーダーオブジェクト作成
   int VertexShader = Gl.glCreateShader(Gl.GL_VERTEX_SHADER);
   int PixelShader = Gl.glCreateShader(Gl.GL_FRAGMENT_SHADER);
   
   // シェーダーファイル読み込み
   string[] path = {"effect/basic.vert", "effect/basic.frag"};
   int[] shader = {VertexShader, PixelShader};
   
   StreamReader read = null;
   
   for(int i = 0; i < path.Length; i++)
   {
    try{
     // ファイル読み取り
     read = new StreamReader(path[i]);
     string[] data = new string[]{ read.ReadToEnd() };
     
     // ソース作成
     Gl.glShaderSource(shader[i], 1, data, IntPtr.Zero);
     
     // コンパイル
     int compiled = 0;
     Gl.glCompileShader(shader[i]);
     Gl.glGetShaderiv(shader[i], Gl.GL_COMPILE_STATUS, out compiled);
     
     if (compiled == Gl.GL_FALSE)
      throw new Exception("Compile Error.");
    }
    catch(Exception e){ throw e; }
    finally
    {
     if(read != null)
      read.Close();
     
     read = null;
    }
   }
   
   // シェーダープログラムオブジェクト作成
   Program = Gl.glCreateProgram();
   
   // シェーダーオブジェクトの登録
   Gl.glAttachShader(Program, VertexShader);
   Gl.glAttachShader(Program, PixelShader);
   
   // 不要になったシェーダーオブジェクトを開放
   Gl.glDeleteShader(VertexShader);
   Gl.glDeleteShader(PixelShader);
   
   // シェーダープログラムのリンク
   int linked = 0;
   Gl.glLinkProgram(Program);
   Gl.glGetProgramiv(Program, Gl.GL_LINK_STATUS, out linked);
   
   if (linked == Gl.GL_FALSE)
    throw new Exception("Link Error.");
   
   // シェーダーを適用(シェーダーを切り替える場合に必要) 
   Gl.glUseProgram(Program);
  }
  
  /// <summary>
  /// シェーダー切り替え
  /// </summary>
  public void Active()
  {
   Gl.glUseProgram(Program);
  }
  
  /// <summary>
  /// 開放
  /// </summary>
  public void Release()
  {
   if(!IsDisposed)
   {
    Gl.glDeleteProgram(Program);
    IsDisposed = true;
   }
  }
 }
}
頂点シェーダー
void main(void)
{
 // ピクセル座標
 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
 
 // 法線
 vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
 
 // ピクセル色
 gl_FrontColor.rgb = 0.5 * normal + 0.5;
 gl_FrontColor.a = 1.0;
}
ピクセルシェーダー
void main (void)
{
 gl_FragColor = gl_Color;
}

シェーダーの実装には床井研究室さんやProject ASURAさんの記事を参考にさせていただきました。シェーダーのソースも丸々使わせていただきました。

使ってみて結構不便だと思いました。まぁ私がHLSLに慣れてるので前回のCgfxの方が親しみやすかったという理由なんですけど。Linux上でCgかCgfxのどちらか使えないかな?

さらにGTAViewerのMMDインポーターを移植してMMDを描画できるようにしました。
ボーンやテクスチャなどを無視して形状だけ表示したものがコチラです。

video

かなり遅いです。てかシェーダー使わない方が速いです。高速化の手段を考えないとまだまだ実用化は出来ないレベルです。

ただ、かなりめんどうですけどGTAViewerのLinux+OpenGLへの移植は何とかできそうですね。まぁ先に今やってるのを完成させないといけないんですけどね。本格的に扱うのはまだまだ先ですね。

描画周りのソース
/// <summary>
/// 描画
/// </summary>
public void Draw()
{
    // ジオメトリ描画
    foreach(Geometry geometry in Geometries)
    {
        // 頂点データ設定
        Gl.glVertexPointer(3, Gl.GL_FLOAT, 0, geometry.Vertices.ToArray());

        // 法線設定
        Gl.glNormalPointer(Gl.GL_FLOAT, 0, geometry.Normals.ToArray());

        // UV座標設定
        Gl.glTexCoordPointer(2, Gl.GL_FLOAT, 0, geometry.Texcoords.ToArray());

        // マテリアル毎に割り当てられてる面を描画 
        foreach(Material material in geometry.Materials)
            Gl.glDrawElements((int)material.Triangle.Type, material.Triangle.Indices.Count, Gl.GL_UNSIGNED_SHORT, material.Triangle.Indices.ToArray());
    }
}

2011年6月26日日曜日

LinuxでC#.NET + OpenGLやってみた【MonoDevelop×Tao Framework】

ふと「C#.NETをLinux上で動かせないかな?」と思い、急遽やってみました。
とりあえず用意した開発環境↓

OS:Ubuntu11.04 USBブートで起動させる
開発ツール:MonoDevelop、Tao Framework

『MonoDevelop』は、NET Framework 互換環境をLinuxで実装する『Mono』の開発エディタ。Formアプリケーション作成はもちろんのこと、VisualStudioで作ったソリューションファイルを開くことも可能。VC#で作ったプログラムをLinuxに簡単に移植することが出来る優れものです。

『Tao Framework』は、C#でOpenGLを使えるようにしてくれるラッパークラスライブラリです。ちなみに、どちらも無料でそろえることが出来ます。

まずは導入から。Synapticパッケージマネージャを起動して『monodevelop』で検索し、一番上に出てくる項目にチェックを入れます。

他にも色々出てきますがお好みで。私はPythonとJavaにチェックを入れました。


次にTao Frameworkの導入。『mono tao』と検索すると、MonoDevelop用のTao Frameworkの参照ライブラリが出てきます。VC#で『参照の追加』で追加できるアレです。

これも色々と出てきますが、今回は『gl』・『glu』・『glut』を使うので『libtaoframework-freeglut』と『libtaoframework-opengl』にチェックを入れます。
『libtaoframework-freeglut』がglutを、『libtaoframework-opengl』がglとgluをサポートしてます。


C#なのにわざわざglut使っちゃいました。次はglut使わずに描画する予定です。
以下はMonoDevelopの起動画面です。VisualStudioやEclipseと大差ないので問題なく使えました。


ソリューションディレクトリから『参照』→『参照アセンブリの編集』からTao frameworkライブラリを追加します。これでC#上でOpenGLが使える環境が整いました。



早速OpenGLでポリゴンを描画するプログラムを組んでみました。

 実行画面。OSの切り替えが面倒だったので、XPからPortable-VirtualBoxでLiveUSBを起動させました。キャプチャはUbuntu側で行ったんですけど、メモリが足りないせいかカクカクしてます。実際は結構ヌルヌルと動いてます。

video

将来的には、今作ってるC#+XNAのアプリが完成したらコレを使ってLinux版作ってみようかな?と思ってます。

ただ、アニメーションさせるのにシェーダーが必要なんだけど、
シェーダー動くのかな?

今回のプログラムのソースコード
using System;
using System.Text;
using System.Windows.Forms;

using Tao.FreeGlut;
using Tao.OpenGl;

namespace WindowsForm
{
 public class Program
 {
  static float angle = 0;

  /// <summary>
  /// メインルーチン
  /// </summary> 
  public static void Main(string[] args)
  {
   //Application.EnableVisualStyles();
   //Application.SetCompatibleTextRenderingDefault(false);
   //Application.Run(new Form1());
   
   int argc = args.Length;
   StringBuilder[] argv = new StringBuilder[args.Length];
   
   for(int i = 0; i < argc; i++)
    argv[i] = new StringBuilder(args[i]);
   
   Glut.glutInit(ref argc, argv);
   Glut.glutInitDisplayMode(Glut.GLUT_RGBA | Glut.GLUT_DEPTH | Glut.GLUT_DOUBLE);
   Glut.glutInitWindowSize(640, 480);   
   Glut.glutCreateWindow("Linux C# + OpenGL ver.glut");
   
   Glut.glutTimerFunc(0, Timer, 1000 / 60);
   Glut.glutDisplayFunc(Draw);
   
   Gl.glClearColor(0, 0.5f, 1f, 1f);
   Gl.glEnable(Tao.OpenGl.Gl.GL_DEPTH_TEST);
   Gl.glShadeModel(Tao.OpenGl.Gl.GL_SMOOTH);
   
   Gl.glMatrixMode(Gl.GL_PROJECTION);
   Gl.glFrustum(-1,1,-480/640.0, 480/640.0, 1, 100);
   Glu.gluLookAt(0, 0, -5, 0, 0, 0, 0, 1, 0);
    
   Glut.glutMainLoop();
  }
  
  /// <summary>
  /// タイマー
  /// </summary> 
  /// <param name="second">
  static void Timer(int second)
  {
   angle++;
   
   Glut.glutPostRedisplay();
   Glut.glutTimerFunc(second, Timer, second);
  }
  
  /// <summary>
  /// 描画
  /// </summary> 
  static void Draw()
  {
   Gl.glClear(Tao.OpenGl.Gl.GL_COLOR_BUFFER_BIT | Tao.OpenGl.Gl.GL_DEPTH_BUFFER_BIT);
   
   // 行列
   Gl.glMatrixMode(Gl.GL_MODELVIEW);
   Gl.glLoadIdentity();
   Gl.glRotatef(angle, 0, 1, 0);
   
   Gl.glColor3f(1, 0, 0);
   
   Gl.glBegin(Gl.GL_TRIANGLES);
   Gl.glVertex3f(-1, -1, 0);
   Gl.glVertex3f(1, -1, 0);
   Gl.glVertex3f(1, 1, 0);
   Gl.glEnd();
   
   Glut.glutSwapBuffers();
  }
 }
} 

追記:
Tao.Platform.X11を参照追加することで『glx』も使えるようになるみたいです。
XWindowも出来る!すごいですね。

他にも物理演算やOpenALも対応してるみたいです。優秀だなぁ。

2011年6月24日金曜日

Blender2.58版メタセコイア・MMDスクリプトリリース

Blender 2,58リリースされてたんですね・・・全然知らなかったです。
先ほどユーザーの方からメールを頂いて、Blender2.58の更新情報と修正内容を教えてもらいました。

メールを送ってくださった方、どうもありがとうございました。

早速メタセコイアとMikuMikuDanceのスクリプトを更新しましたので、新バージョンのBlenderをお使いの方はコチラのほうをご利用下さい。

なお、前バージョン2.57のスクリプトでは動作不可能です

Blender関連記事
Blender 2.58がリリース!
ダウンロードページ

新たな開発作品置き場→【開発作品倉庫

開発倉庫の更新

更新内容①
今まで使っていたアップローダーの都合上アップロードできないツールがあったので、アップローダーを変更しました。『ZE10の開発倉庫』自体は消さずに残しています。

今までよりも大きなサイズのファイルをアップロード出来るようになりましたが、それぞれのリンクから直接ダウンロードできなくなりました。多少不便かもしれませんがご了承下さい。

更新内容②
PMDForColladaのバグ取りを行いました。さらに、新たにSlimDX版のツールを登録しました。XNA版が動かない方はコチラのほうをお試し下さい。

・動作環境
.NET Framework 4.0 Client Profile
SlimDX net4.0 x86(同梱)

2011年6月19日日曜日

GTAViewer 現在の開発状況①

現在までの開発状況を動画にまとめました。
興味のある方は是非御覧下さい。

・DFF、TXD読み込み対応
・PMD、VMD読み込み対応
・Xファイル読み込み対応
・BVH読み込み対応
・アニメーション再生機能追加
・Collada出力対応

こんなとこです。

2011年6月14日火曜日

XNA4.0で画像をプロジェクトリソースに含めず直接読み込む

今回のツールを作成するのに構造上必要だったのでまとめました。
実際のところ大分前に作ってたんですが今日になって一つ問題が発生し、その解決方法を忘れないうちにまとめておこうと思ったのが事の発端です。

XNAはゲームに使用するファイルをコンテントプロセッサでビルドして『.xnb』フォーマットに変換して再度ファイルを読み込むという仕様になっています。

プログラム起動後、プロジェクトリソースに含まれてないモデルデータ・音声データ・画像データは一部を除き自力で読み込む他ありません。

幸いにも画像に関してはこのようなメソッドが用意されてます。
public static Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream)
// graphicsDevice グラフィックデバイス
// stream データストリーム
このメソッドに読み込みたいデータのFileStreamを渡すことでコンテントプロセッサを通すことなく画像データを読み込むことが出来ます。

しかし困ったことにこのメソッド、.png, .jpg, .gifの3種類しか読むことが出来ません。
コンテントプロセッサでは.bmp、.tga、.ddsを使うことが可能なんですが、FromStreamメソッドだとこの3種類のファイルを読み込むことが出来ません。

そこで、C# .NETにデフォルトで用意されてるこのクラスを使います。
using System.Drawing;
public static Image.FromFile(string filename, bool useEmbeddedColorManagement)
// filename 読み込む画像のファイルパス
// useEmbeddedColorManagement 色管理情報を使用するかのフラグ
これを使用することで、.bmp、.jpg、.png、.gifはもちろん、.ico、.wmf、.exif、.tiffといったファイルも読み込むことが出来ます。

このクラスを試用してファイルを読み込み、そこから色情報を表すバイト配列を抽出します。
using System.Drawing;

int stride = 4; // iピクセル辺りの色情報を表すデータの数(RGBAなので4つ)
Bitmap image = (Bitmap)Image.FromFile(filename, true);
byte[] pixel_data = new byte[image.Width * image.Height * stride];

// 画像をロックする領域
Rectangle lock_rect = new Rectangle{ X = 0. Y = 0, Width = image.Width, Height = image.Height };

// ロックする
// Rectangle rect ロック領域
// ImageLockMode flags ロック方法 今回は読み取るだけなのでReadOnlyを指定する
// PixelFormat format 画像のデータ形式 RGBAデータがほしいのでPixelFormat.Format32bppPArgbを指定する
BitmapData bitmap_data = image.LockBits(
lock_rect,
ImageLockMode.ReadOnly,
PixelFormat.Format32bppPArgb);

// 色情報取得
for (int y = 0; y < image.Height; y++)
{
  for (int x = 0; x < image.Width; x++)
  {
    int pixel_target = x * stride + bitmap_data.Stride * y;
    int array_index = (y * image.Width + x) * stride;

    // ロックしたポインタから色情報を取得(BGRAの順番で格納されてる)
    for(int i = 0; i < stride; i++)
      buf[array_index + i] = 
          System.Runtime.InteropServices.Marshal.ReadByte(
          bitmap_data.Scan0,
          pixel_target + stride - ((1 + i) % stride) - 1 );
  }
}

// ロックしたらアンロックを忘れずに!
image.UnlockBits(bitmap_data);
これで色データを取ることができます。 ロックのフォーマットには『PixelFormat.Format32bppArgb』ではなく『PixelFormat.Format32bppPArgb』を指定してます。

実はコレが今日陥った問題の原因です。
フォーマットに『PixelFormat.Format32bppArgb』を指定すると、.pngのように透過色を持つデータフォーマットの場合、透過データが消えてしまうことがあります。

以下は実際に透過色データをもったpngファイルをPixelFormat.Format32bppArgb』と『PixelFormat.Format32bppPArgb』で読み込んだものです。
左が『32ビットARGB』、右が『32ビットPARGB』です。


右は背景が透明になり、左は真っ白になっているのがわかると思います。どちらもA, R, G, Bそれぞれに8ビット使用して色データを表しますが、『PixelFormat.Format32bppPArgb』の場合、さらにアルファ コンポーネントに応じてRGBにコンポーネントが前乗算する処理が入ります。あらかじめ赤、青、緑にアルファ値を掛け合わせることで透過色を形成する仕組みらしいです。

これに気付くのに時間が掛かりました。
さて、最後にバイト配列をTexture2Dに渡します。やり方はこのようになります。
using Microsoft.Xna.Framework.Graphics;

// GraphicsDevice graphicsDevice グラフィックデバイス
// int width 画像の縦幅
// int height 画像の横幅
// bool mipMap 完全なミップマップを生成するかのフラグ ここではfalseを指定
// SurfaceFormat format 画像フォーマット RGBA形式なのでSurfaceFormat.Colorを指定
Texture2D texture = new Texture2D(
    GraphicsDevice,
    image.Width,
    image.Height,
    false,
    SurfaceFormat.Color);

// 色データ配列を設定
texture.SetData<byte>(buf);

// ビットマップを開放
image.Dispose();
これで変換完了です。
これで作成したデータはスプライトにもテクスチャーにも使えます。
テクスチャで使用する場合、mipMapはfalse推奨です。

上がmipMapをOn, 下がmipMapをOffにした状態のものです。


このような残念な結果になります。まあミップマップだから当たり前ですね。カメラの距離によって結果が大分変わります。

スプライトで使う分には全く問題ありません。

ちなみに.tgaと.ddsですが、これはSystem.Draing.ImageでもTexture2Dでも読み込むことは出来ません。直接読み込むしかないわけですが、私はめんどくさいのでC++でDirectXを使って.bmp、.jpg、.pngのいずれかに変換するツールを別途用意しました。
HRESULT D3DXCreateTextureFromFileEx(
    LPDIRECT3DDEVICE9 pDevice,
    LPCTSTR pSrcFile,
    UINT Width,
    UINT Height,
    UINT MipLevels,
    DWORD Usage,
    D3DFORMAT Format,
    D3DPOOL Pool,
    DWORD Filter,
    DWORD MipFilter,
    D3DCOLOR ColorKey,
    D3DXIMAGE_INFO *pSrcInfo,
    PALETTEENTRY *pPalette,
    LPDIRECT3DTEXTURE9 *ppTexture
);

HRESULT D3DXSaveTextureToFile(
  LPCTSTR pDestFile,
  D3DXIMAGE_FILEFORMAT DestFormat,
  LPDIRECT3DBASETEXTURE9 pSrcTexture,
  CONST PALETTEENTRY * pSrcPalette
);
XNA側からこの変換ツールをProcessクラスで呼び出し、出力した画像を先ほどの方法で読み取るのです。一手間掛かりますが、これで.tgaと.ddsファイルも読み込むことが出来ます。

実際私はこの方法で.tgaファイルを読み込んでます。
ちなみに、MSBuildクラスライブラリを使うことでXNAのコンテントを独立した形でビルドすることが出来るようです。


これを使うと、実行後に外部のモデルデータ・音声データ・画像データ等をビルドして.xnbファイルに変換することが出来るらしいです。
まぁ私はソース見ましたけどチンプンカンプンでした(笑)。

さて、GTAViewerの更新情報もちょこっと。
Xファイル読み込みにバグがあったので修正しました。それと武器シェーダーも少し修正を加えました。前回よりもメタリック感がでました。

キューブマップ覚えたのが1年前で、それからずっと扱ってなかったんですけどなんとか実装できました。結構覚えてるもんですね。




マップはバトーキン島を使わせていただきました。
マップがいいと移りこみもいいですね。
最初武器シェーダーがうまくいった時には興奮したなぁ・・・

video

2011年6月13日月曜日

PMDForColladaの後継機?『GTA Viewer』

一週間前からこのような(↓)ツールを作っていました。
GTAのモデルフォーマット『.dff』を読み込んでXNAで描画するツールです。

動画では紹介してたんですけど、ブログの方で紹介してなかったので書きました。
ブログのタイトルどおりGTA読み込みだけでなく、PMDForColladaの機能を
付加させる予定です。PMDForColladaの次期バージョンといったツールです。

現在実装した機能
・.dff、.txdフォーマット読み込み+描画
・.pmd読み込み+描画
・武器モデル用キューブマップ
・ツリー構造情報一覧
・テクスチャ一覧

また、今回新たな機能も加えてます。
・xファイル読み込み(開発者本人は使う予定全く無し)
・武器モデル用キューブマップ+フォンシェーダー+ランバート(のようなもの)
・背景用マップデータ読み込み

以下は起動画面です。
モデル・武器は.dff、マップはxファイルで、武器にはエフェクトかけてます。

video


今後の実装予定
・Colladaフォーマットの読み込み+書き出し(まずは書き出しが先)
・.vmd、.bvh等のモーションファイル読み込み+再生機能

最終的には、『Collada』・『MMD』・『X File』・『Metasequoia』・『GTA』の
五つのフォーマットを相互変換できるようになればいいかな?と考えてます 。

まぁ自分が出来る範囲で、地道にコツコツと作り上げてきます。
なお、PMDForColladaはコチラのツールに移植するので、今回限りで開発を打ち切るつもりです。

2011年6月2日木曜日

IK計算処理の勉強 Part.2

前回の計算式がやっぱりおかしかったので修正しました。
以下は修正後のツール。ダイブマシに動作するようにはなりましたけど
MMDに比べたらまだまだって感じです。

video

ちなみに修正後に参考にしたサイトのソースを見比べてみたら、計算処理は殆ど同じようなものになってました。結局図だけで出来ちゃいました。あとはMMDのIKに設定されてる重みをどう使うかを理解したら完成しそうです。

以下は今回の計算の為に作成したボーンクラスの一部。
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;

namespace InverseKinematics
{
  /// <summary>
  /// ボーンクラス
  /// </summary>
  class Bone
  {
    /// <summary>
    /// 名前
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// オフセット
    /// </summary>
    public Vector3 Offset { get; set; }

    /// <summary>
    /// ローカル行列
    /// </summary>
    public Matrix Transform { get; set; }

    /// <summary>
    /// ワールド行列
    /// </summary>
    public Matrix AbsoluteTransform
    {
      get
      {
        Matrix transform = Matrix.CreateTranslation(Offset) * Transform;
        if (Parent != null)
          transform *= Parent.AbsoluteTransform;

        return transform;
      }
    }

    /// <summary>
    /// ワールド座標
    /// </summary>
    public Vector3 Position
    {
      get
      {
        return Vector3.Transform(Vector3.Zero, AbsoluteTransform);
      }
    }

    /// <summary>
    /// 親
    /// </summary>
    public Bone Parent { get; set; }

    /// <summary>
    /// 子供
    /// </summary>
    public List<bone> Children { get; set; }

    /// <summary>
    /// 選択中
    /// </summary>
    public bool Selected { get; set; }

    /// <summary>
    /// IKボーン
    /// </summary>
    public bool IK { get; set; }

    /// <summary>
    /// IKボーンの座標
    /// </summary>
    public Vector3 IKPosition { get; set; }
        
    /// <summary>
    /// チェイン
    /// </summary>
    public int Chain { get; set; }

    /// <summary>
    /// 再帰
    /// </summary>
    public int Iterations { get; set; }

    /// <summary>
    /// 重み
    /// </summary>
    public float Weight { get; set; }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public Bone()
    {
      Children = new List<bone>();
      Transform = Matrix.Identity;
    }

    /// <summary>
    /// IKの計算
    /// </summary>
    public void SetIK()
    {
      // 再帰数分ループ
      for (int i = 0; i < Iterations; i++)
      {
        // 最初のターゲット
        Bone target = this.Parent;

        // 影響数分親をたどる
        for (int j = 0; j < Chain; j++)
        {
          if (target != null && target.Parent != null)
          {
            Vector3 before = Vector3.Normalize(Parent.Position - target.Parent.Position); // IKターゲットボーンまでのベクトル
            Vector3 after = Vector3.Normalize(IKPosition - target.Parent.Position); // IKボーンまでのベクトル
                        
            // 2つのベクトルの外積(上軸)を算出
            Vector3 axis = Vector3.Cross(before, after);
                        
            // 軸の数値が正常で、2つのベクトルの向きが不一致の場合(ベクトルの向きが同じ場合、外積は0になる)
            if (!float.IsNaN(axis.Length()) && axis != Vector3.Zero)
            {
              // 法線化
              axis.Normalize();
                            
              // 2つのベクトルの内積を計算して、ベクトル間のラジアン角度を算出
              float angle = (float)Math.Acos(MathHelper.Clamp(Vector3.Dot(before, after), -1, 1));
                            
              // 任意軸回転クォータニオンを作成
              Quaternion qt = Quaternion.CreateFromAxisAngle(axis, angle);

              // 変換行列に合成
              target.Transform *= Matrix.CreateFromQuaternion(qt) ;
            }

            // 次のボーンへ
            target = target.Parent;
          }
        }
      }
    }
  }
}

2011年6月1日水曜日

IK計算処理の勉強 Part.1

IKの仕組みはなんとなくわかるけどアルゴリズムを全く知らないので今回から勉強することにしました。『XNAでMMD読み込み【アニメーションの実装 Part5】』をやってから早一ヶ月強、ようやく取り組みだしました。

まぁXNAのインポーターもしかりPMDForColladaもしかり、色んなところでIKが出てくるのでここらが丁度いいかな?という感じではじめて見ました。

ネットで色々調べ、ここを参考にさせていただきました。

図でわかりやすく説明されてたので、まずは計算式やサンプルソースを一切見ず、図からおおよその計算式を組み立ててみました。今回採用したアルゴリズムは『CCD』です。

以下はIK計算用に作成したツール。
親ボーンを選択し、親からのオフセット値を入力してボーンを追加します。
IKボーンはさらに『影響下ボーン数』、『影響度』、『再帰回数』を入力します。
今現在、『影響度』は反映されてません。

video

形にはなったけど、なんか違う気がする。でも初めてにしては上出来だと思う。
参考サイトの動画みたいに、ヌルヌルと動かないかな?

次回は図以外も見てIKについて理解を深めていこうと思います。