【three.js】3Dの太陽系をつくってみる

three.jsで3Dの太陽系をつくってみました。

惑星をクリックすると、回答がでます。

 

サンプル

サンプルページ

 

HTML


<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>太陽系</title>
</head>
<body>
  <div id="app">
    <div class="question">
      <h1>地球はどれですか?</h1>
    </div>
    <div class="myCanvass">
      <canvas id="myCanvas"></canvas>
      <p id="kaitou">画像の中の地球をクリックしてください。</p>
    </div>
    <!-- three.jsを読み込む -->
    <script src="https://unpkg.com/three@0.139.2/build/three.min.js"></script>
    <script src="https://unpkg.com/three@0.131.3/examples/js/controls/OrbitControls.js"></script>
  </div>
</body>
</html>

 

CSS


* {
    margin: 0;
    padding: 0;
    list-style: none;
    box-sizing: border-box;
    text-decoration: none
}
.pointer {
    cursor: pointer
}
.myCanvass {
    position: relative
}
.myCanvass #kaitou {
    position: absolute;
    bottom: 0;
    left: 0;
    padding: 25px;
    font-size: 24px;
    color: #fff;
    z-index: 100
}
 

JavaScript


/* IE11に対応させるためes5にて書いてます。 */
"use strict";
var width,
  height,
  renderer,
  camera,
  canvas,
  controls,
  revolution_speed = 0,
  meshList = [],
  earthElement,
  renderer,
  planet = -1,
  planet_stop = 0,
  box = [],
  texture,
  loader,
  geometry,
  material,
  stars,
  satellite;
