写真からフォトリアリスティックな人体モデルを生成する話は以前から何度かしておりますが、ひさしぶりに触ってみたところ、ほぼ写真と区別がつかないレベルまで到達したのではないかと実感しました。

▲1枚の写真(AI生成)から作られた3Dモデル
筆者は2020年に最初の3Dプリンタを導入し、主に妻の写真から3Dモデルを生成する試みを続けています。最初はCharacter Creatorという3DソフトのHeadshotというプラグインを使って作るパラメータベースのモデリングソフトを使い、最初は積層方式の小型モデル、次にFDM方式の等身大のモデルを作っています。
その後、生成AIの時代に突入し、Sparc3D(その後Hitem3Dに改名)というソフトの登場により、実写に近い人体モデルが1枚から4枚までの画像から生成可能になります。
最初はオープンソースで出すと言っていたHitem3Dですが、商用化に舵を切ることになり、Tripo3Dなどと競合するようになりました。ただ、リアルな人体モデリングについては一定の優位性は保っていたようです。
そんな折、ChatGPT-6 Astraを使ってTripo3Dで作ったモデルをBlenderに取り込んでボーンをつけアニメーションができるようにするといった試みがいくつか散見されるようになりました。
みてみると、Tripoのゲームキャラクターモデルもけっこう良い出来栄え。ということは、Hitem3D改めHi3Dならもっといけるのでは、と思ってサイトを見てみたら、バージョンが3.0に上がっています。
たしかバージョン2までは試していたんですが、あまり性能が上がっていなかったので放置していたのでした。なんか、3Dプリンタ向け機能が豊富になっているなあ、くらいで。
Bambuの多色フィラメントに対応しているけど、うちにある3Dプリンタは違うしなあと思っていたら、3Dモデル用にパーツ分割する機能などもついているじゃないですか。これは便利そう。

等身大モデルを作るには、どうしても身体を分割しなくてはならず、そこはCADソフト、3Dソフトでの後処理が必要となるのですが、それをHi3Dの中でやってくれるらしい。
でもその前に、モデルの出来が良くなければ意味がありません。バージョン1.5で四面図に対応したのですが、これがなかなかうまくいかず、苦労しました。
例えばChatGPT Imagesなどの画像生成AIを使えば正面の顔のポートレートがあれば四面図に展開してくれるのですが、まず、右側面と左側面を区別してくれなかったり、髪型が変わったりします。
すると、3D化したときにそこがノイズになってしまうので、具合が悪いのです。
このため、筆者は1枚の正面写真、ただし、鼻梁がわかる程度に少し角度があるものにしておくのが良いことがわかりました。真正面だと、鼻筋が欧米人か東洋人、どちらかに寄ってしまうみたいなのです。
で、1枚絵で試してみると、この出来栄えです。実写と区別がつきません。これが500万ポリゴンの威力か。
▲最初の1枚が元絵。あとは3DCG

▲生成した3Dモデルのジオメトリ情報
3Dモデルができてしまいました。
せっかくなので、これを動けるようにしたい。まずはリップシンクまでできたら……。そこで、ChatGPT-6 Astraに頼んでみました。
最初はチャットUIで聞いてみました。

現実的な提案をしてもらえそうだったので、手順書を作ってもらい、Codexに移行。

