開発ノウハウブラウザ3Dの基礎

Three.js|WebGLを直接書かずに3Dを動かす仕組みと重くなる原因


Three.jsは、ブラウザの3D描画APIであるWebGL・WebGPUを直接書かずに3Dを表示できるJavaScriptライブラリです。立方体を回すところまでは数十行で届きますが、実際の制作でつまずくのは描画そのものではなく、表示する3Dモデルをどう用意するかと、動かしたときに重くならないかの2点です。仕組み・最小構成のコード・モデルの用意の仕方・React Three Fiberとの関係・速度の落とし穴までを順に整理します。

公開2026.07.26
最終更新2026.07.26
読了 18 分 / 約7,380
この記事をシェアポスト
開発ノウハウブラウザで3Dを動かす

Three.jsで3Dを動かす
仕組みと重さの原因

3Dモデルを読み込んでいます…

 

1,936本の柱を波打たせています。1本ずつ描くと描画命令が1,936回発生しますが、まとめて1回で描く仕組み(InstancedMesh)を使うと1回で済みます。この記事のために書いたコードで、モデルファイルは使っていません。

Webサイトで製品を回して見せたり、地図や工場のレイアウトを立体で表示したりする実装の多くは、Three.jsで作られています。Three.jsのリポジトリは、2026年7月時点でGitHubのスターが約11万に達しているMITライセンスのJavaScriptライブラリで、README冒頭では、使いやすく軽量でブラウザを問わない汎用の3Dライブラリを目指すと説明されています。ライセンス表記は2010年からの著作年を掲げており、15年以上更新が続いているライブラリです。

C
結論
難所は描画ではなく、モデルの用意と表示速度

Three.jsが担うのは、ブラウザの3D描画API(WebGL・WebGPU)を直接叩く部分です。逆に言えば、それ以外は担いません。

  • 立方体を置いて回すだけなら数十行で書けます。学習の最初の一歩は、想像より軽い負担で踏み出せます
  • 実務でつまずくのは、表示する3Dモデルをどう用意するかです。形そのものはライブラリの担当外です
  • もう1つの難所は表示速度で、原因の多くは描画命令の回数とテクスチャの容量に集約されます

01.結論:Three.jsが担うのは描画だけ

Three.jsは、3D空間を「シーン(置き場)」「カメラ(見る位置)」「ライト(光)」「メッシュ(物体)」という日常的な言葉で組み立て、それをブラウザの描画APIへ変換して画面に出すライブラリです。担うのは、この変換と描画の部分だけです。

そのため、制作全体の作業量は次のように分かれます。ここを取り違えると「ライブラリを覚えたのに作りたいものが作れない」という状態になります。

工程Three.jsが担うか実際にやること
3Dモデルを作る・調達する担わない3DCGソフトで作る、配布・販売されているモデルを使う、コードで組み立てる
画面に描く(シーン・カメラ・ライト・材質)担うThree.jsのAPIでシーンを組み、描画ループを回す
操作・アニメーションを付ける部品は担う回転・拡大の操作、アニメーション再生の仕組みは用意されている
軽くする部品は担う描画命令をまとめる仕組みはあるが、使うかどうかは実装者の判断

02.WebGL・WebGPUと何が違うのか

ブラウザがGPUを使って絵を描くための標準API(アプリケーション同士をつなぐ窓口)がWebGLで、その後継として設計されたのがWebGPUです。どちらも仕様としては低レイヤーで、三角形を1枚出すだけでも、頂点データの並べ方やシェーダー(GPU上で動く小さなプログラム)を自分で書く必要があります。

Three.jsは、この低レイヤーのAPIを包んで扱いやすくするライブラリです。実装者は「箱を置く」「光を当てる」と書き、描画APIへの変換はライブラリ側が行います。ただし、APIの呼び出しを1対1で置き換えるだけの薄い層ではありません。物体を入れ子にして管理する仕組み(シーングラフ)や、材質・光といった概念そのものがThree.js側の設計で、描画APIには存在しません。READMEの説明によれば、現在の配布物はWebGLとWebGPUの両方のレンダラーを備えており、SVG・CSS3Dのレンダラーはアドオンとして提供されています。WebGPUが使えない環境向けのWebGLフォールバック用のコードもリポジトリに含まれており、新しいAPIへ寄せつつ古い環境を切り捨てない構成になっています。

