🐜

Scaniverseでスキャンした広大な平面データを画質を保持したまま軽量化して扱いやすくする

2023/03/23に公開

本記事の情報はCC-BY 4.0で公開しています。
好きに利用して構いませんが、「これを使ってこんなことをしてみたよ」と私に通知がいく形で使用してもらえると嬉しいです。(twitter: @ymgsknt

モチベーション

3Dスキャンといえば立体形状を記録する手法ですが、最近は「平面だけど大きいもの」を記録するための手段として使っています。
私は道路標示を記録・収集し、地理情報の分析やグラフィカルな表現に転用することに関心があるのですが、道路標示は斜めから見つめることはあっても真上から俯瞰できる機会は多くありません。
斜めに撮った写真を画像処理で矩形に変換するスクリプトなんかを組んでみたこともあったのですが、変換のための頂点情報を手打ちで入力するのは案外大変だし、画像一枚を引き伸ばすとカメラから遠い方にある部分は拡大率が大きくなってしまい画質が悪くなるのが問題でした。


illustratorで人力でパースをとって、変換に使用する頂点情報を求め、変換用のスクリプトに数値を打ち込んで台形補正をかけるプロセスと、その結果。かなりしんどい。

そこで3Dスキャンでまるっと記録し、データを真上から俯瞰する、というのが一番手軽で高精細だというのに最近気づきました。

この記事でやること

タイトルの通り、Scaniverseで撮影した3Dスキャンデータを、特にその形状を「平面」に絞った上で軽量化の方法について説明します。私はこのデータをStylyに公開したり、Unity上で使用することを考えているため、モデルそのものの軽量化に取り組む必要があります。

基本的には下記記事を参考にしており、私は特別新しいことは何もしていません。
https://creator.cluster.mu/2023/01/11/blender-3dweight/
https://styly.cc/ja/tips/blender-adjustment-scanmodel/

ただ、こちらの記事がとても親切で、様々なケースの軽量化に対応して色々な情報を提供してくださっているので、ここでは表題のような用途に絞って、手順どおりにステップバイステップで取り組めば、だれでも目的を達成できるよう再編成しました。
また、今回私は大量のモデルに対して同等の手順を踏んで変換を行っていく作業をせねばならぬので、モデルごとに別々の対応をとる必要が発生しない処理に絞って構成しています。自分自身がBlender初心者なため手順をかなり細かく記載していますが、慣れてきたら3分くらいでできます。

なお、表題にある通り本記事は「広大な平面データ」に対して適用できる方法ですので、複雑な形状に対して行う場合は別の対応が必要な部分が出てくると思います。その場合は適宜、上記記事などを参考にしてみてください。

手順

Decimateを用いた頂点の削除

修正する前のモデルを残しておくために、Shift + D + Enter でmeshを複製します。

Modifier Properties(画面右下のスパナのアイコン)-> Add Modifier, Decimateを選択。Decimateを使用すると、大まかな形状を保持したまま頂点データを自動で大量に削減してくれます。(全然余談ですが、Decimateの意味は「<人・動物などを>多数殺す、<作物・森などを>大量にだめにする」だそうです。)他のサイトでは人力で頂点削減を行う方法が紹介されていますが、今回のような平面オブジェクトはとくに、こうした自動化に頼りきってしまっても問題なさそうです。

Decimate内にあるRatioの項目は、もともとのデータにおける頂点数に対して、変更後の頂点数をどれくらいの割合にするか設定できるもの。平面のオブジェクトなので、経験的には0.05くらい(もとの頂点数から95%削減)まで極端に落としてしまっても問題ありません。

もともとのデータ
Decimate適用前
Decimate適用後
Decimate適用後

Decimate適用後、最後にApplyを選択して変更をモデルに反映します。

マージによる大まかな穴塞ぎ

この状態でメッシュの軽量化は十分に行えていますが、ところどころ穴が空いてしまっているのが気になります。全体にマージをかけて穴を塞いでいきます。

Object ModeからEdit Modeに変更した上で、Decimateを反映させたモデルの頂点を全選択(Command + A)し、その状態でMを押します。するとMergeメニューが開くので、一番下の「By Distance」を選択します。

画面の左下に、Merge時の設定を行う項目があるので、こちらを開いてMerge Distanceを設定します。こちらも経験則ですが、0.02m程度で大方穴を塞ぐことができるかと思います。

完全に塞げているわけではありませんが、このDistanceをあまり大きくしすぎてしまうとオブジェクトの形状を破壊してしまうことになるので、今回はこれくらいで勘弁してやりました。

ベイク

しかし、これまでの処理でメッシュの形状が大きく変化してしまっているため、ところどころテクスチャに歪みがでてしまっています。

なので、最初に複製しておいた軽量化前のモデルを使って、現在のモデルへとBakeします。
軽量化したモデルを選択した状態で、Material Propertiesタブを開き、+ボタンを押してマテリアルを追加。さらに、「New」を選択。

新規作成したマテリアルを選択して、Base Color のところにある丸をクリックし、表示された項目の中から「Image Texture」を選択します。

さらに、その下に表示された「New」ボタンを選択し、新規画像を作成します。(今回は画像名をBakedImageとしました)。ここに、もとのモデルのテクスチャを焼き付けます。
このとき、widthとheightは2048pxに設定、alphaのチェックは外しておきます。widthとheightについて、デフォルトでは1024pxとなっていますが、どうやらscaniverseで撮影された時のテクスチャ画像の大きさはそれよりも高解像度なようで、デフォルトのままだと画質が悪くなってしまいます。また、今回はテクスチャ画像に透過情報を含まないのでalphaのチェックは外しました。

ここまできたら、現在貼られているマテリアルはもう使用しないので、マテリアル一覧から「main」のマテリアルを選択し、-ボタンを押して削除します。

画面上部から「Shading」ウインドウを選択し、先ほど作成したノードを選択。

次に、Render Propertiesを開き、下記の設定を行います:

  • Render Engineを「Cycles」に変更
  • Deviceを「GPU Compute」に変更(GPU演算を使用できない環境もあるので、その場合はデフォルトのCPUを選択)
  • Bakeタブを開き、
    • Bake Typeを「Diffuse」に設定
    • Contributionsからdirect, indirectの項目のチェックを外し、colorのみチェックをつけた状態にする(通常ベイクはライティングの処理などをテクスチャとして焼き付けてしまって処理を軽量化するために行うもので、こうしたライティング設定を外すことはないのですが、今回はモチベーションが異なり、単に色情報を再マップしたいだけなのでライティング設定を外します。)
    • Selected to Activeにチェックを入れる
    • Extrusionを0.1mに設定

ここまできたら、軽量化したモデル、もとのモデルの順にモデルを両方とも選択し、Bakeを実行します。

Bakeがうまくいかず、ところどころ破断したテクスチャが現れてしまった場合は、先の「Extrusion」の項目の数値を上げて、再度実行してください。


Bake実行結果

書き出し

今回はunity向けに書き出したいので、下記noteを参考にしてfbx形式での書き出しを実行します。
https://note.com/info_/n/n1dd54ce4545b
書き出したい3Dオブジェクトを選択した状態で、file->export->fbx(.fbx)を選択し、下記の設定で書き出し。

総括

軽量化の流れは以上です。冒頭でもお話しした通り、「モデルごとに別々の対応をとる必要が発生しない処理に絞って」構成されているので、今後pythonスクリプトで自動化することもできるでしょう。フローの言語化はできたので、最近流行りのChat GPTにでも投げ込んでスクリプトを大まかに作ってもらえたら、また記事を書こうと思います。
(全然余談ですが、この記事を書きながら「Chat GPTに適宜必要なことを聞けるこの頃、わざわざ人力でこんな記事を書くこと、ないしこれを参照して作業を進める人がどのくらいいるんだろう」なんてことを考えていました。そして現代において記事執筆をする意味...どうせこの記事もChat GPTの餌になるのだから、Chat GPTに良い教師データを作ってるとか?それとも人間由来のオーガニックで香ばしいテキストが付加価値を帯びる日がやってきて、日の目を見るのことになるのだろうか...むにゃむにゃ)おしまい。

Discussion