見出し画像

SDFマップを利用した影テクスチャツールを作ってみた。

本記事をご覧いただきありがとうございます。
とりあえず「何はともあれツールだ!!!」という方は、下記目次の「3.ツールにしてみる」をご覧ください。


0.はじめに

先日X(旧Twitter)において、セルルックキャラクターの顔影を1枚のテクスチャで制御する技法に関する記事が話題になっていました。

原理や仔細などに関してはalwei様(https://x.com/aizen76)の以下の記事において詳細に記載されております。自分もここで基礎原理を学びました。
本記事はこちらの記事を前提として書かれておりますのでご了承ください。

1.どうしたものか

さて、こちらの方法は昔トライして原理は理解できているつもりでしたので、割とまっすぐ学ぶことが出来ました。
実装してライトを動かすとしっかり顔影が動きます!が、

「ジャギるなぁ・・・」

0~255までの値を「ここ以上は白、ここ以上は黒」と分けているので寄って見たときに2値ジャギーが出てしまいます。
それならとスムーズステップ(0~127までは黒、127~129までをグラデーション、129以上は白にするみたいな処理)を入れると、

「階段バレしてるなぁ…」

階段のようなテクスチャバレが出てしまいます。
そもそもが情報が0~255までの8bitまでしかないわけだからしょうがないわけですね。顔正面180度くらいに対して256分割だから、平均0.7度くらいの階段が出来るのは当然のことです。
最初2値化状態にアンチエイリアスをかけるべきなのかなと思ったのですが、その技術が無い自分が思い至ったのは、

「1段が大きいのなら、さらに256分割すればスムーズになるのでは?」
「そもそも画像が5枚だとして合間を0~63の値、~127の値、~191の値、~255の値と4分配してるわけだけど、255超えたとしても最終的に正規化すればいいわけだから枚数縛る必要ないな?」

つまり「8bitで不足なら16bit使って、ついでに正規化すればいいじゃない」という力業理論でした。
これを実証してみることにします。

2.16ビット化を試す

実証にあたって下記動画の配布ファイルを使用させて頂きました。
こちらのファイルをダウンロードすると、参考の顔影白黒画像がありますので今回こちらを使用しました。

顔影白黒画像8枚。
参考として付属している完成品ファイルがこちら。サイズは2048*2048。

上記の白黒グラデーション画像を詳しく見てみます。

画像右側の頬あたりの部分ですね。

テクスチャサイズが256*256以下であればまだ大丈夫かもしれませんが、大きくなると8bit=256分割は同じ色の幅が発生しがちになります。
もう1チャンネル8bit情報を使い、この幅をさらに細分化していきます。
16進数で言うと、今まで「00~FF」で分割していたものを「0000~FFFF」で分割してやろうって感じですね。

荒い方のグラデーションです。上記の参考結果とほぼ同じ。
上記グラデーションの256段階をそれぞれさらに256分割したものがこちら。

これを画像のRchとGchに割り当てたものが以下になります。

画像としては「なんだこれ」という感じ。うまくいくのか。

このテクスチャをエンジンに持って行ってみます。
テクスチャを読み込むときにはRGBの値をあくまで8bitの数字として読んでほしいため、そういった仕様のフォーマットに設定する必要があります。

今回はUnrealEngineで。
VertexDisplacementMapとやらがRGBA8で読んでくれるためこれでテスト。
デフォルト設定だとなんかダメでした。
R値を256倍してG値と加算、それを256で割りました。
その他のBP構成は記事冒頭alwei様のブログに準拠しています。
…絶対こういう系のノードどっかにあると思うなぁ…

結果…。

ボケました。
ボケてます!

さすがに256*256=65536分割すれば段差等は目立たなくなるようです。

もう1チャンネル使って256*256*256≒16777216分割してもいいかもですけど流石にテクスチャ解像度が追いつかないだろうから無駄かなと判断し今回はオミットしました。

また、幸いなことにSDF作成時の距離計算がFloat32出力だったため、これを加算していって最後に0~65536に正規化することで正規化も滞りなく対応することが出来ました。

3.ツールにしてみる

無事に理論実証が出来ましたので、pythonでツール化してみました。
以下のリンクからダウンロードしてください。フリーソフト扱いです。

https://drive.google.com/drive/folders/1XjDY4eTqojipTRje_xICw2o42omFLCe9?usp=drive_link

exeファイルを起動すると発行元が確認できないファイルの為、windowsに怪しまれますが気にせず起動して頂ければと思います。
起動するとこんな感じです。

python様様。

使い方は、

①左側の白欄に白黒で作成された同サイズの顔影テクスチャ連番画像ファイルをドラッグアンドドロップで追加する
②順番がばらけた場合、右上のCtrl内のボタンで順番を整える
③出力チャンネル形式や計算方式などを選択
④Exportボタンを押して画像出力

となります。

SDFの生成等の画像処理類はOpenCVによって行っています。
距離計測のマスクや方法のパラメータがいくつか存在したため、Optionに変更可能なパラメータ要素として入れています。
Channel部分には画像への出力オプションを用意しました。

RG(16bit)…RGチャンネルを使った16ビットの細かい分割
Ronly(8bit)…Rチャンネルのみを使用した従来の8ビット分割
GrayScale(8bit)…8ビット分割をRGBチャンネルに適用しグレースケール化


となります。

画像の入出力形式は以下になります。
tiff png bmp jpeg gif

Targaも搭載したかったんですが、OpenCVのデフォルト機能になかったため断念しました。

出力画像のサイズはインポートした画像群の先頭のものに準拠します。

4.おわりに

ノリと勢いでツール作成しましたという記事でした。

こちらのツールはそもそも上記ブログの記事・理論から着想を得たものですし、ツールを動かす理論などに関してはざっくりと上に公開した通りなので別のツールなどで応用して下さって全然構いません。
(ただし、当ツールそのものを無断で再配布したり、有料再配布などを行う事は禁止させて頂きます。)

デバッグはかなり甘いのでふとした瞬間に動作停止したりする場合があるかもしれません。
更新対応はできるかわからないですが、当記事にコメントを残して頂ければ随時確認したいと思います。

商用利用も無償・ノンクレジットで可能です。実用に耐えられればですが…商用作品で少しでもお役に立てた場合、ページ下部で記事をサポートして頂けると嬉しいです。

最後までお読みいただきありがとうございました!
不具合報告のほか、使用所感などの感想も是非書いてください!!

5.追加更新

(24.6.23_v1.01)
R単色もしくはグレースケールの16ビット出力を追加しました!

Ronly(16bit)もしくはGrayScale(16bit)を選択してください。

R,Gチャンネルに分割しているわけではないため、単純な白黒グラデーションとして使用することが出来ます。
UE5で読み込む際は、グレースケールは「Grayscale」形式、Rチャンネル単体のものは「Harf Float」形式で読み込んで下さい。

グレースケールの読み込み。16bit対応しているので綺麗なグラデになります。
Rのみ16ビットであればHarf Float形式でOK。
他チャンネルにも情報を入れている場合はHDR形式で。

もしツールや記事などが少しでもお役に立てましたら、サポートして頂けると嬉しいです!