素人がVRでプラネタリウムを作るまで2020

この記事はVCIアドベントカレンダー2020参加記事です。

また、この記事は去年のVCIアドベントカレンダー2019[参加記事](https://qiita.com/sakkinonana/items/2d82f1ac8f80b12b49ff)の続きになります。あれから一年でどうなったのかを技術的に掘り下げていきます。

#VRでプラネタリウムとな？
![バーチャルプラネタリウム](https://camo.qiitausercontent.com/8973397521c6a3364e8a4038d4a7e8f64b0347b5/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3439343833372f63313433333739632d636632642d643033612d376536642d3332336461666137363334652e706e67)
[バーチャルプラネタリウム](https://seed.online/products/332757ee5e9bfdc8147e8ef499170ec0a9618278b71c89a98fda1428e75e318b)は国産VRSNSである[バーチャルキャスト](https://virtualcast.jp/)のアイテムとして私が公開している背景アドオン形式アイテムです。VRChatで言うところのワールドのようなものですが、vrchatと違って、バーチャルキャストではアイテムとして持ち歩けるので、好きな背景に星空を合わせて持っていくことが出来ます。バーチャルキャストの連携サービスTheSeedOnlineにて[無料で公開](https://seed.online/products/332757ee5e9bfdc8147e8ef499170ec0a9618278b71c89a98fda1428e75e318b)されています。
<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://twitter.com/hashtag/VCsakkinonana?src=hash&amp;ref_src=twsrc%5Etfw">#VCsakkinonana</a><a href="https://t.co/3ItSmatSrM">https://t.co/3ItSmatSrM</a><a href="https://twitter.com/hashtag/%E3%83%90%E3%83%BC%E3%83%81%E3%83%A3%E3%83%AB%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88?src=hash&amp;ref_src=twsrc%5Etfw">#バーチャルキャスト</a> <a href="https://twitter.com/hashtag/virtualcast?src=hash&amp;ref_src=twsrc%5Etfw">#virtualcast</a> <a href="https://t.co/vBfDTTGOdY">pic.twitter.com/vBfDTTGOdY</a></p>&mdash; さっきのなな☆彡vket5 デフォルトキューブ (@sakkinonana) <a href="https://twitter.com/sakkinonana/status/1208729649418686470?ref_src=twsrc%5Etfw">December 22, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
このアイテムを基軸とする作品群についての記事になります。

#前回のおさらい
- hipparcos星表を使ってモデルで、3dモデルベースのプラネタリウムを実装した
- ふたご座流星群を模した流星エフェクトを付けた
- 公開要請があったので公開したら170件以上の取り込み(2020/12月現在)（参考：バーチャルキャストのsteam chartの結果を見るとここ一ヶ月くらいの毎日の同時接続ユーザー数のピークは60付近

#今年施した工夫
## 星座線
今年のわかりやすい成果は星座線ですね。例によってblenderのpythonスクリプトを使って書かせたのですが、興味のある方は[3次元幾何学図形をワンクリックで作る方法【三角錐からフラクタルまで】](https://qiita.com/sakkinonana/items/afb27d4832c940eccb0d)をご参照ください。星座線データはt_issyさんのastro commonsより使わせていただきました。

## メッセージによる制御への対応
こちらは最近のアップデートで仕様変更を迫られそうなんですが、バーチャルプラネタリウムは

```lua:main.lua
%f/%f/%f/%f/%f/%f/%f
```
の形式で一秒ごとにメッセージを飛ばしています。前半3つが座標で残り4つが姿勢です。これによって他のVCIとの連携が可能になっています。
ですが、先日univciのアップデートがありまして、テーブルをmessageで飛ばせるようになったのでそちらのほうが自由度が高く安全になると思われます。今後対応していきたいと思っています。

## UIの推敲

これはぶっちゃけまだまだ終わってないんですが、、、

### 天体情報パネル
これもUIを考えるにあたってまず最初に考えて一旦行き詰まってしまったものなんですが、**情報パネルを手元とかすぐ横とかに表示したい**なと。現実のプラネタリウムですと、スマホなど使おうものならせっかく暗くしたドームが明るくなってしまい、星が消えてしまいますから、光を出す機器の電源はお切りくださいってなってしまいます。でも**VRだと明かりは関係ないので欲しい情報をわかりやすく表示しちゃおう**というものです。
アストロアーツの星空ナビやNeosVRのplanetariaでは実装されていて、実際に見させてもらったけど、こういうふうに見せ方も工夫すればVRならではの体験になるよね。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">VRプラネタリウムを作りました！！🤪🌌🌟<br>リアルタイムで星が動くよ！☄️<br><br>めっちゃ頑張ったからぜひみに来て～！😍<br>アクセスはこちら～↓<a href="https://t.co/eQfz6PekZb">https://t.co/eQfz6PekZb</a><a href="https://twitter.com/hashtag/NeosVR?src=hash&amp;ref_src=twsrc%5Etfw">#NeosVR</a> <a href="https://t.co/rHz6s8SDWG">pic.twitter.com/rHz6s8SDWG</a></p>&mdash; オレンジ (@mikan3134) <a href="https://twitter.com/mikan3134/status/1299914277709438976?ref_src=twsrc%5Etfw">August 30, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
これをやろうとしたのが今年の４月頃なので、それからバーチャルキャストも色々とアップデートが入りましたから、またチャレンジしてみようと思います。

### 制御コンソール
現実世界でよくあるコンソールは席の周りにボタンやツマミが並んでいるもので、すぐに実装できると言えばできると思います。ですが、、、正直僕はよくあるcubeを掴んでuseしたらなにかが起こるというUIがあまり好きではありません。たしかに作るのは楽なんですが、せっかくのVRのポテンシャルを発揮しきれていない気がしてなりません。ジェスチャにしようかとか色々悩んだ末、ブレードタイプのもののmockを作りました。当時はユーザーの位置とボーン情報を取得できなかったのでこれもまた来年の課題です。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">操作盤もあります <a href="https://t.co/DwrYVuNdDY">pic.twitter.com/DwrYVuNdDY</a></p>&mdash; さっきのなな☆彡vket5 デフォルトキューブ (@sakkinonana) <a href="https://twitter.com/sakkinonana/status/1254731181871915010?ref_src=twsrc%5Etfw">April 27, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">もちろんボタンでも動かせますし、恒星時もわかります。 <a href="https://t.co/HWEtGh4toO">pic.twitter.com/HWEtGh4toO</a></p>&mdash; さっきのなな☆彡vket5 デフォルトキューブ (@sakkinonana) <a href="https://twitter.com/sakkinonana/status/1254731345684652032?ref_src=twsrc%5Etfw">April 27, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/%E3%81%93%E3%82%8C%E3%81%BF%E3%81%A6V%E3%82%B1%E3%83%83%E3%83%88%E3%81%A1%E3%82%83%E3%82%93?src=hash&amp;ref_src=twsrc%5Etfw">#これみてVケットちゃん</a><br>ボタンで動かす以外にも、手で動かすことができます <a href="https://t.co/k0hNiEdQgA">pic.twitter.com/k0hNiEdQgA</a></p>&mdash; さっきのなな☆彡vket5 デフォルトキューブ (@sakkinonana) <a href="https://twitter.com/sakkinonana/status/1254731115287343104?ref_src=twsrc%5Etfw">April 27, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">連投で申し訳ないのですがもう一度vketちゃんを召喚させてください<br><br>動画による説明を下記につけます。<br>バーチャルキャストではアイテムを持ち込んで様々な操作ができますので、ぜひいじって遊んでみてください。 <a href="https://t.co/pGkhyhol4p">pic.twitter.com/pGkhyhol4p</a></p>&mdash; さっきのなな☆彡vket5 デフォルトキューブ (@sakkinonana) <a href="https://twitter.com/sakkinonana/status/1254730871732498432?ref_src=twsrc%5Etfw">April 27, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
手で直接回せるのは、唯一だったりします。

### 恒星時の計算と制御
ということで、測定用の望遠鏡を作りました。
こいつのUIもかなり悩んで、ドラムタイプにしようかとしましたがconstraintsの効き方が不可解な動作をするため、お蔵入りになっています。

###  設定の変更
流星を消したいという要望が多いので、消せるようにした（未公開）

### 惑星や太陽系内天体の追加
太陽系内の主要な天体を20ほどアップロードしました。その中でも8惑星と月・火星の衛星・ガリレオ衛星は公転・自転するようになっています（未公開）。これをそのうちバーチャルプラネタリウムの中で連動するようにしたいですね。

<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://twitter.com/hashtag/VCsakkinonana?src=hash&amp;ref_src=twsrc%5Etfw">#VCsakkinonana</a><a href="https://t.co/3ItSmatSrM">https://t.co/3ItSmatSrM</a><a href="https://twitter.com/hashtag/%E3%83%90%E3%83%BC%E3%83%81%E3%83%A3%E3%83%AB%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88?src=hash&amp;ref_src=twsrc%5Etfw">#バーチャルキャスト</a> <a href="https://twitter.com/hashtag/virtualcast?src=hash&amp;ref_src=twsrc%5Etfw">#virtualcast</a> <a href="https://t.co/vlFfeaZACs">pic.twitter.com/vlFfeaZACs</a></p>&mdash; さっきのなな☆彡vket5 デフォルトキューブ (@sakkinonana) <a href="https://twitter.com/sakkinonana/status/1254417811679490049?ref_src=twsrc%5Etfw">April 26, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
### 星の温度と色の関係教材の開発
詳しくは拙著[スライドバー的なUIを作りたい](https://qiita.com/sakkinonana/items/f0892a67e9990975f535)に書いたのでご興味あればそちらも合わせてお読みください。

### 天の川アドオン
今年のわかりやすい成果その３。7月辺りからかかりきりになってます。もともとの計画で、背景をテクスチャアドオンにすると決めていました。というのも、数百万個以上の星数をプロットしなければなりませんでしたが、数十万個の時点で20MBほどあり、モデルだけではだめなのは火を見るより明らかです[^1]。
とはいえ、なるべくきれいな画像を作りたいわけで、最初はベクター画像を作って加工するというアプローチを試しました。例によってpythonを使ってやりたいのでそういうライブラリがないか探したところ、pycairoという物が見つかりました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">これ全部眼で見えない星です<br>天の川は眼で見えない星でできている <a href="https://t.co/jfe2xR9l02">pic.twitter.com/jfe2xR9l02</a></p>&mdash; さっきのなな☆彡vket5 デフォルトキューブ (@sakkinonana) <a href="https://twitter.com/sakkinonana/status/1291054171655872512?ref_src=twsrc%5Etfw">August 5, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

↑がpycairoでとりあえずテスト描画してみた結果です。
最初の計画としては、ベクター画像→座標変換による補正→ラスタライズ→テクスチャ化　とする予定だったのですが、あえなく没になりました。理由はベクターのまま変換補正したほうがきれいだろうと思っていたのがあまり変わらなかったからです。
アルファの焼き込みとかが自動で適用されるので楽だと思ったのですが、そうは問屋が卸さないようです。。

そこでテクスチャを直に書くことにしました。
流れとしては以下のようになります。
カタログ読み込み→座標・等級からバッファに書き込み→（すべての星で繰り返す）→射影による補正→チラツキ防止の補正→階調補正→（カラー化→）unity読み込

となります。今回対象としたカタログデータはGaiaDR2を使いました（現在はDR3になっていますが、ちょっと最近すぎるので今回はDR2のお話をさせていただこうと思います）。GAIA DR2は約17億の星の位置と等級が記録されています。これだけあれば天の川を構成する星も一つ一つ描けてしまうため、実質天の川になります。一部のプラネタリウムでは、このあたりをぼかして描かれているものも多いですが、しっかりやっていきます。


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">できたぞ、、、10億プロット、、、<br>あとはリンドウとカンパニュラ（ホタルノフクロ）、SLの客車・・<br>リンドウは目処がついたから一番難しそうなそうな客車を <a href="https://t.co/lFLWv0kcpR">pic.twitter.com/lFLWv0kcpR</a></p>&mdash; さっきのなな☆彡vket5 デフォルトキューブ (@sakkinonana) <a href="https://twitter.com/sakkinonana/status/1299964180301185024?ref_src=twsrc%5Etfw">August 30, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

上図は作成途中のものです。ディテールがかなり細かく出ているのがよくわかります。ですが、画像が非常に重いです。というのも、星は明るさの差が激しい為、普通の画像形式だと表現できないどころか作成中に情報落ちが多数発生して暗い星の情報が反映されなくなってしまいます。一つ一つは目に見えなくても、たくさん集まってぼんやりと見えるのが天の川ですから、これではいけません。だから容量の大きい特殊な形式（fits）を使って保存・処理しています。この形式は天文学でもよく使われる由緒正しい形式なのですが、メモリを非常に多く取ります。その上、テクスチャは最大解像度の8kにしていて、更にRGBを別に計算し、17億の星の座標もメモリに展開した上で作業領域まで確保しなければなりませんから、メモリが処理速度上のボトルネックになってきます。実際、最初のうちは1/10書くのに何日もかかるようなスピードで大変でした。そのへんの苦労を記録しておこうと思います。

#### アルゴリズムを見直す
pythonは色々世間ではAIだの何だので騒がれていますが、実は結構遅いスクリプトです。特にfor文やバッチ処理は苦手で、高速化はまずこれらを整えることから始まります。for文をなくし、なるべくnumpyでバッチ処理させる。これだけでも倍以上早くなります。更にpandasで読み込み・処理を行っているのですが、これらもなるべくnumpyのオブジェクトに変換して保存しておきます。これらについては[うわっ…私のpandas、遅すぎ…?って時にやるべきこと（先人の知恵より）](https://shinyorke.hatenablog.com/entry/pandas-tips)や[pandasで1000万件のデータの前処理を高速にするTips集](https://qiita.com/kishiyama/items/b9d0a7ba3f03fd4089f3)で勉強させていただきました。

#### メモリの展開を早くする
これだけのデータ量があるとメモリに展開するだけでも一苦労です。まずは愚直にデータを減らしました。gaiaのデータの中には、位置と等級以外にも速度や距離などいろいろなデータが入っています。データが多ければ多いだけ展開に時間がかかりますから、まずは単純に減らすことにしました。これだけでも3倍以上早くなります。

#### 読み出しを早くする
読み込みには定番pandasのread_csvを使っていますが、これに結構時間がかかります。一方でpandas互換のパッケージがあり、それらの読み込みが早いということで使ってみました。その名前はdaskとvaex。daskは並列処理ができるように作られているそうで、実際早かったです。vaexはhdf5形式のファイルを逐次読み出してくれるため、メモリ上に一気に展開せずに住むという利点があります。特にvaexはロゴからしてもそうですが、天文系に使われることをある程度意識しているフレームワークのようです。（むしろgaia用？）

また、別の手段もあります。csvを一旦読み込んだ後メモリに展開されるときには当然バイナリ形式になっているのですが、それをそのまま保存できるpickleという形式があります。これに一旦変換してしまえば、次から読む時間はかなり早くなります。初期ではdaskにしたりvaexにしたり色々と変えながら試行錯誤を繰り返していました。最終的にdaskと比較した結果、若干pickleのほうが早かったので今はpickleでやっています。pandas五感と言っても同じことができるわけではないのでそこも懸念点でした。将来並列処理が早く実装できるようになったらまた試してみたいと思います。vaexもとりあえずメモリはギリギリで足りるのと、hdf5のダウンロードページが壊れてたので、今回は部分的採用になりました。

更に、HDDの速度も律速になっているようでしたので、SSDを増設しそちらにデータを置くようにしました。

#### 最終手段Numbaを使う
python自体は余り早い言語ではないというのは冒頭でお話しましたが、一方でなぜデータサイエンスや人工知能といった計算コストの高いタスクでpythonがよく使われているのかというと、別言語の早いライブラリを使うことができるからです。実際numpyなども他の言語で書かれたプログラムが走ってます。じゃあ同じことを自分でやってしまえという考えが浮かんでくると思います。でもせっかくpythonで書いてるのにCとかも併用するなんて面倒だなと言う人のためにあるパッケージがNumbaです。端的に言えば、Numbaはその場で対象の関数をコンパイルして一時的にライブラリ化し呼び出します。これによって動作を格段に早くすることができます。他の言語を書くより圧倒的に簡単です。%timeitなどで計測して重たい関数をNumba化しましょう。

以上の対処をすることで10分程度以内で出力するのが可能になりました。

### きりフェス参加作品生放送"【きりふぇす参加作品】PLANETARIUMICS-人類の最も古い VRのお話-【皐月野観測所】"番組制作
10月以降はこちらにかかりきりでした。プラネタリウムの歴史をさらっと紹介したものになります。構想をねってるうちに20時間分くらいになったのを2時間にぐっと縮めたので語り尽くせなかったところも多くありますが、将来のプラネタリウム展望などについても言及した番組になっています。そのうちアーカイブを公開予定ですので、もし見ていただいてご感想等もらえると喜びます。

このために作成したモデルや取材が結構お蔵入りになってるから、これらも供養したいですね、、、

# 今後の予定
- 画質最適化
- 容量最適化
- 連携アイテムの発表
きりフェスでも取り上げましたが、解説番組を流す装置を使って、一人でも教養を楽しめるようにしたいですね。
- 出力を上げたい
なんと行っても今年一番思い知ったのが、”自分ひとりの効率ではたかが知れている”ということです。VRSNSで展開されているプラネタリウムの中でも一番力を入れている自負はあるのですが、いかんせん自分の能力不足に起因する非効率さがあちこちで目立つ様に感じました。要するにもっとたくさんやりたいことがあったのに出来なかったということです。

課題も多い一年でしたが、来年も黙々と進歩していきたいと思っています。

# 最後に
最後に、この記事はVCIアドベントカレンダー2020の最後の記事になります。皆様の温かいご協力のおかげで、今年はすべての枠を埋めることができました。大変お疲れさまでした＆ありがとうございました。
VCIではわかりやすい以前にまだまだ言語化されていないノウハウがたくさんあります。これからバーチャルキャストを始める皆様に対して、少しでもお役に立てるのならば幸いです。


[^1]: というか、例えばgaia dr2の約17億個の星が一つ1byteだとしても、17GBある計算になるわけですからこれができるようになるのはかなり遠い未来だと思います。（そして、実際double型は8byteあるので、、、）