俺と旅行とLiDARスキャン

# この記事の要約

ダラダラ書いたら無駄に長くなった上に、後半につれて割愛の度合いが強くなってしまったので、ここだけ読めばいいよう要約を添えておきます。

旅行とか行ったらLiDARスキャンして、シーン作ってSTYLYにアップしようぜ
Blenderで要らない部分をごっそり削除
ポリゴンリダクションしてサイズ減らす
Unityに持ってきてCollider付けて、TextMeshProでコメント置いて
STYLYにアップして完成

# はじめに

みなさんはよく旅行しますか？
私は温泉サウナ好きの友人に連れられて、年に何度か旅行します。
そして良い宿に泊まったり、観光スポットを訪れた際に当然やることと言えば。
はい、そうですね。LiDARスキャンです。
iPhone12以降のpro系にはLiDARスキャナが搭載されているので、スマホ一つでササッとスキャンできて良いですね。

## 過去に作ったシーンを幾つか

とりあえず無目的にスキャンしたデータなんかを雑に並べて、STYLY上にシーンを作ったりしています。

### 野沢温泉

確か、初めて旅行でLiDARスキャンしたのは2021年に野沢温泉で外湯巡りをした際だったと記憶している。
この時はスキャンデータを、本当にただ並べただけ。

https://gallery.styly.cc/scene/56c939fa-45d4-4b86-a022-4dc227e14c64

### 九州サウナ温泉旅行

三泊四日の九州旅行で、あちこちのサウナと温泉を巡った時に作ったシーン。
バラバラの土地で撮ったものをわざと繋げて置いたり、ちょっとしたコメントを残してみた。
あと、適当に昔作った変な曲をBGMにした。

https://gallery.styly.cc/scene/31e137fd-3854-4fd9-87e3-7f513f97ca19

### サーマルクライムスタジオ富士

できたばかりのサウナ施設が撮影OKだったので、小分けにして全体をスキャンした。
この頃から少しBlenderの使い方を覚えて、多少のバリ取りとかをし始めている。

https://gallery.styly.cc/scene/2dffce6e-d6e2-4a68-af29-72ae22fa372d

### 道志村12498番地

山梨のサウナ付き貸別荘へ行った際のスキャン。
室内と屋外の２スキャンで構成しているので、テクスチャ解像度はやや粗め。
室内のColliderを工夫した。
あとTextMeshProを使ってみた。

https://gallery.styly.cc/scene/8dbc5cd3-2431-4dc4-a013-eaddde9a0b0b

## 本題

さて、これらのシーンはさして作り込んだわけでもないものに見えます。
しかし実際にはスキャンデータを置いただけではなく、複数のツールにまたがる処理フローを経ていたりします。
そのため基礎的な知識だったり、ツールの基本操作だったりを分かってないと、すんなりと作れなくて躓くかなと思いました。
とは言えわざわざチュートリアルを一通りやるほど真面目にそのツールを使い始めたいわけでもなく、ただ最低限の操作だけ知りたかったりすると思うんですよね。

てなわけで、一連のフローについてピンポイントかつ詳細な手順を示すことで、スキャンシーンをSTYLYで公開できるまでのフローを身に付けよう！というのが本稿の狙いであり願いであります。

## 大まかなフロー

1. LiDARスキャン
2. Blenderでのバリ取り
3. ポリゴンリダクション
4. Unityでのシーン作成
5. STYLYで公開

# スキャン編

