[three.js] 地球儀 (4)

three.jsを使って、地球儀を作るよ。 :boy:

threejs_earth4

WebGLRendererで描画するよ。
SphereGeometry, MeshPhongMaterial, Mesh を使ってみたよ。 :bouzu:

さらに、DirectionalLight も使ったよ。 :cake:
んでもって、TextureLoader, CubeTextureLoader, Texture も使ったりして。 :beer:

index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0">
<title>地球儀 (4) | three.js</title>
<script src="js/three.min.js"></script>
<script src="js/detector.js"></script>
<script src="js/stats.min.js"></script>
<style type="text/css">
  html { overflow:hidden; }
  body { margin:0; padding:0; }
</style>
</head>
<body style="background-color:#000000">
<script>
var scene, camera, renderer, stats;
var texture, ctexture, ntexture;
var container, content, earth, clouds;
var light;
var textureCube;
var radius = 500;
var inclination = 23.4;
var sun = {x: 0.649, y: 0.281, z: -0.707};
var angle = - 90;
var radian = Math.PI/180;
var center = new THREE.Object3D();
var filePath = "assets/world.jpg";
var cloudsPath = "assets/world_clouds.png";
var normalsPath = "assets/world_normals.jpg";

window.onload = init;

function init() {
  if (!Detector.webgl) Detector.addGetWebGLMessage();

  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.1, 1000);
  camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 0.1, 10000);
  scene.add(camera);
  light = new THREE.DirectionalLight(0xFFFFFF);
  scene.add(light);
  renderer = new THREE.WebGLRenderer({antialias: true});
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);
  setup();
  //background();
  //initialize();
  var loader = new THREE.TextureLoader();
  loader.load(filePath, loaded);
  loader.load(cloudsPath, loaded);
  loader.load(normalsPath, loaded);
  var basePath = "assets/environments/space/";
  var textures = [
    basePath + "right.png", basePath + "left.png",
    basePath + "top.png", basePath + "bottom.png",
    basePath + "front.png", basePath + "back.png"
  ];
  var cloader = new THREE.CubeTextureLoader();
  cloader.load(textures, loaded);

  stats = new Stats();
  stats.setMode(0);
  stats.domElement.style.position = "fixed";
  stats.domElement.style.right = "0px";
  stats.domElement.style.top = "0px";
  document.body.appendChild(stats.domElement);

  //render();
  window.addEventListener("resize", resize, false);
}
function setup() {
  //renderer.setClearColor(0x000000, 1);
  camera.position.z = -radius;
  light.position.set(sun.x, sun.y, sun.z).normalize();
  renderer.autoClear = false;

  container = new THREE.Group();
  scene.add(container);
  container.rotation.z = inclination*radian;
  content = new THREE.Group();
  container.add(content);
}
function loaded(data) {
  if (!data.image.length) {
    switch (data.image.attributes.src.nodeValue) {
      case filePath :
        texture = data;
        break;
      case cloudsPath :
        ctexture = data;
        break;
      case normalsPath :
        ntexture = data;
        break;
    }
  } else {
    textureCube = data;
  }
  if (texture && ctexture && ntexture && textureCube) {
    background();
    initialize();
    render();
  }
}
function background() {
  var geometry = new THREE.BoxGeometry(10000, 10000, 10000);
  var shader = THREE.ShaderLib["cube"];
  var uniforms = THREE.UniformsUtils.clone(shader.uniforms);
  uniforms[ "tCube"].value = textureCube;
  var material = new THREE.ShaderMaterial({vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, uniforms: uniforms, depthWrite: false, side: THREE.BackSide});
  var sky = new THREE.Mesh(geometry, material);
  scene.add(sky);
}
function initialize() {
  var geometry = new THREE.SphereGeometry(160, 40, 20);
  var material = new THREE.MeshPhongMaterial();
  material.map = texture;
  material.color.set(0xFFFFFF);
  material.emissiveMap = texture;
  material.emissive.set(0x333333);
  material.normalMap = ntexture;
  material.normalScale = new THREE.Vector2(1, 1);
  material.shininess = 0;
  earth = new THREE.Mesh(geometry, material);
  content.add(earth);

  var cgeometry = new THREE.SphereGeometry(164, 40, 20);
  var cmaterial = new THREE.MeshLambertMaterial();
  cmaterial.map = ctexture;
  cmaterial.color.set(0xFFFFFF);
  cmaterial.transparent = true;
  clouds = new THREE.Mesh(cgeometry, cmaterial);
  content.add(clouds);
}
function render() {
  requestAnimationFrame(render);

  content.rotation.y += 0.5*radian;
  clouds.rotation.y += 0.2*radian;

  angle -= 0.25;
  camera.position.x = radius*Math.cos(angle*radian);
  camera.position.z = radius*Math.sin(angle*radian);
  camera.lookAt(center.position);

  renderer.render(scene, camera);

  stats.update();
}
function resize(event) {
  camera.aspect = window.innerWidth/window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}
</script>
</body>
</html>


地球表面の画像(1024*512)をJPGで、雲の画像(1024*512)を透過PNGで用意。
さらに、凹凸用の画像(1024*512)をJPGで用意。
:check: 「Planet Earth Texture Maps」の画像を使わせていただきました。

SkyBox用の画像は、こんな感じ。

awayjs_earth_skybox


[修正] (14/12/09 Tue 13:00)
背景の設定を変更。カメラの far の値も変更。
[修正] (14/12/27 Sat 21:23)
HTML記述を変更。
[修正] (18/12/28 Fri 17:58)
three.js r99 にバージョンアップ。material の設定方法を変更。ImageUtils.loadTexture() を TextureLoader() に変更。ImageUtils.loadTextureCube() を CubeTextureLoader() に変更。