目次
はじめに
こんにちは!プログラマーの家弓です。
代表の堂前さんから、「blogを書いてほしい!」と言う依頼をうけました。
という事で、みんな大好き UnrealEngine4 からネタを探してみます。
ある日の事、
「SkeletalMeshの頂点カラーがキレイにインポートされないの!何とかして!」
という相談を受けましたので、その解決方法を、検証、調査、修正まで順を追って説明しようと思います。
※使用したバージョンはUE4.14.0になります。
※エディタの言語環境は日本語になります。
検証用の簡単なメッシュデータを用意する
問題を検証する前に、単純な作り、かつ解決したい問題を含むメッシュデータを用意します。
なぜかというと…
- 結果を確認しやすい。
- インポートの時間が短くなり、繰り返しの検証がしやすくする。
- データが分かりやすくなり、デバッグも容易になる。
という事で、下半分が黒、上半分が白の頂点カラーが設定された、簡単なメッシュデータを用意しました。
(SkeletalMeshで問題が起こるので、ボーンも含んでいます。)

メッシュのインポート後、頂点カラーをチェック
早速、調査用のメッシュデータをインポート、チェックしてみましょう。
その前に、頂点カラーを見やすくするため、頂点カラーが確認出来るマテリアルを作成します。
(ガンマによる補正はとりあえず置いておきます…)

メッシュをインポート後、早速このマテリアルを貼り付けて確認してみましょう。えい!

うーん、見た目が何か違う…。
望んだ通りにはインポートされていないようですね。
インポート時のオプションを変えて検証
もしかしたら、インポート時に何かしらのオプションがあるかも!
という訳で、インポートオプションを調べてみます。
すると、気になるものが…

説明を読むと、同じような情報を持つ頂点をフィルタリングせず、そのまま読み込む機能のようです。
オリジナルのメッシュデータは、中央の部分で、白い頂点、黒い頂点が同じ位置に配置されています。
もしかして、インポート後は近い座標にある頂点が同じ頂点とみなされて、1つになってしまってるのかも…

このオプションを有効すれば、近い座標の頂点もそのままインポートされるのでは…?
という訳で早速やってみましょう!

出来たー!ばんざーい!何もする必要ねー!
…
と思いきや…、よく見ると問題に気づいてしまいました…

20頂点が60頂点に…、3倍になってますね…。
頂点カラーの問題は解決しましたが、頂点数がすごく増えてしまうようです。
「別にさー、60頂点くらい無視してもいいじゃんかー」
と思いきや、もしかすると20,000頂点が60,000頂点に、50,000頂点が150,000頂点に…!(単純計算)
そうなると流石に困っちゃいますよねえ。
頂点カラーが正しくインポートされたのは良いのですが、頂点数が3倍に増えてしまうのはいただけません。
ソースコードの調査
頂点処理数は凄く増えてしまいますが、この「Keep Overlapping Vertices」オプションで
頂点データのインポート結果が変わる事が分かりました。
ということは、このフラグが関連する箇所を見れば何か分かるのでは?
ちょっとソースを見てみましょう!
まずはソリューションを開いて「KeepOverlappingVertices」をグローバル検索(Ctrl+Shift+F)します。
検索結果から全部調べるのは面倒なので、
- フラグで処理が分岐してそうな「if」の行
- 「SkeletalMesh」の処理っぽい所
に焦点を見てみます。

