[JavaScript] Three.js Vector3完全理解 —— 3D数学の基礎から実践までを一気に掴む

1. Vector3 とは何者か

THREE.Vector3 は 3D空間で使う「3つ組の数」 を、ただの入れ物ではなく “3D計算の道具” として扱うためのクラス。

Three.js では、次のようなものが全部 Vector3 で表現される。

  • 位置(どこにいるか): mesh.position
  • 方向(どっちを向くか/どっちへ進むか)
  • 速度(どれくらいの速さで動くか)
  • 力(押す・引く)
  • 法線(面がどちらを向いてるか:光や反射に関係)

「座標 = Vector3」だけじゃなくて、「向き・速度・力」みたいな “ベクトル”っぽいもの全般に使う。


まず「3Dの数」をイメージする

2Dなら (x, y) の2つで位置が決まる。 3Dだと高さが増えて (x, y, z) の3つになる。

  • x: 横(右が+)
  • y: 縦(上が+)
  • z: 奥行き(手前/奥が±、Three.jsは手前が+奥が−…ではなく、カメラ等で体感が変わるので「奥行き軸」くらいでOK)

だから Vector3 は基本こういう形:

const p = new THREE.Vector3(1, 2, 3); // x=1, y=2, z=3
console.log(p.x, p.y, p.z);

配列との違い([1,2,3] ではダメ?)

配列でも x,y,z を持つこと自体はできる。

const p = [1, 2, 3];

でも Three.js の世界では、配列は「ただの数字の箱」なので、3Dで必要な計算をするたびに地獄になる。

例:距離

Vector3 ならこれだけ:

const d = a.distanceTo(b);

配列だと毎回こういう関数が必要:

function distance(a, b) {
  const dx = b[0] - a[0];
  const dy = b[1] - a[1];
  const dz = b[2] - a[2];
  return Math.sqrt(dx*dx + dy*dy + dz*dz);
}

例:方向(目標へ向かう向き)

Vector3(あなたが書いてたやつ):

const dir = new THREE.Vector3().subVectors(target, current).normalize();

配列なら:

function direction(target, current) {
  const x = target[0] - current[0];
  const y = target[1] - current[1];
  const z = target[2] - current[2];
  const len = Math.sqrt(x*x + y*y + z*z) || 1;
  return [x/len, y/len, z/len];
}

この時点で「Vector3で良くね?」ってなる。

さらに致命的な違い

Three.js の API の多くは Vector3 前提。

  • mesh.position が Vector3
  • camera.position が Vector3
  • raycaster.ray.origin が Vector3
  • object.getWorldPosition() が Vector3

つまり配列で持っても、結局 Vector3 に変換が必要になりがち。


パフォーマンス面での利点(初心者向けに現実的な話)

「Vector3の方が速い」と断言するより、初心者はこう捉えるのが安全。

  • Vector3を使うと“余計な自作関数”や“変換”が減る
  • その結果、コードが短くなってバグも減り、処理も安定する

ただし、Three.jsでも毎フレーム new THREE.Vector3() を大量に作ると、ブラウザの GC(メモリ掃除)が増えて重くなることがある。

なので基本姿勢はこれ:

  • 計算用の Vector3 は使い回す(後の章で扱う)
  • clone() の乱用に注意

GPU パイプラインとの整合性(初心者向けに噛み砕く)

WebGL(GPUで描画する仕組み)は、内部で

  • 位置
  • 回転
  • 拡大縮小

などを 行列(Matrix) で処理する。

Three.js はその面倒なところを隠してくれているけど、内部的には

  • Vector3(位置など)
  • Quaternion(回転)
  • Matrix4(変換)

が連携して動くようにできてる。

だから Vector3 を使っていると、そのまま Three.js の内部の変換処理にスムーズに乗る。

初心者向けに一言で言うと:

  • Vector3は、Three.jsの“標準の部品”だから相性が最高