iPhoneユーザーであれば、まぁ[Scaniverse](https://apps.apple.com/jp/app/scaniverse-3d-scanner/id1541433223)がメジャーではないかと思います。
LiDARセンサー非搭載機種でもフォトグラメトリでスキャンできます。
Androidユーザーなら[WIDAR](https://www.widar.io/ja)とか[RealityScan](https://www.unrealengine.com/ja/realityscan)あたりでしょうか。
大概のアプリはスキャンデータをfbxなりobjなりglbなりで出力できると思うので、ひとまずはそこまでこだわらなくても良いかと思います。

スキャンの仕方というのもなかなか奥が深いものです。
LiDARなのかフォトグラメトリなのかでちょっと違ったり、屋内か屋外か、スキャンする広さは、光源は、どういう用途でどの程度のクオリティが欲しいか、などなど。
ケースバイケースなので一概にこうすれば良いと断言できないです。
かなり広いエリアや、複数の部屋で構成された建物内部をスキャンする場合は、１回のスキャンで撮ろうとすると最終的に面積当たりのテクスチャ解像度が低くなってしまいます。
なのでこういう場合は部屋単位とかでスキャンしておいたものを、後でうまく繋ぎ合わせる方が綺麗に仕上がります。

ひとまずスキャンしたデータをfbxでexportした、ということで以降を説明していきます。

# Blender編

先に断っておくと、私はあまりBlenderスキル高くないです。
というよりも、自分にとって必要な機能の使い方以外は全く触ったことすらないです。
でもね、別に本格的にCGアーティストを目指すとかってことでもなければそれでいいと思うんですよ。
基礎からちゃんとチュートリアルをやらねば、みたいに気負っちゃうと面倒くさくて結局いつまで経ってもやらないままなんで、その時の自分に最低限必要な使い方だけ覚えていけばいいんですよ。
そんなわけでスクショを添えつつ説明しますね。

Blenderを起動したらデフォルトでcubeがあると思うので削除しときましょう。
ここで選択してxキーを押すと削除できます。

![スクリーンショット 2024-05-11 19.00.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/de5be18b-4552-36eb-c67d-562f3acd831e.png)

メニューからfbxをインポートします。
![スクリーンショット 2024-05-11 19.24.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/95a75ec8-e5a5-b9f6-1922-5e8737351f61.png)

ここらへんを押すとテクスチャが表示されます。
![スクリーンショット 2024-05-11 19.34.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/447ba663-7bca-1073-6462-629b12c6dea7.png)

ここでstatics表示しておきましょう。
左の方に頂点数などが表示されます。
![スクリーンショット 2024-05-11 19.30.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/cd2dcf03-68cc-4e38-a80f-6b2cb30b16e9.png)

屋内をスキャンした場合、裏ポリゴンが表示されると確認しづらいことがあるので、ここにチェックを入れてカリングしちゃいましょう。
![スクリーンショット 2024-05-11 19.37.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/e0214de9-a17a-cd1c-6825-f2cd41175e67.png)

ではここから不要な頂点を手作業で削除していきます。
まずMeshを選択して左上の部分でEditModeにします。
![スクリーンショット 2024-05-11 20.05.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/5d8aa19b-7266-eaf8-166c-d80db4acc578.png)

この選択ツールは長押しするとサブメニューみたいなものが開きます。
SelectCircleにしておきましょう。
![名称未設定.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/efdf90ac-51bb-485f-27f3-830f789e00da.gif)

面の裏側に隠れた頂点も選択できた方が作業しやすいので、ここにもチェックを入れておきます。
![スクリーンショット 2024-05-11 20.05.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/a503d839-8ee3-3d81-b6c2-496e56d5e625.png)

こんな感じでガサッと選択し、xキーでdeleteメニュー開いてVerticesを選択して削除します。
![名称未設定 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/14d84661-361d-c50f-e9a8-638da556b0af.gif)

不要な部分をちまちまと手作業で削除していきます。
今の自分にはこのぐらいのことしかできませんが、もっとBlender力をつければより綺麗にモデルを修正できそうですね。
STYLY Magazineに参考になりそうな記事があったので、こちらも参考にしてみると良いかと思います。
[【Bledner】3Dスキャンしたモデルを修正する方法まとめ](https://styly.cc/ja/tips/blender-adjustment-scanmodel/)

では編集したモデルをexportしましょう。
![スクリーンショット 2024-05-11 23.03.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/c1005619-7af4-2004-d4ea-9b203f0d5ad1.png)

fbxのexport設定には少し注意が必要です。
PathModeをCopyにして隣りのアイコンを押すと、textureをfbxファイルの中に含めることができます。
また、ObjectTypesも大抵の場合はMesh以外は不要なので外しておくと良いかと思います。
![スクリーンショット 2024-05-11 23.04.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/4fec5477-9e6a-aa9f-bb81-7d28c1f443b3.png)

そもそもtextureをfbxファイルに含めるかべきかどうかは、ワークフローとかに依るところはあるかと思います。
含めた方が1ファイルで受け渡しできるので楽です。
しかしtexture自体をペイントツール等で修正したりする場合は、fbxファイルに含めず相対パス指定で分けておいた方がバージョン管理はし易いかもしれません。

# ポリゴンリダクション編

ポリゴンリダクションとは、元の形状をなるべく保ちながらメッシュのポリゴン数を削減する処理のことです。
リダクションすべきかどうかは、最終的に１シーンにどれだけのスキャンモデルを置くつもりなのかだったり、どういうデバイスを対象としているかなどに依ります。
なので、一部屋スキャンしてみたものをただ置いただけのシーンを作る場合なんかはやらないことが多いです。
しかし幾つものスキャンデータを繋ぎ合わせて大きなシーンを作ったりする場合は、データをある程度削減しておいた方が良いでしょう。
また、ごっそりと削減したデータの使い道は色々あるのですが、その一例は後述します。

ポリゴンリダクションを行えるツールやサービスは幾つかあります。
高度なツールだと特定の領域は崩れないように指定したりだとか、特定のカメラから見えない範囲だけ削ったりだとか、そういう機能があったりします。
このリダクションだけでもかなり大きなトピックになるので、今回はサラッと流します。

以前、**BlenderのDecimate**と[Simplygon](https://www.simplygon.com/)と[RapidCompact](https://www.rapidcompact.com/)でのリダクション結果を比較するシーンを作りました。
頂点数を50%、25%、10%にしたデータを並べています。

https://gallery.styly.cc/scene/bb7a27dc-9eec-435c-bdb8-0bfbbd4ef716

BlenderのDecimateはただ頂点を削ってるだけっぽいので、かなり穴だらけになりますね。
![スクリーンショット 2024-05-12 1.08.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/a48230d7-19f1-e668-75ff-064929bed2bb.png)

RapidCompactもSimplygonもかなり綺麗ですね。
10%まで落としてもかなりクオリティを残している感じがします。
![スクリーンショット 2024-05-12 1.08.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/aa34fae2-2673-b561-14f6-4e6581debea6.png)

個人的には、サーバ側で処理してくれるRapidCompactが便利かなと思います。
無料プランでも月20モデルほど処理できるので試してみると良いでしょう。

# Unity編

## fbxをimportする

ではexportしたfbxファイルを、UnityEditorに取り込んでみましょう。
おや？テクスチャが貼られずに真っ白になってしまいますね。
![スクリーンショット 2024-05-12 16.46.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/8b00d8d2-47f6-e50f-6a68-36394ff02f52.png)

基本的にBlender上でのmaterial設定は、UnityEditorと互換性があるものではありません。
なので改めてmaterial設定しましょう。
まずfbxアセットを選択して、Inspectorのmaterialsページを開いてください。
ここのExtract MaterialsとExtract Texturesのボタンを押します。
![スクリーンショット 2024-05-12 17.37.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/22aadf75-5d4a-f9c3-c1ca-8a762ec7a80f.png)

するとこんな感じでmaterialとtextureを取り出せます。
![スクリーンショット 2024-05-12 17.38.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/c6546547-50de-5bc0-a7c1-3988fc59f8ae.png)

ではmaterialを設定していきましょう。
デフォルトだとShaderはStandardになっているかと思います。
これを**Unlit/Texture**にしちゃいましょう。
意図的にLightingの影響を与えるようにしたい場合でなければ、ひとまずは何も考えず**Unlit/Texture**で良いです。
ついでにtextureも設定します。
![スクリーンショット 2024-05-12 17.40.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/0d46bc5b-f1c0-2f7a-cca9-023083be912c.png)

はい、これでテクスチャも反映されました。
![スクリーンショット 2024-05-12 17.43.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/68933c55-69c5-0dfe-44f8-cdde98606bf4.png)

## Colliderを設定する

※本項は自分でも書いててちょっと結論が揺らぐところがあって、ちょっと冗長です。

ここまでの状態で完成でも良いのですが、VR版STYLYでは当たり判定を使ったraycastでのワープ移動方式を採用しています。
そのため当たり判定がないとVRでシーン内を歩き回ることができません。

ちなみに話が少し前後してしまいますが、STYLYでVRシーンを作成するとデフォルトでTransparentFloorというアセットが置かれています。
これは高さ0の位置に置かれた透明な床です。
![スクリーンショット 2024-05-12 17.48.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/28696da8-7786-ac88-f4b6-0e96aaf45acb.png)

これがあるので一応移動できますが、ちょっとした三和土や小上がりなどの段差あると足元がめり込んだり浮いてしまったりしがちです。
![スクリーンショット 2024-05-12 18.27.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/b530de9f-3011-3e94-3dc8-bb1f4779b6b6.png)
また、こんな感じのスキャンだと階段にColliderがないと、上の階に上がることができません。
![スクリーンショット 2024-05-12 18.51.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/7a969ce8-6f59-79a8-836a-50aabaa4c663.png)

スキャンモデル自体にMeshColliderを付けるのが、一番手っ取り早い方法かと思います。
ただ、スキャンモデルってやたらに頂点が多かったりするんので、それにMeshCollider付けるのはパフォーマンス的になんだかな、と個人的には思っちゃうんですよね。
実際に自分の目的通りに動いてるならそれでいいはずなんですけど、なるべくサイズや処理負荷は軽くしたい欲求というか。

なので、私はよく[ProBuilder](https://unity.com/ja/features/probuilder)を使って当たり判定用のオブジェクトを置いたりしていました。
別にProBuilderじゃなくてもいいんですが、階段を作る機能が非常に使い勝手が良いんですよね。
ProBuilder自体の説明はこれまた長くなるので割愛します。
こんな感じで色分けしながら当たり判定用にオブジェクトを置いといて、後でMeshRenderをオフにします。
![スクリーンショット 2024-05-12 19.13.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/4cdd31d8-d765-8160-3544-a2dc4931afd4.png)

ただ、やっぱりMeshColliderじゃないとうまく対応し辛いパターンもあったりします。
例えば、こういう自然環境とかだと緩やかに傾斜や凹凸があったりするので、MeshColliderを入れる方が実際にVRで移動しやすいと思います。
![スクリーンショット 2024-05-12 18.12.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/7c2c6e98-6289-43d0-a3c6-fb80fb1f52a0.png)

じゃぁそもそも全部MeshColliderでいいんじゃないか、という考えが復活してきますね。
でも処理負荷の問題とは別の問題もあります。
一般的な日本の住宅とかをスキャンすると、廊下とかが狭いんですよね。
![名称未設定.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/65fae8eb-f872-d4df-5836-a709d8c45078.gif)
この状態でMeshCollider付けちゃうと、レイがやたらと壁やら何やらにぶつかって快適に移動しづらいです。

この問題もあってMeshColliderを使わずにProBuilderで頑張ってたんですが、つい最近良い方法を思いつきました。
それがこれです。
![スクリーンショット 2024-05-12 22.13.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/2ee7f9a7-fcc7-e1fc-92ae-351559898fc8.png)
膝丈以上の高さをごっそり削った**当たり判定用**のモデルを用意します。
このモデルにMeshColliderを適用して非表示にすることで、結構イイ感じになりました。
もちろん穴がある部分はすぐ下に適当なCubeとかを置いて落っこちないようにします。

この方法だと細かい起伏に対してもColliderが効く上に、作業コストもかなり安いのが良いですね。
更に負荷やサイズが気になるようなら、この**当たり判定用**のモデルをかなりがっつりとポリゴンリダクションしちゃえば良いです。
こういう、見た目以外にモデルを利用するケースもあったりするのでポリゴンリダクションについて触れたりしたんですね。

## TextMeshProでコメントを残す

さて、いよいよ大詰め。
せっかくのスキャンデータをただ置いただけってのも味気ないので、自分なりに旅の思い出を一言コメントとかで置いていきましょう。
単純にuGUIのCanvasを、RenderModeをWorldSpaceにして置いていくというのが簡単かもしれません。
ただこれだとプラットフォームによっては日本語がちゃんと表示されなかったり漢字が変だったりします。
なのでTextMeshProを使いましょう。
uGUIのTextよりも色々装飾できたり、綺麗に表示できるので基本機能として覚えておいた方が今後の役に立ちます。

が、細かい説明は割愛します。
まぁTextMeshProの使い方の記事はググればたくさんあるので。
特に網羅的で詳しい解説をしていた記事を紹介しておきます。

https://madnesslabo.net/utage/?page_id=12863

一点注意しておきたいことがあります。
TextMeshProで日本語を表示する方法としてよく紹介されているものに、[日本語文字コード範囲指定のtxtデータ](https://gist.github.com/kgsi/ed2f1c5696a2211c1fd1e1e198c96ee4#file-japanese_full-txt)をCustomCharacterListとして使用するものがあります。
必ずしもこの方法が駄目というわけではないですが、当然フォントアセットのサイズは大きくなるので注意しましょう。
ユーザーの自由入力テキストがなく、トータルの文章量も限られているなら使用文字だけでフォントアセットを作る方が良いと思います。

これは冒頭で紹介した[道志村12498番地](https://gallery.styly.cc/scene/8dbc5cd3-2431-4dc4-a013-eaddde9a0b0b)のシーンに配置したテキストの内容です。
<details><summary>テキスト</summary>

```
山の夜は冷えるが、暖炉がとても心地良い
ここは寝室
追加の布団もある
キッチンには食器や調理器具だけでなく、スパイスが揃っている
リビングはとっても良い感じ
ソファには毛皮があったり、トルコランプがたくさんあって素敵
夜はここで焚き火を囲んで、お酒を呑みつつ長々と話をした
人数が多いとこの離れも使えるらしい
薪サウナは自分で好みの温度に調整できて非常に良い
更衣室らしきスペースだが、これまた凝っている
右の小さいのが水風呂
左の大きいのはお風呂で、三人ぐらいは入れるサイズ
椅子がうまくスキャンできていないけど、ここが整いスペース
プロジェクター(オプション)を借りれば、このスクリーンに映したりできる
ここにも焚き火スペースがある
とにかく広い！
ここはトイレとお風呂
スキャンしてないが、ここはテラススペース
ハンモックを吊るしたりできる
ここでバーベキューをした
```
</details>

**Character Set**を**Characters from File**にして、**Character File**に指定すると重複を除外して使用されている文字だけでフォントアセットを作ることができます。
![スクリーンショット 2024-05-12 23.27.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/3ec56ca6-a49f-7b9d-94c4-1bd029e1d82b.png)

この辺りはワークフローにも関わってくるので、機能を把握した上で自分の作業の流れと合わせてうまいやり方を見つける必要がありますね。
私の場合はスキャンモデルを配置してみて、どこにどういうコメントを置きたいのかを先にイメージしてテキストに起こしてから、フォントアセットを作成してテキスト配置をしています。
人によっては前述の日本語文字コード範囲全部のフォントアセットをひとまず作って、テキストを考えながら配置した後で、実際使われている文字だけでフォントアセットを作り直す方がやりやすいかもしれません。

### trouble shooting

実際に文字を配置して実機確認した際に、文字の周りが白い矩形で囲われてしまうという現象に遭遇しました。
以下の記事を参考に、Paddingの値を引き上げることで解決したので困ったら試してみてください。

https://qiita.com/Clpsplug/items/fb1c8475d26b5e49faa2

# STYLY編

[公式ドキュメント](https://styly.cc/ja/manual/unity-asset-uploader/)があるので、この通りやれば特に難しい部分はないと思います。

# 総括

さて、かなり駆け足で流れをざっくり説明しました。
こんな感じで旅先の宿やら、何やらをスキャンしてシーンを作ってみると楽しいものですよ。

ただ冒頭に紹介したシーンをVRで見ると、圧倒的に足りないものがあることに気付くと思います。
それは音です。
無音の空間ってリミナルスペース的な不気味さがあるんですよね。
となると自分の次の課題はフィールドレコーディングなのかな、なんてことを考えたり。

# 宣伝
STYLYではUnityエンジニア・サーバーサイドエンジニアを募集しています！！
ご応募お待ちしています！！

https://www.wantedly.com/projects/1360135

https://www.wantedly.com/projects/1360123