[Astro] #186 今日の空 — 太陽・月・星を計算して、全天 3D と Quest の VR で見る日課アプリ

[Astro] #186 今日の空 — 太陽・月・星を計算して、全天 3D と Quest の VR で見る日課アプリ

はじめに

前回は、トップページの 3D の世界に、風・雪・夜を足しました。

今回は、毎日開く「日課アプリ」として、今日の空(Sky Today)を作りました。場所と日付を入れると、その日の太陽と月がいつ出て、どこにあるかが分かります。そして、星空も見られます。ブラウザだけで動き、通信はしません。

最初は、数字を出すだけのものでした。見せたところ、「ほしもなにもない、ただのデータやん」と言われました。そのとおりだったので、星の図を足しました。さらに、「リアルタイムで、VR で見られるのでは?」と言われて、Quest の VR で見られるところまで作りました。そこから、1 年の図、全画面、天の川と、足していきました。この記事は、できたところまでの、実装のまとめです。

作ったもの

  • 今日の数字: 日の出・日の入り、薄明(市民・航海・天文)、南中、月の出・月の入り、月齢、輝面、昼の長さ
  • 1 日の高度のグラフ: 太陽と月の高度。クリックやドラッグで時刻を選ぶと、その時刻の高度と方位が出ます
  • 1 年の日の出・日の入りの図: この場所・この時間帯での、1 年分。クリックで、日付を選べます
  • 場所: 緯度・経度・標高を入れます。「現在地」のボタンもあります。入れた値は、このブラウザの中にだけ保存します
  • 空の図(平面): 真上を中心にした半球の図に、太陽・月・惑星・星・星座の線と名前を出します
  • 全天(3D・VR): 同じ空を、three.js で球に貼って、見回せます。画面いっぱいの全画面と、キーボード操作があります。星の色、天の川、星雲・星団、流れ星、日付アニメ付きです。Quest では、VR と MR でも見られます
  • リアルタイム: 開いている間、15 秒ごとに、いまの空に更新します
  • F1 のヘルプ: ふだんの画面には、余計なものを置かず、操作の説明は、F1 に入れました

計算は、astronomy-engine(MIT ライセンス)を使いました。自作はしていません。検算できる相手が、いなくなるからです。星のデータは、d3-celestial(BSD-3、元は Hipparcos 星表)から作りました(5,044 個、等級 6 まで)。

今日の空の画面。上に数字のカード、下に空の図、高度のグラフ、1 年の日の出・日の入りの図

日の出の「定義」

日の出の時刻は、「太陽の上の端が、地平線に触れたとき」です。太陽の中心の高度で言うと、−0.833° です。太陽の見かけの半径(約 0.27°)と、大気による屈折(約 0.57°)を足した値です。

薄明は、太陽の中心の高度で決まります。

  • 市民薄明: −6°
  • 航海薄明: −12°
  • 天文薄明: −18°

ここで、1 つ、つまずきました。

屈折を含めたら、定義とずれました

画面に出す太陽の高度を、屈折を含めた値にしていました。すると、検算で、日の出の瞬間の高度が −0.22°、市民薄明の始まりが −5.39° と出ました。定義は、−0.833° と −6° です。

原因は、屈折の補正が、地平線の下では、一定の値で効くことでした。出入りの時刻は、屈折を含めて探します。ところが、その時刻の高度まで屈折を含めて出すと、定義と合わなくなります。

そこで、画面に出す高度は、屈折を含まない値に変えました。出入りの時刻のほうだけに、屈折を含めます。日の出のときに、高度が約 −0.8° と出るのは、そのためです。これは、検算のテストに足しました。テストをしていなければ、気づきませんでした。

検算の結果

