UnityでPoint Cloudを表示する方法

3次元点群を3Dビューアでグリグリとただ表示するだけならPoint Cloud Libraryを使ってすぐにできるんだけど、インタラクティブなコンテンツとして点群を利用したい場合にはPoint Cloud Libraryだけだとちょっと味気ない。Point Cloud Libraryだと視点を動かす時は点群の更新が止まっちゃうし。

何でUnityで点群を扱いたいかというと、お察しの通り、Kinectで取得した点群をカッコよく表示したいのです。Kinect V2になってからMicrosoftが公式にUnityプラグインを配布し始めたし、せっかくだから自前でOpenCVとかPoint Cloud Libraryでゴリゴリ頑張るよりも、Unityで他のAssetと組み合わせてサクッとリッチなコンテンツに仕上げたいのだ。

Microsoft公式のKinect v2 for Unityプラグインには、サンプルとしてKinectのColorとDepthをMesh化して表示するScriptコードが付属しているんだけど、無理やりMesh化して表示してるからちょっと汚らしい。
Microsoft公式のUnityプラグインおよびサンプルプロジェクトは以下のページの”Unity Pro packages“のリンクから。↓
https://www.microsoft.com/en-us/download/details.aspx?id=44561

こちらの公式サンプルでは、Kinect V2から取得した点群を4分の1の量にダウンサンプリングしてから、それらを頂点としたポリゴンメッシュ化してリアルタイム表示している。
Kinect V2で取得可能な点群(つまりDepth画像の解像度)は
512 × 424 = 217,088
だけど、Unityには「1つのMeshオブジェクトが持つ三角形の数は最大65,535まで」という制限があるため、ダウンサンプリングで頂点の数を減らしてMesh化してるんだと思う。
このサンプルでは解像度をダウンサンプリングする代わりに、Depth値を近傍ピクセルの平均から求めたりもしてるんだけど、結果として表示されるMeshがあんまりきれいじゃない。

2017年12月 追記:Meshオブジェクトのポリゴン数制限はUnity 2017.3から解消されたようです。

ということで、Unityで「面」ではなく「点」の描画を実現する方法を調べてみた。


スポンサーリンク


ちょっと調べてみると、どうやら点群ファイルを表示するための有料のAssetでPoint Cloud Viewer and Toolsというものがすでにあるようだ。以下サポートページに詳細が載っている。↓

Point Cloud Viewer & Tools for Unity

Point Cloud Viewer and Tools

このツールはファイル読み込み用なので、どうやら動的にKinectの情報を表示はできなさそう。読み込みをサポートしているフォーマットはXYZ, XYZRGB, CGO, ASC, CATIA ASC, PLY (ASC)。読み込んだファイルを自動でUnityのポリゴン制限数で分割して表示と、バイナリデータへの保存ができるようだ。

Kinectの点群をリアルタイムで表示するにはやっぱり自分でScriptを書くしかなさそうなので点の描画方法を調べてみたら、ドンピシャなブログ記事を見つけた。↓

Rendering a Point Cloud inside Unity

Here is a short example of how to render a point cloud using MeshRenderer inside Unity, have in mind that you have a limit of 65k points per mesh, so if you want to render more points, you need to split them.


スポンサーリンク

ソースコードも全部載せてくれている。OpenGLのAPIコマンドを有効にしてPoint SizeとSmooth PointをEnableにして、Shader側で受け取ってやるってことね。

上記の記事だけだとShaderへのパラメータ渡しの記述が欠けてるけど、以下のフォーラムの記事に全部載っている。↓

How do I use PSIZE in a Unity 4.5.4 shader?

このフォーラム記事では、DirectX環境ではPoint Sizeの指定が描画に反映されないことについて話題になっている。要するに、残念ながらOpenGL環境でしか動かない仕様っぽい。


こちらが描画用のPoint Cloudをランダムに生成するC# Scriptのサンプルコード↓

using UnityEngine;
using System.Collections;
 
 
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class PointCloud : MonoBehaviour {
 
    private Mesh mesh;
    int numPoints = 60000;
 
    // Use this for initialization
    void Start () {
        mesh = new Mesh();
 
        GetComponent<MeshFilter>().mesh = mesh;
        CreateMesh();
    }
 
    void CreateMesh() {
        Vector3[] points = new Vector3[numPoints];
        int[] indecies = new int[numPoints];
        Color[] colors = new Color[numPoints];
        for(int i=0;i<points.Length;++i) {
            points[i] = new Vector3(Random.Range(-10,10), Random.Range (-10,10), Random.Range (-10,10));
            indecies[i] = i;
            colors[i] = new Color(Random.Range(0.0f,1.0f),Random.Range (0.0f,1.0f),Random.Range(0.0f,1.0f),1.0f);
        }
 
        mesh.vertices = points;
        mesh.colors = colors;
        mesh.SetIndices(indecies, MeshTopology.Points,0);
 
    }
}


点を受け取って描画するShaderのコード。↓

