バーチャルプラネタリウムを作るお話【初心者がスクリプトでモデルを作ってなんとかまとめるまで】

#出来たもの
![2019121421085379.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/c143379c-cf2d-d03a-7e6d-323dafa7634e.png)
#やることお品書き
0.作り始める前に：コンセプトについて
1.星空を作る準備
2.星空を作る
3.明るさを調整する
4.流星のエフェクトを作る
ep.脚注
#0:コンセプト
よく勘違いされてしまうのではじめに断っておきたいのだが、私は**きれいな星空を作りたいわけではない。**

私がここでやりたいのは、**「VRという非常に柔軟性が高いメディアを使って宇宙空間の再現をしたい」**のである。（ちょっと長くなるので、この辺については脚注1で補足しますね。）つまるところ、結果としてきれいになるかもしれないけど、綺麗にすることが真の目的ではないということですね。（脚注2）
#カタログの種類
プラネタリウムを作るにあたって、星の位置、明るさ（等級）、場合によっては色等いくつかの情報を参照しなければならない。詳細は脚注に譲るが、どんなに横着してもプラネタリウムには数千個程度以上の星を入れないといけない。
そのため、それらのデータをどこからか持ってこないといけない。ハンドメイドするのであれば、ウラノメトリアなんかの星図をコピーして、穴を開けて作るてもあるが、コンピュータでやるには数字の羅列が表のようにまとまっているのが好ましい。このデータのまとまりをスターカタログ（星表）というのだが、これらはその作られた年代や基準によっていくつもあるため選ばなければならない。
例を上げると、

