【Unity】meshlabで出力した点群データを軽量化して可視化する
IT技術
Unityで点群の表示に挑戦!
スマートフォンアプリや、FacebookGameroom、Oculus などの VR のゲームまで開発することのできるソフト「Unity(ユニティ)」。
近頃はゲームアプリだけではなく、土木関係のビューワーや、医療関係のシュミレーションでも使用されています。
Unity を使用すれば、3Dモデルを利用した作品を簡単に作成することができます。
ですが、一番大変なのはモデリングの作業です。
人物のモデルの場合、プロのモデラーに頼むと、1つでも数万円から数十万円の費用が掛かることも。
点群とは
実際に存在する建物や地形などを、簡単に表現する方法があります。
それが「点群」です。
ドローンなどで撮影した航空写真から、自動で作成した点の集まりです。
拡大すると点の集まりですが、全体的に見ると 3Dモデルと遜色ありません。
これを Unity で扱っていきましょう。
注意点
- Unity の ve rは 2018.3.14f1 です。
それ以前、以後のバージョンではエラーが出る恐れがあります。 - Point Cloud Free Viewer の情報は、2020年2月20日現在のものです
- 点群を扱うので、ある程度のマシンスペックを必要とします。
メモリは 16GB 以上を、グラフィックボードは GEFORCE GTX1060 以上を推奨します。
Unity Assets Store からアセットをインストール
今回は、「Point Cloud Free Viewer」というアセットを利用します。
使用するアセットはこちら!
【Point Cloud Free Viewer】
https://assetstore.unity.com/packages/tools/utilities/point-cloud-free-viewer-19811
「Open in Unity 」を選択し、その後 Unity で「Import」を押します。
(画像をクリックすると、別ウィンドウで大きなサイズの画像が開きます)
そして、Unity のヒエラルキーの横にある、プロジェクトビューのなかに「Point Cloud」というフォルダができていれば完了です。
【注意】点群の拡張子を確認する
点群には、加工に使用したソフトによって様々な拡張子があります。
今回使用しているアセットは、「off」という拡張子のみに対応しています。
「off」でない場合は、「meshlab」などで変更しましょう。
変換できる拡張子
ply、obj、3ds、ptx、v3d、pts、apts、xyz、txt など
変換できない拡張子
csv、lsproj、cl3 など
Unity に点群を読ませる
それでは、実際に Unity に点群を読ませていきます。
アセットをインポートした時に「Point Cloud」ができたと思います。
この中に「Example」というシーンが出来ているので、それを選択してください。
開くと、ヒエラルキーに「PointCloudManager」というゲームオブジェクトがあるので、そこに注目してください。
そこに、オブジェクトと同名のスクリプトがアタッチされています。
そこの「Data Path」を点群のデータのものにします。
パスを見ていただければわかると思いますが、「PointCloud」のフォルダ直下に入れるようにして下さい。
「playボタン」を押せば、下記画像のように点群が表示されます。
カメラ操作をつける
これだけでは、視点の変更ができないので、カメラに以下のスクリプトをアタッチして、視点変更ができるようにしましょう。
スクリプト
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using UnityEngine;
5using UnityEngine.SocialPlatforms;
6using UnityEngine.EventSystems;
7using UnityEngine.UI;
8
9
10
11
12//カメラを動かすためのスクリプト
13public class Cameramover : MonoBehaviour
14{
15 public int movepower;
16 public int rockon;
17 GameObject mainCamObj;
18 Camera cam;
19
20 //カメラの移動量
21 [SerializeField, Range(0.1f, 10.0f)]
22 private float _positionStep = 2.0f;
23
24 //マウス感度
25 [SerializeField, Range(30.0f, 150.0f)]
26 private float _mouseSensitive = 90.0f;
27
28 //カメラ操作の有効無効
29 private bool _cameraMoveActive = true;
30 //カメラのtransform
31 private Transform _camTransform;
32 //マウスの始点
33 private Vector3 _startMousePos;
34 //カメラ回転の始点情報
35 private Vector3 _presentCamRotation;
36 private Vector3 _presentCamPos;
37 //初期状態 Rotation
38 private Quaternion _initialCamRotation;
39 //UIメッセージの表示
40 private bool _uiMessageActiv;
41
42 void Start()
43 {
44 _camTransform = this.gameObject.transform;
45
46 //初期回転の保存
47 _initialCamRotation = this.gameObject.transform.rotation;
48
49
50 mainCamObj = GameObject.Find("Camera");
51 cam = mainCamObj.GetComponent<Camera>();
52 }
53
54 void Update()
55 {
56
57 CamControlIsActive(); //カメラ操作の有効無効
58
59 if (_cameraMoveActive)
60 {
61 if (!(EventSystem.current.IsPointerOverGameObject()))
62 {
63 ResetCameraRotation(); //回転角度のみリセット
64 CameraRotationMouseControl(); //カメラの回転 マウス
65 CameraSlideMouseControl(); //カメラの縦横移動 マウス
66 CameraPositionKeyControl(); //カメラのローカル移動 キー
67 float scroll = Input.GetAxis("Mouse ScrollWheel") * 15.0f * _positionStep;//マウススクロール
68 transform.position += transform.forward * scroll;
69 }
70
71 }
72 }
73
74
75
76 //回転を初期状態にする
77 private void ResetCameraRotation()
78 {
79 if (Input.GetKeyDown(KeyCode.P))
80 {
81 this.gameObject.transform.rotation = _initialCamRotation;
82 Debug.Log("Cam Rotate : " + _initialCamRotation.ToString());
83 }
84 }
85
86 //カメラの回転 マウス
87 private void CameraRotationMouseControl()
88 {
89
90 //左右クリックによる視点を変えながらの前進後退をするために両方に回転を割り当て
91 //単純な回転は右クリックのみで可能
92
93
94 if (Input.GetMouseButtonDown(1))
95 {
96 _startMousePos = Input.mousePosition;
97 _presentCamRotation.x = _camTransform.transform.eulerAngles.x;
98 _presentCamRotation.y = _camTransform.transform.eulerAngles.y;
99 }
100
101 if (Input.GetMouseButton(1))
102 {
103 /*
104 //(移動開始座標 - マウスの現在座標) / 解像度 で正規化
105 float x = (_startMousePos.x - Input.mousePosition.x) / Screen.width;
106 float y = (_startMousePos.y - Input.mousePosition.y) / Screen.height;
107
108 //回転開始角度 + マウスの変化量 * マウス感度
109 float eulerX = _presentCamRotation.x + y * _mouseSensitive;
110 float eulerY = _presentCamRotation.y + x * _mouseSensitive;
111 _camTransform.rotation = Quaternion.Euler(eulerX, eulerY, 0);
112 */
113
114 //(移動開始座標 - マウスの現在座標) / 解像度 で正規化
115 float x = (_startMousePos.x - Input.mousePosition.x) / Screen.width;
116 float y = (_startMousePos.y - Input.mousePosition.y) / Screen.height;
117
118 //回転開始角度 + マウスの変化量 * マウス感度
119 float eulerX = _presentCamRotation.x + y * _mouseSensitive;
120 float eulerY = _presentCamRotation.y + x * _mouseSensitive;
121 _camTransform.rotation = Quaternion.Euler(eulerX, eulerY, 0);
122
123
124
125
126 }
127
128
129 if (Input.GetMouseButtonDown(0))
130 {
131
132
133 _startMousePos = Input.mousePosition;
134 _presentCamRotation.x = _camTransform.transform.eulerAngles.x;
135 _presentCamRotation.y = _camTransform.transform.eulerAngles.y;
136
137
138 }
139
140 if (Input.GetMouseButton(0))
141 {
142
143
144 //(移動開始座標 - マウスの現在座標) / 解像度 で正規化
145 float x = (_startMousePos.x - Input.mousePosition.x) / Screen.width;
146 float y = (_startMousePos.y - Input.mousePosition.y) / Screen.height;
147
148 //回転開始角度 + マウスの変化量 * マウス感度
149 float eulerX = _presentCamRotation.x + y * _mouseSensitive*1.5f;
150 float eulerY = _presentCamRotation.y + x * _mouseSensitive*1.5f;
151
152
153 //計算した値を角度に適用
154 _camTransform.rotation = Quaternion.Euler(eulerX, eulerY, 0);
155
156
157 }
158
159
160 }
161
162 //カメラの移動
163 private void CameraSlideMouseControl()
164 {
165
166 //予備関数です。空っぽです。
167 }
168
169 //カメラのローカル移動
170 private void CameraPositionKeyControl()
171 {
172 Vector3 campos = _camTransform.position;
173
174 //w前進 s後退 d右 e左 q下降 e上昇 qとeについては重力優先
175 //前進後退はロック
176 if (Input.GetKey(KeyCode.D)) { campos += _camTransform.right * Time.deltaTime * _positionStep*movepower; }
177 if (Input.GetKey(KeyCode.A)) { campos -= _camTransform.right * Time.deltaTime * _positionStep * movepower; }
178 if (Input.GetKey(KeyCode.E)) { campos += _camTransform.up * Time.deltaTime * _positionStep * movepower; }
179 if (Input.GetKey(KeyCode.Q)) { campos -= _camTransform.up * Time.deltaTime * _positionStep * movepower; }
180 if (Input.GetKey(KeyCode.W)) { campos += _camTransform.forward * Time.deltaTime * _positionStep * movepower; }
181 if (Input.GetKey(KeyCode.S)) { campos -= _camTransform.forward * Time.deltaTime * _positionStep * movepower; }
182
183
184 //右と左同時クリックで後退
185 if ((Input.GetMouseButton(1))&&(Input.GetMouseButton(0))) { campos -= _camTransform.forward * Time.deltaTime * _positionStep * movepower; }
186 //右のクリックで
187 if ((Input.GetMouseButton(0))&&!(Input.GetMouseButton(1))) { campos += _camTransform.forward * Time.deltaTime * _positionStep * movepower; }
188
189 //中ボタンでパン
190 if((Input.GetMouseButton(2))){
191 campos -= _camTransform.right * Time.deltaTime * _positionStep*movepower*Input.GetAxis("Mouse X")*6;
192 campos -= _camTransform.up * Time.deltaTime * _positionStep * movepower*Input.GetAxis("Mouse Y")*6;
193 }
194
195 //回転した値を位置に適用
196 _camTransform.position = campos;
197 }
198//---------------------------------------------------------------------------------以下予備システム無視 -----------------------------
199 //UIメッセージの表示
200 private IEnumerator DisplayUiMessage()
201 {
202 _uiMessageActiv = true;
203 float time = 0;
204 while (time < 2)
205 {
206 time = time + Time.deltaTime;
207 yield return null;
208 }
209 _uiMessageActiv = false;
210 }
211
212 void OnGUI()
213 {
214 if (_uiMessageActiv == false) { return; }
215 GUI.color = Color.black;
216 if (_cameraMoveActive == true)
217 {
218 GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height - 30, 100, 20), "カメラ操作 有効");
219 }
220
221 if (_cameraMoveActive == false)
222 {
223 GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height - 30, 100, 20), "カメラ操作 無効");
224 }
225 }
226
227 //カメラ操作の有効無効
228 public void CamControlIsActive()
229 {
230 if (Input.GetKeyDown(KeyCode.Space))
231 {
232 _cameraMoveActive = !_cameraMoveActive;
233
234 if (_uiMessageActiv == false)
235 {
236 StartCoroutine(DisplayUiMessage());
237 }
238 Debug.Log("CamControl : " + _cameraMoveActive);
239 }
240 }
241
242
243
244
245
246
247}
カメラの操作方法
操作方法は以下のようになっています。
W、A、S、D | 前後左右の移動 |
Q、E | 上昇、降下 |
右ドラッグ | カメラの回転 |
左ドラッグ | 移動 |
中ドラッグ | パン |
軽量化とエラー対策
この状態で、ビルドしようとするとエラーが出ることがあります。
「PointCloudManager」のフォルダにアクセスする機能が、悪影響を及ぼしています。
プレイ状態で、点群のオブジェクトをプレハブで保存します。
そのプレハブを配置して、「PointCloudManager」はプロジェクトビューから削除してしまいます。
そうすることで、エラーも出ず、起動が早くなります。
さいごに
いかがでしたか?
実際の建物や、地形などを使用しているので、モデリングの代用という考え以上に、いろいろと楽しい使い方が出来るかもしれませんね!
ご紹介した内容を参考に、点群データを使って、地形や建物を表現してみてください!
こちらの記事もオススメ!
2020.07.28Unity 特集知識編おすすめのゲームエンジン5選実装編※最新記事順に並べています。VR環境でテキストを表示する方法非同期式の入れ子処...
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit