ゲーム開発
Unity
UnrealEngine
C++
ゲーム数学
ゲームAI
サウンド
アニメーション
GBDK
制作日記
3DCG
Houdini
Blender
USD
グラフィックス
テクノロジ
ツール開発
フロントエンド関連
サーバサイド関連
ソフトウェア設計
ハードウェア関連
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
その他
都会のエレキベア
ラーメン日記
四コマ漫画
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • ゲーム数学
    • ゲームAI
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • 3DCG
    • Houdini
    • Blender
    • USD
    • グラフィックス
  • テクノロジ
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • ソフトウェア設計
    • ハードウェア関連
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • その他
    • 都会のエレキベア
    • ラーメン日記
    • 四コマ漫画
    • おすすめアイテム
    • おもしろコラム
  1. ホーム
  2. 20260215_01_three_basic

【Three.js】Three.js入門 - シーン構築・モデル読み込み・ポストプロセスまで

JavaScriptグラフィックスThree.jsWebGLシェーダー
2026-02-15

ぷらずも
ぷらずも
みなさんこんにちは! ぷらずもです。
エレキベア
エレキベア
こんにちクマ〜〜
ぷらずも
ぷらずも
今日は Three.js の基本的な使い方についてまとめていきたいと思います! ブラウザ上で3Dグラフィックスを手軽に実装できるライブラリで、WebGLの知識がなくても直感的に扱えるのが魅力です!
エレキベア
エレキベア
ブラウザで3Dが動くクマ? それは気になるクマ〜〜〜

Three.jsとは

ぷらずも
ぷらずも
Three.js はWebGLを使ったブラウザ上での3Dグラフィックスを簡単に実装できるJavaScriptライブラリです。 WebGL自体は低レベルなAPIで直接扱うには多くのボイラープレートコードが必要ですが、Three.jsを使うことでシーン・カメラ・ライト・マテリアルなどを直感的なAPIで操作できるようになります。
✔ Three.jsの主な特徴
  • WebGLの低レベルAPIをラップし、直感的に3D描画が可能
  • Geometry(形状)Material(質感) を組み合わせた Mesh を作成してシーンに配置する
  • FBX・GLTF形式の3Dモデルやテクスチャの読み込みに対応
  • カスタムシェーダー(GLSL)やポストプロセッシングエフェクトにも対応
20260215_01_06
▲様々な3Dを使用したサイトが公開されている

基本機能のサンプル

ぷらずも
ぷらずも
それでは実際にいくつかのサンプルを実装しながら、Three.jsの基本機能について触っていきます。 今回は シーン構築からモデル読み込み・シェーダー実装の概要まで の紹介となります。

各サンプルコードについては、以下のGitHubリポジトリにまとめています。

GitHub - three-basic-sample

今回はCDNからimportmapを使って読み込む形式で各サンプルを作成しています。

<script type="importmap">
  {
    "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/"
    }
  }
</script>

記事上での確認用にCodePenも用意しているため、ブラウザ上で直接動作を確認できます。
ローカルで実行する場合は対応するフォルダ内で以下のコマンドを実行してください。

npx serve .
エレキベア
エレキベア
たのしみクマ〜〜〜

Sample1: 最小構成でキューブを表示する

まずは最もシンプルなシーンを作成してみます。
こちらは以下の公式チュートリアルを参考にさせていただいています。

20260215_01_01
▲ワイヤーフレームのキューブが回転するシンプルなシーン

Three.jsの基本的な構成要素は大きく3つです。

✔ Three.jsの基本構成
  • WebGLRenderer: WebGLで描画を実行するクラス。setAnimationLoopでアニメーションループを開始する
  • Scene: 3Dオブジェクトを配置する空間
  • Camera: シーンを映すカメラ。PerspectiveCameraで遠近感のある投影ができる

サンプルシーンのコードは非常にシンプルで、Geometry(形状)とMaterial(マテリアル)を組み合わせてMeshを作成し、scene.add()でシーンに追加しています。
Rendererにシーンとカメラを渡すことで描画を行います。

