WebGLで宇宙をつくる

こんにちは、[yohei kojima](https://twitter.com/youhe__) です。

今回は宇宙をつくります。
星表データを元にWebGLで宇宙を表現していきます。

# まえがき
[Ryohei Fukuda](https://twitter.com/fuguzen)さんのTweetを穴が空くほど参考にさせていただきました。本当にありがとうございます。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ヒッパルコス星表から8.5等星まで描画してみました。今のところB-V色指数からRGBをちゃんと計算していません 👾 （WebGL） <a href="https://t.co/9XKM8rtuPD">pic.twitter.com/9XKM8rtuPD</a></p>&mdash; Ryohei Fukuda (@fuguzen) <a href="https://twitter.com/fuguzen/status/1070139863805026305?ref_src=twsrc%5Etfw">2018年12月5日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


# デモ
スマホでも見れます！！！ちょっと重いかも。
https://youhe.jp/jiyuchou/space/

![aaa.png](https://qiita-image-store.s3.amazonaws.com/0/84990/264cdb2c-1c6f-dd7c-c2ef-a7b248faa7bf.png)

・GUIの説明
constellation line -> 星座線の表示・非表示
bright -> bloomのしきい値
blur horizontal -> 水平方向のブラーしきい値
blur vertical -> 垂直方向のブラーしきい値

# 星のデータ
[ヒッパルコス星表](http://astronomy.webcrow.jp/hip/)を使っています。
約12万の星と約700の恒星データが収録されています。素晴らしいです。

# 解説
全部書くと長くなるので実装の大まかな流れと作るのがめんどくさそうなソースだけ載せておきます。

## 星データの取得
[ヒッパルコス星表](http://astronomy.webcrow.jp/hip/)の中で今回使ったのは以下の３つです。（）内は用途。
・基礎データ（星の位置と大きさ）
・星座線恒星データ（恒星の色）
・星座線データ（星座）

csvなので以下を参考に配列に変換します。
https://qiita.com/bambuu/items/1902453ca13e4d8662ee

・データについて
基礎データは２分割されているため合わせる
値が文字列になっているので数値に変換しておく

## 星の座標・サイズを求める
基礎データ(hip)を使って星の座標・サイズを求めます。
約12万の星を全て使うと流石に多すぎてかっこ悪かったので、視等級が大きいもの（小さく見える星）は省くことにしました。今回は視等級8.0以下の星を使います。

・座標について
赤経・赤緯をXYZ座標に変換します。
赤経・赤緯は各星の方向を表しており、その値に半径(r)をかけることでプラネタリウムのような球上に星を配置することができます。

・サイズについて
実際の比率に合わせて実装するとえらいことになるので、最大を10にしました。
また、マイナス視等級の恒星が存在するのでこれも10とします。

```javascript
const r = 1; // 半径

for ( let i = 0; i < hip.length; i++ ) {
  if (hip[i][8] < 8.0) { // 視聴級が8.0以下のものみ使用する

    let size = 1 / hip[i][8] * 20;
    if (10 < size) size = 10; // サイズの最大値を決める
    if (hip[i][8] < 0) size = 10; // マイナス視等級の恒星を最大値にする

    // 赤経・赤緯を変換
    const a = (hip[i][1] + (hip[i][2] + hip[i][3] / 60) / 60) * 15 * Math.PI / 180;
    const f = (hip[i][4] == 0) ? -1 : 1;
    const c = f * (hip[i][5] + (hip[i][6] + hip[i][7] / 60) / 60) * Math.PI / 180;

    // XYZ座標に変換
    const px = r * Math.cos(a) * Math.cos(c);
    const py = r * Math.sin(a) * Math.cos(c);
    const pz = r * Math.sin(c);
  }
}
```
参考
https://www.rikanenpyo.jp/kaisetsu/koyomi/koyomi_012.html

## 恒星の色を求める
色はめんどいです...。星座線恒星データ(hipLS)を使います。
B-V色指数 → 色温度 → 色度座標 → XYZ表色系 → RGBと変換します。

・色補間について
RGBの値が1以上になることが多く白に偏ってしまうので、RGBの中の最大値で他の値を除算して色を強調させています。

```javascript
for ( let i = 0; i < hipLS.length; i++ ) {
  const bv = hipLS[i][11]; // B-V色指数
  const t = 9000 / (bv + 0.85); // 表面温度

  // 色度座標
  let c_x, c_y;
  if (1667 <= t && t <= 4000) c_x = -0.2661239 * Math.pow(10, 9) / Math.pow(t, 3) - 0.2343580 * Math.pow(10, 6) / Math.pow(t, 2) + 0.8776956 * Math.pow(10, 3) / t + 0.179910;
  else if (4000 < t && t <= 25000) c_x = -3.0258469 * Math.pow(10, 9) / Math.pow(t, 3) + 2.1070379 * Math.pow(10, 6) / Math.pow(t, 2) + 0.2226347 * Math.pow(10, 3) / t + 0.240390;
  if (1667 <= t && t <= 2222) c_y = -1.1063814 * Math.pow(c_x, 3) - 1.34811020 * Math.pow(c_x, 2) + 2.18555832 * c_x - 0.20219683;
  else if (2222 < t && t <= 4000) c_y = -0.9549476 * Math.pow(c_x, 3) - 1.37418593 * Math.pow(c_x, 2) + 2.09137015 * c_x - 0.16748867;
  else if (4000 < t && t <= 25000) c_y = +3.0817580 * Math.pow(c_x, 3) - 5.87338670 * Math.pow(c_x, 2) + 3.75112997 * c_x - 0.37001483;

  // XYZ表式系
  const y = 1.0;
  const x = (y / c_y) * c_x;
  const z = (y / c_y) * (1 - c_x - c_y);

  // RGB
  let r = (3.240970 * x) - (1.537383 * y) - (0.498611 * z);
  let g = (-0.969244 * x) + (1.875968 * y) + (0.041555 * z);
  let b = (0.055630 * x) - (0.203977 * y) + (1.056972 * z);

  // 色補間
  const rgbMax = Math.max(r, g, b);
  if (1 < rgbMax) {
    r = r / rgbMax;
    g = g / rgbMax;
    b = b / rgbMax;
  }
}
```
参考
http://www.geocities.jp/p451640/hr_diagram/hrdiagram1.html
http://blog.yumix.net/entry/2016/12/19/013858

恒星以外の色は少しだけ赤が強い白にしました。

```javascript
const r = Math.random() * 0.1 + 0.9;
const g = Math.random() * 0.1 + 0.8;
const b = Math.random() * 0.1 + 0.8;
```

## 星座を作る
星座線データには各項目にHIP番号が2つずつ入っているため、
基礎データのHIP番号と照らし合わせて座標を作っていきます。

THREE.Lineを使うと線が全て繋がってしまうのでTHREE.LineSegmentsを使います。

## Bloomを追加する
最後に光っている感じをより出すためにBloomを追加します。
Bloomの実装はedoさんの記事がとてもわかりやすいのでそちらを参考にしてください。

[[WebGL] Bloom表現を実装してみる](https://qiita.com/edo_m18/items/c43177c0a18a2ea210b6)

これで一通り実装できます。


# あとがき
後半少し簡単に書いてしまいましたが、星の位置と色の計算ができればあとはどうにかなると思います。
わからないところがあったらTwitterで聞いてください。 → [yohei kojima](https://twitter.com/youhe__)

始めようか 天体観測