という訳で、まずSkeletalMeshTools.cppの147行目辺りからチェックしてみましょうー。
for(int32 VertexIndex = 0; VertexIndex < 3; ++VertexIndex)
{
int32 WedgeIndex = FaceIndex * 3 + VertexIndex;
const FSoftSkinBuildVertex& Vertex = RawVertices[WedgeIndex];
int32 FinalVertIndex = INDEX_NONE;
// ここに注目
if(bKeepOverlappingVertices)
{
FinalVertIndex = Chunk->Vertices.Add(RawVertices[WedgeIndex]);
}
else
{
:
:
bKeepOverlappingVertices が true の場合、頂点データをそのまま追加している…
とすると、else の方で頂点データをフィルタリングをしているのでは?
という予測を立てた上で、else の中を見ていきましょうー。
else
{
DupVerts.Reset();
RawVerts2Dupes.MultiFind(WedgeIndex, DupVerts);
DupVerts.Sort();
for(int32 k = 0; k < DupVerts.Num(); k++)
{
if(DupVerts[k] >= WedgeIndex)
{
// the verts beyond me haven't been placed yet, so these duplicates are not relevant
break;
}
int32 *Location = FinalVerts.Find(DupVerts[k]);
if(Location != NULL)
{
// ここは・・・!
if(SkeletalMeshTools::AreSkelMeshVerticesEqual(Vertex, Chunk->Vertices[*Location]))
{
FinalVertIndex = *Location;
break;
}
}
}
if(FinalVertIndex == INDEX_NONE)
{
FinalVertIndex = Chunk->Vertices.Add(Vertex);
FinalVerts.Add(WedgeIndex, FinalVertIndex);
}
:
:
- SkeletalMeshTools::AreSkelMeshVerticesEqual
名前からして、頂点と頂点を比較している所があります。
これが頂点同士のフィルタリングをしているのでは…!?
早速、AreSkelMeshVerticesEqual関数の中身を見てみます。
bool AreSkelMeshVerticesEqual( const FSoftSkinBuildVertex& V1, const FSoftSkinBuildVertex& V2 )
{
if(!PointsEqual(V1.Position, V2.Position))
{
return false;
}
:(中略)
:
return true;
}
頂点同士の中身を比較して、同じ頂点とみなせるならtrue、違う頂点ならfalseを返しているようです。
比較している内容を見ると、
- 頂点座標(Position)
- テクスチャ座標(UVs)
- 法線情報(Tangent[X,Y,Z])
- ボーンのウェイト情報(InfluenceBones, InfluenceWeights)
もうお気づきかもしれませんが、
SkeletalMeshTools::AreSkelMeshVerticesEqual関数は、頂点カラーを比較してないようです!
そして、先程気になったもう一つのif文も見てみます。
int32 AddSkinVertex(TArray<FSoftSkinBuildVertex>& Vertices,FSoftSkinBuildVertex& Vertex, bool bKeepOverlappingVertices )
{
if (!bKeepOverlappingVertices)
{
for(uint32 VertexIndex = 0;VertexIndex < (uint32)Vertices.Num();VertexIndex++)
{
FSoftSkinBuildVertex& OtherVertex = Vertices[VertexIndex];
if( AreSkelMeshVerticesEqual( Vertex, OtherVertex ) )
{
return VertexIndex;
}
}
}
return Vertices.Add(Vertex);
}
似ている頂点を発見したらそのインデックスを、なければ配列に追加して、そのインデックスを返してます。
頂点同士の比較は、「SkeletalMeshTools::AreSkelMeshVerticesEqual」を使用してますので、
こちらも頂点カラーの比較はしていないようですね。
エンジンに手を加える
「SkeletalMeshTools::AreSkelMeshVerticesEqual」関数が頂点カラーを比較していない事がわかりました。
では、頂点カラーの比較を追加して、カラーが同じならtrue、違うならfalseを返すコードを追加してみましょう。
※エンジン側にコードを追加する場合は、必ずそれとわかるコメントを記述しましょう!
詳しくはこちらにも書いてあります。
https://docs.unrealengine.com/latest/JPN/Programming/Development/CodingStandard/index.html#第三者コード
bool AreSkelMeshVerticesEqual( const FSoftSkinBuildVertex& V1, const FSoftSkinBuildVertex& V2 )
{
if(!PointsEqual(V1.Position, V2.Position))
{
return false;
}
:(中略)
// @LogicalBeat Takuro.K - BEGIN AreSkelMeshVerticesEqualに頂点カラーの比較を追加
if(V1.Color != V2.Color)
{
return false;
}
// @LogicalBeat Takuro.K - END
return true;
}
早速ビルドして実行、インポートします。

頂点カラーが望んだ通りに表示されていて、3倍になっていた頂点数も改善されました!
まとめ
検証から結果の確認、関連性がありそうな箇所の調査方法、そして修正までの流れを書いてみました。
改造に関しては、あくまで自己責任である事だけには気をつけてくださいね。
追加で頂点カラーの比較をON/OFFするインポートオプションが欲しいですね。
改造後に別の問題が発生した時は、機能をオフにする事で、
問題が改造した部分にあるのか、それとも関係ないのか、この確認がしやすくなります。
(このオプションの追加も、「bKeepOverlappingVertices変数」を追っていけば、わりと簡単に追加が出来ると思います。)
起こった問題の原因を調べながらプロジェクトに合った改造をしている時、
やはりUnrealEngineは楽しいなーと思います。
このような解決方法は色々と応用が出来ると思いますので、色々と試して楽しんでくださいね!
それでは!
【免責事項】
本サイトでの情報を利用することによる損害等に対し、株式会社ロジカルビートは一切の責任を負いません。