import * as THREE from "three";

// Rendererを作成してDOMに追加
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// SceneとCameraを作成
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000,
);
camera.position.z = 5;

// BoxGeometry + MeshBasicMaterial でCubeを作成してシーンに追加
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
  color: 0x00ff00,
  wireframe: true,
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// フレームごとにCubeを回転させる
function update(deltaTime) {
  cube.rotation.x += 1 * deltaTime;
  cube.rotation.y += 1 * deltaTime;
}

// アニメーションループを開始
const clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
  const deltaTime = clock.getDelta();
  update(deltaTime);
  renderer.render(scene, camera);
});

THREE.ClockgetDelta()で前フレームからの経過時間(deltaTime)を取得し、回転速度に掛けることでフレームレートに依存しないアニメーションを実現しています。
この辺りはUnity等のゲームエンジンの思想と同じ仕組みです。

エレキベア
エレキベア
緑のキューブが回ってるクマ これがThree.jsの最小構成クマね

02: クラス設計で管理しやすくする

次に、コードをクラスで整理してより管理しやすい設計に改善 します。
表示内容はSample1と同じですが、OrbitControlsという仕組みも導入し、クリックによってビューポートの回転などが行えるようにもしています。

✔ クラス設計の改善ポイント
  • MainApp: アプリのエントリーポイント。Renderer・OrbitControls・リサイズ処理を担当
  • MainScene: SceneとCameraを管理し、子オブジェクトのupdateを呼び出す
  • Cube: メッシュ作成と更新ロジックをクラスにカプセル化

WebGL.isWebGL2Available()でWebGL2のサポートチェックを行い、非対応環境ではエラーメッセージを表示するようにもしています。

import * as THREE from "three";
import WebGL from "three/addons/capabilities/WebGL.js";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { MainScene } from "./main-scene.js";

class MainApp {
  constructor(container) {
    if (!container) return;

    // WebGL2対応チェック
    if (!WebGL.isWebGL2Available()) {
      this.container.appendChild(WebGL.getWebGLErrorMessage());
      return;
    }

    this.clock = new THREE.Clock();
    this.main_scene = new MainScene(this.width, this.height);
    this.renderer = new THREE.WebGLRenderer();
    this.renderer.setSize(this.width, this.height);
    this.renderer.setAnimationLoop(() => {
      const deltaTime = this.clock.getDelta();
      this.onUpdate(deltaTime);
    });
    this.container.appendChild(this.renderer.domElement);

    // マウスでカメラを操作できるOrbitControlsを追加
    this.orbit_controls = new OrbitControls(
      this.main_scene.camera,
      this.renderer.domElement,
    );

    // ウィンドウリサイズに対応
    window.addEventListener("resize", () =>
      this.onWindowResize(this.width, this.height),
    );
  }

  get width() { return this.container.clientWidth; }
  get height() { return this.container.clientHeight; }

  onUpdate(deltaTime) {
    this.main_scene.onUpdate(deltaTime);
    this.orbit_controls.update(deltaTime);
    this.renderer.render(this.main_scene.scene, this.main_scene.camera);
  }

  onWindowResize(width, height) {
    this.renderer.setSize(width, height);
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.main_scene.onWindowResize(width, height);
  }
}

window.addEventListener("load", () => {
  const container = document.getElementById("container");
  new MainApp(container);
});

ウィンドウサイズが変わった際は、camera.aspectを更新した後にcamera.updateProjectionMatrix()を呼び出すことで再計算することができます。

onWindowResize(width, height) {
  this.camera.aspect = width / height;
  this.camera.updateProjectionMatrix(); // 投影行列を再計算
}

Rendererを表示する対象要素についても、コンテナとして渡せるよう改良してあります。
特定の領域に表示させる場合には汎用的に使用できると思います。

