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

このツールはファイル読み込み用なので、どうやら動的に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を指定すれば三角形じゃなくてもいけるのね。
リアルタイムで動く点群をグリグリできると、スターウォーズに出てくる立体映像みたいでカッコイイね。
関連記事
BlenderのGeometry Nodeで遊ぶ
UnityでOpenCVを使うには?
DCGAN (Deep Convolutional GAN)...
Google App Engineのデプロイ失敗
Photo Bash:複数の写真を組み合わせて1枚のイラスト...
Qlone:スマホのカメラで3Dスキャンできるアプリ
仮想関数
Multi-View Environment:複数画像から3...
世界一下品なクマと世界一紳士なクマ
布のモデリング
ニンテンドー3DSのGPU PICA200
OpenCVでPhotoshopのプラグイン開発
機械学習で遊ぶ
OpenVDB:3Dボリュームデータ処理ライブラリ
参考になりそうなサイト
ZBrushで仮面ライダー3号を造る 仮面編 DynaMes...
Web経由でRaspberry PiのGPIOを操作したい
定数
openMVGをWindows10 Visual Studi...
ZBrushからBlenderへモデルをインポート
iPhoneで3D写真が撮れるアプリ『seene』
PSPNet (Pyramid Scene Parsing ...
Subsurface scatteringの動画
Blendify:コンピュータービジョン向けBlenderラ...
ZBrushでUndo Historyを動画に書き出す
BlenderのRigifyでリギング
Google製オープンソース機械学習ライブラリ『Tensor...
AnacondaとTensorFlowをインストールしてVi...
OpenCVでiPhone6sのカメラをキャリブレーションす...
3D映画のポストプロダクション 2D-3D変換
Kinect for Windows v2の日本価格決定
UnityのuGUIチュートリアル
プログラムによる景観の自動生成
チャットツール用bot開発フレームワーク『Hubot』
ROSの薄い本
Google App Engine上のWordPressでA...
Maya LTのQuick Rigを試す
ZBrushでゴジラ2001を作ってみる 姿勢の変更
iPhone欲しいなぁ
OANDAのfxTrade API
Structure from Motion (多視点画像から...
GoB:ZBrushとBlenderを連携させるアドオン


コメント
参考にさせていただきました。ありがとうございます。
そのままでは動かなかったのですが、
struct VertexOutput {
float4 pos : SV_POSITION;
float4 col : COLOR;
float4 size : PSIZE;
};
を
struct VertexOutput {
float4 pos : SV_POSITION;
float4 col : COLOR;
float size : PSIZE;
};
に書き換えたところ動きました。
すみません、書き間違えてました。
確かに、sizeはベクトルじゃなくてスカラー値ですよね。本文のコードも修正しました。