|
|
|
||
【Vocaloid初音ミクとMikuMikuDanceという驚愕のソフトウェア】 |
||
|
【リンクフリー】 私設研究所ネオテックラボ Neo-Tech-Lab.co.uk 【記載者】 東京工業大学 ソリューション研究機構 (特任教授) 上田智章 |
![]() |
|
ここにチェックボックス型外部コンテンツ・メニューが入ります。 | ||
|
|
|
||
|
【メニュー】 [index] ■メモ [PMD1] ●【VBA】ポリゴン・フィルとテクスチャー・マッピング [PMD2] ●【VBA】透視変換 [PMD3] ●【VBA】光源計算 [Sensor]★【NyARToolkitCS】拡張現実センサ【ソース公開】C# [PMD6] ■【NyARToolkitCS】MMDのPMDで拡張現実(モデル描画)C# [PMD4] ■【NyARToolkitCS】MMDのPMDで拡張現実(表情処理)C# [PMD5] ●IK bone制御 [Kinect]★【Kinect Sensor】拡張現実センサ2【ソース公開】C# [TTS] ★【iSpeech APIの音声合成TTS】【ソース公開】JavaScript [シンセ]★『JavaScriptでWebシンセサイザ/ボーカロイド(なんちゃって版)を自作してみた』 |
![]() |
【Webシンセサイザ/ボーカロイドのページ】JavaScriptだけでシンセサイザやボーカロイドの歌唱/音声合成を実装してみようとする試みを始めました。【対象ブラウザ】Google Chrome12, Mozilla FireFox5, Apple Safari ●『なんちゃって初音ミク』NTL版Text-To-Speech API [JavaScript Ver.0.06] ●『なんちゃって初音ミク』Ieavan Polkka [JavaScript Ver.0.06] スタンダード・ドラム・セット付 音が出るまでに10~30秒を要します。気長にお待ちください。 【WebGLのページ】JavaScriptでリアルタイム3次元グラフィックスが楽しめる時代の到来までもうすぐです。現在、Google Chrome12、Mozilla FireFox5、及び開発版のSafari、Operaで使うことができます。 WebGLは、JavaScriptエンジンv8とDirectXを利用して高速化したOpenGLを融合して構成されたものです。 2011年5月に指摘されたセキュリティー問題はありますが、処理速度の高速性から利便性も高いので、将来的には普及するのではないでしょうか? ●WebGLとは? ●事前準備:利用できるウェブブラウザは? ●事前準備:FireFox5でWebGLが動作しない場合には? ●WebGLのサンプル ●WebGLのデモ ●クロスドメインテクスチャーを将来も利用できる方法 ●ローカルのPMDファイルを読み込んでJSON形式に変換し、3次元モデルを表示 ●物理エンジンBullet.jsのWebGLデモの紹介(Pl4n3氏のragdollデモの修正版)[btBoxShapeとbtCapsuleShape] |
![]() ![]() 【図1】上図3例は表示例 ![]() 【図2】起動直後の表示画面。複数のウェブカメラがあるときは選択します。次にPMDファイルを選択して、OKボタンをクリックします。 ![]() 【図3】PMDファイル選択画面の例。MikuMikuDanceからUserFile/Modelフォルダ内のデータを全てコピーすれば表示可能なモデルが増えます。 ![]() 【図4】pironさんのmikuモデルを表示したところ。多くのユーザーモデルではテクスチャーマッピングに様々な形式が使用され、名付け規則もあるようであるが、それらには対応できていません。pironさんのモデルでも第1テクスチャーファイル名が『MIKUHada.png*h.spa』となっており、.pngの後に*を使った形式が指定されていますが対応できておらずエラーとなります。上のカットは、デバッガーでエラー部分をスキップさせて表示させました。これに関しては以前にMikuMikuDanceのREADMEで仕様を読んだ気がするのですが、対応するかどうか悩むところです。 【参考】MikuMikuDanceのreadme.txtにはSphere Mappingに関して以下の記述があります。 【Ver.5.09】(2009/08/25) ・スフィアマップ機能追加 スフィアマップ用BMPを拡張子sphとしてモデル・アクセサリのテクスチャに指定することにより、スフィアマップが展開されて表示されます 【Ver.5.10】(2009/08/29) 【Ver.5.11】(2009/09/7) ・スフィアマップの計算法をVer.5.09に戻す ・テクスチャBMP上にスフィアマップ表示できる仕様に変更 テクスチャ名を "テクスチャ名.bmp/スフィア名.bmp"にすることにより テクスチャ上にスフィアマップが展開されます(例:fuk1.bmp/metal.sph) *ただし、PMDフォーマットはテクスチャ名の長さが19文字分しか無いため、モデルで使用する場合はファイル名全体(テクスチャ+スフィア)で19文字以下に収める必要があります(アクセサリ(xファイル)は256文字までOK) ・その他バグ色々修正 【Ver.5.12】(2009/09/10) ・テクスチャBMP上にスフィアマップ表示する際のファイル名を"テクスチャ名.bmp*スフィア名.bmp" に変更。Ver.5.11で"/"を使っていた方は"*"に変更して下さい。 ・スフィアマップのファイル名拡張子"sph"を、"spa"にすることによりスフィアマップの展開が乗算でなく、加算で行われる仕様を追加 ●もしかしたら、その後、仕様変更があったかもしれないが、私はまだblendも理解できていないので『テクスチャ』上にSphere Mappingを乗算/加算で展開というのをどうすればよいのか、わからない。 ![]() |
【PMDを読み込み、NyARToolkitCSで拡張現実する】個人的な事だが、Visual C# 2010でManaged DirectXを使う方法が漸くわかったので、NyARToolkitCSでPMDの拡張現実をして遊ぶことにした。その方法に関しては、リンク先の【事前準備作業1】の【メモ】【Visual C# 2010 Express EditionでNyARToolkitCSを動かすには】に記載した。要するに、①Managed DirectXの参照を取る ②Frameworks 2.0~3.5を対象にコンパイルする の2点でManaged DirectXを使うことができた。 【まずはNyARToolkitCSをダウンロードしよう】【NyARToolkitCS】 ホームページNyARToolkitCSトップページ 【MikuMikuDanceの最新版をダウンロードしよう】【MikuMikuDanceのマルチモデルの最新版をダウンロード】【C#プロジェクトファイル(ARToolKit_PMD_Load)のダウンロード】最新版はMikuMikuDanceの標準モデルである『初音ミクmetal』のSphere Mappingにも対応しています。下のソースはV003版なので、Sphereに関しては表情編のソースを参考にされることをお勧めします。 【ダウンロード】プロジェクトファイル一式(Version0.05 表情処理のテスト版) V005は表情を表情番号ではなく、表情名称で探すようにしたバージョン。 異なるモデル(初音ミクmetalicを含む)でもそれなりにデモできるはずです。 【ダウンロード】プロジェクトファイル一式(Version0.03 表情処理のテスト版) V003は表情を表情番号で指定して実行します。 ちびミクと初音ミクver.2.0以外では動作しません。 【ダウンロード】プロジェクトファイル一式(Version0.02 Sphere Mapping対応版) 【ダウンロード】プロジェクトファイル一式(Version0.01) 【注意事項】当然のことながらバイナリー(実行形式ファイル)が添付されていませんので、動作させるにはビルドさせるためにVisual C# 2010 Express Edition(無料)が必要です。 [ARToolKit_PMD_Load] +-[PMD] 【MikuMikuDanceのモデルデータ】 | MikuMikuDanceのUserFileフォルダの下のModelフォルダの中身を全てここにコピーします。 +-[AR_Data] 【NyARToolkitCS】camera_para.dat, patt.hiroをここに格納 +-[Library] 【NyARToolkitCS】 | DirectShowLib-2005.dll, NyARToolkitCS.dll, NyARToolkitCSUtils.dllをここに格納 +-[Marker] 【印刷用マーカー】hiroパターン2.docの一番小さなマーカーがお勧め +-ソース一式とsln(ソリューション) 取り敢えず、Excel VBA版の再現とSphere MappingまでをC# & Managed DirectXで実施。 C#及びDirectX初心者の私にはちょっと困難だった。 苦労したのは、 ①PMDファイルからバイナリーでデータを読み込む PMDファイルをFileStreamにして、それをBinaryReaderで読み込むことで解決した。 ②MMDは右手系、NyARToolkitは左手系なので、3次元座標のZ成分と法線ベクトルのNz成分を反転(-Z, -Nz)して読み込む。 ③PMDの裾やスカートの中が透明化するのを防ぐため、デバイスのレンダリングをCull.Noneモードで実行。 this._device.RenderState.CullMode = Cull.None; // カーリング ④3次元座標と法線ベクトルで材質表示する(PositionNormal)と3次元座標とUV空間でテクスチャーマッピングする(PositionTextured)の共存のさせ方 CustomVertex.PositionNormalTextured形式のVertexBufferを使い、材質(Material)と同数のテクスチャー(Texture)を用意。但し、TextureFileNameが""のときはTextureもnullで定義する。 PMDファイルの材質情報にあるレンダリングを同一材質、同一テクスチャーに対して分割して行う。
int j = 0; int k = 0;
for (int i = 0; i < PMDm.nMaterial; i++)
{
if (PMDm.TextureFileName != null)
{ this._device.SetTexture(0, PMDm.Texture[i]); }
else
{ this._device.SetTexture(0, null); }
this._device.Material = PMDm.Material[i];
k = PMDm.nPoints[i];
this._device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, _nPoints , j, k );
j += PMDm.nPoints[i];
}
という部分。まだ、テクスチャーマッピングではjpgファイルでUV空間が反転してしまう問題には対応していない。エッジも未処理。 MikuMikuDanceの標準モデルはメタル仕様の初音ミクも含め全部表示させたかったので、Sphere Mappingには対応した。 ⑤NyARToolkitCSの座標系単位がmmなので、ミクセル単位のままでは小さく表示されてしまう。なので、読み込み時に座標のみ20.0倍に変更してみた。 私としては、拡張現実で表示させているPMDを幾つかのセンサ入力に反応させて動かしたいだけなので、細かな仕様は積み残したままにするつもりだ。 この場合、Bone制御はVMDからではなく、物理演算やAIプログラムで求めるかもしれないので現在ペンディング。 BoneはオリジナルVertexBufferから各部の座標変換をかけてからコピーするようにすればいいだろうと予想し、取り敢えず表情のコーディングに進もうと思う。 ここまでのソースは大人の都合により『バイナリを配布せずにC#プロジェクト・ファイル(ソース)公開』の方針。 MaterialのEmissiveColorはMikuMikuDanceでは未使用のようなので、PMD読み込み時に PMDm.Material[i].EmissiveColor = new ColorValue(0.0F, 0.0F, 0.0F); とした。 ![]() 【図】概略フロー図 【今後の予定】センサ入力に対応してプログラムで処理を行い、拡張現実で表示中のモデルに反応させることが目的なので、『表情』と『ボーン』を追加しています。『表情』は完成し、現在ボーン制御部分を製作しています。取り敢えず、【トランジスタ技術 増刊】『今すぐ使える パソコン計測USBマイコン基板』の付録基板に内蔵の温度センサの計測値に対応させて何か反応するというようなサンプルプログラムを製作する予定です。(アバウト!) Kinectがセンサ単独で購入できるらしく、USBインターフェースも解析されてオープンソース化されているようです。なんか新しい知的センシングシステムに応用できそうなんでやりたいことは尽きないですね。 【YouTube動画】NyARToolkitCSによる拡張現実のテスト『表情』編 |
【PMDのヘッダー情報、頂点情報、インデックス情報、材質情報を読み出す】
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
namespace PMDMikuDirect3d
{
public partial class SimpleLiteD3d : IDisposable
{
public struct PMD_Header //【PMDファイル ヘッダー情報】
{ public byte[] ID; //ID 3バイト "P","m","d"
public float vNum; // バージョン番号
public byte[] ModelName; //モデル名称 20バイト
public byte[] CopyRight; //著作権情報 256バイト
public string sID; //●Shift_JIS
public string sModelName; //●Shift_JIS
public string sCopyRight; //●Shift_JIS
}
public struct PMD_vertex //【PMD_頂点データ】 4*3+4*3+4*2+2*2+2 = 12+12+8+4+2 = 38bytes/頂点
{ public int nPoints; //【頂点個数】
public CustomVertex.PositionNormalTextured[] UserVertex; //【頂点座標】【法線ベクトル】【uv座標】
public CustomVertex.PositionNormalTextured[] OriginalVertex; //【頂点座標】【法線ベクトル】【uv座標】SphereMap対応
public short[] Bone1Num; //【Bone番号】Bone番号1 モデル変形(頂点移動)時に影響
public short[] Bone2Num; //【Bone番号】Bone番号2 :
public Byte[] BoneWeight; //【Bone重み係数】Bone1に与える影響度 0-100 Bone2への影響度は(100-bone_weight)で与えられる。
public Byte[] EdgeFlag; //【エッジ・フラグ】0:通常、1:エッジ無効 輪郭線が有効の場合
}
public struct PMD_index //【PMD_インデックスデータ(面情報)】MikuMikuDanceのモデルではtriangle(三角形)しか使っていません。
{ public int nIndex; //【配列要素数】=面数(三角形の個数)*3
public int nPolygons; //●三角形の個数
public short[] IndexBuffer; //【頂点番号リスト】3個/面
} //注)本質的にはGPU側の制約によるが、この配列のためにMMDでは最大65535頂点までのモデルしか取り扱えない。
public struct PMD_material //【PMD_材質データ】
{ public int nMaterial; //【材質個数】
public Material[] Material; //【材質データ】DirectXの材質データ構造体と同じ
public byte[] ToonIndex; //【トゥーン・インデックス番号】toon??.bmp 0.bmp:0xFF, 1.bmp:0x00, ・・・10.bmp:0x09
public byte[] EdgeFlag; //【エッジ・フラグ】輪郭、影
public int[] nPoints; //【面構成要素数】面数*3 = 面頂点数 実際のindexに直すには材質0から累積加算する。
public string[] TextureFileName; //【テクスチャ・ファイル名】20bytesギリギリまで使える 20bytes時は0x00がなくても可。'
public Texture[] Texture; //【テクスチャ】
public bool[] SphereMap; //【SphereMapフラグ】
}
public PMD_Header PMDh; //【PMDヘッダー情報】
public PMD_vertex PMDv; //【PMD頂点バッファ】
public PMD_index PMDi; //【PMDインデックス】
public PMD_material PMDm; //【PMD材質情報】
public byte[] bShortName = new byte[20]; //【テクスチャーファイル名の一時バッファ】
public const float Zoom = 20.0f; //【変換倍率】座標系をMMD⇒ARに変換する際、1ミクセルを20倍にして表示。
public const string PMDPath = "../../PMD/"; //【PMDファイルの相対パス】
//***********************************************************************************************
//*** FileStreamとBinaryReaderを使ってPMDファイルからMMD構造体に情報を読み込む
//***********************************************************************************************
public void ReadPMDFile(string PMDFilePath) //【MikuMikuDanceのPMDファイルからHeader, Vertex, Index, Meterialを読み込む】
{
FileStream PMDFile = new FileStream(PMDFilePath, FileMode.Open); //【PMDファイル読み込みの為のFileStream】
BinaryReader bRD = new BinaryReader(PMDFile); //【PMDファイル読み込みの為のBinaryReader】
//【PMDヘッダー情報を読み込む】
PMDh.ID = new byte[3]; //ID 3バイト "P","m","d"
PMDh.ModelName = new byte[20]; //モデル名称 20バイト
PMDh.CopyRight = new byte[256]; //著作権情報 256バイト
PMDh.ID = bRD.ReadBytes(3); // ID 3バイト "P","m","d"を読み込む
PMDh.vNum = bRD.ReadSingle(); // バージョン番号を読み込む
PMDh.ModelName = bRD.ReadBytes(20); // モデル名称を読み込む
PMDh.CopyRight = bRD.ReadBytes(256); // 著作権情報を読み込む
PMDh.sID = Encoding.GetEncoding(932).GetString(PMDh.ID); // Shift JISに変換
PMDh.sModelName = Encoding.GetEncoding(932).GetString(PMDh.ModelName); // Shift JISに変換
PMDh.sCopyRight = Encoding.GetEncoding(932).GetString(PMDh.CopyRight); // Shift JISに変換
//【PMD頂点情報を読み込む】
PMDv.nPoints = bRD.ReadInt32(); //
PMDv.UserVertex = new CustomVertex.PositionNormalTextured[PMDv.nPoints]; // Tu,TvはSphereMapで変更されることがある。
PMDv.OriginalVertex = new CustomVertex.PositionNormalTextured[PMDv.nPoints]; //【Original】
PMDv.Bone1Num = new short[PMDv.nPoints]; //【Bone番号】Bone番号1 モデル変形(頂点移動)時に影響
PMDv.Bone2Num = new short[PMDv.nPoints]; //【Bone番号】Bone番号2 :
PMDv.BoneWeight = new Byte[PMDv.nPoints]; //【Bone重み係数】Bone1に与える影響度 0-100 Bone2への影響度は(100-bone_weight)で与えられる。
PMDv.EdgeFlag = new Byte[PMDv.nPoints]; //【エッジ・フラグ】0:通常、1:エッジ無効 輪郭線が有効の場合
for (var i = 0; i < PMDv.nPoints; i++) //【右手系から左手系に】Z⇒-Z, Nz⇒-Nz
{ PMDv.UserVertex[i].Position = new Vector3(bRD.ReadSingle()*Zoom, bRD.ReadSingle()*Zoom, -bRD.ReadSingle()*Zoom); //【位置ベクトル】X,Y,Z
PMDv.UserVertex[i].Normal = new Vector3(bRD.ReadSingle(), bRD.ReadSingle(), -bRD.ReadSingle()); //【法線ベクトル】Nx, Ny, Nz
PMDv.UserVertex[i].Tu = bRD.ReadSingle(); //【テクスチャーUV空間】u
PMDv.UserVertex[i].Tv = bRD.ReadSingle(); //【テクスチャーUV空間】v
PMDv.Bone1Num[i] = bRD.ReadInt16();
PMDv.Bone2Num[i] = bRD.ReadInt16();
PMDv.BoneWeight[i] = bRD.ReadByte();
PMDv.EdgeFlag[i] = bRD.ReadByte();
PMDv.OriginalVertex[i] = PMDv.UserVertex[i]; //【ShpereMapでTu, Tvを壊すのでオリジナルを保存しておく】
}
//【PMD面(インデックス)情報を読み込む】
PMDi.nIndex = bRD.ReadInt32();
PMDi.nPolygons = PMDi.nIndex / 3;
PMDi.IndexBuffer = new short[PMDi.nIndex];
for (var i = 0; i < PMDi.nIndex; i++) { PMDi.IndexBuffer[i] = bRD.ReadInt16(); }
//【PMD材質情報を読み込む】
PMDm.nMaterial = bRD.ReadInt32();
PMDm.Material = new Material[PMDm.nMaterial];
PMDm.ToonIndex = new byte[PMDm.nMaterial];
PMDm.EdgeFlag = new byte[PMDm.nMaterial];
PMDm.nPoints = new int[PMDm.nMaterial];
PMDm.TextureFileName = new string[PMDm.nMaterial];
PMDm.SphereMap = new bool[PMDm.nMaterial];
for (var i = 0; i < PMDm.nMaterial; i++)
{ PMDm.Material[i].AmbientColor = new ColorValue(bRD.ReadSingle(), bRD.ReadSingle(), bRD.ReadSingle(), bRD.ReadSingle()); //【環境色】Red,Green,Blue,Alpha
PMDm.Material[i].SpecularSharpness = bRD.ReadSingle(); //【鏡面反射強度】
PMDm.Material[i].SpecularColor = new ColorValue(bRD.ReadSingle(), bRD.ReadSingle(), bRD.ReadSingle()); //【鏡面反射色】Red, Green, Blue
PMDm.Material[i].DiffuseColor = new ColorValue(bRD.ReadSingle(), bRD.ReadSingle(), bRD.ReadSingle()); //【拡散光色】 Red, Green, Blue
PMDm.Material[i].EmissiveColor = new ColorValue(0.0F, 0.0F, 0.0F); //【発光色はMikuMikuDanceでは未使用】
PMDm.ToonIndex[i] = bRD.ReadByte();
PMDm.EdgeFlag[i] = bRD.ReadByte();
PMDm.nPoints[i] = bRD.ReadInt32();
bShortName = bRD.ReadBytes(20);
string s = Encoding.GetEncoding(932).GetString(bShortName);
PMDm.TextureFileName[i] = "";
for (var k = 0; k < 20; k++)
{ if (s[k] == 0) { break; } else { PMDm.TextureFileName[i] += s[k]; }
}
PMDm.SphereMap[i] = false;
if (PMDm.TextureFileName[i] != "")
{
string[] p = PMDm.TextureFileName[i].Split(new char[] { '.' });
if (p[1] == "sph") { PMDm.SphereMap[i] = true; } //【ファイル名がsph形式の場合】
PMDm.TextureFileName[i] = PMDPath + PMDm.TextureFileName[i];
}
}
//【PMDファイルを閉じる】
bRD.Close();
PMDFile.Close();
}
}
}
|
【改編したSimpleLiteD3d】
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using NyARToolkitCSUtils.Capture;
using NyARToolkitCSUtils.Direct3d;
using NyARToolkitCSUtils.NyAR;
using jp.nyatla.nyartoolkit.cs;
using jp.nyatla.nyartoolkit.cs.core;
using jp.nyatla.nyartoolkit.cs.detector;
namespace PMDMikuDirect3d
{
public partial class SimpleLiteD3d : IDisposable, CaptureListener
{
private const int SCREEN_WIDTH=640;
private const int SCREEN_HEIGHT=480;
private const String AR_CODE_FILE = "../../AR_Data/patt.hiro";
private const String AR_CAMERA_FILE = "../../AR_Data/camera_para.dat";
private CaptureDevice _cap; //DirectShowからのキャプチャ
private NyARSingleDetectMarker _ar; //NyARToolkit
private DsBGRX32Raster _raster; //NyARToolkit
private NyARD3dUtil _utils; //NyARToolkit
private NyARSurface_XRGB32 _surface; //背景テクスチャ
private Device _device = null; // Direct3D デバイス
private NyARTransMatResult __OnBuffer_nyar_transmat = new NyARTransMatResult();
private bool _is_marker_enable; //NyARToolkit
private Matrix _trans_mat; //NyARToolkit
private VertexBuffer _vertexBuffer = null; //【GPU側VertexBuffer】
private IndexBuffer _indexBuffer = null; //【GPU側IndexBuffer】
private float rot3 = 0.0f; //【モデルを回転表示するために使用】
//*******************************************************************************************
//【非同期イベントハンドラ】 CaptureDeviceからのイベントをハンドリングして、バッファとテクスチャを更新する。
//*******************************************************************************************
public void OnBuffer(CaptureDevice i_sender, double i_sample_time, IntPtr i_buffer, int i_buffer_len)
{
int w = i_sender.video_width;
int h = i_sender.video_height;
int s = w * (i_sender.video_bit_count / 8);
NyARTransMatResult nyar_transmat = this.__OnBuffer_nyar_transmat;
//テクスチャにRGBを取り込み()
lock (this)
{
//カメラ映像をARのバッファにコピー
this._raster.setBuffer(i_buffer, i_sender.video_vertical_flip);
//マーカーは見つかったかな?
bool is_marker_enable = this._ar.detectMarkerLite(this._raster, 120);
if (is_marker_enable)
{
//あればMatrixを計算
this._ar.getTransmationMatrix(nyar_transmat);
this._utils.toD3dMatrix(nyar_transmat, ref this._trans_mat);
}
this._is_marker_enable=is_marker_enable;
this._surface.CopyFromXRGB32(this._raster); //テクスチャ内容を更新
}
return;
}
//【キャプチャを開始する関数】
public void StartCap() { this._cap.StartCapture(); return; }
//【キャプチャを停止する関数】
public void StopCap() { this._cap.StopCapture(); return; }
//*******************************************************************************************
//【Direct3Dデバイスを準備する関数】
//*******************************************************************************************
private Device PrepareD3dDevice(Control i_window)
{
PresentParameters pp = new PresentParameters();
pp.Windowed = true;
pp.SwapEffect = SwapEffect.Flip;
pp.BackBufferFormat = Format.X8R8G8B8;
pp.BackBufferCount = 1;
pp.EnableAutoDepthStencil = true;
pp.AutoDepthStencilFormat = DepthFormat.D16;
CreateFlags fl_base = CreateFlags.FpuPreserve;
try{
return new Device(0, DeviceType.Hardware, i_window.Handle, fl_base|CreateFlags.HardwareVertexProcessing, pp);
}catch (Exception ex1){
Debug.WriteLine(ex1.ToString());
try{
return new Device(0, DeviceType.Hardware, i_window.Handle, fl_base | CreateFlags.SoftwareVertexProcessing, pp);
}catch (Exception ex2){
// 作成に失敗
Debug.WriteLine(ex2.ToString());
try{
return new Device(0, DeviceType.Reference, i_window.Handle, fl_base | CreateFlags.SoftwareVertexProcessing, pp);
}catch (Exception ex3){
throw ex3;
}
}
}
}
//*******************************************************************************************
//【アプリケーションの初期化を行う】
//*******************************************************************************************
public bool InitializeApplication(Form1 topLevelForm, CaptureDevice i_cap_device)
{
topLevelForm.ClientSize=new Size(SCREEN_WIDTH,SCREEN_HEIGHT);
//キャプチャを作る(QVGAでフレームレートは30)
i_cap_device.SetCaptureListener(this);
i_cap_device.PrepareCapture(SCREEN_WIDTH, SCREEN_HEIGHT, 15);
this._cap = i_cap_device;
//ARの設定
//ARラスタを作る(DirectShowキャプチャ仕様)。
this._raster = new DsBGRX32Raster(i_cap_device.video_width, i_cap_device.video_height, i_cap_device.video_width * i_cap_device.video_bit_count / 8);
//AR用カメラパラメタファイルをロードして設定
NyARParam ap = new NyARParam();
ap.loadARParamFromFile(AR_CAMERA_FILE);
ap.changeScreenSize(SCREEN_WIDTH, SCREEN_HEIGHT);
//AR用のパターンコードを読み出し
NyARCode code = new NyARCode(16, 16);
code.loadARPattFromFile(AR_CODE_FILE);
//1パターンのみを追跡するクラスを作成
this._ar = new NyARSingleDetectMarker(ap, code, 80.0, this._raster.getBufferType(), NyARSingleDetectMarker.PF_NYARTOOLKIT);
//Direct3d用のユーティリティ準備
this._utils = new NyARD3dUtil();
//計算モードの設定
this._ar.setContinueMode(true);
//3dデバイスを準備する
this._device = PrepareD3dDevice(topLevelForm);
this._device.RenderState.ZBufferEnable = true; // 陰面処理をzバッファ方式で行う
this._device.RenderState.ZBufferFunction = Compare.LessEqual; //.LessEqual;
// this._device.RenderState.ZBufferFunction = Compare.Less; //【SphereMapping対応のMiku(metalic)では全身緑のメタリックになる】
this._device.RenderState.Lighting = true; // 光源計算処理を行う true;
// 平行光源(無限遠に設定される方向を持った光源。太陽光の様な光源)
this._device.Lights[0].Type = LightType.Directional;
this._device.Lights[0].Ambient = Color.FromArgb(255, 200, 200, 200);
this._device.Lights[0].Diffuse = Color.White;
this._device.Lights[0].Direction = new Vector3(-0.57735f, -0.57735f, -0.57735f);
this._device.Lights[0].Enabled = true;
// 環境光(全ての物体を均一に照らす光源)
this._device.RenderState.Ambient = Color.FromArgb(0x202020);
//カメラProjectionの設定
Matrix tmp = new Matrix();
this._utils.toCameraFrustumRH(ap, ref tmp);
this._device.Transform.Projection = tmp;
// ビュー変換の設定(左手座標系ビュー行列で設定する) (0,0,0)からZ+方向を向いてY軸が上方向
this._device.Transform.View = Matrix.LookAtLH(
new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 1.0f), new Vector3(0.0f, 1.0f, 0.0f));
Viewport vp = new Viewport();
vp.X = 0;
vp.Y = 0;
vp.Height = ap.getScreenSize().h;
vp.Width = ap.getScreenSize().w;
vp.MaxZ = 1.0f;
//ビューポート設定
this._device.Viewport = vp;
//【頂点バッファの準備】頂点データ(X,Y,Z,Nx,Ny,Nz,Tu,Tv)を登録するバッファの準備
this._vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionNormalTextured),
PMDv.nPoints, this._device, Usage.None, CustomVertex.PositionNormalTextured.Format, Pool.Managed);
//【インデックスバッファの準備】第2引数はバッファの総バイト数=(三角ポリゴンの数)*(ひとつの三角ポリゴンの頂点数)*(Int16は2bytes)
this._indexBuffer = new IndexBuffer(this._device, PMDi.nPolygons * 3 * 2, Usage.WriteOnly, Pool.Managed, true);
//【頂点バッファをロックする】
// using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
// {
// data.Write(PMDv.UserVertex); // 頂点データを頂点バッファにコピーします
// this._vertexBuffer.Unlock(); // 頂点バッファのロックを解除します
// }
//【インデックスバッファをロックする】
// using (GraphicsStream data = this._indexBuffer.Lock(0, 0, LockFlags.None))
// {
// data.Write(PMDi.IndexBuffer); // インデックスデータをインデックスバッファにコピーします
// this._indexBuffer.Unlock(); // インデックスバッファのロックを解除します
// }
//【テクスチャーの準備】PMDは材質ごとにTextureFileName[i]でテクスチャー使用の有無を判定
PMDm.Texture = new Texture[PMDm.nMaterial];
for (var i = 0; i < PMDm.nMaterial; i++)
{
if (PMDm.TextureFileName[i] != "")
{ PMDm.Texture[i] = TextureLoader.FromFile(this._device, PMDm.TextureFileName[i]); }
else
{ PMDm.Texture[i] = null; }
}
//背景サーフェイスを作成
this._surface = new NyARSurface_XRGB32(this._device, SCREEN_WIDTH, SCREEN_HEIGHT);
this._is_marker_enable = false;
return true;
}
//*******************************************************************************************
//【メインループ処理】
//*******************************************************************************************
public void MainLoop()
{
//ARの計算
lock (this)
{
// 背景サーフェイスを直接描画
Surface dest_surface = this._device.GetBackBuffer(0, 0, BackBufferType.Mono);
Rectangle src_dest_rect = new Rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
this._device.StretchRectangle(this._surface.d3d_surface, src_dest_rect, dest_surface, src_dest_rect, TextureFilter.None);
//【3Dオブジェクトの描画初期化】
this._device.BeginScene(); //【レンダリングのシーン作成開始】
this._device.Clear(ClearFlags.ZBuffer, Color.DarkBlue, 1.0f, 0); //【フレームバッファ・クリア】Zバッファも含む
//マーカーが見つかっていて、0.4より一致してたら描画する。
if (this._is_marker_enable && this._ar.getConfidence()>0.4)
{
this._device.SetStreamSource(0, this._vertexBuffer, 0); //【頂点バッファをデバイスのデータストリームにバインド】
this._device.VertexFormat = CustomVertex.PositionNormalTextured.Format; //【頂点バッファ書式をセット】
this._device.Indices = this._indexBuffer; //【インデックスバッファをセット】
this._device.RenderState.Lighting = true; //【光源計算処理を行う】
this._device.Lights[0].Enabled = true; //【光源設定を有効にする】
this._device.RenderState.CullMode = Cull.None; //【袖やスカートが透明にならないようにカーリングを行わない】
Matrix transform_mat2 = Matrix.Translation(0.0f, -250.0f, 0.0f); //【PMDのマーカーからの移動距離を設定】
//【PMDを回転表示させる処理】
Matrix transform_rot3 = Matrix.RotationYawPitchRoll( ( rot3 / 180.0f) * 3.141592f, 0.0f, 0.0f);
rot3 += 2.0f; //【2°ずつ回転させる。1回転は180フレーム】
if (rot3 >= 360.0f) { rot3 = 0.0f; }
transform_mat2 *= transform_rot3 * this._trans_mat; //【座標変換マトリックスを計算】
this._device.SetTransform(TransformType.World, transform_mat2); //【上記マトリックスで座標変換を行う】
int j = 0; int k = 0; int sw; //【Traverse関連】[sw] 0:通常 1:テクスチャー 2:Sphere
Matrix matWorld, matView, matWV; //【SphereMap関連】
float m11, m12, m13, m21, m22, m23, m31, m32, m33; //【SphereMap関連】
float nx, ny, nz; //【SphereMap関連】
for (int i = 0; i < PMDm.nMaterial; i++)
{
this._device.Material = PMDm.Material[i];
k = PMDm.nPoints[i];
this._device.SetTexture(0, null);
sw = 0;
if (PMDm.TextureFileName[i] != "")
{ sw =1; if (PMDm.SphereMap[i]) { sw = 2; }
}
switch(sw)
{ case 0: //【Polygon Fill】通常のポリゴン描画
this._device.SetTexture(0, null);
//【頂点バッファをロックする】
using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
{
data.Write(PMDv.OriginalVertex); // 頂点データを頂点バッファにコピーします
this._vertexBuffer.Unlock(); // 頂点バッファのロックを解除します
}
break;
case 1: //【Texture Mapping】
this._device.SetTexture(0, PMDm.Texture[i]);
//【頂点バッファをロックする】
using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
{
data.Write(PMDv.OriginalVertex); // 頂点データを頂点バッファにコピーします
this._vertexBuffer.Unlock(); // 頂点バッファのロックを解除します
}
break;
case 2: //【Sphere Mapping】
this._device.SetTexture(0, PMDm.Texture[i]);
// 現在のワールドビュー座標を取得
matView = this._device.GetTransform(TransformType.View);
matWorld = this._device.GetTransform(TransformType.World);
matWV = matWorld * matView;
// 処理速度を上げるためにワールドビュー座標の要素を抽出
m11 = matWV.M11; m21 = matWV.M21; m31 = matWV.M31;
m12 = matWV.M12; m22 = matWV.M22; m32 = matWV.M32;
m13 = matWV.M13; m23 = matWV.M23; m33 = matWV.M33;
// 各頂点に対してをループし、トランスフォームと正しいテクスチャ座標の計算を行う
int jj;
for (int ii = j; ii < (j + k); ii++)
{
jj = ii % PMDv.nPoints; //【VertexBufferを何回もなめているようだ。MMDはAlphaBlendしてる?】
nx = PMDv.UserVertex[jj].Normal.X;
ny = PMDv.UserVertex[jj].Normal.Y;
nz = PMDv.UserVertex[jj].Normal.Z;
// zコンポーネントをチェックして後ろ側を向いている頂点をスキップ
if (nx * m13 + ny * m23 + nz * m33 > 0.0f)
{ // スフィアマップのテクスチャ座標を割り当てる
PMDv.UserVertex[jj].Tu = 0.5f * (1.0f + (nx * m11 + ny * m21 + nz * m31));
PMDv.UserVertex[jj].Tv = 0.5f * (1.0f - (nx * m12 + ny * m22 + nz * m32));
}
}
//【頂点バッファをロックする】
using (GraphicsStream data = this._vertexBuffer.Lock(0, 0, LockFlags.None))
{
data.Write(PMDv.UserVertex); // 頂点データを頂点バッファにコピーします
this._vertexBuffer.Unlock(); // 頂点バッファのロックを解除します
}
break;
}
this._device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, PMDv.nPoints - 1, j, k);
j += k;
}
//【インデックスバッファをロックする】
using (GraphicsStream data = this._indexBuffer.Lock(0, 0, LockFlags.None))
{
data.Write(PMDi.IndexBuffer); // インデックスデータをインデックスバッファにコピーします
this._indexBuffer.Unlock(); // インデックスバッファのロックを解除します
}
}
this._device.EndScene(); //【レンダリングのシーンここまで】
this._device.Present(); //【実際のディスプレイに描画】
}
return;
}
// リソースの破棄をするために呼ばれる
public void Dispose()
{
lock (this)
{
// 頂点バッファを解放
if (this._vertexBuffer != null) { this._vertexBuffer.Dispose(); }
// インデックスバッファを解放
if (this._indexBuffer != null) { this._indexBuffer.Dispose(); }
if (this._surface != null) { this._surface.Dispose(); }
// Direct3D デバイスのリソース解放
if (this._device != null) { this._device.Dispose(); }
}
}
}
}
|