ここで覚えるべき最小ポイント

  • Vector3は「x,y,zの3つ組」だけじゃなく、3D計算の道具
  • 配列でも持てるが、Three.jsのAPIや計算に弱すぎる
  • Three.js世界では Vector3 を使うのが最短で、バグも減る

2. まずは基本操作

Three.js の Vector3 は、3D空間でよく使う計算を “1行で” 済ませる道具。 その基本操作を理解すると、以降の移動・回転・カメラ制御が一気にシンプルになる。


Vector3 の基本メソッドとその意味

まずはサンプルコードを見てみる:

const v = new THREE.Vector3(1, 2, 3);
v.add(new THREE.Vector3(1, 0, 0));
v.sub(new THREE.Vector3(0, 1, 0));
v.multiplyScalar(2);
v.normalize();

これらがどういう働きをするのか、初心者にも分かるように順番に解説する。


add() – ベクトルの足し算

v.add(new THREE.Vector3(1, 0, 0));

x,y,z の全部に一括で足し算する。

数値で考えるとこう:

v = (1,2,3)
v + (1,0,0) = (2,2,3)

これ何に使う?

  • オブジェクトの位置に「移動量」を足す
  • 速度に加速度を足す

3Dゲーム・WebXRでは頻出する。


sub() – ベクトルの引き算

v.sub(new THREE.Vector3(0, 1, 0));

x,y,z をまとめて差し引く。

(2,2,3) – (0,1,0) = (2,1,3)

よく使う場面

  • ターゲットとの差(方向ベクトルの基礎)
  • マウス操作時の位置差分の計算
  • 距離測定の準備

multiplyScalar() – ベクトル全体に数を掛ける

v.multiplyScalar(2);
(2,1,3) × 2 = (4,2,6)

主な用途

  • 速度 × delta(1秒あたりの速度 → フレーム速度へ変換)
  • 方向ベクトル × スピード = 移動量
  • 距離の拡大縮小

“全成分に一括で掛け算”が地味に強い。


normalize() – 長さを「1」にする(方向だけ残す)

v.normalize();

これは初心者が一番つまずくところ。

normalize が何をするか

v の長さ(距離)を 1 にする。 つまり 向きだけを取り出した状態 にする。

元が (4, 2, 6) だとしても normalize すると「その方向を向いた長さ1の矢印」になる。

方向を維持 → 長さだけを1に変換

これが重要な理由

方向ベクトルをそのまま使うと、距離(長さ)がバラバラなので不安定になる。

例:

const direction = target - position; // 近いと小さく、遠いと大きい

このままだと 遠いと急加速 → 近いとスロー みたいな妙な挙動になる。

normalize すると安定する:

const direction = new THREE.Vector3()
  .subVectors(target, position)
  .normalize();

position.add(direction.multiplyScalar(speed));

これで「どんな距離でも一定速度で移動」になる。


単位ベクトルのメリット(初心者にも分かる形で)

normalize した結果できる「長さ1のベクトル」を 単位ベクトル と呼ぶ。

単位ベクトルのメリット:

  • 方向が分かりやすい
  • 速度や距離と掛け合わせしやすい
  • 計算が安定する
  • 複数のオブジェクトで共通処理にできる

特に Three.js では、 「方向 = normalizeしたVector3」 という前提が多い。


■ 初心者が陥りやすいポイント

  • normalize() したら元の数値は変わる(元の値は保持されない)
  • dir = target - pos のまま使うと速度が不安定になる
  • 単位ベクトルは「方向専用」であり、「位置」には使わない

ここを理解すれば、Three.js の動きが一気に読みやすくなる。

3. 方向ベクトルの作り方(ゲームで最も使う部分)

3Dゲーム・WebXR・Three.jsで一番よく出るのが 「方向ベクトル」。 方向が分かれば、移動も回転も当たり判定も作れるようになる。

まずは完成形の一行:

const dir = new THREE.Vector3().subVectors(target, origin).normalize();