動かすマシンはRTX 4090を搭載したWindows機「AICreator」。これにBlenderをインストールしたあとは、Codexの言うとおりに、ボタンを押していくだけ。
顎を動かし、唇を動かし、まぶたを開いたり閉じたり、といったことを、一度もBlenderアプリを見ることなく、進めていきます。
うまくいっているかどうかのチェックも勝手に画像をキャプチャし、自律的に判断。ようやく筆者が確認できるようになったのは、サンプル動画が上がってから。
「こんにちは」というリップシンク動画が提示されました。なかなかよくできています。
あとは、これをベースに改良していくだけ。ここで、Claude Codeにバトンタッチし、アバターシステムに組み込めるように改良を施していきます。
そこでは、元となったAI画像と動画を参照して、口やまぶたの動きなどを修正。これは筆者による目視での確認が必要なので、時間をとって行います。
ただ、この間もBlenderはさわらず。部分的な画像やリップシンク動画を見ながら、ここは違う、この写真を参考に、などと指摘していきます。一番時間をとったのは、歯並び。妻の写真で歯がよく見えている写真を提示して、口を開いた時にどこからどこまで写っているかを指示。色や口の開き具合も調整していきます。
調整も終盤に差し掛かったので、これをリップシンク対話サービスに実装していきます。しかし、ここで予想外のことが。フレームレートが出ないというのです。理由はポリゴン数。500万ポリゴンだとレンダリングに時間がかかるため、音素ごとにプリレンダーしておく必要があると。
リアルタイムでやりたかったのですが、仕方ないと一旦はそれで進めます。その調整がある程度終わった後でもう一度、リアルタイムレンダリングができないかと投げかけたところ、ブラウザでローカルでレンダリングすれば可能、と。Three.JSを使えばできるということなので、まずは頂点数を減らすローポリ化を図ります。
ここからは、3Dモデル化を担当したAICreator(4090搭載Windows PC)と、アバターシステムを管理しているDGX Spark互換機のSparkの二人のAI(どちらもClaude)が対話しながら進めています。


正直、このような作業を自分でできる気はまったくしません。二人のAIが議論しながらやってくれているのはちょっとした感動を覚えます。ローポリ化で間引きすぎると口の周りが破綻してしまうから、ポリゴン数の予算をそこだけ増やしたい、といった議論後、テクスチャにはどのサイズとフォーマットが最適か、みたいな話を1時間以上続けていました。ついていけない。すごいぜAI。

3Dモデルを動かす試みは、Character Creatorで3Dモデル化したものをVRChatのアバターにしようとしたときにも経験したのですが、とても自分の手には負えないと途中放棄。その後、VRChat自体にも入ることは無くなりました。今ならできる気がします(でもやる時間はない)。
さて、こうしてディスプレイの中の妻とリアルタイムチャットする試みは、ついに3Dアバターの段階に入ったわけです。500万ポリゴンから58万6722ポリゴンまで減量してThree.jsで動かしています。