シェーダーの書き方も変わりつつあります。Three.js Shading Language(TSL)の解説によると、TSLはシェーダーをGLSLやWGSLの文字列ではなくJavaScriptのノードとして組み立てる仕組みで、WebGL 2向けにはGLSL、WebGPU向けにはWGSLへ自動で変換されます。同じシェーダーの記述が2つの描画APIに対応する形になっており、シェーダーを書くための前提知識のハードルは以前より下がっています。

i
補足
更新の頻度は速い

Three.jsはリリースページを見ると数か月おきに版が上がり、最新はr185(2026年7月1日公開)です。版が上がるときに非推奨のコードが削除されることがあるため、古い記事のサンプルがそのままでは動かない場合があります。写経するときは、参照元がどの版を前提にしているかを確認してください。

03.Three.jsで作れるものの類型

「3Dが動くサイト」と一括りにされがちですが、実装の重さは目的によってかなり違います。作りたいものがどの類型かで、必要な準備も変わります。

類型内容作業の重心
製品・商品のビューアー1つのモデルを回して見せる。色違い・パーツ差し替えを付けることも多いモデルの品質と質感の作り込み
データの立体表示地図・建物・センサー値などを3Dのグラフや配置図として見せる大量の物体を軽く描くこと
演出としての背景・装飾スクロールに連動する図形やパーティクル、粒子の動きシェーダーと動きの設計
簡易なゲーム・体験コンテンツ操作して動き回れる空間。VR/AR機器へ出すこともある当たり判定・状態管理などライブラリ外の実装

このうち、Three.js単体でほぼ完結するのは上2つです。ゲーム的なコンテンツになるほど、当たり判定や物理演算、入力の扱いといった描画以外の実装が増え、そこは別のライブラリを組み合わせるか自作することになります。

04.最小構成のコードで仕組みを掴む

言葉で読むより、動く最小構成を見たほうが早く掴めます。次のコードは、オレンジ色の立方体を1つ置いて回し続けるだけのものです。Three.jsの基本要素がすべて入っています。

import * as THREE from "three";

// 1. 置き場(シーン)と見る位置(カメラ)
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  45,                                    // 画角
  window.innerWidth / window.innerHeight, // 縦横比
  0.1,                                   // これより近いものは描かない
  100,                                   // これより遠いものは描かない
);
camera.position.set(0, 1, 5);

// 2. 描く先(レンダラー)をページに差し込む
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 3. 光。これが無いと物体は真っ黒になる
scene.add(new THREE.AmbientLight(0xffffff, 0.6));
const light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.set(3, 5, 2);
scene.add(light);

// 4. 物体=形(ジオメトリ)+ 材質(マテリアル)
const box = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshStandardMaterial({ color: 0xf7931a }),
);
scene.add(box);

// 5. 毎フレーム呼ばれるループ
renderer.setAnimationLoop(() => {
  box.rotation.y += 0.01;
  renderer.render(scene, camera);
});

このコードから読み取れるのは、物体は「形(ジオメトリ)」と「材質(マテリアル)」の組み合わせで表現され、光が無ければ何も見えないという2点です。画面が真っ黒になる、何も表示されないといったつまずきの原因も、たいていはこのあたりにあります。光を置き忘れている、カメラが物体の内側や反対側を向いている、材質が光を必要としない種類になっている、といった具合です。

逆に言えば、ここから先の「立方体を実在の製品に置き換える」部分こそが本題になります。

05.3Dモデルをどう用意するか

Three.jsが用意している形は、箱・球・円柱・平面といった基本図形です。実在の製品やキャラクターを出すには、別の方法でモデルを用意する必要があります。選択肢は大きく3つです。

