
Procedural HairCard Reduce
DCCツールで作られたヘアカードをいい感じにリダクションする仕組みを、Houdiniで作ってみました。
大まかな仕組みができたので、Houdiniゆるゆる会で発表したところ、改善点を教えていただきました!
Houdiniゆるゆる会はテーマに沿ったシーンを事前に作成し、当日に発表形式で見せ合う勉強会です。より良いロジックの相談などもできる楽しい会なので、興味のある人はご参加お待ちしております。
今回の仕組みは大きく分けて2つの要素から成り立ちます。
一つはヘアカードの枚数を間引く仕組み、もう一つはヘアカードごとのポリゴン数を削減する仕組みです。
ヘアカードの特徴
一般的にヘアカードと呼ばれるものは、いくつかの特徴があります。
ヘアカードとは、短冊状のポリゴンで髪の毛のモデルを作成し、アルファ抜きした髪の毛の束のテクスチャを貼ることで、髪の毛を表現する手法です。比較的リアルめのゲームのキャラクターの髪の毛によく使われます。
一枚のテクスチャに複数の髪の毛の束を書き、UVをずらしてバリエーションを出すことも多いです。
髪の毛の束のUVを使用するため、UVがグリッド上に並んでいます。

https://sketchfab.com/3d-models/bobhair-2149a717f39b42c9a37e04ad07e6230e

ヘアカードの枚数を間引く
ヘアカードの枚数を間引く仕組みです。

ヘアカードの根本は頭部に密着していることが多いです。そのためカードの根本にポイントを配置しそれを集めると、頭部のシルエットに近い形状になると言えそうです。根本の位置が近いものから削ることで髪の毛全体の印象が崩れずに削れそうなので、ポイントとそれに付随するヘアカードを減らすことで、ヘアカードの枚数を間引いくというのが大まかな仕組みです。
まずはヘアカードの根本にポイントを配置する部分です。
ヘアカードのUVはグリッド上に並んでいるという特徴を活かし、ヘアカードごとにUVの端だけを残してからfuseすることで、根本のポイントを作成します。


Attribute PromoteでUVの最大値をdetailにコピーし、最大値以外をVEXで消しました。
vector maxuv = detail(0,"uv");
float maxuvv = maxuv.y;
if(@uv.y != maxuvv )
removepoint(0, @ptnum, -1);
ヘアカードの根本のポイントだけを可視化すると以下のようになります。
ヘアカードはPack化しておきます。Packにすることで1ポリゴンとして扱えるので削除が楽になります。


根本のポイントをfuseで削減し、どれくらいヘアカードを減らすかコントロールします。
Attribute PromoteのArray of Allで残ったポイントごとのIDを配列で取得します。この配列から対応したIDのヘアカードをグループにいれるVEXを作成し、グループに含まれないヘアカードをblastで削除します。
int ptn[] = detail(1, "__class");
foreach(int pt; ptn){
if(pt==@ptnum) i@group_livept = 1;
}

ヘアカードのポリゴン数を削減する
ヘアカードのポリゴンを削減する仕組みです。最初、ヘアカードのポリゴンを素直に削減する仕組みを模索していたのですが、めんたいこさん(https://x.com/kickbase)より素晴らしい方法を教わったので、その方法でネットワークを作り直しました。
ヘアカードのメッシュのポイント位置をrestで保存しておき、ポイントの位置をUVの形に変形します。グリッドで新規の短冊状のポリゴンを作成し、MatchSizeでUV形状に変形したヘアカードに大きさを合わせます。
PrimUVの仕組みを使って、ヘアカードから新しく作ったグリッドにrestやUV情報を転写し、もとのヘアカードの形状に直せるようにしておきます。
この方法の優れた点は、もとの形状を使わないので、頂点の位置や分割を自由にコントロールできることです。ヘアカードが三角ポリゴン化されていても新しく作ったグリッドを使うので自動的に四角化も行えます。

PrimUVをもとにAttributeを転写するVEXは以下になります。
float maxdist = 10;
int pr;
vector primuv;
float d = xyzdist(1,@P, pr, primuv, maxdist);
v@__pointuv = primuv(1, "__pointuv", pr, primuv);
v@rest = primuv(1, "rest", pr, primuv);

UV形状になったヘアカードを元の形に治す前に、もう一つやってきたいことがあります。ヘアカードの形が大きく変化している部分だけにポイントを残し、あまり変わっていない部分は削減したいです。

どれくらい変化しているかを出すのにcurvatureを作りますが、ヘアカードのままだとカードのたわみも計算されるので一旦、カーブだけの状態にしてからcurvatureを出します。これで髪の毛のラインの曲率が計算されます。

曲率をAttributeにしたカーブも同じくPrimUVで転写し、曲率が小さいポイントを削減しました。

今回は作成していませんが、ヘアカードの曲率だけでなく、ヘアカードの幅の変化が大きい場所も優先的に残せばもっときれいにリダクションできるかもしれません。
今回の仕組みが動いている様子を動画にしてみました。
Procedural HairCard Reduce pic.twitter.com/IGFNJqo1IZ
— くろさわ (@kurosaurus) September 29, 2024