LoginSignup
37
37

More than 5 years have passed since last update.

サクッと作ります。

もくじ
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.目標

img1.png
http://codepen.io/Xanmia/pen/fDHnc
これをforkして作った

img2.png
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

では早速環境構築しましょう。

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オブジェクトのみです。

これではじめに作るものは完了です。
ここは何つくろうとしてもそんなに変わらないはず!きっと。

tips
・_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に入れる無数の星達です。

addParticles([1,1,1,2,2,3])
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元で死ぬほど参考になった部分でした。みんな頭いいなあ。

tips
・materialで使用しているmapのtextureは白色の中央から透明になっている円です。
var material = new THREE.ParticleBasicMaterial( { size: 4 });
のようにtextureを使わずにも作れますが、四角いドットがガツンとが表示されてしまいます。

5.addSphere

そうです。そろそろ地球を作ります。

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()
// 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自体が大きいのでこれでも動きすぎかもしれません。

tips
・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;
}
37
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
37