これが「origin → target へ進む向き(方向)」を作っている。


なぜ (target - origin) で方向になるのか

先に結論だけ示すと:

  • 位置の差 = 向いている方向

数学としては

方向 = target位置 − origin位置

で求まる。

イメージしやすい例

origin = (0, 0, 0) target = (3, 1, -2)

なら差は

(3 - 0, 1 - 0, -2 - 0)
= (3, 1, -2)

これは「origin から見てどっち方向に target があるか」を示す矢印そのもの。

向き(方向)というのは 基本的に“差分” で決まる ということだけ覚えれば充分。


なぜ normalize するのか(方向だけ取り出すため)

(target - origin) のままでは、向き+距離が混ざった ただの差ベクトル。

近いほど小さく、遠いほど大きくなる。

ゲーム処理で使う場合は 向きだけ必要なことが多いので、

.normalize()

をつけて 長さ1の矢印(単位ベクトル)に変換しておく。

単位ベクトルにする意味

  • どんな距離でも 一定速度で移動できる
  • 速度の大きさを直接掛け算できる
  • 回転処理が安定する
  • 行動AIやパーティクル処理で扱いやすい

差ベクトルのまま使うと、距離によって挙動が狂いやすい。 normalize は必須。


速度と掛け算すれば「移動ベクトル」に変わる

方向ベクトル dir は「向きだけ」。 そこに速度 speed を掛けると「進むための量」になる。

model.position.add(dir.multiplyScalar(speed));

この3つの処理で キャラが“ターゲットへまっすぐ進む” が完成する。

  1. 方向を作る
  2. 速度でスケールする
  3. 位置に足す

ゲームエンジンも Three.js も、 移動の基本は“方向 × 速度” で統一されている。


方向ベクトルを使う典型ケース

  • プレイヤーがターゲットへ移動する
  • 敵がプレイヤーへ追尾する
  • パーティクルが一定方向に飛ぶ
  • カメラが対象物を追いかける
  • Raycaster の方向を決める
  • LookAt の仕組みを自前で書く

方向ベクトルは 3D の動きの土台そのもの。


つまずきポイント

  • subVectors(target, origin) と subVectors(origin, target) を逆にすると逆方向になる
  • normalize を忘れると速度が不安定になる
  • multiplyScalar は「dir を汚しながら進む」ので、必要に応じて clone の使いどころがある

(ここは後の章で丁寧に扱っても良い)


ここまで理解できていれば、もう「3Dキャラを自由に動かす基礎」が身についている。次は距離の扱いに進むと、当たり判定や到達処理が自然に読めるようになる。

4. 距離の扱い(distanceTo の正しい使い方)

Vector3.distanceTo() は、2点間の“直線距離”を求めるためのメソッド。 Three.js で距離計算をするなら、まずこれを使う。

const distance = v1.distanceTo(v2);

計算しているのは数学でいうユークリッド距離:

