Three.jsの雛形を作成する

目次

解説

私も学習中なので抜け漏れがあるかもしれませんがご了承ください🙇‍♂️

今回はベースとなるThree.jsを使うための雛形を作成します。

インストール

npm i -D three

TypeScriptのプロジェクトでThree.jsを使うためには、Three.jsの型定義ファイルもインストールする必要があります。

npm i -D @types/three

基礎知識

Three.jsの基本要素

Three.jsはシーン、カメラ、レンダラー、ジオメトリ、マテリアル、メッシュなどの基本的な要素を使って3Dシーンを作成します。

GLSLの基本要素

シェーダーはGPUで実行されるプログラムです。

Three.jsではシェーダーを使ってマテリアルを作成することができます。

コードを書いてみる

  1. import文でThree.jsを読み込みます。
import * as THREE from "three";
  1. Three.js の3Dシーンを作成します。
  /** Three.jsの3Dシーンを作成します*/
  const scene = new THREE.Scene();
  1. カメラを設定する カメラの種類もいくつかありますが、今回は透視投影カメラを使います。
  /** カメラを設定します */
  const camera = new THREE.PerspectiveCamera(
    40, // 視野角
    window.innerWidth / window.innerHeight, // アスペクト比
    0.1, // カメラの視点の最短距離
    1000, // カメラの視点の最長距離
  );

このままだと最初のwindow幅で固定されてしまうので、リサイズした時に画面幅を合わせるようにします。

  /** リサイズした時画面幅を合わせる */
  window.addEventListener(
    "resize",
    () => {
      renderer?.setSize(window.innerWidth, window.innerHeight);
      if (camera) {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
      }
    },
    false,
  );
  1. レンダラーを作成する WebGLレンダラーを作成し、アンチエイリアシングを有効にします。
  /** WebGLレンダラーを作成し、アンチエイリアシングを有効にします */
  const renderer = new THREE.WebGLRenderer({
    antialias: true,
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  1. エレメントの取得 DOM要素を取得して、レンダラーのDOM要素を追加します。
  /** エレメントの取得 */
  const containerElement = document.querySelector("#webgl");
  if (!containerElement) return; // #webgl要素が存在しない場合は処理を終了
  containerElement.appendChild(renderer.domElement);
  renderer.setSize(window.innerWidth, window.innerHeight);
  1. ジオメトリを作成する

ジオメトリは3Dオブジェクトの形状を決めるものです。

いくつか種類はありますが、今回は立方体を作成します。

  /** Geometry を作成します */
  const geometry = new THREE.BoxGeometry();

他にも様々な種類があるのでドキュメントを参照してください。

three.js docs
three.js docs favicon threejs.org
  1. マテリアルを作成する

今回はシェーダーマテリアルを使います。 vertexShaderとfragmentShaderは分かりやすくするため別ファイルに分けて書きます。

vertexShader.glsl

projectionMatrix * modelViewMatrixはカメラの位置とオブジェクトの位置を合わせるために使います。

positionはジオメトリの頂点の位置です。

varying vec2 vUv;
void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

fragmentShader.glsl

vec4(1.0, 0.0, 0.0, 1.0)はRGBAの値です。

void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

main.ts

import fragmentShader from "./shaders/fragmentShader.glsl";
import vertexShader from "./shaders/vertexShader.glsl";
  /** ShaderMaterialを作成します */
  const material = new THREE.ShaderMaterial({
    vertexShader,
    fragmentShader,
  });
  1. メッシュを作成する

ジオメトリとマテリアルを組み合わせてメッシュを作成します。

  /** Mesh を作成します */
  const cube = new THREE.Mesh(geometry, material);
  1. シーンに追加する

最後にシーンに追加します!

これがないと3Dオブジェクトが表示されないので注意が必要です!

  /** シーンに追加します */
  scene.add(cube);

ここまでで3Dシーンの作成は完了です。

あとは細かい調整やアニメーション周りの設定をすれば、3Dシーンを作成することができます。

デモ

デモを見る

全体のコード

main.ts


import * as THREE from "three";
import fragmentShader from "./shaders/fragmentShader.glsl";
import vertexShader from "./shaders/vertexShader.glsl";

(async () => {
  /** Three.jsの3Dシーンを作成します*/
  const scene = new THREE.Scene();

  /** カメラを設定します */
  const camera = new THREE.PerspectiveCamera(
    40, // 視野角
    window.innerWidth / window.innerHeight, // アスペクト比
    0.1, // カメラの視点の最短距離
    1000, // カメラの視点の最長距離
  );

  /** リサイズした時画面幅を合わせる */
  window.addEventListener(
    "resize",
    () => {
      renderer?.setSize(window.innerWidth, window.innerHeight);
      if (camera) {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
      }
    },
    false,
  );

  /** WebGLレンダラーを作成し、アンチエイリアシングを有効にします */
  const renderer = new THREE.WebGLRenderer({
    antialias: true,
  });
  renderer.setSize(window.innerWidth, window.innerHeight);

  /** エレメントの取得 */
  const containerElement = document.querySelector("#webgl");
  if (!containerElement) return; // #webgl要素が存在しない場合は処理を終了します
  containerElement.appendChild(renderer.domElement);
  renderer.setSize(window.innerWidth, window.innerHeight);

  /** Geometry を作成します */
  const geometry = new THREE.BoxGeometry();

  console.log(geometry.attributes); // uv座標を確認できます

  /** ShaderMaterialを作成します */
  const material = new THREE.ShaderMaterial({
    vertexShader,
    fragmentShader,
  });

  /** Mesh を作成します */
  const cube = new THREE.Mesh(geometry, material);

  /** シーンに追加します */
  scene.add(cube);

  camera.position.z = 5;

  /** アニメーションループ*/
  function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x = cube.rotation.x + 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
  }

  animate();
})();


vertexShader.glsl

varying vec2 vUv;
void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

fragmentShader.glsl

varying vec2 vUv; // 頂点シェーダから受け取る変数

void main() {
  gl_FragColor = vec4(vUv, 1.0, 1.0);
}