window.addEventListener("load", () => {
  const container = document.getElementById("container");
  new MainApp(container);
});
エレキベア
エレキベア
クラスに分けるだけで随分整理されるクマね OrbitControlsでグリグリ動かせるのも便利クマ

03: GeometryとMaterialを組み合わせる

Three.jsにはさまざまなGeometry(形状)とMaterial(マテリアル)が標準で用意されています。
以下のサンプルでは各GeometryとMaterialの組み合わせをいくつか表示してみたものです。

20260215_01_02
▲各Geometryと各Materialの組み合わせ一覧

ライティングの効果を受けるマテリアルを使う場合は、AmbientLight(環境光)DirectionalLight(平行光源) をシーンに追加する必要があります。

// 環境光を追加
const aoLight = new THREE.AmbientLight(0xffffff, 0.5);
this.scene.add(aoLight);

// 平行光源を追加
const dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
dirLight.position.set(10, 10, 5);
this.scene.add(dirLight);
▲ライトの設定

表示しているGeometry、Materialの種類は以下の通りです。

主なGeometry一覧
クラス名
説明
BoxGeometry
直方体
SphereGeometry
球体
CircleGeometry
円・扇形
CylinderGeometry
円柱
TorusKnotGeometry
トーラスノット
ExtrudeGeometry
Shapeを押し出した任意形状
主なMaterial一覧
クラス名
説明
MeshBasicMaterial
ライティングなし。常に同じ色で表示
MeshLambertMaterial
拡散反射のみ。計算が軽い
MeshPhongMaterial
拡散反射 + 鏡面反射。`shininess`で光沢を調整
MeshNormalMaterial
法線方向をRGBで可視化
MeshToonMaterial
トゥーンシェーディング

また、THREE.Shapeにベジェ曲線で任意の形状を定義し、ExtrudeGeometryでZ方向に押し出すとカスタム形状を作ることもできます。

const heartShape = new THREE.Shape();
const x = -2.5, y = -5;
heartShape.moveTo(x + 2.5, y + 2.5);
heartShape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
heartShape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
// ...(ベジェ曲線でハート形状を定義)

const heartExtrudeSettings = {
  steps: 2, depth: 2, bevelEnabled: true,
  bevelThickness: 1, bevelSize: 1, bevelSegments: 2,
};
const heartGeometry = new THREE.ExtrudeGeometry(heartShape, heartExtrudeSettings);
heartGeometry.scale(0.075, 0.075, 0.075);
▲ハート型のExtrudeGeometry

これら以外にもさまざまなものが用意されていますが、こちらについては公式ドキュメントをご参照ください。

エレキベア
エレキベア
デフォルトでいろいろ用意されている上に、独自形状を作ることも出来るのクマね 素材の組み合わせで見た目が変わるのも面白いクマ
ぷらずも
ぷらずも
THREE.Shapeでベジェ曲線を定義すれば任意の形状を作れます! ライティングを受けるマテリアルはシーンにライトを追加するのを忘れずに!

04: 3Dモデルやテクスチャを読み込む

Three.jsでは3DモデルやテクスチャなどのアセットをLoaderで非同期に読み込むことができます。

20260215_01_03
▲エレキベアモデル(FBX)と星モデル(GLB)を読み込んだシーン

GLTFLoader.glb形式のモデルを読み込む場合は以下のようになります。読み込み後にmodel.scene.traverse()でメッシュを巡回してマテリアルを差し替えることができます。

import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

async loadModelAsync() {
  const loader = new GLTFLoader();
  const model = await loader.loadAsync("public/models/SM_Star.glb");

  // MeshToonMaterialに差し替え
  const material = new THREE.MeshToonMaterial({ color: "yellow" });
  model.scene.traverse((child) => {
    if (child.isMesh) {
      child.material = material;
    }
  });
  this.root.add(model.scene);
}

FBXLoader でアニメーション付きの.fbxモデルを読み込む場合は、AnimationMixerを使ってアニメーションを再生します。
サンプルコードでは、各メッシュのマテリアルもトゥーン調のものに差し替えています、

