3D空間ベクトルと外積(クロス積)
3次元空間におけるポリゴンの表裏・法線ベクトルと3DCGライティングの数理
- 1
- 3次元のベクトルは空間内の「向きと大きさ」を持ち、内積は角度や影の濃さ、外積(クロス積)は面に直交する「法線ベクトル」を求める
- 2
- 外積 a × b の向きは「右手系」の親指の向きになり、その大きさは2つのベクトルが張る平行四辺形の面積に一致する
- 3
- 現代の3Dゲーム・CG映画では、ポリゴンの法線ベクトルと光源の内積を計算することで、すべてのポリゴンの陰影(シェーディング)を決定している
なぜこれを学ぶのか? — 現実社会とテクノロジーでの価値
なぜ生まれたのか?
2次元(平面)の幾何学だけでは、3次元空間の傾きや面の向き、立体同士の相互作用を記述できなかったため。
身につく武器・思考力
3D空間内のあらゆる物体に対し、「面がどこを向いているか(法線)」や「光がどの角度で当たっているか」を瞬時に計算できる空間把握力が手に入る。
実社会・ITでの活躍現場
- •『ゼルダ』や『フォートナイト』など現代3Dゲームのグラフィックス描画
- •自動運転車やドローンのLiDAR(光センサー)による3D点群・空間認識
- •ロボットアームの先端の3次元姿勢制御(順運動学・逆運動学)
- •3DCG映画のライティング(ランバート拡散反射・フォン鏡面反射)
Layer 1 — 直感・シミュレーター
スライダーで3Dポリゴン面の傾き(回転角)や光源の位置を操作してみよう。ポリゴンから突き出る「法線ベクトル(赤い矢印)」の向きと、光の当たり具合(ランバート反射モデル)によって、面の明るさがリアルタイムに変化する仕組みを体感できる。
ベクトル a = (1, 0, 0) と b = (0, 1, 0) の外積 a × b はどれか?
Layer 2 — 定義・受験・証明
定義
3次元ベクトル と に対し、両者に直交するベクトルを「外積(クロス積)」と呼び、 と表す。 成分表示:
幾何学的性質: 大きさは であり、向きは から へ右ネジを回したときに進む方向(右手の法則)である。
公式・数理モデル
証明・導出
法線ベクトル直交性の証明: 外積の定義から、 と の内積を計算すると、
展開すると、
すべての項が完全に相殺して になる。同様に となる。 内積が 0 であることは両者が 直交していることを意味するため、外積は2つのベクトルが張る平面に対して常に真垂直な「法線ベクトル(Normal Vector)」を与える。
押さえておきたいポイント
- 外積は2本のベクトルから「面に垂直な法線ベクトル」を一瞬で生み出す
- (掛ける順序を逆にすると向きが真逆の裏側を向く)
- ポリゴンの表裏判定(裏向きのポリゴンを描画しないカリング処理)は外積と視線ベクトルの向きで判定する
- ランバート反射モデル: 面の明るさ (法線 と光線 の内積)
本トピックの内容・数理モデルは、文部科学省学習指導要領(算数・数学・情報)および国際標準カリキュラムに準拠し、エンジニアリング現場の実装事例と照らし合わせてMath World Lab編集部により検証・作成されています。
Layer 3 — 工学応用・現場実装
3DCGポリゴンの法線ベクトル計算とランバート拡散反射シェーディング
3次元の三角形ポリゴン(3頂点 )から外積を用いて法線ベクトル を算出し、平行光源ベクトル との内積によってポリゴンの陰影(シェーディング)を計算するTypeScript実装。
1type Vec3 = { x: number; y: number; z: number };23// 3次元ベクトルの外積(クロス積): a × b4function crossProduct(a: Vec3, b: Vec3): Vec3 {5 return {6 x: a.y * b.z - a.z * b.y,7 y: a.z * b.x - a.x * b.z,8 z: a.x * b.y - a.y * b.x,9 };10}1112// ベクトルの正規化(長さを1にする)13function normalize(v: Vec3): Vec3 {14 const len = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);15 return len === 0 ? { x: 0, y: 0, z: 0 } : { x: v.x / len, y: v.y / len, z: v.z / len };16}1718// 3頂点からポリゴンの単位法線ベクトルを算出19function computePolygonNormal(p1: Vec3, p2: Vec3, p3: Vec3): Vec3 {20 const edge1 = { x: p2.x - p1.x, y: p2.y - p1.y, z: p2.z - p1.z };21 const edge2 = { x: p3.x - p1.x, y: p3.y - p1.y, z: p3.z - p1.z };22 return normalize(crossProduct(edge1, edge2));23}2425// ランバート拡散反射による明るさ計算 (0.0〜1.0)26function calculateLambertShading(normal: Vec3, lightDir: Vec3): number {27 const dot = normal.x * lightDir.x + normal.y * lightDir.y + normal.z * lightDir.z;28 return Math.max(0, dot); // 光と反対側は0(暗黒)29}3031// 例: XY平面上の三角形ポリゴンと、斜め上からの平行光32const p1: Vec3 = { x: 0, y: 0, z: 0 };33const p2: Vec3 = { x: 1, y: 0, z: 0 };34const p3: Vec3 = { x: 0, y: 1, z: 0 };3536const normal = computePolygonNormal(p1, p2, p3);37console.log(`ポリゴン法線: (${normal.x}, ${normal.y}, ${normal.z})`); // (0, 0, 1)3839const light = normalize({ x: 0, y: 0.5, z: 0.866 }); // 約60度の光40const brightness = calculateLambertShading(normal, light);41console.log(`面の明るさ: ${(brightness * 100).toFixed(1)}%`); // 約 86.6%
次に学ぶならコレ!おすすめトピック
この単元と深い繋がりを持つ、あわせて学ぶと理解が深まるおすすめ単元