ARKitのフェイストラッキングで顔の頂点をいじる

きARKitのフェイストラッキングはめちゃくちゃ簡単に実現できる。こういうのはゼロからつくっても5分ぐらいでできると思う。

ではこういうのはどうか。

北千住デザインという屋号で活動されている方の作品。シンプルな指針でいうと、ARKitによって顔のジオメトリは取れているので、

このジオメトリの頂点を動かしてやればいい。SCNGeometryの頂点をいじるだけなら、Metalシェーダを書かなくても、こんな感じで取り出して変化させて、というのをSwiftコードで(※CPU側の処理になる)書ける。

・・・のだが、それだけだとこうなる。

(おでこのあたりの頂点を引っ張っている)

当たり前の話だが、ここに映っている僕の顔はただの2Dの写真であって、ARKitが提供してくれている3Dの顔のジオメトリの上にテクスチャとして貼られているものではない。ので、顔のジオメトリの頂点を動かしても僕の顔は変形しない。

というわけで、顔のジオメトリの頂点をいじって顔を変形させるには、そのジオメトリの上に顔のテクスチャを貼る必要がある。

自分はこの処理を次のように実装した。

・ARKitのカメラから取得した2枚のテクスチャ(Y, CbCr)をMetalで1枚のRGBテクスチャとしてレンダリング・・・(1)
・ARFaceAnchorから取得したARSCNFaceGeometryのテクスチャ座標のジオメトリソース(SCNGeomertySource)を、頂点座標をスクリーン座標に変換したもので置き換える・・・(2)
・(1)を(2)のジオメトリのテクスチャとして設定する

これで、ちゃんとカメラから取得した画像を顔ジオメトリのテクスチャとして貼ることができた。

貼り付けがうまくいったらあとは頂点を好きなように動かせばいいのだが、それはSceneKitの力を借りつつMetalでレンダリングする場合はSCNProgramを使用して頂点シェーダで実装する。(SCNProgramについては以前別の記事に書いたことがある。)

たとえば顔の中心から放射線状に頂点を動かせばこうなる。

北千住さんのに比べるとまだまだ汚いことこの上ないが、まぁとにかく頂点を動かせてはいる。。

ハマった点のメモ

・顔のジオメトリへのテクスチャの貼り方

恥ずかしながら、テクスチャ座標を意識的にどうこうしたことがなく、最初はまったく見当はずれの方向で作業していた。

まず、顔のジオメトリを使ってカメラ画像(1)をマスクし、そのマスクで顔部分だけを切り抜いた画像を顔ジオメトリにテクスチャとして貼ろうとしていた。 → ジオメトリが見えなくなった

次に、カメラ画像(1)をそのまま((2)を行わずに)ARSCNFaceGeomertyが元から持っているテクスチャ座標のままで貼り付けてみたらこうなった。

これは大失敗しているが、これが「何がおかしいか」のヒントになった。カメラ画像をそのまま(マスクして切り抜かずに)使うことは問題なさそうで、テクスチャ座標を適切に設定すればいけそう、と指針がこの失敗から得られた。

・displayTransform, viewportの取り扱い

カメラから得られる画像は常に横向きで、ARFrameのdisplayTransformでUIの向き、スクリーンサイズを指定することで変換行列が得られる。

let displayTransform = frame.displayTransform(for: .portrait, viewportSize: viewportSize)

で、これはいいのだが、前述の(1)の処理で得たcapturedImageTextureは既に回転・ビューポートが考慮されたものになっていて、しかしSCNRendererが持つカメラにはARFrameのカメラと同じ設定を施していて、というので実行してみたら90度ずれていた/横に伸びていた、みたいなことがよくあった。今でもあまり整理できてなくてソースコードからその混乱が見て取れる。

あと、ひとくちに"viewport"といっても、上のARFrameのメソッドにおける"viewportSize"引数の単位は「ポイント」だが、こちらのSCNRendererのレンダリングメソッドにおける"viewport"引数の単位は「ピクセル」だったりする。まぁSceneKitやMetalの世界では基本的にはサイズはピクセル、と思っておけばいいか。。

func render(atTime time: CFTimeInterval, viewport: CGRect, commandBuffer: MTLCommandBuffer, passDescriptor renderPassDescriptor: MTLRenderPassDescriptor)

今後の展望

もともと顔を変形させる表現自体がやりたかったわけではなくて、もっと3Dプログラミング・GPUプログラミング(Metal)に慣れたくてこれをやってみた。今まで頂点シェーダはパススルーしか書いてなかったので、今回の題材のおかげで法線やテクスチャ座標等、理解が曖昧だったところが補強できたし、まだよくわかってないところも浮き彫りになった。

次のステップとしては、「頂点情報を使いつつ塗る」ということをやりたい。唇のあたりだけ赤で塗るとか。あと、今回テクスチャ座標や法線の再計算をCPU側でやってジオメトリを再作成するというコストの高いことをやっているので、それをGPU側に持っていくようにもしたい。

この続きをみるには

この続き:0文字
記事を購入する

ARKitのフェイストラッキングで顔の頂点をいじる

shu223

100円

この記事が気に入ったら、サポートをしてみませんか?気軽にクリエイターを支援できます。

6

shu223

フリーランスiOSエンジニア兼サンフランシスコのFyusion社勤務。 著書に「iOS×BLE Core Bluetooth プログラミング」「iOSアプリ開発 達人のレシピ100」。GitHubで「iOS Sampler」シリーズ他さまざまなオープンソースを公開している。

日々の学びメモ

技術的なメモ、思いついたアイデア、考えたこと、お金の話等々、頭をよぎった諸々を気軽に垂れ流していきたいと思います。
コメントを投稿するには、 ログイン または 会員登録 をする必要があります。