import { FBXLoader } from "three/addons/loaders/FBXLoader.js";

async loadModelAsync() {
  const loader = new FBXLoader(manager);
  const model = await loader.loadAsync("public/models/SM_Elekibear.fbx");

  // テクスチャを読み込んでMaterialに設定
  const baseColorTex = await new THREE.TextureLoader().loadAsync(
    "public/textures/T_Elekibear.png",
  );
  baseColorTex.colorSpace = THREE.SRGBColorSpace; // ← 重要!

  const material = new THREE.MeshToonMaterial({
    color: new THREE.Color(0xffffff),
    map: baseColorTex,
  });
  model.traverse((child) => {
    if (child.isMesh) child.material = material;
  });

  // AnimationMixerでアニメーションを再生
  this.animMixer = new THREE.AnimationMixer(model);
  const clip = model.animations[0];
  const action = this.animMixer.clipAction(clip);
  action.setLoop(THREE.LoopPingPong);
  action.play();

  this.root.add(model);
}

onUpdate(deltaTime) {
  if (this.animMixer) {
    this.animMixer.update(deltaTime); // Mixerをフレームごとに更新
  }
}

テクスチャを読み込んだ際にはカラースペースの設定にも注意が必要です。
ベースカラーなどsRGBテクスチャの場合はcolorSpace = THREE.SRGBColorSpaceを設定するようにしましょう。

また、FBXファイルに埋め込まれたテクスチャパスが存在しない場合、THREE.LoadingManagersetURLModifierでパスを強制的に差し替えることでエラー回避することができます。

const manager = new THREE.LoadingManager();
manager.setURLModifier((url) => {
  // 画像ファイルのURLを強制的に差し替える
  if (/\.(png|jpe?g|tga|bmp|gif)$/i.test(url))
    return this.BASE_COLOR_TEX_PATH;
  return url;
});
const loader = new FBXLoader(manager);
▲LoadingManagerでテクスチャパスを差し替える
エレキベア
エレキベア
エレキベアが3Dになってるクマ アニメーションも動いてるクマ?
ぷらずも
ぷらずも
FBXに埋め込まれたアニメーションもそのまま再生できます! アニメーション用のFBXが分かれている場合でも、それぞれ読み込んで設定するといったことも出来そうです。

05: ポストプロセッシングで画面効果を加える

Three.jsではEffectComposerを使ってポストプロセッシングエフェクトを実装できます。複数のエフェクトをパスとして順番に重ねがけすることで、フィルムグレインやグリッチなどの画面効果を追加できます。

また、このサンプルではトゥーン + 背面法アウトラインのカスタムシェーダーも実装していますが、長くなりそうだったので別記事に分けています。
ご興味ある方は以下の記事をご参照ください。

【Three.js】カスタムシェーダーでトゥーン+背面法アウトラインを実装する
2026-02-15
20260215_01_04
▲アウトライン付きのトゥーン描画にポストプロセスを加えたもの

20260215_01_05
▲ポストプロセス無し版

EffectComposerのセットアップは以下のようになります。
最後に必ずOutputPassを追加して最終出力の色空間変換を行います。

import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { GlitchPass } from "three/addons/postprocessing/GlitchPass.js";
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";

this.composer = new EffectComposer(this.renderer);
this.composer.addPass(new RenderPass(scene, camera));    // 通常のシーン描画
this.composer.addPass(new CustomFilmGrainPass(...));     // フィルムグレイン(カスタム)
this.composer.addPass(new GlitchPass());                 // グリッチ(組み込み)
this.composer.addPass(new OutputPass());                 // 最終出力(必須)

// renderer.render()の代わりにcomposer.render()を呼ぶ
this.composer.render();
▲EffectComposerのセットアップ

