[PYTHON] Build a starry sky on Unity from a stellar database (released)

Completion drawing

image.png

From the left

--Green Spring Triangle --Red Summer Triangle --Blue winter diamond --Purple autumn quadrilateral

The one played as a game screen is as follows. </ s> Released as a WebGL app.

https://hibit-at.github.io/StarSphere/

Stellar database

It seems that there is a stellar database in the world, so if you arrange this as a sphere as it is, you can make a realistic planetarium? I thought about it and tried it. Below is the script code.

using System.Collections.Generic;
using UnityEngine;
using System.IO;

public class Reader : MonoBehaviour
{
    TextAsset csvFile;
    List<string[]> csvDatas = new List<string[]>();
    public Material[] _material;//This material requires advance preparation

    //List for coloring specific constellations
    List<string> Spring = new List<string> { "Arcturus" , "Spica" , "Denebola" };
    List<string> Summer = new List<string> { "Deneb", "Altair", "Vega" };
    List<string> Autumn = new List<string> { "Markab", "Sheat", "Algenib" , "Alpheratz" };
    List<string> Winter = new List<string> { "Capella", "Aldebaran", "Rigel", "Pollux" ,"Procyon" , "Sirius" };

    int Color(string target) //A function that returns a number to color the constellations
    {
        foreach (string c in Spring) if (target == c) return 1;
        foreach (string c in Summer) if (target == c) return 2;
        foreach (string c in Autumn) if (target == c) return 3;
        foreach (string c in Winter) if (target == c) return 4;
        return 0;
    }

    void Start()
    {
        csvFile = Resources.Load("mdata") as TextAsset; //Return your own data here
        StringReader reader = new StringReader(csvFile.text);
        while (reader.Peek() != -1)
        {
            string line = reader.ReadLine();
            csvDatas.Add(line.Split(','));
        }

        foreach (string[] c in csvDatas)
        {
            int lon1 = int.Parse(c[3]);
            int lon2 = int.Parse(c[4]);
            float theta = lon1 * 15 + lon2 / 4;
            theta *= 2.0f * Mathf.PI / 360;
            int lat1 = int.Parse(c[7]);
            int lat2 = int.Parse(c[8]);
            float phi = lat1 + lat2 / 60;
            if (c[6] == "0") phi = -phi;
            phi *= 2.0f * Mathf.PI / 360;
            GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            sphere.transform.position = new Vector3(10 * Mathf.Cos(theta) * Mathf.Cos(phi),
                                                    10 * Mathf.Sin(phi),
                                                    10 * Mathf.Sin(theta) * Mathf.Cos(phi));
            float tokyu = float.Parse(c[10]);
            tokyu = 0.6f - tokyu / 10;
            if (tokyu < 0) tokyu = 0;
            sphere.transform.localScale = new Vector3(tokyu, tokyu, tokyu);
            sphere.GetComponent<Renderer>().material = _material[Color(c[2])];
        }
    }
}

If you attach this to an appropriate game object, about 3,000 stars (spheres) will be drawn in the scene at the start of the game. If you actually try to do it, some pretreatment is required as described later.

Pre-data processing

I use the data from the database mentioned above in hip_lite_major.csv (about 3,000 rows), but it doesn't contain the names of the stars. For all stars [Hipparcos Catalog](https://ja.wikipedia.org/wiki/%E3%83%92%E3%83%83%E3%83%91%E3%83%AB%E3% It seems that the number (HIP number) is given by the table 82% B3% E3% 82% B9% E6% 98% 9F% E8% A1% A8) (Wow!), But of course the name is given in it. There are only a few prominent stars that can be attached. The data is organized separately in hip_proper_name.csv, but they need to be so-called relational combined. Fortunately, the HIP number is the primary key, so the process is quick.

The pre-processing around here is done quickly with python and pandas.

import pandas as pd
data = pd.read_csv('hip_proper_name.csv',header=None)
detail = pd.read_csv('hip_lite_major.csv',header=None)
mdata = pd.merge(detail,data,on=0,how="left")
mdata.to_csv('mdata.csv',header=None)

