サクッと作ります。
もくじ
0.three.jsとは
1.目標
2.概要
3.createWorld
4.addParticles
5.addSphere
6.render
7.おわり
0.three.jsとは
WebGLの使い勝手をよくしたjsライブラリです。
- three.js wiki
http://www56.atwiki.jp/threejs/
そもそものWebGL各ブラウザサポート状況はこちらを参照してください。 - Can I use...
http://caniuse.com/#feat=webgl
1.目標
http://codepen.io/Xanmia/pen/fDHnc
これをforkして作った
http://codepen.io/uriuriuriu/pen/hiJnj
こいつを今日は目指します。
2.概要
まずは全体の流れを説明します。
codepenで動くものとソースが見れますので↑のリンクをどうぞ。
下記がざっくりしたものです。
var _scene, _camera, _light, _renderer, _windowHalfX, _windowHalfY;
var _mouseX = _mouseY = 0;
// init function
var createWorld = function(){
// three.jsの環境構築を行います。
}
// add element
var addParticles = function(pre_sizeAr){
// 無限に流れる星を作ります。for文ってほんとすごい。
}
var addSphere = function(){
// 地球の天地創造メソッドです。
};
var randomColor = function(){
// ランダムカラーを返す。星の色用です。
}
// render task
var render = function() {
// 毎フレームのタスクです。マウス座標からカメラ位置の設定&星を動かす。
}
// main
createWorld();
addParticles([1,1,1,2,2,3]);
addSphere();
render();
// event
window.addEventListener('resize', onWindowResize, false);
function onWindowResize(){
// windowサイズ変更した際の描写範囲調整です。まあ、おまけです。
}
window.addEventListener('mousemove', onWindowMouseMove, false);
function onWindowMouseMove(event){
// マウスイベントからその座標をグローバル変数に入れます。
}
になります。思ったより難しいことはしていません。
では次項から
createWorld();
addParticles([1,1,1,2,2,3]);
addSphere();
render();
の中身について一つづつ説明に入ります。
3.createWorld
では早速環境構築しましょう。
// init function
var createWorld = function(){
//threejs - demo - http://threejs.org/examples/webgl_particles_random.html
var ele = document.createElement('div');
document.body.appendChild(ele);
_windowHalfX = window.innerWidth / 2;
_windowHalfY = window.innerHeight / 2;
_camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 3000)
_camera.position.z = 25000; // first view position, going zoom
_scene = new THREE.Scene();
_scene.fog = new THREE.FogExp2(0x600090, 0.0009); //color of fog - objects are all color
_light = new THREE.DirectionalLight(0xffffff, 1);
_scene.add(_light);
_renderer = new THREE.WebGLRenderer();
_renderer.setSize(window.innerWidth, window.innerHeight);
ele.appendChild(_renderer.domElement);
_renderer.render(_scene, _camera); // ここで描写
}
three.jsはcamera, scene, rendererとlight, mesh, particle等で成り立っています。
sceneにmeshやparticleなど表示したいものやlightを入れて世界を作り、
renderer.render(scene, camera)で描写させます。
ですのでDOMに追加するのはrendererオブジェクトのみです。
これではじめに作るものは完了です。
ここは何つくろうとしてもそんなに変わらないはず!きっと。
・_sceneに設定しているfogは遠くにある物体をその設定した色で塗っていきます。
詳しくは下記ページが参考になりました。
- 多彩な表現力のWebGLを扱いやすくする「Three.js」 (4/5)
http://www.atmarkit.co.jp/ait/articles/1210/04/news142_4.html
・lightはDirectionalLightを入れて、全部で4種類あります。
4.addParticles
次は_sceneに入れる無数の星達です。
var addParticles = function(pre_sizeAr){
var geometry = new THREE.Geometry();
for (i = 0; i < 5000; i ++){
var vertex = new THREE.Vector3(Math.random() * 2000 - 1000,
Math.random() * 2000 - 1000,
Math.random() * 2000 - 1000);
geometry.vertices.push(vertex);
}
for(var i = 0; i < pre_sizeAr.length; i++){
var material = new THREE.ParticleBasicMaterial({
color: randomColor(),
size: pre_sizeAr[i],
map: THREE.ImageUtils.loadTexture("https://dl.dropboxusercontent.com/u/90712768/img/point.png"),
blending: THREE.AdditiveBlending,
transparent: true
});
var particles = new THREE.ParticleSystem(geometry, material);
particles.rotation.set(Math.random() * 6,
Math.random() * 6,
Math.random() * 6);
_scene.add(particles);
}
}
...
// 使用部分
addParticles([1,1,1,2,2,3]);
particles = new THREE.ParticleSystem(geometry, material);で表示させたい星を作るのですが、
geometryは座標、materialは各点のサイズやtextureです。
ここではランダムな座標を5000個入れたgeometryを作り、
引数の数字サイズ&ランダムカラーでmaterialを作り、
引数あるだけparticleを作成しています。
ここでのポイントはgeometryの流用とparticle.rotationのrandom()です。
これがあることによって5000個の星を6つ回転させて置いてるだけなのに、
後述するrenderで簡単で自然に星が動くようになります。
ちまちま全部動かす必要なかったんだ!!!
fork元で死ぬほど参考になった部分でした。みんな頭いいなあ。
・materialで使用しているmapのtextureは白色の中央から透明になっている円です。
var material = new THREE.ParticleBasicMaterial( { size: 4 });
のようにtextureを使わずにも作れますが、四角いドットがガツンとが表示されてしまいます。
5.addSphere
そうです。そろそろ地球を作ります。
var addSphere = function(){
var cubeR = 20;
var geometry = new THREE.SphereGeometry(cubeR, cubeR, cubeR);
// ■ Google、地球から雲を消す──Googleマップ/Earthが“雲一つない”衛星写真に - ITmedia ニュース
// http://www.itmedia.co.jp/news/articles/1306/27/news132.html
var material = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture("https://dl.dropboxusercontent.com/u/90712768/img/earth.jpg"),
fog:false
});
var mesh = new THREE.Mesh(geometry, material);
_scene.add(mesh);
};
ここはそんなに説明するところが無いはず。
コード読んで貰えれば雰囲気でわかるはず。
なのでさっと説明、
materialでTHREE.MeshLambertMaterial()が出てきました。
particleで使用したTHREE.ParticleBasicMaterial()の他にもmaterialはたくさんあります。
MeshLambertMaterial()はランバート反射表現が可能になるマテリアルです。単語が超かっこいい。
そいつに地球の四角い画像をmappingしているわけです。
これで簡単に地球が出来てしまいました。
6.render
最後!毎フレームタスクです。
// render task
var render = function() {
requestAnimationFrame(render);
_camera.position.x += (_mouseX - _camera.position.x) * .05;
_camera.position.y += (- _mouseY - _camera.position.y) * .05;
var posZ = _camera.position.z + (- _mouseY - _camera.position.z) * .05;
if(posZ < 80) posZ = 80;
_camera.position.z = posZ;
_light.position.set(_camera.position.x,
_camera.position.y * 2.5,
_camera.position.z);
_camera.lookAt(new THREE.Vector3(0,0,0));
for (var i = 0; i < _scene.children.length; i++){
var object = _scene.children[i];
if (object instanceof THREE.ParticleSystem){
object.rotation.y += .0005;
}
}
_renderer.render(_scene, _camera);
}
やってることは
・camera座標をマウス座標で動かし、近付き過ぎないようにもし、地球がある(0,0,0)座標を向かせてます。
・lightをcameraとちょっとずらした位置に置いてます。
・for文はparticleでお話したところの続きで、
ここで5000個入ってるparticleをちょっとずつy回転させてます。
ほんのちょっとですがparticle自体が大きいのでこれでも動きすぎかもしれません。
・requestAnimationFrame は setTimeoutと同じような毎フレームアニメーション設定です。
three.js特有のメソッドではありません。違いは下記ページが参考になりました。
- requestAnimationFrame でフレームと再描画更新を制御する
http://yomotsu.net/blog/2013/01/05/fps.html
7.おわり
おわり!!
あと、その場で試せるのでcodepen楽しいですよ!!
最後に一応、2013/12/19現在の全js載せておきます。
http://codepen.io/uriuriuriu/pen/hiJnj
var _scene, _camera, _light, _renderer, _windowHalfX, _windowHalfY;
var _mouseX = _mouseY = 0;
// init function
var createWorld = function(){
//threejs - demo - http://threejs.org/examples/webgl_particles_random.html
var ele = document.createElement('div');
document.body.appendChild(ele);
_windowHalfX = window.innerWidth / 2;
_windowHalfY = window.innerHeight / 2;
_camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 3000)
_camera.position.z = 250; // first view position, going zoom
_scene = new THREE.Scene();
_scene.fog = new THREE.FogExp2(0x600090, 0.0009); //color of fog - objects are all color
_light = new THREE.DirectionalLight(0xffffff, 1);
_scene.add(_light);
_renderer = new THREE.WebGLRenderer();
_renderer.setSize(window.innerWidth, window.innerHeight);
ele.appendChild(_renderer.domElement);
_renderer.render(_scene, _camera);
}
// add element
var addParticles = function(pre_sizeAr){
var geometry = new THREE.Geometry();
for (i = 0; i < 5000; i ++){
var vertex = new THREE.Vector3(Math.random() * 2000 - 1000,
Math.random() * 2000 - 1000,
Math.random() * 2000 - 1000);
geometry.vertices.push(vertex);
}
for(var i = 0; i < pre_sizeAr.length; i++){
var material = new THREE.ParticleBasicMaterial({
color: randomColor(),
size: pre_sizeAr[i],
map: THREE.ImageUtils.loadTexture("https://dl.dropboxusercontent.com/u/90712768/img/point.png"),
blending: THREE.AdditiveBlending,
transparent: true
});
var particles = new THREE.ParticleSystem(geometry, material);
particles.rotation.set(Math.random() * 6,
Math.random() * 6,
Math.random() * 6);
_scene.add(particles);
}
}
var addSphere = function(){
var cubeR = 20;
var geometry = new THREE.SphereGeometry(cubeR, cubeR, cubeR);
// ■ Google、地球から雲を消す──Googleマップ/Earthが“雲一つない”衛星写真に - ITmedia ニュース
// http://www.itmedia.co.jp/news/articles/1306/27/news132.html
var material = new THREE.MeshLambertMaterial({
map: THREE.ImageUtils.loadTexture("https://dl.dropboxusercontent.com/u/90712768/img/earth.jpg"),
fog:false
});
var mesh = new THREE.Mesh(geometry, material);
_scene.add(mesh);
};
var randomColor = function(){
var color = Math.floor(Math.random() * 0xFFFFFF + 0xAAAAAA).toString(16); //#RRGGBBを取得
color = ("000000" + color).slice(-6);
color = "#" + color;
return color;
}
// render task
var render = function() {
requestAnimationFrame(render);
_camera.position.x += (_mouseX - _camera.position.x) * .05;
_camera.position.y += (- _mouseY - _camera.position.y) * .05;
var posZ = _camera.position.z + (- _mouseY - _camera.position.z) * .05;
if(posZ < 80) posZ = 80;
_camera.position.z = posZ;
_light.position.set(_camera.position.x,
_camera.position.y * 2.5,
_camera.position.z);
_camera.lookAt(new THREE.Vector3(0,0,0));
for (var i = 0; i < _scene.children.length; i++){
var object = _scene.children[i];
if (object instanceof THREE.ParticleSystem){
object.rotation.y += .0005;
}
}
_renderer.render(_scene, _camera);
}
// main
createWorld();
addParticles([1,1,1,2,2,3]);
addSphere();
render();
// event
window.addEventListener('resize', onWindowResize, false);
function onWindowResize(){
_windowHalfX = window.innerWidth / 2;
_windowHalfY = window.innerHeight / 2;
_camera.aspect = window.innerWidth / window.innerHeight;
_camera.updateProjectionMatrix();
_renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('mousemove', onWindowMouseMove, false);
function onWindowMouseMove(event){
_mouseX = event.clientX - _windowHalfX;
_mouseY = event.clientY - _windowHalfY;
}