図:3Dモデルを用意する3つの経路
  1. 1
    既製のモデルを読み込む

    配布・販売されているglTF形式のモデルを読み込む。最も早いが、自社製品そのものは基本的に存在しない

  2. 2
    3DCGソフトで作る

    Blenderなどでモデリングし、glTFで書き出す。見た目の自由度は最も高いが、3DCGの技能と工数が要る

  3. 3
    コードで組み立てる

    基本図形を組み合わせ、寸法や配置をコードで書く。差し替え・寸法変更に強いが、有機的な形は苦手

どれか1つに絞る必要はありません。背景はコードで組み、主役の製品だけモデルを読み込む、といった併用が一般的です。

既製のglTFを読み込む

Web用の3Dモデルの形式としては、Khronos Groupが仕様を公開しているglTFがあります。形・材質・アニメーションをまとめて持て、圧縮版の.glbなら1ファイルで完結します。Three.jsのリポジトリには、これを読み込むGLTFLoaderが同梱されているため、モデルさえあれば表示は難しくありません。

3DCGソフトで作る

自社製品を正確に出したい場合は、3DCGソフトでのモデリングか、CADデータの変換が主な手段になります。ここで見落とされやすいのが、制作用のデータとWeb配信用のデータは要件が違う点です。制作側は細部まで作り込むほど良いとされますが、Web配信では面の数もテクスチャの解像度も減らすほど良くなります。書き出し時に軽量化する工程を、制作の見積もりにあらかじめ含めておくと後戻りが減ります。

コードで組み立てる

3つ目は、基本図形の組み合わせと寸法計算でモデル自体をコードとして書く方法です。ファイルではなくコードなので、寸法や色を変数で持てて、差分も追えます。機械部品・什器・建物のように直線と回転体が主体の形には向く一方、生き物や布のような有機的な形には向きません。

参照画像1枚からこの組み立てを自動化しようとする試みも出てきています。img2threejsは、写真測量やメッシュの切り出しではなく、基本図形と手続き的な材質の組み合わせでThree.jsのコードとして書き起こす、Apache License 2.0のプロジェクトです。裏側が写っていない形は分からないという限界はありますが、寸法や色を変数として持てる形で出力されるため、見せ方の検討を正式なモデル制作より前に始める用途に向きます。

06.コードだけで組み立てた実例を動かす

コードで組み立てる方式で実際どこまで作れるのかは、動くものを見たほうが早いので、この記事のために2体を用意しました。よくある構成のロボットアームと、二足歩行のキャラクターです。どちらもメッシュファイルを一切読み込まず、箱・円柱・カプセル・球の組み合わせと関節の親子関係だけで組んでいます。ドラッグすると視点を回せます。

5軸+グリッパーのアーム

1体目は、土台の旋回・肩・肘・手首の曲げ・手首のひねりの5軸に、開閉するグリッパーを付けたアームです。研究用・教育用に出回っているアームによくある構成で、立方体を拾って反対側へ置く動きを繰り返します。

3Dモデルを読み込んでいます…

 

5軸+グリッパーのアーム。床の目印から立方体を拾い、土台を旋回させて反対側の目印へ置きます。この記事のために作ったモデルです(実在の製品の再現ではありません)。

3Dで見ると分かりやすいのが、関節が入れ子(親子関係)になっているという点です。土台の中に肩、肩の中に肘、肘の中に手首、というように入れ子にしておくと、土台を回すだけで先端のグリッパーまで一緒に動きます。座標の計算を自分で書く必要はなく、どこに何をぶら下げるかを決めるだけで済みます。

// 親子関係を組むと、親を回した分だけ子も付いてくる
const pan = new THREE.Group();          // 土台の旋回
const lift = new THREE.Group();         // 肩の上下
const elbow = new THREE.Group();        // 肘の曲げ

lift.position.y = 0.42;                 // 肩は土台の上
elbow.position.y = 1.15;                // 肘は上腕の先
pan.add(lift);
lift.add(elbow);