This will create a mdata.csv that is a merge of the two, so put this in your Unity project.

Also, material settings are done manually on the Unity side. If you do your best, you can write it in a script, but you can compromise around here.

image.png

Create a material with the color of the constellation and set it in the Material slot of the GameObject to attach the script.

Source code explanation

    //List for coloring specific constellations
    List<string> Spring = new List<string> { "Arcturus" , "Spica" , "Denebola" };
    List<string> Summer = new List<string> { "Deneb", "Altair", "Vega" };
    List<string> Autumn = new List<string> { "Markab", "Sheat", "Algenib" , "Alpheratz" };
    List<string> Winter = new List<string> { "Capella", "Aldebaran", "Rigel", "Pollux" ,"Procyon" , "Sirius" };

The name of the constellation (it's not a constellation but something that straddles multiple stars, but I can't find any other good name, so I'll call it a constellation) is not on the csv, so you need to prepare it yourself. There is. List each constellation.

For how to read the csv file, I referred to https://note.com/macgyverthink/n/n83943f3bad60 entirely. Thank you very much.

From each line of csv, we will calculate the angle and scale.

The angle is calculated from the right ascension (hours, minutes, seconds) and declination (degrees, minutes, seconds) in the database. It's a bit confusing because it's degrees, minutes, and seconds, but the declination angle $ \ phi $ is

\ phi = degree + \ frac {minutes} {60} + \ frac {seconds} {3600}

You can find out that it is. About RA $ \ theta $, it is ** hours **, minutes, seconds, which did not come out even if I checked it, but the point is that 360 degrees is divided into 24, so ** It will be 15 degrees ** per hour. So you can see that you can add $ 15 $ to the above formula.

\ theta = hour * 15 + \ frac {minutes} {4} + \ frac {seconds} {240}

You can calculate with the above. Note that Unity's Rotate is in radians and the coordinate system is left-handed.

            theta *= 2.0f * Mathf.PI / 360;
(Omitted)
            phi *= 2.0f * Mathf.PI / 360;
(Omitted)
            sphere.transform.position = new Vector3(10 * Mathf.Cos(theta) * Mathf.Cos(phi),
                                                    10 * Mathf.Sin(phi),
                                                    10 * Mathf.Sin(theta) * Mathf.Cos(phi));

You can place each star at the correct coordinates with. In addition, the difference in brightness for each grade is shown by the size of the sphere.

            float tokyu = float.Parse(c[10]);
            tokyu = 0.6f - tokyu / 10;
            if (tokyu < 0) tokyu = 0;
            sphere.transform.localScale = new Vector3(tokyu, tokyu, tokyu);

This will give you a feeling of a starry sky.

Thing you want to do

Since it can be handled as data on Unity, an interactive planetarium can be realized if a mechanism is created that allows the displayed stars to be manipulated and the constellation lines to emerge. If you build it with Quest, it seems that you can do something like a stand-alone and easy planetarium (and disappointed with the real starry sky shobo every time you remove the headset) even outdoors. I would like to try it if I have the time and energy.

Added (released)

After that, I asked a friend who is familiar with astronomical observation to see it, adjusted the size and color, and released it as a WebGL application. The latest version of the code.

using System.Collections.Generic;
using UnityEngine;
using System.IO;

public class Reader : MonoBehaviour
{
    TextAsset csvFile;
    List<string[]> csvDatas = new List<string[]>();
    public Material[] _material;

    List<string> Spring = new List<string> { "Arcturus" , "Spica" , "Denebola" };
    List<string> Summer = new List<string> { "Deneb", "Altair", "Vega" };
    List<string> Autumn = new List<string> { "Markab", "Sheat", "Algenib" , "Alpheratz" };
    List<string> Winter = new List<string> { "Capella", "Aldebaran", "Rigel", "Pollux" ,"Procyon" , "Sirius" };

    int Colorization(string target)
    {
        foreach (string c in Spring) if (target == c) return 1;
        foreach (string c in Summer) if (target == c) return 2;
        foreach (string c in Autumn) if (target == c) return 3;
        foreach (string c in Winter) if (target == c) return 4;
        return 0;
    }

    Color Spectrum(string s)
    {
        if(s == "")
        {
            return new Color(.5f, .5f, .5f, 1);
        }
        char c = s[0];
        if(c == 'O')
        {
            return new Color(.3f, .3f, .8f,1);
        }
        else if (c == 'B')
        {
            return new Color(.3f, .3f, .8f, 1);
        }
        else if (c == 'A')
        {
            return new Color(.5f, .5f, 1, 1);
        }
        else if (c == 'F')
        {
            return new Color(1, 1, 1, 1);
        }
        else if (c == 'G')
        {
            return new Color(.8f, .8f, .4f, 1);
        }
        else if (c == 'K')
        {
            return new Color(.8f, .4f, 0, 1);
        }
        else if(c == 'M')
        {
            return new Color(.4f, 0, 0, 0);
        }
        else
        {
            return new Color(.5f, .5f, .5f, 1);
        }
    }
    
    void Start()
    {
        csvFile = Resources.Load("mdata") as TextAsset;
        StringReader reader = new StringReader(csvFile.text);
        while (reader.Peek() != -1)
        {
            string line = reader.ReadLine();
            csvDatas.Add(line.Split(','));
        }

        foreach (string[] c in csvDatas)
        {
            int lon1 = int.Parse(c[3]);
            int lon2 = int.Parse(c[4]);
            float theta = lon1 * 15 + lon2 / 4;
            theta *= 2.0f * Mathf.PI / 360;
            int lat1 = int.Parse(c[7]);
            int lat2 = int.Parse(c[8]);
            float phi = lat1 + lat2 / 60;
            if (c[6] == "0") phi = -phi;
            phi *= 2.0f * Mathf.PI / 360;
            GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            sphere.transform.position = new Vector3(10 * Mathf.Cos(theta) * Mathf.Cos(phi),
                                                    10 * Mathf.Sin(phi),
                                                    10 * Mathf.Sin(theta) * Mathf.Cos(phi));
            float tokyu = float.Parse(c[10]);
            tokyu = 0.3f - tokyu / 20;
            if (tokyu < 0) tokyu = 0;
            sphere.transform.localScale = new Vector3(tokyu, tokyu, tokyu);
            MeshRenderer r = sphere.GetComponent<MeshRenderer>();
            r.material = _material[0];
            r.material.EnableKeyword("_EMISSION");
            Color StarColor = Spectrum(c[11]) * tokyu / 0.3f;
            if (float.Parse(c[10]) <= 1.5f)
            {
                r.material.SetColor("_EmissionColor", StarColor * .3f);
            }
            
            //sphere.GetComponent<Renderer>().material = _material[Colorization(c[2])];
        }
    }
}

The color of the star is [Spectrum Classification](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%9A%E3%82%AF%E3%83%88%E3%83% It is associated with AB% E5% 88% 86% E9% A1% 9E). Since it is classified by letters such as O and K, it is processed by if statement. The color is adjusted by setting it to the emission color of the material with r.material.SetColor ("_EmissionColor", StarColor * .3f); .

The GitHub repository will be here.

What I want to do 2

--VR application --Display of constellation lines

Recommended Posts

Build a starry sky on Unity from a stellar database (released)
Build a python3 environment on CentOS7
Build a "Deep learning from scratch" learning environment on Cloud9 (jupyter miniconda python3)
I made a webAPI! Build environment from Django Rest Framework 1 on EC2
Build a Pypi cache server on QNAP
Build a python environment on MacOS (Catallina)
Build a simple WebDAV server on Linux
Make Unity Accelerator a service on Linux
Build a Samba server on Arch Linux
Build a web server on your Chromebook
Build a Python + OpenCV environment on Cloud9