天体の計算は、合っているかどうかが、見た目では分かりません。そこで、別の計算と比べました。

  • 太陽の出・入り・南中を、NOAA の近似式と、6 地点 × 6 日付 = 108 件で比較しました。最大の差は 0.62 分でした
  • 北緯 69.65°(トロムソ)で、6/21 が白夜、12/21 が極夜と判定されました
  • 北極星の高度が、1 日中、緯度の前後 1° 以内でした
  • シリウスの最高高度は 37.57°(期待は 37.60°)、南中の方位は 179.9° でした
  • 東京の 2026-10-21 は、日の出 05:52、日の入り 16:58、月齢 10.5 でした

astronomy-engine を信頼しているので、月の出入りは、独立には検算していません。

星の投影

星のデータは、J2000(2000 年の春分点)の赤経・赤緯です。見たい日の、見たい場所の空に直すには、2 つの回転が要ります。

  1. 歳差: J2000 から、その日の座標へ
  2. 恒星時: その場所・その時刻で、空がどれだけ回っているか

これで、地平線の座標(高度と方位)が出ます。3D 用には、同じ計算を、行列 1 つにまとめました。東・上・南の座標にして、星のすべてを、フレームごとに、行列 1 つで回します。星の位置を、毎フレーム計算し直しているわけではありません。

2D の図と 3D の座標が合っているかは、4 地点 × 2 日付 × 6 星の 48 点で比べて、一致しました(最大差 0.00000°)。

空の図は、中心が真上、上が北、左が東です。地図とは、東西が逆に見えます。空を見上げて持っているからです。

1 年の日の出・日の入りの図

1 年の図は、横が日付、縦が時刻(0〜24 時)で、昼の部分を、帯で塗ります。日ごとに、日の出と日の入りの分を計算して、結ぶだけです。

  • 夏時間がある時間帯でも、日ごとのずれを使うので、段差がちゃんと出ます
  • 白夜・極夜の日は、帯を切ります。白夜が続くところは、1 つの四角にまとめます

最初は、日ごとに細い四角を並べました。すると、縦のすじが出ました。四角の継ぎ目が見えたのです。多角形 1 つで塗る作りに変えて、消えました。

もう 1 つは、丸めです。日の入りが、「18:60」と出ました。分を四捨五入していたのが原因です。日のカードは、秒を切り捨てて出していたので、図も切り捨てにそろえて、直しました。

全天(3D)を、画面いっぱいで

VR のボタンは、押すとダイブします。ところが、パソコンでは、小さな窓でしか見られませんでした。

せっかく星空が見られるのに、もったいないので、全画面を足しました。

  • 「全画面(F)」のボタン、または F キーで、全画面にします。Esc で戻ります
  • 左上に時刻、下に操作のヒントを出します
  • マウスだけでは疲れるので、キーボードも足しました。矢印で見回し、+ − で拡大・縮小、, . で時刻(Shift で 1 時間)、N で「いま」に戻ります。入力欄を触っているときは、効かないようにしました
全画面にした全天の 3D 表示。星座の線と名前、月、土星が見える

見た目を作り込む

星の色と大きさ

星の色は、最初は 4 段階でした。星のデータには、**色指数(B−V)**が入っています。これを、表面温度に直して、黒体の色にしました。白を少し混ぜて、きつすぎない色にします。

大きさは、等級で、連続的に変えました。約 3 等より明るい星には、光のにじみを付けます。暗い星は、うすくします。描画は、星 5,044 個の点群を、1 つのシェーダーで描くままです。

最初の版は、白を 3 割混ぜていたので、「あまり変わらない」と言われました。1 割に下げましたが、星は小さな点なので、変化は、控えめです。

地平線の空の色

空の背景は、太陽の高度で決めた、青から暗い紺の、1 色でした。これを、天頂から地平線のグラデーションにしました。太陽が地平線の近くにあるとき(日の出・日の入りの前後)は、太陽の方角の地平線を、橙から赤紫に染めます。球の殻を 1 つ置いただけで、軽いです。MR のときは、背景を出しません。

星をクリックして、名前を出す

星をクリックすると、名前(名前のある星だけ)、等級、色、いまの高度と方角が出ます。いちばん近い星を選びますが、明るい星を、少し有利にします。ドラッグで見回しているときに反応しないよう、動きが 5px 未満・0.5 秒未満のときだけ、クリックとして扱います。

