株式会社デジタル・フロンティア-Digital Frontier

Header

Main

Facial Delta Mush

2015/7/13

Tag: ,,,


Alô pessoas! 皆さん、こんにちは。
開発部のVladimirです。

今回は最近取り組んだ開発案件についてお話させて頂こうかと思います。
それは少し前に話題になっていたDelta Mushです。

初めて聞く人もいるかもしれないので簡単にご説明すると、Delta Mushは2010年にRythm&Hues社によって開発された
アルゴリズムで去年のSIGGRAPHで発表されたdeformer技術です。
そのシンプルかつパワフルな点からも世界中で多くの人々から注目されていたように思います。
ご存じの通り今年Maya2016にも搭載されましたね。
web上にはソース付きで公開されているものがあったりと独自実装をしている方もいるようです。
弊社では今回Facialアニメーション用に開発されました。なぜこれが必要になったのか?というと
blendshapeベースのFacialアニメーションをつけた際に表情によっては望まない凹凸が表れる場合があります。この不正な形状を修正する目的でDelta Mushが使用されました。
Delta Mushを説明するには基礎的な3D幾何学の知識が必要ですが出来るだけわかり易く進めていきたいと思います。

これぞ!Delta Mush!

キャラクターなどのリギングをされたことがあればskinningにおけるskin weightの重要性はご存知だと思います。
多様なポーズ変化に対して適切なデフォーム結果を得るためには非常に重要なデータになります。
一般的にはweightをPaintしていくことになりますが最近のDCCツールではbind時にある程度最適化されたweightが付くような
機能が盛り込まれています。しかし多くの場合それだけでは不十分でやはりpaintでの修正が必要となります。
この面倒なPaint作業をせずにsmoothなdeform結果を得るために使用されるのは代表的な例の1つと言えるでしょう。

Delta Mush Demo

ご覧の通りweightがあまり最適化されていない状態でもディテールを保持しつつ不要な凹凸、めり込みのないsmoothな結果が得られています。ではなぜ?これが可能なのか見てみいきましょう。

The Algorithm

アルゴリズムについてはSIGGRAPHのTalkにて公開されていますが
ここでは図を使いながら説明していこうと思います。

どのようにsmoothなdeform結果を得るか?を簡潔に言うならば
表面をスムーズ化することで不要な凹凸、めり込みをなくした後、元々のディテールを復元します。
処理の工程としては下記の二段階に分けられます。

事前計算
  1. undeformedポーズのメッシュがあるとします。それはReference poseと呼びます。
  2. Reference poseをスムーズ化したものが、 Smoothed Reference poseになります。
  3. 頂点毎の差分をローカル座標で計算し、結果を格納します。
実際の変形
  1. 変形されたメッシュのポーズがあるとします。それはDeformed poseと呼びます。
  2. Deformed poseをスムーズ化したものが, Smoothed Deformed pose になります。
  3. Smoothed Deformed poseに頂点毎の事前計算された差分をローカル座標で与えます

Delta Mushのアルゴリズムは以上です。まず”スムーズ化”、次に”差分の適応”という順で説明していきたいと思います。

Smoothing

スムーズ化によって不要な凹凸を減らして滑らかな表面に変化させることができます。 ただし全てのスムーズ手法がDelta Mushに有効という訳ではありません。 Delta Mushの論文を見ると、”modified discrete Laplacian”という手法が挙げられています。 一見難しそうですが、 実はそうでもないです。Laplacian Smoothingはvertexの近傍を平均してそこに移動するという方法です。 Iso-surfacesや実数の関数を使うなら積分を取るのですが、メッシュの場合は離散データを利用します。あるvertexに隣接するvertexの平均を取ってそこに移動します。これを全てのvertexに与えるとスムーズなメッシュが得られます。 下のコードはそのスムーズ化の実装例です。Smoothnessはスムーズ化の強さを表すパラメータとして挙げています。

void averageSmooth(MFloatPointArray &vertices, vector &neighbors, float smoothness)
{
	int nPoints = vertices.length();
	MFloatPointArray temporaryVertices;
	temporaryVertices.setLength(nPoints);
	for (int i = 0; i < nPoints; i++){
		int nNeighbors = neighbors[i].size();
		if (nNeighbors == 0)
			temporaryVertices[i] = vertices[i];
		else {
			MFloatPoint ponto;
			for (int j = 0; j < nNeighbors; j++)
				ponto = ponto + vertices[neighbors[i][j]];
			temporaryVertices[i] = ponto / nNeighbors;
		}
	}
	for (int i = 0; i < nPoints; i++)
		vertices[i] = vertices[i]*(1-smoothness) + smoothness*temporaryVertices[i];
}

全てのLaplacian Smoothing手法を見て行くときりがないので1つ例を挙げるとすると、先程の平均化の段階で、edgeの長さの逆数をweightとして使い加重平均を取ることが出来ます。
そうするとmeshを全体的にスムーズ化しながらもedgeの長さを保持出来ます。

どの手法を用いるかはuser次第です。 Maya 2016のDelta Mushは普通の平均を使っていますがLaplacian Smoothing以外の手法を使うことも考えられます。
例えば弾性relaxationやmodified catmull subdivisionなども使えるでしょう。ここで重要なことはスムーズ化されたメッシュはオリジナルメッシュより内側に縮んだ形状でないといけません。Volumeを保持する手法を使うと期待するdeform結果が得られない場合があります。下の図ではいくつかのスムーズ手法を示しています。

