Basic - nooneknows2020/three.js_lesson GitHub Wiki

three.jsの基本

three.jsの基本構造

  • Renderer: 描画エンジン(WebGLのラッパー)
  • Scene: 3Dオブジェクトのコンテナ
  • Camera: 仮想カメラ(視点設定)
  • Mesh: 形状(Geometry)+表面特性(Material)の組み合わせ
  • Light: 照明
  • Texture: 3Dオブジェクトの表面に適用される画像や色

最初のシーン

立方体を表示する。

用意するファイル

  • index.html
  • main.js
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>My first three.js app</title>
  <style>
    body { margin:0 }
  </style>
  <script type="importmap">
    {
      "imports": {
        "three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
        "three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
      }
    }
  </script>
</head>
<body>
  <canvas id="c"></canvas>
  <script type="module" src="./main.js"></script>
</body>
</html>
import * as THREE from 'three';

function main() {
  // キャンバス
  const canvas = document.getElementById( 'c' );
  // レンダラ
  const renderer = new THREE.WebGLRenderer( { antialias: true, canvas } );

  // カメラ
  const fov = 75;
  const aspect = 2; // the canvas default(300px * 150px)
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera( fov, aspect, near, far );
  camera.position.z = 2;

  // シーン
  const scene = new THREE.Scene();

  // ジオメトリ
  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry( boxWidth, boxHeight, boxDepth );

  // マテリアル
  const material = new THREE.MeshBasicMaterial( { color: 0x44aa88 } ); // greenish blue

  // メッシュ
  const cube = new THREE.Mesh( geometry, material );
  scene.add( cube );

  renderer.render( scene, camera );
}

main();

アニメーション

立方体を回転させる。

import * as THREE from 'three';