distance = √((x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2)

3D空間で「どれくらい離れているか」を知る標準の方法。


使い所①:衝突判定(当たり判定の基本)

キャラクター同士、またはキャラとオブジェクトが「近づきすぎたら当たり」とする場合、距離で判定できる。

if (v1.distanceTo(v2) < hitRadius) {
  // 衝突!
}

球体ベースの判定は軽く、初心者にも扱いやすい。


使い所②:ターゲットへの到達チェック

移動処理で、目的地に着いたかを判断するのにも使う。

if (model.position.distanceTo(target) < threshold) {
  // 到着!
}

threshold は「だいたいこのくらいでゴール」を決める小さい値。


使い所③:カメラ距離でエフェクトを調整

代表例:

  • カメラが近いと透明にする
  • カメラが遠いとエフェクトを強くする
  • FPS 視点で「近すぎる物体を消す」

例:

const d = camera.position.distanceTo(model.position);

if (d < 2) mesh.visible = false;
else mesh.visible = true;

距離を使うことで、自然な視覚効果が作れる。


distanceTo の誤用例

「移動量」と混同するケース

初心者がよくやるのがこれ:

const distance = position.distanceTo(target);
position.add(direction.multiplyScalar(distance)); // ← 間違い

これだと、ターゲットが遠いほど移動量が激増しておかしくなる。

distance は「距離」であって「移動量ではない」。 移動には必ず 速度を掛ける必要がある。

正しくは:

const direction = new THREE.Vector3()
  .subVectors(target, position)
  .normalize();

position.add(direction.multiplyScalar(speed)); // 速度で移動

distance はあくまで「距離を知るため」にだけ使うのがポイント。


補足:距離²で比較するとさらに高速(上級)

衝突判定など大量に比較する場合:

if (v1.distanceToSquared(v2) < radius * radius) {
  // 衝突
}

平方根を避けるので高速。 最初は無理に覚えなくてもよいが、効率化の基本テクニック。


distanceTo をしっかり理解すると、 移動・衝突・到達・視認距離など、3D演出の幅がかなり広がる。

5. 内積と外積

ここから先は 3D数学の核心部分。 とはいえ、Three.js で必要なところだけ理解すれば十分使える。


内積(dot):2つのベクトルが“どれくらい同じ方向を向いているか”

const d = v1.dot(v2);

内積は 向きの一致度 と覚えるのが一番わかりやすい。

内積の値が示す意味

意味
1 完全に同じ方向
0 直角(90度)で交わる、つまり横向き
-1 真逆の方向

たったこれだけで 「見てる/見てない」 「向いてる/向いてない」 が判断できる。


1. 視野判定(AI / 当たり判定の応用)

敵キャラがプレイヤーを見ているか判定:

const forward = enemy.getWorldDirection(new THREE.Vector3());
const toPlayer = new THREE.Vector3().subVectors(player.position, enemy.position).normalize();

if (forward.dot(toPlayer) > 0.7) {
  // だいたい敵の正面方向(約45°以内)にプレイヤーがいる
}

ゲーム開発で超頻出する。


2. 角度の計算(向きの変化を数値化)

角度を出すなら:

const angle = v1.angleTo(v2); // ラジアン

dot() の内部で角度計算しているようなもの。


3. ライティング(光の当たり具合)

光の方向ベクトルと、面の法線ベクトルの内積で その面がどれだけ光を受けているか が求まる。

dot() が 1 に近いほど明るく、0に近づくほど暗くなる。


外積(cross):2つのベクトルに対して“垂直な方向”を作る

const cross = v1.clone().cross(v2);

外積は 「v1 と v2 の両方に垂直なベクトル」を作り出す計算。

3D空間で“上下”や“表裏”を判定したいときに使う。


外積の用途例

1. 地形に接地した法線(上向きベクトル)の取得

地面の傾斜から 正しい上方向(normal) を取れる。

const normal = edge1.clone().cross(edge2).normalize();

これは照明にも影響するし、接地処理にも必須。


2. 回転軸の生成

2つの方向ベクトルから「回転すべき軸」を作る。

例: キャラをある方向に向けるとき、 今の向き v1 と 目標方向 v2 から

const axis = v1.clone().cross(v2).normalize();

この axis を軸に回転すれば、自然に目標方向を向く。


3. LookAt の補正

Three.js の LookAt が意図した軸回転をしない場合、 外積を使って正しい矢印(up方向)を作り直す。


内積と外積は“3Dに必須の感覚”

初心者は最初ここでつまずくけど、 感覚としてはたったこれだけ:

  • 内積:どれくらい同じ方向か
  • 外積:2つから垂直方向を作る

この2つが理解できると、 移動・回転・照明・AI・カメラ制御 が一気に読みやすくなる。

6. 行列・クォータニオンとの連携(本格3D)

Three.js の 3D空間は、 位置(Vector3)・回転(Quaternion)・変換(Matrix4) の3つがセットで動いている。

Vector3 を扱うなら、 行列(Matrix4) と クォータニオン(Quaternion) の使い方は避けて通れない。

Three.js はこれを非常に分かりやすい API にしてくれている。


applyMatrix4():行列による空間変換をベクトルへ適用する

vector.applyMatrix4(matrix);

行列(Matrix4)は Three.js の “変換の塊”。

行列がまとめてできること:

  • 移動(Translate)
  • 回転(Rotate)
  • 拡大縮小(Scale)
  • ローカル座標 → ワールド座標への変換
  • 視点変換(カメラ行列)

Vector3 を applyMatrix4() すると、 行列に含まれているすべての変換を一度に適用できる。


行列は「まとめて変換できる道具」

たとえば:

  • プレイヤーのローカル座標
  • ボーンアニメーションの変換
  • モデルの親子構造(親オブジェクトの回転 → 子オブジェクトに伝播)

こういう「階層構造の変換」は すべて行列で表現される。

Three.js は内部で 親 → 子 → 孫 と行列を掛け合わせて最終的なワールド座標を作っている。

その変換をベクトルに直接適用できるのがこれ:

vector.applyMatrix4(object.matrixWorld);

これで「オブジェクトのワールド空間での位置や方向」が正しく得られる。


applyQuaternion():回転だけを Vector3 に適用

vector.applyQuaternion(q);

クォータニオン(Quaternion)とは、 3Dの回転を安定して扱うための数学表現。

Euler(オイラー角)には以下の問題がある:

  • ジンバルロック(特定角度で回転軸が死ぬ)
  • 回転順序問題
  • 補間がガタつく

Three.js は内部的に回転をすべて Quaternion で保持している。

applyQuaternion() は ベクトルを“クォータニオンが表す角度”だけ回転させるためのメソッド。


Quaternion の代表的な用途

  • LookAt の実装
  • スムーズな回転補間(SLERP)
  • ボーンアニメーションの回転
  • カメラの向き制御
  • VR空間のデバイス姿勢の取得

方向ベクトルを回転する場面は非常に多い。

例: キャラの「前方向ベクトル」をモデルの回転に同期させる:

const forward = new THREE.Vector3(0, 0, -1); // 初期の前方向
forward.applyQuaternion(model.quaternion);   // モデルが向いている方向に変換

これで正確な「モデルが今どっちを向いているか」が取れる。


行列・クォータニオンは“Vector3を正しく扱うための裏側の仕組み”

3D空間は 位置(Vector3) × 回転(Quaternion) × 変換(Matrix4) の三位一体。

Vector3 は “矢印” Quaternion は “回転” Matrix4 は “空間の変形と座標変換”

という役割分担になっている。

Three.js はこの複雑な処理を内部で自動化してくれているけど、 Vector3 を本格的に扱うなら、このつながりを知っておくと理解が一気に深くなる。

7. 「よくある罠」まとめ

Three.js の Vector3 を扱い始めると、多くの人が同じ場所でつまずく。 ここを押さえておくと、動きのバグや “なんか変な挙動” が一気に減る。


1. normalize を忘れて速度が毎フレーム変わる問題

よくある誤り:

const dir = new THREE.Vector3().subVectors(target, position);
position.add(dir); // ← ダメ

dir は 距離の大きさを含んでいるので、

  • 遠いと大ジャンプ
  • 近いとチョロっと動く

という 距離依存の不安定な移動になる。

正解:

const dir = new THREE.Vector3().subVectors(target, position).normalize();
position.add(dir.multiplyScalar(speed));

方向と距離の切り分けは超重要。


2. clone しないで参照が全部同じになる問題

Vector3 はオブジェクトなので、代入すると 参照がコピーされるだけ になる。

悪い例:

const a = new THREE.Vector3(1, 2, 3);
const b = a; // ← 同じVector3を指している
b.x = 10;

console.log(a.x); // 10 になる(初心者が驚くポイント)

独立したベクトルにしたいなら必ず clone:

const b = a.clone();

特に計算用のテンポラリが絡むと、ここでバグる。


3. subtract の順番を間違えて「逆方向になる」

方向ベクトルのあるあるミス:

const dir = new THREE.Vector3().subVectors(origin, target); // ← 逆方向

正しいのは:

subVectors(target, origin);
// 「origin → target」方向

逆にすると「ターゲットから遠ざかる」挙動になる。

見た目では気づきにくいぶん、バグの温床。


4. 毎フレーム new して GC(ガベージコレクション)負荷が増える問題

初心者コードでありがちなパターン:

function update() {
  const dir = new THREE.Vector3(); // 毎フレーム new
  dir.subVectors(target, position).normalize();
  position.add(dir.multiplyScalar(speed));
}

このように毎フレーム new Vector3() が多発すると、 メモリ負荷 → GC → フレーム落ち が起きやすい。

最適化した書き方:

const dir = new THREE.Vector3(); // 再利用

function update() {
  dir.subVectors(target, position).normalize();
  position.add(dir.multiplyScalar(speed));
}

Three.jsでは “不要な new を減らし、再利用する” がパフォーマンスの基本。


その他の細かい罠(補足)

✔ applyQuaternion の後 normalize が必要なときがある 回転後の方向ベクトルは長さが変わる場合がある。

✔ distanceTo の使いすぎは重い(大量の物体の場合) distanceToSquared() を使うと高速化できる。

✔ 複雑な連続演算では、clone を挟んで意図を明確化 読みやすさと安全性が上がる。


ここを理解しておくと、Three.js の Vector3 を実戦レベルで扱えるようになる。次は応用パターンに行くと、さらに “使いどころ” がクリアに見えるようになる。

8. 応用サンプルコードセット

ここからは 実戦で使う「Vector3 の典型パターン」 をまとめて紹介する。 あなたがゲームシステム・WebXR・Three.jsでよくやる処理ばかりを選んでいる。


プレイヤー追尾(AI)

敵がプレイヤーへまっすぐ進む最も基本的な追尾処理。

const dir = new THREE.Vector3()
  .subVectors(player.position, enemy.position) // 向き
  .normalize();                                // 単位ベクトル化

const speed = 0.05;
enemy.position.add(dir.multiplyScalar(speed));

ポイント:

  • 差分 → normalize → speed の3ステップは追尾の基本形
  • normalize で距離によらず一定速度になる

カメラの追跡(スムーズ追従)

カメラがプレイヤーに“ゆっくり追いつきながら”追跡する処理。

const targetPos = player.position;
const camPos = camera.position;

const dir = new THREE.Vector3()
  .subVectors(targetPos, camPos)
  .normalize();

const followSpeed = 0.02;
camera.position.add(dir.multiplyScalar(followSpeed));
camera.lookAt(player.position);

ポイント:

  • lookAt と組み合わせて安定した追従が作れる
  • 距離によってスピードが変わらないので自然なカメラ移動になる

パーティクルの進行方向

パーティクル1つ1つの「飛ぶ方向」をベクトルで持たせて動かす。

particle.direction = new THREE.Vector3(
  Math.random() * 2 - 1,
  Math.random() * 2 - 1,
  Math.random() * 2 - 1
).normalize();

particle.velocity = 0.1;

function updateParticle(particle) {
  particle.position.add(
    particle.direction.clone().multiplyScalar(particle.velocity)
  );
}

ポイント:

  • direction は単位ベクトルにしておく
  • velocity の値で調整するだけなので管理が楽

Raycaster と組み合わせた当たり判定

Ray(線)をベクトルで飛ばして「何かに当たったか」を調べる典型パターン。

const origin = camera.position.clone();

const direction = new THREE.Vector3(0, 0, -1);
direction.applyQuaternion(camera.quaternion); // カメラの向きに合わせる
direction.normalize();

raycaster.set(origin, direction);

const hits = raycaster.intersectObjects(scene.children, true);

if (hits.length > 0) {
  console.log("Hit:", hits[0].object);
}

ポイント:

  • Raycaster の向きは Vector3 で指定する
  • camera.quaternion を apply して正確な向きを作る

LookAt の裏側をベクトルで書く(内部で何が起きてるか)

object.lookAt(target) を自前でやるとこうなる。

// 前方向を作る(target - object)
const forward = new THREE.Vector3()
  .subVectors(target, object.position)
  .normalize();

// 現在のforward方向
const currentForward = object.getWorldDirection(new THREE.Vector3());

// 回転軸(外積)
const axis = currentForward.clone().cross(forward).normalize();

// 回転角(内積 → angleTo)
const angle = currentForward.angleTo(forward);

// クォータニオンを作って回転
const q = new THREE.Quaternion().setFromAxisAngle(axis, angle);

object.quaternion.multiply(q);

これが lookAt が内部でやっている“方向を合わせる数学”。

ポイント:

  • 外積 → 回転軸
  • 内積 → 回転角
  • Quaternion → 実際に回す

この理解があると、LookAt が意図しない回転をしたときでも調整できる。


Vector3 をここまで使えるようになると、3Dでの「移動・回転処理」がほぼ読めるようになる。 次に書く章(WebDev 出身者が勘違いしがちな部分)まで行けば、記事としても完成度が高くなる。

9. WebDev から Three.js に来た人が誤解しがちなポイント

Three.js に入ってくる多くの Web 開発者が、必ず抱く“勘違いポイント”がいくつかある。 2D/DOM の世界観と、3D/線形代数の世界観はまったく違うため、最初にここを理解しておくとつまずきが減る。


「配列で持ってもよくね?」の誤解

Web 開発者はよくこう思う:

const pos = [1, 1, 1]; // これで良くない?

気持ちは分かるけど Three.js では 決定的に困る。

理由:

  1. 配列には加算・減算・法線計算・距離計算などの 3D数学メソッドが全く無い
  2. Three.js の API のほとんどが Vector3 前提 → 結局どこかで Vector3 に変換する必要がある
  3. 行列計算や回転(Quaternion)との連携ができない → 3D空間の変換が破綻する

「配列でやる」という選択は 長期的に必ず損をする。

Three.js では Vector3 を使う方が圧倒的に自然で安全。


「DOM は2Dだけど Three.js は数学の世界」

Web の世界は基本 2D + レイアウトルール で動いている。

  • 左上スタート
  • 座標は x,y のみ
  • CSS がレイアウトを解決
  • 回転は transform がやる
  • 距離や角度を手計算することは少ない

対して Three.js は 3D数学が土台。

  • x,y,z の3軸で位置が決まる
  • 奥行きの概念がある
  • 視点(カメラ)の存在
  • 回転は Quaternion または行列で処理
  • 距離・角度・方向は自分で計算

DOM に慣れた人ほど初期に混乱する。 しかし Three.js に慣れると、この数学的世界がむしろ直感的に感じてくる。


「線形代数を避けても結局後で理解が必要になる」

最初はベクトルや行列を「なんとなく使う」でも動く。

けれど Three.js に深く踏み込むと、 必ずこういうシーンに出会う:

  • カメラが変な方向を向く
  • アニメーションが不自然な回転をする
  • LookAt が急にグルっと回る
  • 衝突判定が不安定
  • WebXR で視線がズレる
  • 親子モデルの位置調整が破綻する

これらはほぼすべて 「ベクトル・行列・クォータニオンの理解不足」が原因。

数学が目的ではないけど、 “3Dを自在に操るための最低限の知識” として避けられない。

Three.js の強みは、 複雑な部分を簡単に扱える API が揃っていること。 だからこそ、基礎をちょっと理解しておけば 急に世界がシンプルに見えてくる。