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を指定すれば三角形じゃなくてもいけるのね。

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


スポンサーリンク

関連記事

SIGGRAPH ASIA 2009で学生ボランティア募集してるみたい
ZBrushCore
ニンテンドー3DSのGPU PICA200
Konashiを買った
OpenCV 3.1のsfmモジュールのビルド再び
KelpNet:C#で使える可読性重視のディープラーニングライブラリ
ラクガキの立体化 3Dプリント注文
html5のcanvasの可能性
ZBrushでアヴァン・ガメラを作ってみる 口内の微調整・身体のライン修正
SDカードサイズのコンピューター『Intel Edison』
ZBrushで仮面ライダー3号を造る 仮面編 ZRemesher
マルコフ連鎖モンテカルロ法
AmazonEC2のインスタンスをt1.microからt2.microへ移行する
リニアアクチュエータ
3Dグラフィックスの入門書
Multi-View Environment:複数画像から3次元形状を再構築するライブラリ
Oculus Goを購入!
リメイク版ロボコップスーツのメイキング
ZBrush 4R8 リリース!
OpenCVでiPhone6sのカメラをキャリブレーションする
CGのためのディープラーニング
『ハン・ソロ/スター・ウォーズ・ストーリー』のVFXブレイクダウン まとめ
Math Inspector:科学計算向けビジュアルプログラミングツール
第20回 文化庁メディア芸術祭『3DCG表現と特撮の時代』
書籍『イラストで学ぶ ディープラーニング』
DensePose:画像中の人物表面のUV座標を推定する
3D復元技術の情報リンク集
Quartus II
ZBrushでゴジラ2001を作ってみる 身体のアタリを作る
iPhone x ロボットハッカソン~RomoのiPhone用SDKで目覚ましアプリを作る~
Kinect for Windows V2のプレオーダー開始
ポイントクラウドコンソーシアム
OpenFace:Deep Neural Networkによる顔の個人識別フレームワーク
Point Cloud Utils:Pythonで3D点群・Meshを扱うライブラリ
MPC社によるゴジラ(2014)のVFXブレイクダウン
PyDataTokyo主催のDeep Learning勉強会
Maya には3 種類のシェーダSDKがある?
Digital Emily Project:人間の顔をそっくりそのままCGで復元する
UnityでARKit2.0
ZBrushのZmodelerとDynamic Subdivisionを学ぶ
ZBrushのUV MasterでUV展開
プロシージャル手法に特化した本が出てるみたい(まだ買わないけど)

コメント

  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 NegativeMind より:

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