// 毎フレームやるのは、角度を入れることだけ
pan.rotation.y = pose.basePan;
lift.rotation.x = pose.shoulderLift;
elbow.rotation.x = pose.elbowFlex;

なお、手先を床の目印まで正確に下ろすには、角度を目分量で決めるとうまくいきません。この2体でも、腕の各関節の角度はリンクの長さから逆算した値を使っています。

二足歩行のキャラクター

2体目は、丸い頭と短い脚を持つキャラクターです。歩かせるだけなら脚を振れば済みますが、それだけだと機械が往復しているようにしか見えません。腕を振る、触角を揺らす、ときどきまばたきをする、といった要素を足すと、同じ歩行でも生き物らしく見えます。

3Dモデルを読み込んでいます…

 

二足歩行のキャラクター。左右の脚を半周ずらして振り、体の下を通る側の膝を曲げて足を抜きます。腕は同じ側の脚と逆向きに振り、目は数秒に一度まばたきします。この記事のために作ったオリジナルのキャラクターです。

歩いて見えるための規則は、思ったより単純です。左右の脚に半周ぶんの位相差を与え、体の下を通り抜ける側の膝を曲げて足を浮かせ、接地している脚が傾いたぶんだけ胴を沈める。これだけで歩行らしく見えます。歩き方の計算そのものはThree.jsと無関係な普通の関数として書けるため、この2体でも姿勢を計算する部分は描画から切り離し、周期性や可動域を単体テストで固定しています。

// 片脚ぶんの姿勢。位相 p(0〜1)を渡すと関節角度が返る
function walkerLeg(p) {
  const hip = HIP_SWING * Math.sin(p * Math.PI * 2);
  // 体の下を通り抜ける区間(cos が正)で膝を曲げ、足を浮かせる
  const knee = KNEE_BEND * Math.max(0, Math.cos(p * Math.PI * 2));
  return { hip, knee, ankle: -(hip + knee) * 0.45 };
}

// 左右は必ず半周ずれ、腕は同じ側の脚と逆に振る
const left = walkerLeg(p);
const right = walkerLeg((p + 0.5) % 1);
const leftArm = -left.hip * 0.7;

ここで膝を曲げる位相の取り方には注意が要ります。脚を最も振り切ったところで膝も最も曲げると、膝の曲げが股関節の振りを打ち消して、脚がほとんど動いていないように見えます。振り幅が0になる位相に膝の山を置くのが正解です。

なお、この方式が向くのはここまでの2体のような、直線と回転体で近似できる形です。同じやり方で生き物の質感や布の動きを出そうとすると、基本図形では手に負えなくなります。用途を選べば、モデルファイルを1つも持たずにここまで動かせる、という程度に捉えておくとちょうどよい距離感です。

07.Reactで使うならReact Three Fiber

サイト側がReactで作られている場合、Three.jsをそのまま書くと、画面の状態と3Dシーンの状態を二重に管理することになります。これを避けるための選択肢がReact Three Fiber(MITライセンス、2026年7月時点でGitHubのスターは約3.2万)です。Three.jsのオブジェクトをReactのコンポーネントとして書けるようにするレンダラーで、READMEでは、Three.jsで動くものはすべてこちらでも動き、コンポーネントはReactの外側で描画されるため追加の負荷は生じないと説明されています。

先ほどの立方体を書き換えると、次のようになります。同じことを、宣言的な書き方で表現している点に注目してください。

import { Canvas, useFrame } from "@react-three/fiber";
import { useRef } from "react";
import type { Mesh } from "three";

function Box() {
  const ref = useRef<Mesh>(null);
  useFrame(() => {
    if (ref.current) ref.current.rotation.y += 0.01;
  });
  return (
    <mesh ref={ref}>
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color="#F7931A" />
    </mesh>
  );
}

export function Scene() {
  return (
    <Canvas camera={{ position: [0, 1, 5], fov: 45 }}>
      <ambientLight intensity={0.6} />
      <directionalLight position={[3, 5, 2]} intensity={1.5} />
      <Box />
    </Canvas>
  );
}