私が試した感じではシンプルな手法であればあるほどいい結果をもたらすように思います。

Displacements(差分の適応)

次はスムーズ化されたメッシュに対してオリジナルメッシュとの差分の適応です。
任意の頂点P1における差分はローカル座標空間で定義します。ローカル座標はこのように求めます:

まずはP1の法線をXとした時、座標の基底vectorの長さは1なので、Xをnormalizeします。

X’ = normalize(X)

P1に隣接する頂点を1個選択し、それをP2とします。※Reference poseだけでなくDeformed poseの場合も同じP2を使います
そして次にP1からP2へ向かうvectorを

Y = P2 – P1

とします。
YはXに垂直とは限らないので次式にて垂直なvectorを求めてnormalizeします。

Y’ = normalize(Y – dot(X’,Y)X’)

これで垂直な2軸が決まったのであとは外積を使って3軸目を求めます。

Z = cross(Y’,X’)

これによって頂点のローカル座標が定義できます。差分データ:displacement vector(D)はこの座標系で定義しますので

Δ = Preference– Psmoothed

D = ( dot(Δ, X’),  dot(Δ,Y’), dot(Δ,Z))

となり、これを格納します。最後にDeformedされたメッシュにこの差分を与えます。

Final Position = D.x * X’new + D.y * Y’new + D.z * Znew

ここでのX’new, Y’new, ZnewはDeformed Smoothedメッシュの頂点毎のローカル座標となります。

問題点

FacialメッシュがHighレゾな場合、単純にDelta Mushを使用しても不要な凹凸が除去できないケースがあります。

①ノイジーで不要な凹み

下の図のようにSmoothing stepでiteration数を増やすと口の周りなどにノイズが出る場合があります。

先程、”スムーズ化されたメッシュはオリジナルメッシュより内側に縮んだ形状”であるべきという話をしましたが形状によっては
Smoothed PoseがオリジナルなPoseよりも外側に押し出されるエリアがあったりします。アニメーション中、差分の方向は内側と外側の両方が混ざったエリアもあります。
これに対して実際の変形時、Smoothed Deformed Poseが全てのエリアでDeformed Poseよりも内側に縮んだ形状になる場合、

内側への差分が適応されるエリアでは望まない凹みができることになります。またメッシュのレゾが高くHighディテールな程、より高周波でノイジーな凹みが出てくる可能性が高まります。
また同じ形状でもSmoothing iterationが増えるとよりスムーズ化されるため差分量が増えてより大きな凹みになります。結果として望まない凹凸が発生する原因になっていると考えています。

現状でこの問題に対する根本的な解決法は不十分ですがDelta Mushをマルチパス化して反復計算することでかなり軽減できます。

②Deformed Pose自体の不要な凹凸

下の図では口と鼻の間、口の横(黄色枠)におかしな凹みが残っています。このケースではDelta Mushパスを増やしても問題は解決されないだけでなく一部の形状(唇)が変わってしまいます。

この問題の原因はスムーズ化されても凹みが残ることです。下の図はSmoothed Deformed Poseを示していますがスムーズ化されたメッシュでも凹みがあります。これはDelta Mushというよりむしろ状況に応じたスムーズ化が出来ていないと言えるでしょう。

多くのtargetを使ったblendshapeベースのfacialではblend具合によってはこのような予期しない凹凸が表れることがあります。
本来であればblendshape側で適切に修正することが望ましいですがポスト処理として解決できないか挑戦した結果、
弊社では独自のsmoothing手法が使われています。Average smoothingより事前計算のデータが必要ですが、スピード的には2回のSmoothing(Average smoothingと独自smoothing)を行うのと同じくらいの処理時間でこの様な結果になります。

Facial Delta Mushの紹介は以上です。基本的なDelta Mushアルゴリズムは単純なので簡単に実装できると思います。ぜひ独自なDelta Mushを作ってみてください。
皆さんの自由な発想によるよりスマートなDelta Mushに出逢えることを楽しみにしています。

それでは、また今度!

オマケ

Maya 2016の標準 Delta Mush deformerのattributeを説明してみたいと思います。

Mayaでは先ほど説明した Laplacian smoothingのシンプルなAverage方法を使われています。

Smoothing Iterationsは、Displacementsをapplyする前のsmoothingの回数を設定します。ここが増えれば増えるほどSceneは重くなってノイズがひどくなります。

Smoothing Stepはsmoothingの強さを設定できます。先程説明したアルゴリズムでは、vertexのcurrent positionと近傍の平均を補間するsmoothness値です。 0はcurrent positionのままになります(smoothing無し),1は近傍の平均(full smoothing)
またこれはDF Delta Mushとの比較で分かったことですが、Mayaでは少しでもノイズ問題を軽減するためかsmoothness値に0.75を乗算するような補正がかかっているようです。

Pin Border Verticesはラベル名の通りです。Border verticesを動かさないように設定できます。

Displacementは事前計算された差分にかける数値です。

そして、Scaleは各方向のdisplacementにscaleを与えます。


※免責事項※
本記事内で公開している全ての手法・コードの有用性、安全性について、当方は一切の保証を与えるものではありません。
これらのコードを使用したことによって引き起こる直接的、間接的な損害に対し、当方は一切責任を負うものではありません。
自己責任でご使用ください。

Pocket

コメント

コメントはありません

コメントフォーム

コメントは承認制ですので、即時に反映されません。ご了承ください。

*