function main() {
  // キャンバス
  const canvas = document.getElementById( 'c' );
  // レンダラ
  const renderer = new THREE.WebGLRenderer( { antialias: true, canvas } );

  // カメラ
  const fov = 75;
  const aspect = 2; // the canvas default(300px * 150px)
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera( fov, aspect, near, far );
  camera.position.z = 2;

  // シーン
  const scene = new THREE.Scene();

  // ジオメトリ
  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry( boxWidth, boxHeight, boxDepth );

  // マテリアル
  const material = new THREE.MeshBasicMaterial( { color: 0x44aa88 } ); // greenish blue

  // メッシュ
  const cube = new THREE.Mesh( geometry, material );
  scene.add( cube );

  // アニメーション
  function render(time) {
    time *= 0.001;  // convert time to seconds

    cube.rotation.x = time;
    cube.rotation.y = time;

    renderer.render(scene, camera);
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

main();

ライトを追加する

DirectionalLightライトを追加する。

MeshBasicMaterialは、ライトの影響を受けないので、ライトの影響を受けるMeshPhongMaterialに変更する。

import * as THREE from 'three';

function main() {
  // キャンバス
  const canvas = document.getElementById( 'c' );
  // レンダラ
  const renderer = new THREE.WebGLRenderer( { antialias: true, canvas } );

  // カメラ
  const fov = 75;
  const aspect = 2; // the canvas default(300px * 150px)
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera( fov, aspect, near, far );
  camera.position.z = 2;

  // シーン
  const scene = new THREE.Scene();
  // ライト
  {
    const color = 0xFFFFFF;
    const intensity = 3;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);
  }

  // ジオメトリ
  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry( boxWidth, boxHeight, boxDepth );

  // マテリアル
  // const material = new THREE.MeshBasicMaterial( { color: 0x44aa88 } ); // greenish blue
  const material = new THREE.MeshPhongMaterial( { color: 0x44aa88 } ); // greenish blue

  // メッシュ
  const cube = new THREE.Mesh( geometry, material );
  scene.add( cube );


  // アニメーション
  function render(time) {
    time *= 0.001;  // convert time to seconds

    cube.rotation.x = time;
    cube.rotation.y = time;

    renderer.render(scene, camera);
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

main();

立方体を3つ表示する

import * as THREE from 'three';

function main() {
  // キャンバス
  const canvas = document.getElementById( 'c' );
  // レンダラ
  const renderer = new THREE.WebGLRenderer( { antialias: true, canvas } );

  // カメラ
  const fov = 75;
  const aspect = 2; // the canvas default(300px * 150px)
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera( fov, aspect, near, far );
  camera.position.z = 2;

  // シーン
  const scene = new THREE.Scene();
  // ライト
  {
    const color = 0xFFFFFF;
    const intensity = 3;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);
  }

  // ジオメトリ
  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry( boxWidth, boxHeight, boxDepth );

  // マテリアルを設定して、メッシュを返す
  function makeInstance(geometry, color, x){
    const material = new THREE.MeshPhongMaterial({color});
    // メッシュ
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);
    cube.position.x = x;
    return cube;
  }

  // 3つの異なる色とx座標を使用して呼び出し、配列にメッシュインスタンスを格納する
  const cubes = [
    makeInstance(geometry, 0x44aa88, 0),
    makeInstance(geometry, 0x8844aa, -2),
    makeInstance(geometry, 0xaa8844, 2),
  ];

  // アニメーション
  function render(time) {
    time *= 0.001;  // convert time to seconds

    cubes.forEach((cube, ndx) => {
      const speed = 1 + ndx * .1;
      const rot = time * speed;
      cube.rotation.x = rot;
      cube.rotation.y = rot;
    });
    renderer.render(scene, camera);
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

main();

レスポンシブデザイン

canvas要素のサイズ設定

<style>
html, body {
  margin:0;
  height: 100%
}
#c{
  width: 100%;
  height: 100%;
  display: block;
}
</style>

レンダリングループの修正

ウィンドウサイズが変更されるたびに、canvasのサイズとカメラのアスペクト比を自動的に調整する。

import * as THREE from 'three';
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';

function main() {
  // キャンバス
  const canvas = document.getElementById( 'c' );
  // レンダラ
  const renderer = new THREE.WebGLRenderer( { antialias: true, canvas } );

  // カメラ
  const fov = 75;
  const aspect = 2; // the canvas default(300px * 150px)
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera( fov, aspect, near, far );
  camera.position.z = 2;
  // オービットコントロール
  const controls = new OrbitControls( camera, renderer.domElement );

  // シーン
  const scene = new THREE.Scene();
  // ライト
  {
    const color = 0xFFFFFF;
    const intensity = 3;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);
  }

  // ジオメトリ
  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry( boxWidth, boxHeight, boxDepth );

  // マテリアル
  function makeInstance(geometry, color, x){
    const material = new THREE.MeshPhongMaterial({color});
    // メッシュ
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);
    cube.position.x = x;
    return cube;
  }

  // 3つの異なる色とx座標を使用して呼び出し、配列にメッシュインスタンスを格納する
  const cubes = [
    makeInstance(geometry, 0x44aa88, 0),
    makeInstance(geometry, 0x8844aa, -2),
    makeInstance(geometry, 0xaa8844, 2),
  ];

  // アニメーション
  function render(time) {
    time *= 0.001;  // convert time to seconds

    // キャンバスのサイズとカメラのアスペクト比を自動的に調整する
    const canvas = renderer.domElement;
    camera.aspect = canvas.clientWidth / canvas.clientHeight;
    camera.updateProjectionMatrix();

    cubes.forEach((cube, ndx) => {
      const speed = 1 + ndx * .1;
      const rot = time * speed;
      cube.rotation.x = rot;
      cube.rotation.y = rot;
    });
    renderer.render(scene, camera);
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

main();

ブロックノイズの解消

import * as THREE from 'three';
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';

function main() {
  // キャンバス
  const canvas = document.getElementById( 'c' );
  // レンダラ
  const renderer = new THREE.WebGLRenderer( { antialias: true, canvas } );

  // カメラ
  const fov = 75;
  const aspect = 2; // the canvas default(300px * 150px)
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera( fov, aspect, near, far );
  camera.position.z = 2;
  // オービットコントロール
  const controls = new OrbitControls( camera, renderer.domElement );

  // シーン
  const scene = new THREE.Scene();
  // ライト
  {
    const color = 0xFFFFFF;
    const intensity = 3;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);
  }

  // ジオメトリ
  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry( boxWidth, boxHeight, boxDepth );

  // マテリアル
  function makeInstance(geometry, color, x){
    const material = new THREE.MeshPhongMaterial({color});
    // メッシュ
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);
    cube.position.x = x;
    return cube;
  }

  // 3つの異なる色とx座標を使用して呼び出し、配列にメッシュインスタンスを格納する
  const cubes = [
    makeInstance(geometry, 0x44aa88, 0),
    makeInstance(geometry, 0x8844aa, -2),
    makeInstance(geometry, 0xaa8844, 2),
  ];

  // キャンバスのサイズを実際に変更する必要があるかどうかを確認する
  function resizeRendererToDisplaySize(renderer){
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if(needResize){
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  // アニメーション
  function render(time) {
    time *= 0.001;  // convert time to seconds

    // アスペクトはキャンバスの表示サイズが変更された場合にのみ変更する
    if(resizeRendererToDisplaySize(renderer)){
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }

    cubes.forEach((cube, ndx) => {
      const speed = 1 + ndx * .1;
      const rot = time * speed;
      cube.rotation.x = rot;
      cube.rotation.y = rot;
    });
    renderer.render(scene, camera);
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

main();

参考

⚠️ **GitHub.com Fallback** ⚠️