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を指定すれば三角形じゃなくてもいけるのね。
リアルタイムで動く点群をグリグリできると、スターウォーズに出てくる立体映像みたいでカッコイイね。
関連記事
After Effects全エフェクトの解説(6.5)
ポリ男をリファイン
TorchStudio:PyTorchのための統合開発環境と...
Zibra Liquids:Unity向け流体シミュレーショ...
ZBrushで仮面ライダー3号を造る 仮面編 ZRemesh...
Iridescence:プロトタイピング向け軽量3D可視化ラ...
HD画質の無駄遣い
BlenderのRigifyでリギング
ジュラシック・パークのメイキング
第1回 3D勉強会@関東『SLAMチュートリアル大会』
色んな三面図があるサイト
SegNet:ディープラーニングによるSemantic Se...
OpenMVSのサンプルを動かしてみる
MPC社によるゴジラ(2014)のVFXブレイクダウン
Oculus Goを購入!
iPhoneで3D写真が撮れるアプリ『seene』
OpenCV 3.1から追加されたSfMモジュール
SIGGRAPH ASIAのマスコット
ZBrushのZmodelerとDynamic Subdiv...
Arduinoで人感センサーを使う
クラスの基本
GoB:ZBrushとBlenderを連携させるアドオン
UnityのGameObjectの向きをScriptで制御す...
3D映画のポストプロダクション 2D-3D変換
SIGGRAPH 2020はオンライン開催
Mask R-CNN:ディープラーニングによる一般物体検出・...
動的なメモリの扱い
PythonのHTML・XMLパーサー『BeautifulS...
Seleniumを使ったFXや株の自動取引
タイミングとクオリティ
ManuelBastioniLAB:人体モデリングできるBl...
参考書
OpenSfM:PythonのStructure from ...
3DCG Meetup #4に行ってきた
Iterator
TeleSculptor:空撮動画からPhotogramme...
PSPNet (Pyramid Scene Parsing ...
ZBrushでメカ物を作るチュートリアル動画
Leap MotionでMaya上のオブジェクトを操作できる...
『パシフィック・リム: アップライジング』のVFXブレイクダ...
2D→3D復元技術で使われる用語まとめ
トランスフォーマーロストエイジのメイキング


コメント
参考にさせていただきました。ありがとうございます。
そのままでは動かなかったのですが、
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はベクトルじゃなくてスカラー値ですよね。本文のコードも修正しました。