LoginSignup
29
19

More than 5 years have passed since last update.

WebGLで宇宙をつくる

Last updated at Posted at 2018-12-23

こんにちは、yohei kojima です。

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

まえがき

Ryohei FukudaさんのTweetを穴が空くほど参考にさせていただきました。本当にありがとうございます。


デモ

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

aaa.png

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

星のデータ

ヒッパルコス星表を使っています。
約12万の星と約700の恒星データが収録されています。素晴らしいです。

解説

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

星データの取得

ヒッパルコス星表の中で今回使ったのは以下の3つです。()内は用途。
・基礎データ(星の位置と大きさ)
・星座線恒星データ(恒星の色)
・星座線データ(星座)

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

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

星の座標・サイズを求める

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

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

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

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の中の最大値で他の値を除算して色を強調させています。

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

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

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表現を実装してみる

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

あとがき

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

始めようか 天体観測
 
 
 

29
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
19