いいところにビス打ちをしようと思って失敗した話

先日のSubstance Designerゆるゆる会にて出た話。

機械とかで形状のコーナーにビス打ちやネジ止めしたい場合にSDだとどうやってプロシージャルにやるの?
チュートリアル動画だと普通に位置を目合わせして配置してるけど、スマートじゃないしそれならPhotoshopでいいんじゃないか?
Houdiniならできるよ!←このセリフが大量発生

で、できらぁー!

えっ?いい感じの場所に完全自動で?

そんな中、参加者の一人がいいアイデアを出されてました。
仕組みとしてはベベルなどを利用して形状のコーナーと思われる部分だけを検出して [Flood Fill Mapper] で形状を散らすという方法。
なるほど、たしかにこの方法ならなんとかなるか?
とはいえ、四角形はなんとかなったけど六角形とかだとうまくいかないなど、いろいろ問題もあるっぽい。

私の方でも同じ方法でなんとかならないかと調整してみたのですが、色んなパターンを網羅するのが難しかったので別の方法を考えることにしました。
とはいえ、基本はコーナーを検出、Flood Fillで領域作成、Flood Fill Mapperで形状をばらまくという手法を採っています。
ただ、すこし凝った作りをして、エラーを最小限にしようと試みた次第です。

Flood Fill Mapperとは

[Flood Fill Mapper] ノードは Flood Fill の情報を利用するノードで、求められたAABBの範囲に収まるように指定された形状を配置するノードです。
形状のコーナー部分を検出し、Flood Fill でAABBが配置できれば形状を撒くのは難しくないのでは?と思われるかもしれません。
しかし、Flood Fill Mapper は Flood Fill に通した元形状の範囲でしか画像が出てくれません。

どういうことかというと、下の図を見てください。

f:id:monsho:20190901161522p:plain

この図はある形状(白)と Flood Fill のAABB(緑)、Flood Fill Mapperによる合成(赤)を示したものです。
Flood Fill のAABBは図のように三角形を取り込むわけですが、Flood Fill Mapper によって形状を撒いた場合、その形状が表示されるのは元の三角形の白い部分のみとなります。
つまり、黒い部分には形状は表示されませんので、ビス穴のようなものをAABBに対して撒いても正常な形状にはなりません。
コーナーを角丸などを駆使して検出した場合、くの字型になりやすいわけですが、その場合はくの字の中心部分は隙間となってしまうのでビス穴が発生しづらくなるというわけです。

なので単純にコーナーを検出するだけではなく、コーナーをそれなりに適切に拡大していってビス穴を取り付けられる程度の大きさと中身が詰まったAABBを作成しなければならないというわけです。

コーナー検出

コーナー検出は [Bevel] や [Non Uniform Blur] を利用して角丸を作り、元の形状からの差分で検出を試みました。
しかしこれは四角形であれば割と悪くない結果をもたらすのですが、六角形のように角の角度が甘い部分では検出結果が長く伸びすぎてAABBの精度がいまいちでした。

そこでもう少し検出結果を良くするため、[Pixel Processor] を使ったレイマーチ的な手法を用いることにしました。
図のようにあるピクセルから8方向にレイを飛ばし、エッジ部分を検出したレイの本数を計数します。
このレイの本数がしきい値を超えるようならコーナーとして認識し、白を設定、そうでない場合はコーナーではないとして黒を設定します。
レイは8ステップで処理し、パラメータで指定可能な距離を最大距離としてチェックを行います。

レイ1本分の処理は Pixel Processor で以下のように実装しています。

f:id:monsho:20190901170849p:plain

これを角度を変えて8方向に対して処理しています。
コーナー検出の結果は以下のようになります。

f:id:monsho:20190901171205p:plain

左の入力画像に対して右の結果が求められます。
しかしながら、1種類では完全に検出できなかったので、実際には2種類の検出結果をそれぞれ [Blur HQ] + [Histogram Scan] で小さすぎるものや細くラインになってしまったものを除去しています。

コーナーを拡張

コーナーは検出できましたが、これだけではビス穴をつけられません。
ここからはコーナー部分の画像を拡張して、ビス穴を打てる程度の大きさを確保します。

拡大は単純な方法として [Distance] ノードを使用する方法がありますが、単純に適用してしまうと隣のアイランドに接触してしまったりします。
これではおかしな部分にビスが打たれてしまうので、拡大方法も少し考えないといけません。

今回適用したのは少しだけ拡大して元形状でマスクと言う手法を繰り返す方法です。
マスクは元の形状情報を利用します。形状からはみ出した部分を削除するためですね。
複数回繰り返すことで元形状の内部にのみコーナーマスクを広げるわけですが、広げすぎても問題がありますので回数は10回以内で選択できるようにしました。
このためのグラフ、CornerExpand グラフは以下のような実装となります。

f:id:monsho:20190901173123p:plain

終結

このようになりました。

f:id:monsho:20190901173556p:plainf:id:monsho:20190901173626p:plainf:id:monsho:20190901173651p:plain

四角、五角、六角ではパラメータさえきちんと調整すればうまくいきます。
しかし、このような形状の場合はうまくいきません。

f:id:monsho:20190901174227p:plain

このような形状の場合は丸の中心にビスを打ちたいと思うはずですが残念ながらうまくいきません。
このような場合は別途の手法を用いたほうがいいでしょうね。

というわけで、一定の成功はあったものの、最終的には失敗してると言えるような結果となりました。