クリックできることに、作った自分が、しばらく気づきませんでした。画面下のヒントと F1 に、1 行ずつ足しました。

天の川

天の川を足すと、画面の印象が、ガラッと変わりました。「白い霧みたいなのが追加されるだけで、イメージが変わる。背景は大事」と言われました。

天の川は、銀河座標の小さな画像に、起動時に 1 回だけ焼きます(1024 × 512)。帯の濃さは、銀河面からの距離で決めます。銀河中心(いて座)の方向は、太く明るくして、ノイズで濃淡と暗黒帯を作ります。描画は、空の球に、その画像を、銀河座標へ戻す行列で引くだけです。毎フレームのノイズ計算は、ありません。

実際の天の川とは違う、模式図です。確かめたのは、位置です。8 月の 22 時に南を向くと、さそり座からいて座に中心が来て、わし座・はくちょう座の方向に暗い筋が出ました。10 月は、ペルセウス座・ぎょしゃ座を通ります。

最初の版は、2 つ失敗しました。

  • 画像の中心の置き方を、描画の側とずらしました。銀河の反対側(ぎょしゃ座)が、中心の明るさで光りました
  • 透明度つきの画像に、低い値を入れたら、色が荒れて、縞が出ました。強さを色に掛けて、透明度は使わない形にしました。明るさも、なだらかに頭打ちする形に直しました

色は、自然な青白にしました。サイトマップの背景にある紫の煙は、宇宙空間の色のサンプルで、天の川には使いません。

星雲・星団・銀河

アンドロメダ銀河、オリオン大星雲、すばる、M13、マゼラン雲など、約 41 個を、ぼんやりした光の玉で置きました。種類で色を変えます(銀河は暖色、散光星雲は桃、惑星状星雲は青緑、散開星団は青白、球状星団は黄白)。大きさは、見かけの大きさです。クリックで、名前、記号、種類、等級、大きさ、高度、方角が出ます。

形は描いていません。位置は、座標どおりです。等級・大きさは、手元の記憶ベースの代表値で、検算していません。目で、位置を確かめました。

流れ星

流れ星は、飾りです。S キーで入・切にして、ふだんは切にしました。夜(太陽高度が −6° より下)だけ、流れます。

流星群の時期(ピークの前後 3.5 日)は、その放射点から、遠ざかる向きに流れます。しぶんぎ座、こと座、ペルセウス座、オリオン座、しし座、ふたご座の 6 つです。時期でないときは、ランダムです。放射点の座標とピーク日は、記憶ベースの代表値で、検算していません。

確かめるのが、難しい機能でした。描画の遅い環境では、流れ星の寿命(0.5〜1 秒)が、スクリーンショットより短いのです。ページの時計を、0.04 倍に遅らせて、やっと 2 本写しました。

実機でも、「出た、動いてる」と、確認できました。

日付アニメ

[ と ] で、日付を 1 日ずつ動かします(Shift で 10 日)。Y キーで、1 年のアニメです。時刻は、そのままで、日付だけが動くので、同じ時刻の空が、季節で動く様子が見えます。下の日の出・日の入りの図の、縦の線も、一緒に動きます。

夜の全天 3D。日付アニメ

VR で見る

「VR で見られるのでは?」と言われたとき、私は、実機で確認してから後で、と考えていました。ただ、日課アプリで、見たものが VR で見えない、というのは、確かにもったいないことでした。それで、先に作りました。

  • three.js で、星を 1 つの点群にして、空の球に貼ります
  • 空の回転は、1 フレームに行列 1 つ
  • 太陽・月・惑星は、20 秒ごとに計算し直します
  • Quest では、WebXR の immersive-vr(と immersive-ar = MR)を使います。ボタンは、対応した端末で開いたときだけ出ます
  • 開始時は、北を向いた状態です
  • スティックの左右で、空を回して、北を合わせます
  • スティックの上下で、時間を動かします(最大、1 秒で 2 時間)
  • A ボタンで、「いま」に戻ります

