UE4 で4次元 point cloud

まったくの Unreal Engine 初心者ですが、見よう見まねでこんなの↓を作ってみました。
![ScreenShot00000.png](https://qiita-image-store.s3.amazonaws.com/0/236509/9b6e2fc2-82c3-05c7-3407-4da243a81d1b.png)

3次元の point cloud については以下に情報があったので、
- [Point Cloud DataをUnreal Engineで](https://ishiguro440.wordpress.com/2017/01/04/point-cloud-data%E3%82%92unreal-engine%E3%81%A7/)
- [How to place single GPU particles at specified locations?](https://forums.unrealengine.com/development-discussion/blueprint-visual-scripting/63159-how-to-place-single-gpu-particles-at-specified-locations?91501-How-to-place-single-GPU-particles-at-specified-locations=)
これを利用して4次元の point cloud を表示して回転してみました。

# Point cloud のデータを画像ファイルとして保存する

3次元の場合は xyz 座標をそれぞれ RGB として保存していました。
4次元にする場合は単にアルファチャンネルも使って RGBA にすればいいだけです。
- [Creating PNGs with libPNG - The Lab Book Pages](http://www.labbookpages.co.uk/software/imgProc/libPNG.html)
にあるコードを利用して PNG ファイルを作成しました。

上の Ishiguro さんのページにもありますが、
8bit では解像度が足りないので 16bit にしようと思って
最初 16bit PNG を作ってみたのですが、
どうも UE4 で保存する時に自動的に 8bit に圧縮されてしまうようです。
結局上位 8bit と その次の 8bit を別々の PNG ファイルにして、
あと各点の色データ (これはRGBAそのまま) も別の PNG ファイルにしました。

これらをテクスチャとして UE4 に取り込みます。コンテンツブラウザの「インポート」ボタンを押して画像を選択します。取り込めたら、ダブルクリックして開いて以下のように設定します。
- Compression Settings を VectorDisplacementmap (RGBA8) にする
- Texture Group を ColorLookupTable にする
- Texture の一番下にある(隠れているかもしれません) Filter を Nearest にする
取り込んだ全ての画像に対してやっておいてください。

# Material の作成

上の forum に3次元の場合のブループリントのスクリーンショットがあります。
それを参考に、以下のようにしました。
![MaterialBP.png](https://qiita-image-store.s3.amazonaws.com/0/236509/2d8d2ad7-9f47-700c-a186-b29c901c749a.png)
簡単に何をしているか説明しておくと、

- 左半分：Particle position の x 座標を uv 座標に変換
後で実際に配置したオブジェクトを自動的に移動するようにします。
移動した分だけ指定した場所に点が現れるようになります。

- 右下：4次元点データの読み込み
2つの画像から色データ(=座標データ)を読みこみ、それを組合わせて4次元点データを生成します。

- 右中央：4次元空間での回転
生成した4次元の点をCustomノードに放りこみます。
Customノードには4次元空間で回転させるHLSLコードが書いてあります。
```
float4x4 A = { cost, sint, 0, 0,
              -sint,  cost, 0, 0,
	       0, 0,  cost, -sint,
	       0, 0, sint, cost };
float3 val = mul(pos, A).xyz;
return val;
```
HLSL では横ベクトルを用いることに注意してください。
この例では xy 平面で $t\pi$ 回転、zw 平面で $-t\pi$ 回転しています。
数学好きな人のために書いておくと、$\mathbb{C}^2$ で $\begin{pmatrix}e^{\pi it} & 0 \\\ 0 & e^{-\pi it}\end{pmatrix}$ という SU(2) の元をかけることに対応しています。
三角関数はBPで計算しています。この方が計算が1回で済むから早いんじゃないかと思うのですが、特に検証はしていません。詳しい人教えてください。
$t$ の値はこの例では時間に応じて自動的に変化するようにしています。
他のことをしたい場合は入れる値をパラメータにして、 Material instance から変更すればいいです。

- 右上：サイズと位置の調整
色のデータはRGBAそれぞれ0から1までなので、
それを拡大して平行移動して表示したい位置に調整します。
Particle Positionを引いているのは、上にも書いたように実際のオブジェクトはどんどん移動していくようにするので、その分を元に戻すためです。

- 中央上：色データの読み込み
これは特に説明する必要はないでしょう。

# Particle System の作成
作った Material を表示するのは Particle System です。
上の forum のページを見ながら以下のように設定したらうまくいきました。
まだ設定項目の意味がよくわかっていないので、不要な設定もあるかもしれませんが…。

- エミッタ のどこかを右クリックして、タイプデータ → 新規GPU Sprites を選ぶ
- Required モジュールの Material を上で作ったものに設定
- Lifetime モジュールの Distribution を Distribution Float Uniform にして値を 0 に
- Initial Size モジュールの Distribution を適当な値に (各点の大きさ。わたしは Distribution Vector Constant にして全部 0.1 にしています)
- Initial Velocity と Color Over Life モジュールは不要なので削除
- 「バウンド」ボタンの横の三角をクリックして「固定バウンドを設定」を選択。
- Particle System の詳細 (エミッタの横の何もないところをクリックすると現れます) で Bounds を適当に大きな値に設定 (わたしは全部 -100000 から 100000 にしました)
- Spawn モジュールの Distribution の値を適当に大きな値にする
この値によって点の表われる速さが変わります。大きくしすぎるとうまく行かないようです。

これで Particle System を配置すればできると思います。

# 一瞬で表示する?
上のやり方では徐々に現れてくるので、全部表示されるには少し待たないといけません。
forum に一瞬 (2 tick) で表示する方法、というのがあったのでやってみました。
が、わたしのところではうまく行かなかったので、結局徐々に表示するようにしています。

それだったら普通に Spawn でやればいいだけな気もしますが、
なにしろ手元の MacBook Pro では、あまり一度に多く表示すると
一部の方向からは見ると消えてしまうなど、よくわからないバグが現れたので
とりあえずこの方法で落ちついています。

というわけで以下やり方：

- エミッタを右クリックして Spawn → Spawn PerUnit を選んで追加
- Spawn PerUnit の Unit Scalar を 0.01 に
上の forum では 1 にしています。x 座標を(この値)×n 動かすと n 個の点が現れます。後の Blueprint で動かす量と関係してきます。
- 手元では Spawn PerUnit の Process Spawn Rate と Process Burst List のチェックが外れていますが、よくわかっていません。

作った Particle System を右クリックして、アセットアクション → これを使用してブループリントを作成 を選んでブループリントクラスを作ります。
ブループリントはこんな風になっています：
![ParticleSystemBP.png](https://qiita-image-store.s3.amazonaws.com/0/236509/3595f45c-021d-bb14-de8f-3b9ee7c2c336.png)
Done (Boolean), Move (Integer) の2つの変数を追加しています。
forum では Move も Boolean にして、一度動かしたら変更していますが、
うまく行かなかったので、ここでは動かした回数を記録するようにしています。
Move を呼び出される毎に1増やして、指定した値になったら止めるようにしています。
表示したい点の数に応じてその辺は変更してください。

Done が false の間は毎回10.24移動しているので、
Spawn PerUnit のUnit Scalar の設定とあわせると、
1024個の点が 1 tick 毎に現れることになります。

本当はこれだけだと Done は不要です。
なぜなら Done を true にセットすると同時に Actor Tick を disable するので、
これ以上 Tick は呼ばれなくなるからです。
他にも Tick 毎に呼び出される処理がある場合は必要になるので、一応残してあります。

# 最後に
VR で見るのが目的なので、今後

- VR 対応
- 手でオブジェクトを掴んで回転・拡大・縮小などをする
- コントローラで4次元の回転をコントロール
- 4次元 → 3次元を透視投影にしてみたり、その他表示方法を変える

とか色々やる予定です。たぶんきっと。

Oculus Rift (LibOVR) + OpenGL + C++ では最低限のことはできているんですが、Windows MR の方がセンサーが不要な分持ち運びや出先でのキャリブレーションが楽で、そっちにも対応したいと思っています。
でも OpenVR は情報が少なすぎるし、C++ で各ハードウェアに独自で対応するのはやってられない気分になってきたので、最近 Unity や UE4 に手を出し始めています。

必要なことだけいきなり調べてやっていて、全体的にはまったくの初心者なので、おかしなところなどあったら教えてください。よろしくお願いします。