* [**BK5** (BSC)](http://tdc-www.harvard.edu/catalogs/bsc5.html)：約9000：基本星表　天文アマチュアならだいたい要は足りるし、プロでも場所の同定などにはちょうどよいサイズ感なのでよく用いられる。
* **[hipparcos](https://www.cosmos.esa.int/web/hipparcos/catalogues)**：約12万：ESAが打ち上げたヒッパルコスという位置天文衛星によって得られた高精度の位置データが掲載されている。位置以外にも固有運動（実は星はちょっとだけどバラバラに動いている）などのデータも高精度に測定され掲載されている
* **tycho**：約250万：ヒッパルコスの撮影写真を更に詳しく調査することで掲載数を増やしたもの。
* [**SAO**](http://tdc-www.harvard.edu/catalogs/sao.html)：約42万：スミソニアン天文台作成のカタログ。
* **GAIA**：約18億：ヒッパルコスの後継機であるガイアプロジェクトによって作られた高精度位置天体カタログ。
* **USNO**：約10億：アメリカ海軍天文台が作成したカタログ。おそらく地上観測で入手できるものの中で最多の天体数を掲載している。

その他にも様々なカタログがある。上記は目で見える光での観測結果だが、電波や赤外線、X線でのデータなどもある。また、地球近傍かどうかという観点で作られたグリーゼカタログや、銀河、星雲だけ集めたメシエカタログなど様々な観点でのカタログが本当にたくさんある。
#実現方法の検討

以上のように作らなければいけない星の数は膨大なため、（そして、さらにたくさんの困難があるのだが、、、）単純に一つ一つ星を記述しようとすると、ものすごく大変である。そのため、最初に実現方法を検討し、吟味する必要があった。以下にその一部を紹介します。

##使用するカタログ
プラネタリウム作成には、まず、カタログを決めないことには始まらない。今回は3つどうしても譲れないことがあって、

1. 星は天の川を表現できる程度以上入れる（100万程度以上）
2. 将来的に星はインタラクティブに操作できる
3. 星に触れる、手元において見上げることができる

これらは絶対に死守したかった。
そのため、自ずと以下のような仕様がmust事項になった

1. 基本となるカタログはtychoかgaia
2. その他他の波長のカタログも後々入れる(SDSSなど）
3. 触れる天体はグリーゼ
4. 付加的にその他の銀河系外カタログ（銀河や星雲など）

ただし、系外天体は容量の関係から背景VCIとして一つにまとめるのは難しいため、アドオンとして作ることにし、ひとまず２と３と４は後回しとした
##モデルの作成方法
少なくとも100万個以上の星を描写しなければならない事がわかったわけだが、間違いなく**まず手作業では実現できない。**そのため、コンピュータの力を借りて星を作成するしかなかったわけだが、いくつか方法が考えられた。

1. 単純なモデルにテクスチャを貼る
2. パーティクルで表現する
3. モデルで星の形を作る

まず、必要要件から1は論外（脚注3）。２はポリゴンの数が少なく済みそうで最初有力視していたのだが、パーティクルの処理が案外重いらしいとの話を聞き断念（一応理論ベースで計算式は作成済み）。必然的に３が選択された。そうすると今度はモデルデータをどうやって作るかという話になってくるのだが、そこで[前回の記事](https://qiita.com/sakkinonana/items/afb27d4832c940eccb0d)が重要になってくるわけです。リンク先の記事では、blender内臓のpythonを使って幾何学図形を作成していますが、これの応用です。**カタログデータをawkでちょっと前処理加工して、blender内蔵pythonで読み込んで、そのデータに基づいてblenderにbpyモジュールで描画させる流れになります。**

##明るさの表現方法
**結論から言ってしまうとモデルの大きさで表現します。**
なんでそんな回りくどい方法を取るのかと思われると思いますが、その説明を以下に書きます。

明るさなんて色をいじればかんたんと思われるかもしれません。ですが、そうは行かない事情があります。
星の明るさは一般的に「等級」で表され、数字が小さいほど明るくみえます。見える星は太陽系内天体を除くと、だいたい明るいのは-1.5、暗いのは人間の目の限界で+6くらいだとされています。何だたったの8段階くらいかと思われるかもしれませんが、WHの法則と言って、人間の感覚は指数関数に従います。(音も同じで[にゃふさんの記事](https://qiita.com/Nyaf0039V/items/fe75423846f0985c6ac0)に出てくる関数も一緒)
星の等級（相対等級）は基準としてこと座のベガを0.0等、ないし北極星を2.0等にとって、以下のように表します。（脚注5）

```math
m_1 - m_2 = -\frac{5}{2} log_{10}\frac{b_1}{b_2}
```

１等差で2.51倍、-1.5等と+6.0等とでは994倍、つまり約千倍もの差があります。tychoに載っている暗い星は13等とかあるので、更に1000倍の100万倍、、、

一方でHMDのディスプレイはほとんどのディスプレイがそうであるように、階調8bit、おそらく255までしか階調がありません。また、もし色だけで階調を表現できたとしてもマテリアルの数が死にます。またモデルの数も増えてしまうのでいけません。これでは明るさが変えられないではないか、、

一般的なプラネタリウムでは星の大きさでこれを表現しています。（物理的な）プラネタリウムは機材の中心にある電球の光を穴を通して投影しているわけですが、光源の明るさが一定なので、大きさが面積ベースで2.51倍になれば1等級分の差を表現できるわけです。

この方法はメッシュも増えず大変便利なのですが、一つ欠点があって、あまり明るい天体は大きくなりすぎてもはや星に見えなくなってしまいます。ですから、上限として適当な天体（例えば、一番明るい星：シリウスや、金星など）を決めて、そいつの大きさを星として見えそうなギリギリまで大きさを絞ってやる必要が出てきます。そして後述しますが、これが後々VRプラネタリウムの限界を作ってしまうことになります。

#pythonコード
以上を踏まえて星生成pythonコードを作ったのでそのコードと、作っていった際にぶつかった壁について書きます。
##コードの内容
```python
import bpy
from math import sqrt,pi,sin,cos,acos,asin,atan2
import csv

scale = 0.1*2.51*2.51
distance =30
nloop = 9
with open('D:\\Blender\\hipparcos_nolimit_DirectionCosine.dat') as f:
    reader = csv.reader(f)
    j = 1
    for i,row in enumerate(reader):
        l = [float(row[0]),float(row[1]),float(row[2]),float(row[3])]
        if(i<13000*nloop):
            continue
        
        if(l[0]<1.5):
            continue
            #ittousei dake mensekiha naositenai
            ngon = 12
 
        elif(l[0]<3.5):
            continue
            ngon = 6
   
        elif(l[0]<4.5):  
            continue
            ngon = 4
            
        else:
            ngon = 4
             

        bpy.ops.mesh.primitive_circle_add(vertices=ngon,fill_type='NGON',radius=scale*sqrt(10**((1-l[0])/2.5))/(sin(pi/ngon)*cos(pi/ngon)/2*ngon),
                                            location=(l[1]*distance,l[2]*distance,l[3]*distance),
                                            rotation=(0,-asin(l[3])-pi/2,atan2(l[2],l[1])))        

        
        if(i>100*j):
            bpy.ops.object.select_by_type(type = 'MESH')
            bpy.ops.object.join()
            j += 1
        if(i>13000*(nloop+1)):
            break
        
        
bpy.ops.object.select_by_type(type = 'MESH')
bpy.ops.object.join()    
    
#bpy.ops.view3d.snap_cursor_to_center()
#bpy.ops.object.origin_set(type='ORIGIN_CURSOR')           
    
```
実はこのコードだけでワンボタン、というわけには行きません。
まず、生成するモデルは一つではありません。モデルは５つに分けられていて、一等星・brighter・darker・darkest・darkest2という名前がついています。それぞれは明るさで分けられているんですが、なぜかというのが下記の「画面のチラツキ」の文節に書いてあります。
その際に変数をいじったりコメントアウトを外したりしています。いじる変数はscaleで、暗い星を作る際に、一段回ごとに2.51倍しています。コメントアウトは、後述しますが、形の見栄えを微調整しています。

また、nloopですが、これは、描画があまりにも遅いため、モデルを途中まで部分的に描画し、保存して、blenderを再起動してnloopを+1して0から10までやっています。これは、blenderがメモリを開放してくれないことによるもので、描画の最後ではメモリ48Gでも溢れ出ます。描画時間も分割しないと数時間、分割しても十分二十分かかるので根気よくやってください。

##ダイエット
最初は、丸い点を考えていたんですが、どうも３Dモデリングはポリゴンで表現するため、きれいな丸にしてしまうと容量が跳ね上がってしまう。しかし、明るい星はそこそこ大きいので三角形にしてしまうと大きな違和感がある。この点が実際のプラネタリウムにない困難で苦心しました。**解決策は、大きい星像は六角形や八角形などの円に近いモデリングにし、小さい星像は四角形や三角形などの軽いモデリングを行う**というものでした。コメントアウトしたりしているのは、明るさごとに見栄えが違うので、適宜修正しているためです。
ただ、この方法も単純にverticeを変更するだけではだめで、面積が保存するようにしなければなりません。変換式の係数aはn角形の時以下のようになります。lは明るさによる係数（この場合は等級）です。

```math
a=\frac{\sqrt{10^{((1-l)/2.5)}}}{(\sin(\pi/n)*\cos(\pi/n)/2*n)}
```
微調整したい際はscaleをいじります。

##重すぎる！
初期段階では、一つ一つの星を呼び寄せたりしたかったので、すべての星のオブジェクトを別々に作っていました。ですが、重すぎました。800個も入れれば重くて画面がぐにゃぐにゃに、場合によっては出した瞬間部屋が落ちるという凶悪アイテムになっていました。
あまり多すぎるとTSOにアップロードすら撥ねられるようで、**オブジェクトの結合をつかって一つにまとめたら驚くほど軽くなりました**。動作の重さは容量ではなく子オブジェクトの数で決まるというのを実感できました。

##画面のチラツキ
上のスクリプトで最初全部の等級について生成していたのですが、問題発生。
動画をとっても画像をとっても問題がないように見えるのですが、いざHMDを通してみると画面がものすごいちらつきます。体験したのは他には＃らいとさんくらいなものなので説明するのが難しいのですが、端的に言って**不快**。こんなの宇宙じゃねぇ！ってことで治す必要が出てきました。

どうやらこの現象の原因は、HMDのピクセルの大きさより小さいオブジェクトが画面に写った際の処理にありそうだと考えました。ピクセルと同じ大きさで表面の明るさ(brightness)がSの物体Aと、ピクセルの半分の大きさ（面積）で同じ表面の明るさSの物体Bとがあれば、目で見た際にそのピクセルからくる光の量は、それぞれ物体A:S 物体B:0.5Sになるはずです。つまり半分になるはずですですが、**どうやらバーチャルキャストでは、両方ともSになってしまっているようです**。しかも、ピクセルとピクセルの間の格子が不可視領域になっているため、任意の星が隣のピクセルに写った際に描画されたりされなかったりするというのが原因の一つ。もう一つは、大きさで明るさを表現していたため、単位面積あたりの明るさは一定なので、暗くしたかった星が全部最大の明るさになって描画されて、本来は一粒として目に見えない星まで一粒として見えるために数が多くなりすぎていることでした。

正直言うと、この処理はまずいんじゃないかなと思っていて、上記の「物体A:S 物体B:0.5S」の関係って**平たく言うとアンチエイリアス処理なんですよね**。要は**画素数不足のジャギジャギが高頻度で起きている**状態であって、これはどうなんだろうという、、、もちろん計算が重いのでしょうがない面はあると思うんですけど、将来的には解消されてほしい問題ですね。

つまるところ、技術が進めば勝手に解消される問題なので、対処するモチベーションがいまいち上がらないんですが、やっぱりおかしいので治すことにしました。原因が主に、「ピクセルサイズよりモデルが小さく見える状態」になるときに起こるので、**モデルの大きさの下限を「最低4ピクセルにまたがるように」調整しました**。ただ、それぞれのHMDのピクセルの大きさがわからないので、計算せず、何度も星を出力しながら自分の目で確認しました。（そのため、oculus rift S以下の解像度の機種ではチラツキがまだ残ってるかもしれません。）そして、**足りなくなった等級表現の階調は、明るい方をbloomに頼ってサイズを維持したまま階調上限をあげ、下の方はマテリアルの色を複数個作って同じサイズでも暗い星を表現する事ができるようにすることで解決させました。**
このせいで、モデルが一等星・bright・darker・darkest・darkest2に分かれているわけです。

#emitionと等級の紐付け
![2019112021513265.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/f16035e4-de8b-9cb9-5137-31d72ea68ca0.png)

以上の明るさの処理のせいで、エミッションをちゃんと使わなくてはならなくなったので、MToonをつかって、最初は補助くらいにしか考えてなかったエミッションについて詳しく調べました。emittionの数値を少しずつ弄って「明るさの表現方法」で検討した方法で作った星を上に並べて官能試験しました。要は見えた感じ差があるような違和感があるかどうかを一つずつテストしていきました。上の画像のような感じです。

実験結果ですが、以下のようになりました

|emit|+1|+2|+3|+4|+5|+6|+7|+8|+9|+10|
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|等級|-1|-2|-3|-4|-5|-6|-7|☓|☓|☓|
☓というのは、あまりにも光りすぎていて、星空に見えないので使えない。という意味です。
見た感じ＋１すると等級が一つ下がるように見えます。流石にここは科学的に出すのは難しいので、感じたままを信じることにしました。

#流星のエフェクト：effekseer
何らかの天文イベントが有ると作りやすいなぁと思い、ふたご座流星群にターゲットを合わせました。
すると流星をどうにかしないといけないわけですが、流れ星はパーティクルで作ることにしました。
正直、パーティクルは初めていじったので自信はないし、大したことも出来てないけど、構成は以下のようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/6595fdb9-4a2c-4f25-7375-d31549dc5e20.png)

#やっとできた
背景のアドオンとして作っているので、**ドームで囲っていない**背景VCIにならなんにでも合わせられます。山とか入れるといい感じですよ（写真はPioさんのVCIとあわせたところ)![2019121520195342.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/6983ec94-5448-73e2-385f-9c8969f01725.png)

この先100万を入れないといけないけど、明るさの下限の問題が出てくるから解決しないとね、、、
#脚注
### 脚注1
そもそも、プラネタリウムというのは名前の通り普通の星空と違う動きをするplanet(惑星)の運行の教育のために作られたものである。**つまるところ最初のプラネタリウムは星空なんてなかったわけで**、例えば[アイセアイジンガのプラネタリウム](https://ja.wikipedia.org/wiki/%E3%82%A8%E3%82%A4%E3%82%BB%E3%83%BB%E3%82%A8%E3%82%A4%E3%82%B7%E3%83%B3%E3%82%AC%E3%83%BB%E3%83%97%E3%83%A9%E3%83%8D%E3%82%BF%E3%83%AA%E3%82%A6%E3%83%A0)などが好例だろうか。それをよりリアルに示すためドイツ博物館がその周りの空間である星空と一緒に投影する機械を設置したものがそのはしりである。

私がやろうと思ってるのは、むしろこの「流れ」であり、
星空がないプラネタリウム→星空もあるプラネタリウム
というジャンプアップがドイツ博物館によってなされたように、
**星空が見えるだけのプラネタリウム→星の間を行き来したり、いろんなことができる空間という体験を作りたいのである。**
つまり一言で言うなら
プラネタリウムを作ると言ったな、あれは嘘だ。
作るのは強いて言うならコスモリウムだ。**星のある宇宙空間が作りたい**
### 脚注2
綺麗にするために”宇宙っぽい模様”を背景に貼り付けている人はもうすでにたくさんいますから、そういう物を作りたいわけじゃないということです。

### 脚注3
「テクスチャ貼るんじゃだめなんですか？」という質問をよく受けるが、絶対ダメです。
なんでかというと、星の数の最低ラインが100万という話をしましたが、これが理由です。
星のある領域とない領域の面積比ってどのくらいあると思いますか？星なんて途方もなく離れた点だし1万？譲りに譲って例えば100倍だとします。そうすると、最低でも100万×100=1億画素必要になります。**こんな画像用意できますか？**そして星が割と無規則かつ偏って存在することを考えると1億画素なんかじゃ全然足りないのですね。

### 脚注4
入れている星の数に驚かれることが結構あるので、補足しておくと、人間の目に見えるとされる星は6等星までで、約6000ことか8000ことか言われています。そうすると10万とか200万とかそんなに入れてどうするの？のような疑問を持たれる方もいらっしゃると思います。ですが、全然無駄ではないです。**見えないというのは、一粒一粒として認識できないという意味であって、たくさんの星が寄り添っていればぼんやりみえますし、それが見えてるのが天の川です**。（極論いえば一粒の分子や原子が熱運動で発光している光の強さなんていくら高エネルギーでもたかが知れていますが、おひさまの光だってその寄せ集めです。太陽見えますよね？ってはなしです。）たくさん集まれば見えます。そうすると天の川を構成している人間の視覚に本当に影響するギリギリは十数等級くらいで、星の数にすると100万くらいになるわけです（本当は夜天光というものもあって、もっと暗いものも夜の明かりに寄与しているんですが話が細かくなりすぎるので割愛）。
理解しにくかったでしょうか。それも無理のない話で、プラネタリウムが現在の形で誕生してから世界のどこでも百年くらい「6等級限界論」に縛られていたのですが、2000年頃になってやっとそれがある一人の日本の大学生によって打開されます。彼の名前は大平貴之。メガスターっていう単語なら聞いたことある方もいらっしゃるかもしれません。彼の作り出した十数等級まで映し出すプラネタリウムは息を呑む美麗な夜空を作り出し大変な好評を得ました。業界に激震を走らせ、常識を大きく変えました。そして現在、ちょっと前まではせいぜい8000こくらいしか星を投影していなかった大手メーカーのプラネタリウムの最新鋭機は、多いものでは1億個以上の星を投影するようになりましたとさ。


###脚注5
厳密には違う。表記のような方法だった時代もあるが、ほとんどの星の明るさは変動することがわかってきたため、現在は決められた規格の測光方法で基準となる複数の星の明るさを測って平均する方法が取られている。