前回のポストで作ったヒッパルコス星表のJSONデータを基に、天の川銀河をプロットします。
3Dグラフィックの世界では、シーンと呼ばれる「世界」に描画すべきオブジェクトを追加し、レンダラーを通して最終的に描画が行われます。ヒッパルコス星表は、11万個以上の恒星を含んでおり、これをすべて独立したオブジェクトとして登録すると、パフォーマンスが低下してしまいます。そこで、このような場合には ParticleSystem という細かいオブジェクト(パーティクル)をまとめて一つのオブジェクトとして扱うための、いわば「みなしオブジェクト」を使います。ParticleSystemでは、各パーティクルの色は個別に指定できますが、大きさやマテリアル(外観)を個別に指定することはできません。このため、天の川銀河のプロットのような用途では、すべての星を同じ大きさとして扱って構わない場合にのみ利用できることに注意が必要でしょう。
具体的なコードとしては、以下のように使います。以下の関数の引数stars
は、前回作成したJSONデータが入っているものと考えてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
function drawStars(stars) { var geometry = new THREE.Geometry(); var material = new THREE.ParticleBasicMaterial({ vertexColors: true, size: 1.0, sizeAttenuation: false }); var colors = []; for (var i in stars) { var star = stars[i]; var ra = star.RA * Math.PI / 180; var dec = star.Dec * Math.PI / 180; var dist = 1000; var x = dist * Math.cos(ra) * Math.cos(dec); var y = dist * Math.sin(ra) * Math.cos(dec); var z = dist * Math.sin(dec); var position = new THREE.Vector3(x, z, -y); geometry.vertices.push(position); colors.push(star.color); } geometry.colors = colors; var particleSystem = new THREE.ParticleSystem(geometry, material); scene.add(particleSystem); } function init() { /* 省略 */ } function main() { /* 省略 */ } $(function() { init(); $.getJSON("hipp.json", drawStars); main(); }); |
ここで、ポイントがいくつかあります。まずはマテリアルのコンストラクタのオプションですが、vertexColors
をtrue
にすることで、各パーティクルの色を個別に指定できるようにしています。また、sizeAttenuation
をfalse
とすることで、各パーティクルからどれだけ近づいたり離れたりしても常にパーティクルがsize: 1.0
で描画されるようにしています。銀河系内を飛行するようなプログラムを書くといった用途では、sizeAttenuation
はtrue
とするべきでしょうが、今回は太陽系シミュレーターということですべての星が距離1000(天文単位)にある天球上にあると仮定しますので、false
としています。星の座標position
について、(x, z, -y)
と座標変換して与えています。これは、Three.js内では基本的にY軸が画面の上方向、Z軸が画面奥に向かって伸びているためで、こうすることによって天球上の座標とThree.js内の座標とを変換しています。なお、この「天球上の座標」はいわゆる赤道座標であり、後に利用する黄道座標で利用するためには再度変換が必要なことに注意してください。
ただ、これだとすべての星が同じ明るさ・大きさで表示され、実際に目で観測される星野とはちょっと雰囲気が違ってしまいます。まず、前回も指摘したとおり暗い星はα値を下げるなどして暗く描画する必要があります。明るさは、等級が1下がると明るさが100の5乗根下がります(暗くなる)。これはCIEのxyYでいうY値が同程度下がるものと考えて良いでしょう。xyY→RGBの計算は一次変換により得られますから、本来なら1等星をY=1.0、2等星をY=0.4、3等星をY=0.1、…とするべきでしょう。しかしこれでは3等星が暗くなりすぎてしまいますので、便宜上、 \(\alpha = 1.0 – \mathrm{V_{mag}}/10 \) くらいにしておくのが良いようです。
また、星の大きさに関しては、一定以上明るい星は肉眼でも撮像素子でも「大きく」見えていますので星の明るさに応じてその描画の大きさを変えるべきですが、ParticleSystemではすべてのパーティクルが同じ大きさである必要がありますから、明るい星を個別のオブジェクトとして扱うなどの処理が必要です。いわゆる一等星(Vmag<1.5)が22個、二等星以上(Vmag<2.5)が93個、三等星以上で285個、四等星以上で907個、五等星以上が2816個あります。数千個のオブジェクトを扱うとパフォーマンスが低下しそうですから四等星以上を個別に扱うくらいがちょうど良いでしょう。(実際問題、4等星くらいなら1ピクセルで十分でしょう) こうしたことを踏まえて書き換えると、以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
function drawStars(stars) { var geometry = new THREE.Geometry(); var material = new THREE.ParticleBasicMaterial({ vertexColors: true, size: 1.0, sizeAttenuation: false }); var colors = [], brightStars = []; for (var i in stars) { var star = stars[i]; var ra = star.RA * Math.PI / 180; var dec = star.Dec * Math.PI / 180; var dist = 1000; var x = dist * Math.cos(ra) * Math.cos(dec); var y = dist * Math.sin(ra) * Math.cos(dec); var z = dist * Math.sin(dec); star.position = new THREE.Vector3(x, z, -y); var alpha = 1 - star.Vmag / 10; alpha = (alpha < 0.1) ? 0.1 : (alpha > 1 ? 1 : alpha); star.color = new THREE.Color(parseInt(star.color)); star.color.r *= alpha; star.color.g *= alpha; star.color.b *= alpha; if (star.Vmag < 4.0) { brightStars.push(star); } else { geometry.vertices.push(star.position); colors.push(star.color); } } geometry.colors = colors; var particleSystem = new THREE.ParticleSystem(geometry, material); scene.add(particleSystem); for (var i in brightStars) { var star = brightStars[i]; var r = 3 - star.Vmag * 0.4; var c = star.color; var material = new THREE.MeshBasicMaterial({ color: c, blending: THREE.AdditiveBlending }); var mesh = new THREE.Mesh(new THREE.SphereGeometry(r, 4, 4), material); mesh.position = star.position; scene.add(mesh); } } function init() { /* 省略 */ } function main() { /* 省略 */ } $(function() { init(); $.getJSON("hipp.json", drawStars); main(); }); |
(画像をクリックすると実際のデモ画面を表示します)
(続く)
はじめまして。
個人サークルのブログにて、コードをリビジョン74に移植してみました↓
今後、さらに発展させる形で使いたいと思い、連絡させていただきます。
よろしくお願いします。
Three.jsでお月見をする Ⅱ
http://totsuka-works.github.io/study-03b/