Shader "Custom/VertexColor" {
     SubShader {
     Pass {
         Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
         LOD 200
               
                  
         CGPROGRAM
         #pragma vertex vert
         #pragma fragment frag
         #include "UnityCG.cginc"
   
         struct VertexInput {
             float4 v : POSITION;
             float4 color: COLOR;
         };
          
         struct VertexOutput {
             float4 pos : SV_POSITION;
             float4 col : COLOR;
             float size : PSIZE;
         };
          
         VertexOutput vert(VertexInput v) {
          
             VertexOutput o;
             o.pos = mul(UNITY_MATRIX_MVP, v.v);
             o.col = v.color;
             o.size = 10.0;
             return o;
         }
          
         float4 frag(VertexOutput o) : COLOR {
             return o.col;
         }
  
         ENDCG
         } 
     }
  
 }

そしてUnityでOpenGLのPoint Sizeコマンドを有効にするC# Scriptコード。(これはMain Cameraとかにアタッチする)↓

 #if UNITY_STANDALONE
 #define IMPORT_GLENABLE
 #endif
  
 using UnityEngine;
 using System;
 using System.Collections;
 using System.Runtime.InteropServices;
  
 public class EnablePointSize : MonoBehaviour 
 {
     const UInt32 GL_VERTEX_PROGRAM_POINT_SIZE = 0x8642;
     const UInt32 GL_POINT_SMOOTH = 0x0B10;
      
     const string LibGLPath =
         #if UNITY_STANDALONE_WIN
         "opengl32.dll";
     #elif UNITY_STANDALONE_OSX
     "/System/Library/Frameworks/OpenGL.framework/OpenGL";
     #elif UNITY_STANDALONE_LINUX
     "libGL";  // Untested on Linux, this may not be correct
     #else
     null;   // OpenGL ES platforms don't require this feature
     #endif
      
     #if IMPORT_GLENABLE
     [DllImport(LibGLPath)]
     public static extern void glEnable(UInt32 cap);
      
     private bool mIsOpenGL;
      
     void Start()
     {
         mIsOpenGL = SystemInfo.graphicsDeviceVersion.Contains("OpenGL");
     }
      
     void OnPreRender()
     {
         if (mIsOpenGL)
             glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
             glEnable(GL_POINT_SMOOTH);
     }
     #endif
 }

初めて知ったけど、UnityのMeshってMeshTopologyを指定すれば三角形じゃなくてもいけるのね。

リアルタイムで動く点群をグリグリできると、スターウォーズに出てくる立体映像みたいでカッコイイね。


スポンサーリンク

関連記事

ZBrushトレーニング
UnityのGlobal Illumination
OpenCVベースのマーカーARライブラリ『OpenAR』
『PIXARのひみつ展』に行ってきた
ガメラ生誕50周年
サンプルコードにも間違いはある?
PureRef:リファレンス画像専用ビューア
MFnMeshクラスのsplit関数
Google製オープンソース機械学習ライブラリ『TensorFlow』のWindows版が公開された
SegNet:ディープラーニングによるSemantic Segmentation手法
参考になりそうなサイト
フォトンの放射から格納までを可視化した動画
Raspberry Pi 2のGPIOピン配置
C#で使える可読性重視のディープラーニングライブラリ『KelpNet』
顔のモデリング
Pythonの自然言語処理ライブラリ『NLTK(Natural Language Toolkit)』
ミニ四駆を赤外線制御したりUnityと連携したり
『ゴジラ キング・オブ・モンスターズ』のVFXブレイクダウン
takminさんが機械学習・画像認識の便利ツールを公開しています
イタリアの自動車ブランドFiatとゴジラがコラボしたCMのメイキング
『スター・ウォーズ 最後のジェダイ』のVFXブレイクダウン まとめ
Point Cloud Libraryに動画フォーマットが追加されるらしい
素敵なパーティクル
書籍『The Art of Mystical Beasts』を購入
After Effects全エフェクトの解説(6.5)
日本でMakersは普及するだろうか?
Webスクレイピングの勉強会に行ってきた
ハリウッド版「GAIKING」パイロット映像
ニンテンドー3DSのGPU PICA200
AMIMOTO(PVM版)で作成したインスタンスをAMIMOTO (HVM版)へ移行する
書籍『仕事ではじめる機械学習』を読みました
ミニ四駆を改造してBluetoothラジコン化する
ZBrushのハードサーフェイス用ブラシ
C++の機械学習ライブラリ『Dlib』
iPhone x ロボットハッカソン~RomoのiPhone用SDKで目覚ましアプリを作る~
iOSデバイスのためのフィジカル・コンピューティングツールキット『konashi(こなし)』
シン・ゴジラのファンアート
オープンソースの物理ベースレンダラ『Mitsuba』をMayaで使う
BlenderでPhotogrammetryできるアドオン
adskShaderSDK
トランスフォーマーロストエイジのメイキング
トランスフォーマー/ロストエイジのメイキング

コメント

  1. kusano より:

    参考にさせていただきました。ありがとうございます。
    そのままでは動かなかったのですが、
    struct VertexOutput {
    float4 pos : SV_POSITION;
    float4 col : COLOR;
    float4 size : PSIZE;
    };

    struct VertexOutput {
    float4 pos : SV_POSITION;
    float4 col : COLOR;
    float size : PSIZE;
    };
    に書き換えたところ動きました。

    • NegativeMind より:

      すみません、書き間違えてました。
      確かに、sizeはベクトルじゃなくてスカラー値ですよね。本文のコードも修正しました。