2019年9月 5日 (木)動的にモデルを読み込む
おはようございます。
プログラマーのM.Iです。
夏ですね。今年もCEDECが近づいてきました。
ゲーム開発では効率化のため、モデルの修正をリアルタイムで確認したい、といったモチベーションがあります。
ゲームを動かしながらモデルを修正できれば調整しやすくなりますし、シェーダがのった状態で確認できるのもいい点です。
また、Unity等だとVRで見られるので、知識があればVRでのアニメーションやモデリングツールも作れるかもしれません。
間近でモデルを見ることで確認できる不具合もあるかもしれないですしね。
と言うわけで今回のお題はモデル・テクスチャを動的に読みこむ方法です。
UnityではSkinnedMeshRendererのsharedMeshを変更すれば動的にモデルを変更できます。
頂点や面、マテリアルは以下で設定できます。
例えばテレビのリモコンのモデルがあるとすれば、ゴム部分、プラスチック部分、金属部分の3つのマテリアルが必要ですね。
Mesh mesh = new Mesh();
// インデックスバッファはデフォルトでは帯域幅節約のため16bitになっている
// 頂点数が16bitを越えると32bitになるようにする
if ( verticeList.Length > 65535 )
{
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
}
// Vector3の頂点位置の配列
mesh.vertices = verticeList;
// Vector2のuvの配列
mesh.uv = uvList;
// マテリアル分サブメッシュを設定する
mesh.subMeshCount = MaterialList.Length;
// 法線をモデルから読みこまないならこれで自動計算してくれる
mesh.RecalculateNormals();
// MaterialList[ i ].Indiceはマテリアル毎のインデックスリスト
for ( int i = 0 ; i < MaterialList.Length ; i++ )
{
// 第1引数はインデックスの配列、
// 第2引数はインデックス形式、第3引数はマテリアル番号
mesh.SetIndices( MaterialList[ i ].Indice , MeshTopology.Triangles , i );
}
例えば最初のマテリアルが
0,1,2 と 2,1,3
次のマテリアルが
3,4,5
番目の頂点を参照し、両方とも3角形であるなら
mesh.SetIndices(new int[]{0,1,2,2,1,3} , MeshTopology.Triangles , 0);
mesh.SetIndices(new int[]{3,4,5} , MeshTopology.Triangles , 1);
と呼ぶとマテリアル毎の面を作ってくれるというわけです。
ちなみにMeshTopology.Pointsにすれば頂点のみ描画してくれるようになります。
形が違うモデルを複数用意しておき、それぞれの形をブレンドする事をブレンドシェイプと言います。
プログラム的には頂点のインデックスと、どのくらい移動するかのオフセットさえあれば計算できます。
ブレンドシェイプは以下のようにすれば追加できます。
// モデル頂点分のVector3配列を作る
Vector3[] vertTemp = new Vector3[ VertexArray.Length ];
// ブレンドシェイプの頂点インデックス、座標のオフセットが入っている
foreach( var shapeVert in blendShapeList )
{
vertTemp[ shapeVert.Index ] = shapeVert.Position;
}
// ブレンドシェイプの名前 座標のオフセット 法線や接線のオフセット
// 法線や接線のオフセットは不要ならnull
// 第2引数は複数フレームのブレンドシェイプを作る場合のウェイト。
// 1フレームだけならこれで100%のウェイトと言う意味
mesh.AddBlendShapeFrame( blendName , 1 , vertTemp , normal , tangent );
美顔ライトなどで影の入り方も調整する場合、法線のオフセットも設定できるようです。
スキニングはunity公式のバインドポーズのページをもとに、骨インデックスやウェイト等を設定すればいいので割愛します。
テクスチャ、マテリアルは以下のように設定できます
// テクスチャの大きさを指定
Texture2D tex = new Texture2D(width, height);
// 32bitの色配列 BinaryReaderなどでテクスチャを読んでimageDataに入れておく
tex.SetPixels32(imageData);
tex.Apply();
// シェーダ名
string shaderName = "Unlit/Texture";
Material newMat = new Material( Shader.Find( shaderName ) );
// マテリアルのメインテクスチャを設定
newMat.mainTexture = tex;
これをマテリアル分作れば、SkinnedMeshRendererのmaterialsに設定できます。
最後にSkinnedMeshRendererのsharedMeshにメッシュを設定すれば反映完了です。
※クリックで拡大表示
maya等からモデル情報をudpなりで送ればボタンを押すだけでゲームエンジンに反映、といったことができるわけです。
.netの資源があるのでBinaryReader、Writerを使えばデータの入出力もできますしね。
バインドした骨オブジェクトを動かせば骨を動かせますし、SkinnedMeshRendererのSetBlendShapeWeightでブレンドシェイプも確認できます。
ちなみにUE4ではProceduralMeshComponentを使えば、動的にメッシュ作成ができそうなのですが、UV設定やマテリアル等はできそうなのにスキニング設定周りが見当たりません。
ProceduralMeshの機能はプラグインとして提供されていたので、
Engine\Source\Runtime\Engine\Private\Components\SkinnedMeshComponent.cpp
辺りを参考にすれば実装できるかもしれないですね。
「プログラマー」カテゴリの記事
- 技術交流の業(2019.03.07)
- 福袋争奪戦デビュー(2019.01.31)
- 温泉旅行(2019.01.24)
- ゲーセンの近況(2018.11.29)
- 健康的にプログラミングを続けるためのちょっとした習慣(2018.10.18)
この記事へのコメントは終了しました。
コメント