カメラの初期化もレンダラーの差し込みも<Canvas>が担うため、記述量は減ります。ただし、Three.js側の概念を知らずに書けるようになるわけではありません。カメラの位置も光も、名前と役割はそのままです。Reactに慣れているなら無理なく始められる一方、3D特有のつまずき(光が無い、カメラが物体の中にいる)は同じように起きます。

08.表示が重くなる原因と対策

3Dの実装でつまずきやすいのが速度です。手元のPCでは滑らかなのにスマートフォンでは動かない、という形で表面化します。原因はおおむね次の3つに整理できます。

原因起きること対策の方向
描画命令(ドローコール)が多い物体を1つ描くたびにGPUへの指示が発生し、数が増えるほど描画の待ち時間が積み上がる同じ形の繰り返しは1回の命令にまとめる
テクスチャ・モデルの容量が大きい読み込みが終わらない、メモリを使い切って端末側で強制終了する解像度と面の数を配信用に落とす
毎フレームの計算が重い描画ループの中で無駄な計算やオブジェクト生成をしてしまうループ内では計算結果を使い回す

1つ目については、Three.js側に対応する部品が用意されています。同じ形・同じ材質のものを大量に並べるためのInstancedMesh、形が違うものもまとめて扱えるBatchedMesh、遠くの物体を粗いモデルに差し替えるLODです。木を1000本並べるような場面では、素直に1000個の物体として置くか、まとめて1回で描くかで結果が大きく変わります。

2つ目は、モデル制作の段階に戻る話です。読み込みの重さは、実装ではほとんど取り返せません。書き出し時に面の数とテクスチャ解像度を決める段階で握っておくと、後の手戻りが減ります。

!
注意
測ってから直す

速度の問題は、体感で当たりを付けると外れます。ブラウザの開発者ツールで毎秒のコマ数と描画命令の回数を見て、どこが支配的かを確かめてから手を入れてください。命令の回数が少ないのに重いなら、原因は読み込みかフレームごとの計算のほうにあります。

09.Babylon.jsとどう使い分けるか

ブラウザの3Dライブラリとしては、Babylon.js(Apache License 2.0)もよく比較の対象になります。リポジトリの説明文では、レンダリングとゲームのエンジンをJavaScriptのフレームワークにまとめたものと位置づけられており、Three.jsが描画ライブラリを名乗るのとは出発点が違います。どちらも同じことができる場面は多く、優劣というより設計思想の違いとして捉えたほうが選びやすくなります。

Three.jsが向く
  • Webサイトの一部として3Dを載せる。必要な機能だけ足したい
  • 公開されているサンプルを参考にしながら進めたい
  • Reactで書いており、React Three Fiberに乗りたい
他を検討したほうがよい
  • 物理演算・当たり判定・GUIまで一式が最初から欲しい(ゲーム寄り)
  • エディタ上でシーンを組み、非エンジニアも触る前提にしたい
  • 3D表示が主役の大規模アプリで、機能の抜けを自作で埋めたくない
図:Three.jsを選ぶときの判断材料

Three.jsは必要最小限の描画ライブラリで、足りないものは自分で足す構成です。Babylon.jsは物理演算やエディタまで含む統合環境に近く、最初から揃っている代わりに全体が大きくなります。 Webサイトに3Dを載せる用途ではThree.jsが扱いやすく、アプリケーションの本体が3Dである場合はBabylon.jsの守備範囲が効いてきます。

10.学習の進め方

学ぶ順序は、最終的に何を作りたいかに合わせて決めると無理がありません。共通して言えるのは、シェーダーやWebGLの内部から入らないことです。

図:つまずきにくい学習の順序
  1. 1
    基本図形で1つ動かす

    シーン・カメラ・ライト・メッシュ・描画ループの5つを、基本図形だけで一通り触る

  2. 2
    既製のglTFを読み込む

    配布モデルを表示し、大きさ・向き・光の当て方を調整する。ここで質感の設定に慣れる

  3. 3
    操作とアニメーションを足す

    回転・拡大の操作、モデルに含まれるアニメーションの再生、スクロールとの連動

  4. 4
    速度を測って直す

    スマートフォンの実機で確認し、描画命令の回数と読み込み容量を見る

  5. 5
    必要ならシェーダーへ

    既製の材質では出せない表現が必要になった段階で、TSLやシェーダーに踏み込む

