three.jsの基礎中の基礎を学んでみました

ふわふわ知識の私がthree.jsの調査を!とのことで、分からないなりに学んでみたことをまとめます。

そもそもthree.jsって?

javascriptで3DCGを動かすことができる代物です。
WebGL(Web Graphics Library)の一つで、互換性のあるWebブラウザでグラフィックスをレンダリングするためのもの…
とのこと。なにはともあれ触ってみましょう。

three.jsのサンプル

three.jsのページには素敵なサンプルが並んでいます。
https://threejs.org/
これを見ると、ポリゴンがふよふよ動いたり、幾何学的な模様が動いていたりしています。
こういったグラフィックスをページで実装するためのJSのようです。

three.jsでの基本パーツ

three.jsを使うときには、頭の中で撮影現場を思い浮かべます。
撮影現場にはまず、台座の上に被写体があります。
そして被写体を撮影するのに光源とカメラが必要です。

three.jsでは台座をScene、被写体をmesh、光源をlight、カメラをcameraとしてそれぞれ定義しています。
独特なのは被写体(mesh)に形(geometory)と表面素材(material)を持つことです。
惑星を表現したい場合は、球体のgeometoryを作って、そこに惑星表面の画像materialを貼り付けることで実現します。

Scene

three.jsではなにはともあれ、台座を真っ先に作ります。
台座を起点としてmesh、light、cameraをセッティングしていきます。

var scene = new THREE.Scene();

Mesh

立体そのものなので、幅や高さ、角度以外にも奥行きを設定することもできます。

// 立方体
new THREE.BoxGeometry(幅, 高さ, 奥行き)

// 球体
new THREE.SphereGeometry(半径, 経度分割数, 緯度分割数, 開始経度, 経線中心角, 開始緯度, 緯線中心角)

// 円柱
new THREE.CylinderGeometry(上面の半径, 底面の半径, 高さ,円周の分割数, 高さの分割数, フタをしない:true,フタをする:false

この他にも、平面や円、円盤なども用意されています。
球体を使って惑星を、円柱を使ってプリンを…という子供じみたことしか想像できないですが、ポリゴンなどもこれを使って作っているようです。

Material

要素の表面を作っていきます。一番簡単なのは色をつけることです。

new THREE.MeshLambertMaterial({color: 0xから始まる16進数})

0xから始まる16進数ということなので、例えば白色(#ffffff)の場合は0xffffffに変換して指定することが必要です。

Light

実は、ここまでのコードを記載しても、画面が真っ黒のままなのです。
現実と同じように光がないとものが見えないのがthree.jsの特徴です。

new THREE.DirectionalLight(0xから始まる16進数, 光の強さ)

DirectionalLight(平面光源)を例にしていますが、PointLight(点光源)、AmbientLight(環境自然光)を選択することもできます。
AmbientLightは自然光と同じように乱反射も考慮しているので、より滑らかでリアリティのあるグラフィックが表現できます。

Camera

ここまでで物体を台座に配置し、ライトで照らすことができました。
実際に描画されるのはカメラごしに物体をみた画像です。

// カメラの設定
new THREE.PerspectiveCamera(画角, アスペクト比, ニアークリップ, ファークリップ)

// カメラの向き
camera.lookAt(scene.position);

Lender

最後に、three.jsで作成したグラフィックをHTMLに紐付けます。

new THREE.WebGLRenderer({ antialias: true})

全部組み合わせてみる

ここまでできたこと+αで自転する地球を描いて見るとこんな感じです。

HTML

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="./three.js-master/build/three.js"></script>
    <script src="./three.js-master/examples/js/controls/OrbitControls.js"></script>
  </head>
  <body>
    <div id="stage"></div>
    <script src="./earth.js"></script>
  </body>
</html>

JS

(function() {
  var scene;
  var sphereEarth;
  var camera;
  var light;
  var ambient;
  var gridHelper;
  var axisHelper;
  var lightHelper;
  var renderer;
  var width = 640;
  var height = 330;
  var loader;
  var theta = 0;
  var controls;

  // ステージ
  scene = new THREE.Scene();

  // 地表準備!
  loader = new THREE.TextureLoader();
  loader.load("https://82mou.github.io/threejs/img/real-earth.jpg", function(
    texture
  ) {
    createEarth(texture);
    render();
  });

  // 地球作る!!
  function createEarth(texture) {
    sphereEarth = new THREE.Mesh(
      new THREE.SphereGeometry(80, 20, 20), // 形状
      new THREE.MeshLambertMaterial({
        // 材質
        map: texture
      })
    );
    sphereEarth.position.set(0, 0, 0);
    scene.add(sphereEarth);
  }

  // Light
  light = new THREE.DirectionalLight(0xffffff, 1);
  light.position.set(100, 130, 80);
  scene.add(light);

  // 環境光源を作る
  ambient = new THREE.AmbientLight(0x222222);
  scene.add(ambient);

  // Camera
  camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
  camera.position.set(200, 100, 300);
  camera.lookAt(scene.position);

  // helper
  gridHelper = new THREE.GridHelper(200, 20);
  scene.add(gridHelper);
  axisHelper = new THREE.AxisHelper(1000);
  scene.add(axisHelper);
  lightHelper = new THREE.DirectionalLightHelper(light, 20);
  scene.add(lightHelper);
  controls = new THREE.OrbitControls(camera);

  // renderer
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(width, height);
  renderer.setClearColor(0xefefef);
  renderer.setPixelRatio(window.devicePixelRatio);
  document.getElementById("stage").appendChild(renderer.domElement);

  function render() {
    //アニメーションの準備
    requestAnimationFrame(render);
    //Y軸を起点に動かす(自転)
    sphereEarth.rotation.y += 0.01;
    //カメラを起点に動かす
    theta += 0.1;
    camera.position.x = Math.cos(THREE.Math.degToRad(theta)) * 200;
    camera.position.z = Math.sin(THREE.Math.degToRad(theta)) * 300;
    camera.lookAt(scene.position);
    //マウスに連動して動かす
    controls.update();
    renderer.render(scene, camera);
  }
})();

出来上がり

アニメーションをつけているので、自転しているような地球がレンダリングされました。

また、heplerを使うと、X、Y、Z軸や光源位置もわかりやすいので、パラメータを変えたときやうまくレンダリングしてくれないときにわかりやすかったです。

まちいろの技術発表会では、この地球を回しながら「ここマダガスカル!」を発表しました。ややウケでした。



この記事が気に入ったらサポートをしてみませんか?