今回はフィルムグレインエフェクトを独自で実装しています。
カスタムエフェクトはShaderPassを継承して実装し、乱数でノイズを生成してtDiffuse(直前のパスの描画結果)に加算することで表現しています。

import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";

export class CustomFilmGrainPass extends ShaderPass {
  static VERT_SHADER = `
varying vec2 vUv;

void main() {
  vUv = uv;
  gl_Position = vec4(position, 1.0);
}
`;

  static FRAG_SHADER = `
uniform sampler2D tDiffuse;
uniform float uTime;
uniform float uStrength;
uniform float uSpeed;

varying vec2 vUv;

float random(vec2 st) {
  return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453);
}

void main() {

  vec4 col = texture2D(tDiffuse, vUv);

  float noise = random(vUv + uTime * uSpeed * 10.0) - 0.5;
  float flicker = 0.9 + 0.1 * sin(uTime * uSpeed * 8.0);
  col.rgb += noise * uStrength * flicker;

  gl_FragColor = col;
}
`;

  constructor({ strength = 0.25, speed = 0.1, time = 0.0 } = {}) {
    super({
      uniforms: {
        tDiffuse: { value: null },
        uTime: { value: time },
        uStrength: { value: strength },
        uSpeed: { value: speed },
      },
      vertexShader: CustomFilmGrainPass.VERT_SHADER,
      fragmentShader: CustomFilmGrainPass.FRAG_SHADER,
    });
  }

  onUpdate(deltaTime) {
    this.material.uniforms.uTime.value += deltaTime;
  }
}

▲フィルムグレインエフェクトの実装
エレキベア
エレキベア
ポストエフェクトを足すだけでかなり雰囲気が変わるクマ グリッチはちょっとサイバーパンクっぽいクマね
ぷらずも
ぷらずも
composer.addPass()でパスを追加するだけなので、組み合わせも自由自在です! カスタムエフェクトもShaderPassを継承するだけで作れるのが嬉しいですね

おわりに

ぷらずも
ぷらずも
今回はThree.jsの基本的な使い方について解説してみました! どうだったかな?
エレキベア
エレキベア
シンプルな実装でブラウザ上で3D表現ができるのは楽しいクマね WebGLって難しそうだと思ってたけど意外と使いやすいクマね
ぷらずも
ぷらずも
Three.jsはAPIが直感的で、WebGLの低レベルな部分をほとんど意識せずに3Dグラフィックスが実装できるのが魅力です! 数学やシェーダー実装の検証にも使えそうなので、今後も触っていこうと思っています。
ぷらずも
ぷらずも
それでは今日はこの辺で! アデューー!
エレキベア
エレキベア
クマ〜〜〜〜

【Three.js】Three.js入門 - シーン構築・モデル読み込み・ポストプロセスまで 〜完〜


JavaScriptグラフィックスThree.jsWebGLシェーダー
2026-02-15
記事をSNSで共有する
X
Facebook
LINE
はてなブックマーク
Pocket
LinkedIn
Reddit

著者の各種アカウント
フォローいただけると大変励みになります!
X
GitHub

関連記事
【Three.js】カスタムシェーダーでトゥーン+背面法アウトラインを実装する
2026-02-15
【Astro】Astroの使い方と複数UIフレームワーク(React、Vue、Svelte)を組み合わせるサンプル
2026-02-01
【Houdini21.0】3Dビル群っぽいブログヘッダー画像を作成する
2026-01-10
【Houdini21.0】Solaris徹底入門:USD構成を意識した基本的な作業フローについてまとめる
2025-12-31
【VSCode】ドラッグ&ドロップで画像ファイルをリサイズ・保存する拡張機能を作る
2025-11-22
【ゲーム数学】第十回 p5.js(+α)で学ぶゲーム数学「複素数とフラクタル」
2025-11-02
【プロシージャル】Pythonで学ぶ波動関数崩壊アルゴリズム(Wave Function Collapse)
2025-06-22
【UE5.5】Nanite、Lumen、VSMの概要についてまとめる
2025-05-12