Reactで作るなら、1と2はReact Three Fiberで進めても構いません。概念の名前は共通です。

よくある失敗は、実機での確認を最後まで先延ばしにすることです。3D表現は端末による差が大きく、開発機で問題が出ないまま進むと、公開直前に作り直しになります。手順4は最後の工程ではなく、手順2の時点で一度挟んでおくと安全です。

11.よくある質問(FAQ)

Three.jsを使うのにWebGLの知識は必要ですか?

始める段階では不要です。シーン・カメラ・ライト・メッシュという単位で書けるため、頂点データやシェーダーを直接扱わずに表示できます。既製の材質では出せない表現が必要になった段階で、シェーダーの知識が要る領域に入ります。

商用のWebサイトで使えますか?

Three.js本体はMITライセンスで公開されており、著作権表示を残せば商用利用できます。注意が必要なのはライブラリではなく、読み込む3Dモデルやテクスチャのライセンスで、配布サイトごとに商用利用の条件が異なります。

3Dモデルを自分で作れなくても実装できますか?

できます。配布・販売されているglTF形式のモデルを読み込む方法と、基本図形の組み合わせでコードとして組み立てる方法があります。自社製品そのものを正確に出したい場合は、3DCGソフトでの制作かCADデータの変換が必要になります。

スマートフォンで重くなるのを防ぐには?

描画命令の回数を減らすこと、モデルの面の数とテクスチャ解像度を配信用に落とすこと、描画ループの中で無駄な計算をしないことの3点です。同じ形を大量に並べる場合はInstancedMeshBatchedMeshでまとめると効果が大きくなります。

バージョンが上がると既存のコードは動かなくなりますか?

版が上がるときに非推奨のコードが削除されることがあるため、古いサンプルがそのままでは動かない場合があります。リリースページで変更点を確認し、参照する記事やサンプルがどの版を前提にしているかを見てから写経すると、原因の分からないエラーを避けられます。

12.まとめ

Three.jsは、WebGL・WebGPUという低レイヤーの描画APIを、シーン・カメラ・ライト・メッシュという扱いやすい言葉に置き換えるライブラリです。描画そのものの学習コストは想像より低く、最小構成なら数十行で動きます。一方で、表示する3Dモデルの用意と、端末を選ばない表示速度の確保は、ライブラリの外側にある作業です。制作を計画する段階では、この2つを工程として見込んでおくと、後戻りが減ります。

3D表現を載せるかどうかは、見栄えだけでなく、立体でなければ伝わらない情報があるかどうかで判断が変わります。弊社では、AIをフル活用したWeb制作・運用の中で、こうした技術選定のご相談も承っています。

お問い合わせ

Webサイトの3D表現・技術選定をご相談ください

弊社では AI フル活用の運用保守・記事更新・計測改善までを提供しています。3D表現を載せるべきかの判断から実装後の速度改善まで、無料相談からお気軽にお問い合わせください。

TANTOU の詳細を見る

開発ノウハウ

一覧に戻る →
この記事をシェア
澤田 翔太(Shota Sawada)
この記事を書いた人

澤田 翔太

株式会社クリプタル 代表取締役

1988年生まれ、慶應義塾大学卒。創業メンバーとして関わった株式会社セールスサポートを株式会社ネオマーケティング(東証STD 4196)に売却。株式会社クリプタルでも複数の事業立ち上げと売却を経験し、2022年9月には婚活・恋愛メディア「シッテク」「婚活会議」を株式会社ベビーカレンダー(東証GRT 7363)へ売却。現在はAI業務支援事業、TANTOU事業、グロースハック支援事業、メディア事業、SEOコンサルティング事業を手がける。