• トップ
  • ブログ一覧
  • 【Unity】meshlabで出力した点群データを軽量化して可視化する
  • 【Unity】meshlabで出力した点群データを軽量化して可視化する

    メディアチームメディアチーム
    2020.03.31

    IT技術

    Unityで点群の表示に挑戦!

    スマートフォンアプリや、FacebookGameroom、Oculus などの VR のゲームまで開発することのできるソフト「Unity(ユニティ)」。

    近頃はゲームアプリだけではなく、土木関係のビューワーや、医療関係のシュミレーションでも使用されています。

    Unity を使用すれば、3Dモデルを利用した作品を簡単に作成することができます。

    ですが、一番大変なのはモデリングの作業です。

    人物のモデルの場合、プロのモデラーに頼むと、1つでも数万円から数十万円の費用が掛かることも。

    点群とは

    実際に存在する建物や地形などを、簡単に表現する方法があります。

    それが「点群」です。

    ドローンなどで撮影した航空写真から、自動で作成した点の集まりです。

    拡大すると点の集まりですが、全体的に見ると 3Dモデルと遜色ありません。

    これを Unity で扱っていきましょう。

    注意点

    1. Unity の ve rは 2018.3.14f1 です。
      それ以前、以後のバージョンではエラーが出る恐れがあります。
    2. Point Cloud Free Viewer の情報は、2020年2月20日現在のものです
    3. 点群を扱うので、ある程度のマシンスペックを必要とします。
      メモリは 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」はプロジェクトビューから削除してしまいます。

    そうすることで、エラーも出ず、起動が早くなります。

    さいごに

    いかがでしたか?

    実際の建物や、地形などを使用しているので、モデリングの代用という考え以上に、いろいろと楽しい使い方が出来るかもしれませんね!

    ご紹介した内容を参考に、点群データを使って、地形や建物を表現してみてください!

    こちらの記事もオススメ!

    featureImg2020.07.28Unity 特集知識編おすすめのゲームエンジン5選実装編※最新記事順に並べています。VR環境でテキストを表示する方法非同期式の入れ子処...

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background