three.jsを触ってみた
いろんなページを見ながら、three.jsを触ってみた。初めてのjsだったけど、C言語を昔触ったことがあったので、なんとなくすんなり入れた。作ったものは、太陽と地球と月の表示。作ったものは下記のサイトにおいた。
http://toshidev.neocities.org
太陽と地球と月
threejsが用意してる基本球体モデルを利用し、テクスチャを貼ってみた。
テクスチャを拝借したサイトは下記。
太陽 http://www.solarsystemscope.com/textures/2k_sun.jpg
地球 http://earthobservatory.nasa.gov/Features/BlueMarble/BlueMarble_2002.php
月 http://planetpixelemporium.com/earth.html
なお、地球のテクスチャはちょっと暗かったので、gimpで色相などいじって、明るく、コントラスト上げ、パステル的な色味にした。
htmlソース、全文
sun_earth_moon.htmlを作成。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>sun, earth, moon</title> <!-- three.jsを読み込む --> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script> </head> <body> <script> var scene, camera, renderer; var moonRot; var earthRot; var CameraRot; // ----------------------------------------------- // 実行部 init(); animate(); // ----------------------------------------------- // 関数群 function init() { // three.jsの設定 scene = new THREE.Scene(); // ライトの設定 var light = new THREE.PointLight(0xffffff,1,0,0); light.position.set(0,0,0); scene.add( light ); // カメラの設定 camera = new THREE.PerspectiveCamera( 20, window.innerWidth /window.innerHeight, 1, 1000000 ); cameraRot = 0; camera.lookAt({x:0, y:0, z:0 }); // 太陽 geometrySun = new THREE.SphereGeometry( 500, 64, 64 ); materialSun = new THREE.MeshBasicMaterial( { color: 0xffffff, map: THREE.ImageUtils.loadTexture('preview_sun.jpg') } ); meshSun = new THREE.Mesh( geometrySun, materialSun ); scene.add( meshSun ); // 地球 geometryEarth = new THREE.SphereGeometry( 280, 64, 64 ); materialEarth = new THREE.MeshPhongMaterial( { color: 0xffffff, map: THREE.ImageUtils.loadTexture('land_shallow_topo_2048.jpg') } ); meshEarth = new THREE.Mesh( geometryEarth, materialEarth ); scene.add( meshEarth ); earthRot = 0.0; // 月 geometryMoon = new THREE.SphereGeometry( 60, 64, 64 ); materialMoon = new THREE.MeshPhongMaterial( { color: 0xffffff, map: THREE.ImageUtils.loadTexture('moonmap1k.jpg') } ); meshMoon = new THREE.Mesh( geometryMoon, materialMoon ); scene.add( meshMoon ); moonRot = 0.0; // 星 geometryStar = new THREE.SphereGeometry( 20, 1, 1 ); for (i = 0; i < 0x1000; i++) { cl = (Math.random()*0x1000000) & 0xff00ff; cl = ((Math.random()*0x20) * 0x000100) | cl; materialStar = new THREE.MeshBasicMaterial( { color: cl } ); meshStar = new THREE.Mesh( geometryStar, materialStar ); x = Math.random(); z = Math.random(); y = Math.random(); if (Math.random() > 0.5) { x *= -1; } if (Math.random() > 0.5) { z *= -1; } if (Math.random() > 0.5) { y *= -1; } // ベクトルの正規化 l = Math.sqrt( x * x + y * y + z * z); if (l!=0.0) { x = x/l; y = y/l; z = z/l; } // ベクトルのスカラー倍 l = Math.random()*50000+20000; x = x*l; y = y*l; z = z*l; meshStar.position.x = x; meshStar.position.y = y; meshStar.position.z = z; scene.add( meshStar ); } // レンダラー renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setClearColor( 0x101010, 1.0 ); document.body.appendChild( renderer.domElement ); } function animate() { // フレーム内処理 requestAnimationFrame( animate ); // 地球の位置 earthRot += 0.001; meshEarth.position.x = Math.sin(earthRot)*3800; meshEarth.position.z = Math.cos(earthRot)*3800; meshEarth.rotation.x = -Math.PI*(1/8); meshEarth.rotation.y += 0.008; // 月の位置 moonRot += 0.01; meshMoon.position.x = Math.sin(moonRot)*500 + meshEarth.position.x; meshMoon.position.z = Math.cos(moonRot)*500 + meshEarth.position.z; meshMoon.rotation.y += 0.001; // カメラの位置 camera.position.x = -1000; camera.position.z = -5000; camera.position.y = 0; camera.lookAt({x:meshEarth.position.x , y:meshEarth.position.y , z:meshEarth.position.z }); // レンダリング renderer.render( scene, camera ); } </script> </body> </html>
苦労点1
3D / 立体の理解。原点ゼロを起点に、自分たちが考える地面の水平面をXZの平面、頭上の方向はY軸と捉える。今回は太陽が原点。ライト(スポットライト)も原点においた。
苦労点2
地球と月の公転。地球は太陽を中心にした円運動で、月は地球を中心にした円運動。まずは地球の位置を計算し、その次に月の位置を計算した。回転角度(moonRot / earthRot)はどんどん増えていくようにして、
x = sin(回転角度) * 距離
z = cos(回転角度) * 距離
で計算した。久しぶりのsin/cosだった。
苦労点3
背景に星を散りばめた。乱数を使って星をセット。でもランダムだと太陽と地球の間にも星が置かれることになってしまう。かなり悩んで、下記のロジックで対応。
1)x,z,yに 0から1未満の乱数値を入れる
2)これを3Dベクトルと考えて、正規化(単位ベクトル化)する
3)長さを考える。原点を中心にどの範囲は星を配置したくないかl(長さ) = Math.random()*50000+20000;
乱数の幅は 0から50000、その値に20000を足したので、20000から70000未満の乱数値、これで原点から20000までの範囲には星が置かれないことになる。4)この長さを 正規化された3Dベクトルにスカラー倍し、星一つの座標とする。
数字が直値でアレかもしれないけど、なにかの参考になれば幸い。