// リサイズイベント発生時に実行
onResize();
window.addEventListener("resize", onResize);
// 初期化のために実行
window.addEventListener("DOMContentLoaded", init);
//主要関数
function init() {
  var raycaster = new THREE.Raycaster();
  var mouse = new THREE.Vector2(); // canvas 要素の参照を取得する
  // シーンを作成
  var scene = new THREE.Scene();
  // カメラコントローラーを作成
  controls = new THREE.OrbitControls(camera, earthElement);
  //惑星の関数
  function Planet(
    scr,
    planetPosition,
    scale,
    tex,
    satellite,
    satellite_src,
    satellite_position,
    satellite_scale
  ) {
    //惑星
    geometry = new THREE.SphereGeometry(scale, scale, scale);
    loader = new THREE.TextureLoader();
    texture = loader.load(scr);
    if (tex) {
      material = new THREE.MeshBasicMaterial({
        color: 0xffffff,
        side: THREE.DoubleSide,
        map: texture,
      });
    } else {
      material = new THREE.MeshLambertMaterial({
        map: texture,
      });
    }
    box[0] = new THREE.Mesh(geometry, material);
    box[0].receiveShadow = true;
    box[0].position.x = planetPosition;
    scene.add(box[0]);
    meshList.push(box[0]);
    //衛星
    if (satellite) {
      geometry = new THREE.SphereGeometry(
        satellite_scale,
        satellite_scale,
        satellite_scale
      );
      loader = new THREE.TextureLoader();
      texture = loader.load(satellite_src);
      material = new THREE.MeshLambertMaterial({
        map: texture,
      });
      box[1] = new THREE.Mesh(geometry, material);
      box[1].receiveShadow = true;
      box[1].position.x = satellite_position;
      scene.add(box[1]);
      meshList.push(box[1]);
    }
  }
  Planet(baseUrl + "/img/sunmap.jpg", 0, 150, true);
  Planet(
    baseUrl + "/img/land_ocean_ice_cloud_2048.jpg",
    1000,
    80,
    false,
    true,
    baseUrl + "/img/moon.jpg",
    1500,
    50
  );
  Planet(baseUrl + "/img/image_3337_1e-Jupiter-Map.jpg", 2000, 120, false);
  Planet(baseUrl + "/img/saturnmap.jpg", 3000, 80, false);
  var boxs = new THREE.Group();
  //星
  star(1000, 1);
  function star(star_count, star_size) {
    for (var i = 0; i < star_count; i++) {
      geometry = new THREE.SphereGeometry(star_size, star_size, star_size);
      material = new THREE.MeshBasicMaterial({
        color: 0xffffff,
      });
      stars = new THREE.Mesh(geometry, material);
      stars.position.x = Math.random() * 5000 - 3000;
      stars.position.z = Math.random() * 5000 - 3000;
      stars.position.y = Math.random() * 5000 - 3000;
      scene.add(stars);
    }
  }
  // 平行光源
  var light = new THREE.DirectionalLight(0xffffff);
  light.intensity = 2; // 光の強さを倍に
  light.position.set(1, 100, 1); // シーンに追加
  // scene.add(light);
  var light2 = new THREE.PointLight(0xffffff, 1.5, 0, 0);
  light2.position.set(300, 300, 300);
  light.castShadow = true;
  scene.add(light2);
  // 初回実行
  tick();
  earthElement.addEventListener("mousemove", handleMouseMove);
  function handleMouseMove(event) {
    var element = event.currentTarget;
    // canvas要素上のXY座標
    var x = event.clientX - element.offsetLeft;
    var y =
      event.clientY -
      element.offsetTop -
      document.querySelector(".question").getBoundingClientRect().height;
    // canvas要素の幅・高さ
    var w = element.offsetWidth;
    var h = element.offsetHeight;
    // -1〜+1の範囲で現在のマウス座標を登録する
    mouse.x = (x / w) * 2 - 1;
    mouse.y = -(y / h) * 2 + 1;
  }
  //アニメーションの関数
  function tick() {
    var intersects = raycaster.intersectObjects(meshList);
    requestAnimationFrame(tick);
    revolution_speed += 0.001;
    // カメラコントローラーを更新
    controls.update();
    //revolutionHover();
    meshList.forEach(function (box) {
      box.rotation.y += 0.01;
    });
    for (var i = 0; i < meshList.length; i++) {
      meshList[i].position.x =
        Math.cos(revolution_speed + i * 10 * -10) *
        (i * 50000) *
        (Math.PI / 180);
      meshList[i].position.z =
        Math.sin(revolution_speed + i * 10 * -10) *
        (i * 50000) *
        (Math.PI / 180);
      meshList[i].rotation.y += 0.001;
    }
    // レンダリング
    renderer.render(scene, camera);
    meshList.forEach(function (box) {
      box.material.transparent = true;
      if (
        intersects.length > 0 &&
        box === intersects[0].object //&&
        //meshList.indexOf(box) > 0
      ) {
        // 透明にする
        box.material.opacity = 0.6;
        earthElement.classList.add("pointer");
        //if(meshList.indexOf(intersects[0]))
        planet_stop = 1;
      } else {
        // それ以外は元の色にする
        earthElement.classList.remove("pointer");
        box.material.opacity = 1;
      }
      if (planet_stop == 1) {
        planet = meshList.indexOf(box);
        planet_stop = 0;
      }
      console.log("test");
    });
    //camera.lookAt(meshList[2].position);
    raycaster.setFromCamera(mouse, camera);
  }
  earthElement.addEventListener("click", function () {
    var kaitou = document.querySelector("#kaitou");
    if (planet == 0) {
      kaitou.innerHTML = "それは太陽です";
    } else if (planet == 1) {
      kaitou.innerHTML = "正解";
    } else {
      kaitou.innerHTML = "不正解";
    }
    planet == -1;
  });
}
function onResize() {
  // カメラを作成
  camera = new THREE.PerspectiveCamera(45, width / height, 1, 100000);
  camera.position.set(0, 0, +2500);
  // レンダラーを作成
  earthElement = document.querySelector("#myCanvas");
  renderer = new THREE.WebGLRenderer({
    canvas: earthElement,
  });
  // サイズを取得
  width = window.innerWidth;
  height = window.innerHeight - 100;
  // レンダラーのサイズを調整する
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width, height);
  //レンダラーの影を有効
  renderer.shadowMap.enabled = true;
  // カメラのアスペクト比を正す
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
}

投稿者 PASOMEN

関連投稿