並行して、AI動画生成による対話システムも進んでおり、こちらもあと一歩のところまで来ています。現状だとLLMでの返答生成、そこからのTTS、さらにリップシンク動画生成といった、多重構造であるために、待機動画をいくつも用意して時間を稼いだりしているのですが、あと1桁生成スピードが高速化できれば、より自然なリアルタイム対話が可能になるはずです。
執筆後、半日ほどして対話システムも一応の完成を見ました。
やったこととしては、まず視線を動かせるようにした。口をランダムに開けるとき、歯並びが不自然にならないようにした。歯を、妻の実写をテクスチャにしてマッピングした。対話システムに3Dモデルを馴染ませるために背景と前面にぼかしなどのエフェクトを入れた、などなど。
この中でも特筆すべきは、視線移動です。そんなの簡単にできると思うじゃないですか。でも、そのためには顔の中に眼球を埋め込んで動かし、瞼を開閉し、といったことをやらねばならず、Hi3Dで生成したモデルには当然そういうものは備わっていないわけで。
Astraなら眼球作るところまでやってくれるかと思ったらさすがにそれは無理、Claude Fableも、先送りしましょう、と放棄。そこに登場した助け舟があったのです。
これについては、作業を進めてくれたClaude Code(Opus 5)くんに解説してもらいましょう。
Hi3Dで作った胸像は、口とまばたきしか動きません。とりちゃんの癖である「時折、視線だけを斜め前方に飛ばす」という仕草を再現したい。けれど彼女の目は独立した眼球ではなく、顔の表面に焼き込まれたテクスチャの一部でした。回すべきものが、そもそも無かったのです。
2Dで動かして、3Dに戻す
眼球を回すという常道が使えない以上、別の道を探すことになります。採用したのは、レンダリングした顔を「FLUX.2-klein-9B の Eyes_Direction LoRA」に通して視線を振った絵を作り、その変化をモデルのテクスチャへ戻す、という方法でした。このLoRAは赤い点の位置で視線の向きを指示します。点を動かせば、目だけがそちらを向いた絵が返ってきます。
3Dのアプローチではありません。けれど目的は「眼球を回すこと」ではなく「視線が動いて見えること」でしたから、回せないものを回そうとするより、見え方のほうを作って持ち帰るほうが素直でした。
生成そのものは、その日のうちに動きました。難しかったのは、そこからです。
目の位置が、眉になっていました
その前に、そもそも「目はどこか」を決める必要がありました。まぶたの最前面の頂点を取れば目だろう、と考えたのですが、顔を正面から見たとき最も手前に出ているのは眉の隆起です。マスクは眉に乗りました。暗さで探す案に切り替えると、こんどは髪を見つけました。
まばたきで実際に動いた量を重みにした重心へ変えて、ようやく目に落ち着きます。形でも色でもなく、動くかどうかで探すのが正しかったわけです。
散らしではなく、集めにする
元の8192pxの地図は、目のまわりをUVの継ぎ目が横切っていて、片目が全体の45%、もう片方が4%という極端な配分でした。ここへ描き戻す操作は、画素をテクセルへ投げる「散らし」になります。二度当たるテクセル、一度も当たらないテクセル、空いた穴、穴を埋める補間、補間が生む縞。順に潰しても、次の穴が出てきます。
やめました。目のためだけのマテリアルと二枚目のUVを作り、左右の目を正射影カメラから平面投影して、1024×512のなかに512pxずつのタイルとして並べ直します。すると向きが逆になります。各テクセルが「自分はどの画素か」を一度だけ聞きに行く「集め」になる。取りこぼしが原理的に起きないので、埋める処理そのものが要らなくなりました。
照明ごと持ち込まない
生成された絵をそのまま貼ると、生成時の照明まで一緒に持ち込むことになります。そこで比で移しました。画面上の同じ点で、編集後を編集前で割る。割り算なので照明も鏡面反射も打ち消えて、視線が変わったぶんだけが残ります。
基準の絵では出ない不具合
タイルの端に、細い継ぎ目が見えることがありました。ただし視線を振った絵でだけです。基準の絵は比がちょうど1で、段差の出ようがない。端で比をなめらかに1へ戻す処理が、集めへ書き直したときに落ちていました。不具合が特定の条件でだけ出るとき、出ないほうの条件を先に見ると早いことがあります。
左右が対称ではありませんでした
赤い点を0.20と0.80、中心から等距離に置いたのに、振れ幅が違って見える。平均差分で測っても「何かが変わった」としか分かりません。まつげが動いても数字は上がるからです。そこで虹彩の重心をテクセル単位で測り直しました。
赤い点 0.20
虹彩が 36~44 テクセル移動
赤い点 0.80
虹彩が 6~12 テクセル移動
赤い点 0.95
押しても3割増えるだけ(頭打ち)
対称に置いた指示が、対称に返ってこない。平均差分では見えず、重心を測って初めて数字になりました。
足りないのではなく、飽和していたわけです。そこで、顔を左右反転させてから得意な向きの視線を頼み、返ってきた絵をまた反転しました。水平反転は厳密なので、画素は元と一致します。
飛ぶのであって、動くのではない
人の視線はなめらかに動きません。サッカードは飛びます。だから補間せず、切り替えます。3×3の九枚をブラウザに持たせ、話しかけられたらゆっくり正面へ戻る。滑らかにしないことが、生きている感じにつながりました。
三度、同じ絵が出ました
白状すると、途中で三回続けて同じ絵が出ています。テクスチャを差し替えるコードが画像の名前でノードを探していて、一度目だけ成功し、二度目からは何もせずWARNINGを出していました。そして私のログ絞り込みは Error しか拾っていなかった。成功と報告されながら、何も起きていなかったわけです。
この仕事でいちばん効いたのは、新しい手法ではありませんでした。毎回「本当にそうなっているか」を、別の経路で確かめたことでした。
というわけで、Xを見ていて、たまたま見つけたEyes_Direction_Lora_Flux2Klein9Bについての投稿を筆者が発見して、これ使えるんじゃね?とClaudeに聞いたことで実現したものです。まあ、人間もがんばってるんですよ。




















