こんにちは、はっちゃんです。
最近筋トレにハマっています。
前回までの記事では、three.jsを使って宇宙と地球を作りました。 three.jsの基本をおさらいしてみよう!〜基礎の基礎編〜 three.jsで地球を作ってみよう!〜基礎の基礎編2〜
今回は、上記の記事で紹介した技術を応用し、「太陽系」を作ってみたいと思います。
テクスチャの読み込み
まずは星の数分の展開図を用意して、TextureLoaderで読み込ませる必要があります。
展開図は下記からダウンロードできます。
ですが、TextureLoaderのloadメソッドは、ひとつずつしか画像を読み込めません。
前回作った地球は、地表・雲・宇宙空間でそれぞれ別のテクスチャを使用しています。なので、loadメソッドを3回記述する必要がありました。
しかし、今回は使用する画像が多いため、画像をまとめて読み込んだあとに物体を生成させます。
テクスチャの読み込みに使用するツール「Preload.js」
使用するのは、CreateJSのライブラリモジュールのひとつ、「PreloadJS」です。
PreloadJSは、画像・音声・JSONなどの読み込み処理ができ、読み込み率を、イベントで監視して取得することができます。
その読み込み率を演出用のJavaScriptに渡してあげることで、読み込み状況に合わせた演出を実現できます。
使用方法
CDNで用意したJSを読み込みます。
その後JS側でキューを作成し、loadQueue.on(‘complete’)内でロードしたい内容を記述します。最後にloadQueue.loadManifestを実行すれば、読み込み開始です。
▼HTML
<script src="https://code.createjs.com/preloadjs-0.6.2.min.js"></script>
▼Javascript
// 読み込むテクスチャーリスト
let manifest = [
{ id: 'sun', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/sun.jpg'}, // 水星
{ id: 'mercury', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/mercury.jpg'}, // 水星
{ id: 'venus', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/venus.jpg'}, // 金星
{ id: 'earth', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/earth.png'}, // 地球
{ id: 'crowd', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/crowd.png'}, // 雲
{ id: 'mars', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/mars.jpg'}, // 火星
{ id: 'jupiter', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/jupiter.jpg'}, // 木星
{ id: 'saturn', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/saturn.jpg'}, // 土星
{ id: 'saturn-ring', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/saturn-ring.jpg'}, // 土星の輪
{ id: 'ouranos', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/ouranos.jpg'}, // 天王星
{ id: 'ouranos-ring', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/ouranos-ring.jpg'}, // 天王星の輪
{ id: 'pluto', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/pluto.jpg'}, // 冥王星
{ id: 'neptune', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/neptune.jpg'}, // 海王星
{ id: 'universe', src: 'https://raw.githubusercontent.com/82mou/sandbox/master/img/universe.jpg'} // 宇宙空間
];
// ロードキューを作成
let loadQueue = new createjs.LoadQueue();
// ロード完了を監視
loadQueue.on('complete', function() {
// loadQueueからロードした画像データを取得
let sunImg = loadQueue.getResult('sun');
let mercuryImg = loadQueue.getResult('mercury');
let venusImg = loadQueue.getResult('venus');
let earthImg = loadQueue.getResult('earth');
let crowdImg = loadQueue.getResult('crowd');
let marsImg = loadQueue.getResult('mars');
let jupiterImg = loadQueue.getResult('jupiter');
let saturnImg = loadQueue.getResult('saturn');
let saturnRingImg = loadQueue.getResult('saturn-ring');
let ouranosImg = loadQueue.getResult('ouranos');
let ouranosRingImg = loadQueue.getResult('ouranos-ring');
let plutoImg = loadQueue.getResult('pluto');
let neptuneImg = loadQueue.getResult('neptune');
let universeImg = loadQueue.getResult('universe');
// three.jsで使えるテクスチャーに変換
let textureSun = new THREE.Texture(sunImg);
let textureMercury = new THREE.Texture(mercuryImg);
let textureVenus = new THREE.Texture(venusImg);
let textureEarth = new THREE.Texture(earthImg);
let textureCrowd = new THREE.Texture(crowdImg);
let textureMars = new THREE.Texture(marsImg);
let textureJupiter = new THREE.Texture(jupiterImg);
let textureSaturn = new THREE.Texture(saturnImg);
let textureSaturnRing = new THREE.Texture(saturnRingImg);
let textureOuranos = new THREE.Texture(ouranosImg);
let textureOuranosRing = new THREE.Texture(ouranosRingImg);
let texturePluto = new THREE.Texture(plutoImg);
let textureNeptune = new THREE.Texture(neptuneImg);
let textureUniverse = new THREE.Texture(universeImg);
// 【重要】更新を許可
textureSun.needsUpdate = true;
textureMercury.needsUpdate = true;
textureVenus.needsUpdate = true;
textureEarth.needsUpdate = true;
textureCrowd.needsUpdate = true;
textureMars.needsUpdate = true;
textureJupiter.needsUpdate = true;
textureSaturn.needsUpdate = true;
textureSaturnRing.needsUpdate = true;
textureOuranos.needsUpdate = true;
textureOuranosRing.needsUpdate = true;
texturePluto.needsUpdate = true;
textureNeptune.needsUpdate = true;
textureUniverse.needsUpdate = true;
});
// テクスチャーのロードを開始
loadQueue.loadManifest(manifest);
※こちらの記事を参考にさせていただきました。ありがとうございます!
https://ics.media/entry/5239
http://qiita.com/sawa-zen/items/cba55a23411753f1353e
惑星の工房となる関数をつくる
太陽系のオブジェクトを作っていきます。
宇宙空間、及び、太陽系すべては球体でできているので、ひとつの関数に引数を渡してやるだけで作れます。
今回、下記のように引数を命名しました。
- texture・・・textureデータ
- radius・・・半径
- widthSegments・・・経度分割数。多いほど経線が細かくなる。
- heightSegments・・・緯度分割数。緯線が細かくなる。
- planetName・・・一部同じ関数を使い回せない惑星があるので、if文で判定するための文字列。
sun = planetFactory(textureSun, 50, 20, 20, 'isSun');
mercury = planetFactory(textureMercury, 5, 20, 20);
venus = planetFactory(textureVenus,10 , 20, 20);
earth = planetFactory(textureEarth,13 , 20, 20, 'isEarth');
mars = planetFactory(textureMars,7 , 20, 20);
jupiter = planetFactory(textureJupiter, 30, 20, 20);
saturn = planetFactory(textureSaturn, 18, 20, 20);
ouranos = planetFactory(textureOuranos,20 , 20, 20);
pluto = planetFactory(texturePluto,18 , 20, 20);
neptune = planetFactory(textureNeptune, 17, 20, 20);
universe = planetFactory(textureUniverse, 10000, 20, 20, 'isUniverse');
function planetFactory (texture, radius, widthSegments, heightSegments, planetName) {
let sphere,
sphereEarth;
if(planetName === 'isSun') {
sphere = new THREE.Mesh(
new THREE.SphereGeometry(radius, widthSegments, heightSegments), // 形状
new THREE.MeshBasicMaterial({ // 材質
map: texture,
side: THREE.DoubleSide // 裏からも見えるようにする
})
);
sphere.position.set(0, 0, 0);
} else if(planetName === 'isEarth') {
sphere = new THREE.Group();
sphereEarth = new THREE.Mesh(
new THREE.SphereGeometry(13 , 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture
})
);
crowd = new THREE.Mesh(
new THREE.SphereGeometry(14, 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: textureCrowd,
transparent: true,
side: THREE.DoubleSide // 裏からも見えるようにする
})
);
sphere.add(sphereEarth);
sphere.add(crowd);
sphere.position.set(
Math.random() * 500 - 250,
Math.random() * 500 - 250,
Math.random() * 500 - 250
);
} else if(planetName === 'isUniverse') {
sphere = new THREE.Mesh(
new THREE.SphereGeometry(radius, widthSegments, heightSegments), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture,
side: THREE.DoubleSide // 裏からも見えるようにする
})
);
sphere.position.set(0, 0, 0);
} else {
sphere = new THREE.Mesh(
new THREE.SphereGeometry(radius, widthSegments, heightSegments), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture
})
);
sphere.position.set(
Math.random() * 500 - 250,
Math.random() * 500 - 250,
Math.random() * 500 - 250
);
}
scene.add(sphere);
return sphere;
}
長いので、もう少し細かくコードを解説していきます。
オブジェクトをグルーピングする
地球に関してですが、地表と雲に分けてオブジェクトを作っています。これらはひとつのオブジェクトにまとめてしまいましょう。
まとめるには、THREE.Groupクラスを使用します。
sphere = new THREE.Group();
sphereEarth = new THREE.Mesh(
new THREE.SphereGeometry(13 , 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture
})
);
crowd = new THREE.Mesh(
new THREE.SphereGeometry(14, 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: textureCrowd,
transparent: true,
side: THREE.DoubleSide // 裏からも見えるようにする
})
);
sphere.add(sphereEarth);
sphere.add(crowd);
ランダムにおいてみる
正式な位置は難しいので、ランダムにおいてそれっぽくしてみましょう。
太陽と宇宙空間以外をランダムに配置します。なお、太陽はMeshBasicMaterialを使用して光の影響を受けないようにし、光源と同じ位置に配置しています。
sphere.position.set(
Math.random() * 500 - 250,
Math.random() * 500 - 250,
Math.random() * 500 - 250
);
完成
完成したコードがこちらです。
大きめに作っているので、codepen本家サイトで見るのをおすすめいたします。
See the Pen March Blog Solar System by k_hatsushi (@hatsushi_kazuya) on CodePen.
まとめ
テクスチャーを変えて球体を複製するだけで、だいぶ太陽系っぽいものが作れちゃいました!
ここまで理解すれば、あとは工夫次第でさまざまな表現が可能になると思います。是非試してみて下さい!
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。