実機で、VR は問題なく動きました。スティックで時間を動かすと、夜から昼に変わります(空が青くなり、太陽と月が出ます)。見ていて、いちばん楽しい動きでした。星の色、天の川、星雲、流れ星を足したあとも、VR で問題ありませんでした。

MR は、部屋の景色の中に星空が出ましたが、部屋の壁や天井で、空がさえぎられて、不自然でした。屋外で、ゴーグルをかぶって使うモードのようです。機能は、残しました。

夜の全天 3D。VR で見る

詰まったこと

  • 「動く」と言って、動いていませんでした。ページが開かなかったのを、「JS が起動していない」「http で開いている」と、2 回、決めつけました。どちらも、証拠がありませんでした。実際は、ブラウザを再起動したら直りました。原因は、特定していません。エラーが ERR_... のときは、アプリの JS が動く前の話です。まず、開発サーバーのログに、アクセスの行が出ているかを見るべきでした
  • UMD 版の読み込みは、サイトによって失敗します。サイト側に define.amd などがあると、UMD 版の astronomy が起動しません。ES モジュール版に変えて、起動に失敗したら、理由を画面に出すようにしました
  • 画面の色が、思ったより明るくなりました。3D の背景色を、リニアの色として渡していて、sRGB として扱われていませんでした。setRGB に、SRGBColorSpace を渡して直しました
  • テストが、空振りしていました。ボタンを文字(「今日」)で探して、ページ見出しの「今日の空」に当たっていました。role で探すように変えました
  • Quest の録画を、読み違えました。冒頭の 1 コマだけを見て、「MR の場面がない」と判断しました。実際は、冒頭から MR でした。録画は、時間を追って、何コマも見るべきでした
  • 見た目を、聞かずに変えました。画面の縦を縮めようと、カードを 9 枚の 1 行にしたところ、「醜くなった」と言われました。3 段のまま、ブロックを小さくするほうが、正解でした。大きな見た目の変更は、先に聞きます
  • シェーダーの #include を、同じ行に書いて、コンパイルエラーになりました。改行が要ります。headless のブラウザで、見つけました
  • 「機能を足さない」と、言いすぎました。頼まれていない機能を足さない、と決めかけたら、「創作の幅が小さくなる」と言われました。足してもいい。ふだんの画面を、すっきりさせておいて、足したものは、F1 のヘルプか、専用のボタンの後ろに置く、という決め方にしました

未確認のこと・制限

  • 実際の夜空との見比べは、まだです。星の位置が合っているかどうかは、夜に外で見て、確かめるのが確実です。月と、明るい惑星(いまは木星と土星)の、方角と高度を見比べると、分かりやすいと思います
  • 屋外の MRは、試していません。明るい屋外では、パススルーの映像に、星が負けるかもしれません(推測です)
  • 星雲・星団の等級と大きさ、流星群の放射点とピーク日は、記憶ベースの代表値です
  • 天の川は、模式図です。写真ではありません
  • 「現在地」のボタンは、実機で、緯度・経度が入るのを確認しました
  • Chrome と Quest のブラウザで確認しました。Safari と Firefox は、確認していません
  • 時間帯は、「このPC」「日本時間」「UTC」「現地の太陽時」の 4 択です。国や夏時間の決まりは入れていません
  • 日付アニメは、画面の再計算が毎回走るので、遅いパソコンでは、速度が落ちます
  • VR の中では、日付を動かせません(スティックは、時間のみ)

おわりに

次は、2 つ考えています。

  • トップページの昼夜に、日の出・日の入りと月齢を流す。いまは、PC の時刻だけで決めています

  • VR のモードに、プラネタリウムを組み込む。メニューからボタンで呼び出して、表示します。今日の空と、描画の部分を分けて、トップページの 3D の世界の中で動かす形になりそうです

  • 今日の空: https://lain-lab.com/sky-today