第1章:3Dグラフィックスの数学的基礎
1.1 コンピュータグラフィックスの誕生
1.1.1 画面上の点を描くということ
コンピュータグラフィックスの歴史は、「画面上に点を描く」という単純な行為から始まりました。1963年、MITの大学院生アイバン・サザランドは、博士論文として「Sketchpad」を発表しました。これは、コンピュータ画面上で図形を対話的に描画・編集できる最初のシステムでした。
Sketchpadの革新性(1963年):
従来のコンピュータ出力:
┌─────────────────────┐
│ RESULT: 42 │ ← テキストのみ
│ ERROR: OVERFLOW │
└─────────────────────┘
Sketchpadの出力:
┌─────────────────────┐
│ ◇───────◇ │ ← 図形を直接操作
│ ╱ ╲ │
│ ◇ ◇ │
│ ╲ ╱ │
│ ◇───────◇ │
└─────────────────────┘
サザランドの洞察:
「コンピュータは数値だけでなく、視覚的な情報を
処理・表示できる」
この革命的なアイデアは、CAD(コンピュータ支援設計)、映画のCG、ビデオゲーム、そして42のFdFプロジェクトへと繋がっていきます。
1.1.2 ピクセルとフレームバッファ
現代のディスプレイは、小さな光る点(ピクセル)の集合体です。各ピクセルの色を個別に制御することで、あらゆる画像を表示できます。
ピクセルの物理的構造:
1ピクセル(拡大図):
┌─────────────────┐
│ R G B │
│ ┌──┐ ┌──┐ ┌──┐ │
│ │赤│ │緑│ │青│ │ ← 3つのサブピクセル
│ └──┘ └──┘ └──┘ │
└─────────────────┘
各サブピクセルの輝度: 0〜255(8ビット)
色の表現例:
純赤: R=255, G=0, B=0 → (0xFF0000)
純緑: R=0, G=255, B=0 → (0x00FF00)
純青: R=0, G=0, B=255 → (0x0000FF)
白: R=255, G=255, B=255 → (0xFFFFFF)
黒: R=0, G=0, B=0 → (0x000000)
42黄: R=255, G=200, B=0 → (0xFFC800)
フレームバッファは、画面に表示するピクセルデータを保持するメモリ領域です。
フレームバッファの構造:
解像度: 800×600 の場合
メモリ配置(連続した配列):
┌────────────────────────────────────────────────┐
│ P[0,0] │ P[1,0] │ P[2,0] │ ... │ P[799,0] │ ← 1行目
├────────────────────────────────────────────────┤
│ P[0,1] │ P[1,1] │ P[2,1] │ ... │ P[799,1] │ ← 2行目
├────────────────────────────────────────────────┤
│ ... │
├────────────────────────────────────────────────┤
│ P[0,599] │ P[1,599] │ ... │ P[799,599] │ ← 600行目
└────────────────────────────────────────────────┘
ピクセルのアドレス計算:
pixel_address = y * width + x
例: P[100, 50]のアドレス
= 50 * 800 + 100
= 40100
1ピクセル = 4バイト (ARGB) の場合:
byte_offset = pixel_address * 4
= 40100 * 4 = 160400
このアドレス計算は、FdFでピクセルを描画する際の基本となります。
1.1.3 ラスタライゼーションの概念
3D空間の図形を2D画面に表示するプロセスをラスタライゼーションと呼びます。
ラスタライゼーションの流れ:
1. 3D空間の頂点定義
┌─────────────────────────────┐
│ (0,0,5) │
│ △ │
│ ╱ ╲ │
│ ╱ ╲ │
│ ╱ ╲ │
│ (−2,0,0) (2,0,0) │
└─────────────────────────────┘
2. 座標変換(後述)
↓
3. 2D投影
┌─────────────────────────────┐
│ ・ │
│ ╱ ╲ │
│ ╱ ╲ │
│ ・─────・ │
└─────────────────────────────┘
4. ラスタライズ(ピクセル化)
┌─────────────────────────────┐
│ ░░ │
│ ░░░░ │
│ ░░░░░░ │
│ ░░░░░░░░ │
│ ░░░░░░░░░░ │
└─────────────────────────────┘
FdFでは、3Dの地形データを2D画面に投影し、ワイヤーフレームとして描画します。
1.2 座標系の理解
1.2.1 デカルト座標系
17世紀、フランスの哲学者・数学者ルネ・デカルトは、空間内の点を数値の組で表現する方法を考案しました。これがデカルト座標系です。
2D デカルト座標系:
y
↑
│
3 │ ・P(2,3)
│ ╱
2 │ ╱
│╱
1 │
│
─────┼────────→ x
│1 2 3
-1 │
│
点P(2,3)は、原点から:
- x軸方向に2単位
- y軸方向に3単位
の位置にある
3D デカルト座標系:
y
↑
│
│ ・P(2,3,4)
│ ╱│
│ ╱ │
│ ╱ │
│╱ │
─────┼────┼────→ x
╱│ │
╱ │ │
╱ │ │
↙ │
z
右手座標系:
- 親指: x軸正方向
- 人差し指: y軸正方向
- 中指: z軸正方向
1.2.2 FdFにおける座標系
FdFでは、入力データの座標系と画面座標系の関係を理解することが重要です。
入力ファイルの座標系:
ファイル内容(例: 42.fdf):
0 0 0 0 0
0 1 2 1 0
0 2 4 2 0
0 1 2 1 0
0 0 0 0 0
この数値の意味:
┌─────────────────────────────────────┐
│ (x, y, z) の解釈: │
│ │
│ x: 列番号(0から右へ) │
│ y: 行番号(0から下へ) │
│ z: 高さ(数値そのもの) │
│ │
│ 例: "4" は座標 (2, 2, 4) を表す │
│ 列2、行2、高さ4 │
└─────────────────────────────────────┘
3D空間での可視化:
z
↑
4 │ ◆
│ ╱ ╲
2 │ ◇─────◇
│╱ ╲
0 ◇───────────◇──→ x
╱
↙
y
画面座標系:
(0,0)─────────────────────→ x
│
│ ┌────────────────┐
│ │ │
│ │ 画面領域 │
│ │ │
│ └────────────────┘
│
↓
y
注意: 画面座標系では y軸が下向き
→ 数学的座標系とは上下が逆
この座標系の違いは、投影変換で考慮する必要があります。
1.2.3 同次座標
3D変換を効率的に行うために、同次座標という拡張座標系を使用します。
通常の座標と同次座標:
3D点 P(x, y, z):
通常座標: (x, y, z)
同次座標: (x, y, z, 1) ← 4番目の要素を追加
なぜ同次座標が必要か?
平行移動を行列で表現するため:
通常の3×3行列では平行移動を表現できない:
┌ ┐ ┌ ┐ ┌ ┐
│ a b c │ │ x │ │ ax+by+cz │
│ d e f │ │ y │ = │ dx+ey+fz │ ← 回転・スケールのみ
│ g h i │ │ z │ │ gx+hy+iz │
└ ┘ └ ┘ └ ┘
4×4行列(同次座標)では平行移動も可能:
┌ ┐ ┌ ┐ ┌ ┐
│ a b c tx│ │ x │ │ ax+by+cz+tx │
│ d e f ty│ │ y │ = │ dx+ey+fz+ty │ ← +平行移動
│ g h i tz│ │ z │ │ gx+hy+iz+tz │
│ 0 0 0 1 │ │ 1 │ │ 1 │
└ ┘ └ ┘ └ ┘
1.3 ベクトルと行列の基礎
1.3.1 ベクトルの本質
ベクトルは「方向と大きさ」を持つ量です。物理学では力や速度を表現し、グラフィックスでは位置や方向を表現します。
ベクトルの視覚的理解:
位置ベクトル:
原点Oから点Pへのベクトル OP
↗ P(3,2)
╱
╱
╱
O(0,0)
方向ベクトル:
点Aから点Bへのベクトル AB
A(1,1)────→ B(4,3)
AB = B - A = (4-1, 3-1) = (3, 2)
ベクトルの成分:
→
v = (vx, vy, vz)
大きさ: |v| = √(vx² + vy² + vz²)
ベクトルの基本演算:
加算:
→ →
a + b = (ax+bx, ay+by, az+bz)
┌────→ ┌────→────→
│ a │ a b
│ │ └────→
│────→ │ 結果
b
スカラー倍:
→
k × v = (k·vx, k·vy, k·vz)
k = 2 の場合:
→
v ─────→
───→
2v ─────────→ (2倍の長さ)
内積(ドット積):
→ →
a · b = ax·bx + ay·by + az·bz
幾何学的意味: |a| × |b| × cos(θ)
応用:
- θ = 0° → a·b > 0(同じ方向)
- θ = 90° → a·b = 0(直交)
- θ = 180° → a·b < 0(逆方向)
外積(クロス積):
→ →
a × b = (ay·bz - az·by,
az·bx - ax·bz,
ax·by - ay·bx)
結果: aとbの両方に直交するベクトル
→ →
a × b
↑
│
→ │ →
a ──┼── b
1.3.2 行列の役割
行列は、座標変換を表現する最も効率的な方法です。
行列の基本構造:
2×2行列: 3×3行列: 4×4行列:
┌ ┐ ┌ ┐ ┌ ┐
│ a b │ │ a b c │ │ a b c d │
│ c d │ │ d e f │ │ e f g h │
└ ┘ │ g h i │ │ i j k l │
└ ┘ │ m n o p │
└ ┘
行列の乗算:
行列 × ベクトル:
┌ ┐ ┌ ┐ ┌ ┐
│ a b │ │ x │ = │ ax + by │
│ c d │ │ y │ │ cx + dy │
└ ┘ └ ┘ └ ┘
計算の図解:
┌ ┐
│ a b │ ←── 第1行
│ c d │ ←── 第2行
└ ┘
↓
┌ ┐
│ x │
│ y │
└ ┘
↓
第1要素: a×x + b×y (第1行とベクトルの内積)
第2要素: c×x + d×y (第2行とベクトルの内積)
行列 × 行列:
A × B = C の計算:
┌ ┐ ┌ ┐ ┌ ┐
│ a b c │ │ j k l │ │ C[0,0] ... │
│ d e f │ × │ m n o │ = │ ... ... │
│ g h i │ │ p q r │ │ ... C[2,2] │
└ ┘ └ ┘ └ ┘
C[i,j] = Aのi行とBのj列の内積
例: C[0,0] = a×j + b×m + c×p
C[0,1] = a×k + b×n + c×q
...
重要な性質:
A × B ≠ B × A (非可換)
→ 変換の順序が重要!
1.3.3 単位行列と逆行列
単位行列(Identity Matrix):
3×3単位行列:
┌ ┐
│ 1 0 0 │
│ 0 1 0 │ = I₃
│ 0 0 1 │
└ ┘
性質: A × I = I × A = A
(何も変換しない行列)
逆行列:
A × A⁻¹ = A⁻¹ × A = I
意味: 変換を「元に戻す」行列
例: 回転行列 R で30°回転した後、
R⁻¹ を適用すると元の位置に戻る
1.4 座標変換の数学
1.4.1 平行移動(Translation)
平行移動は、全ての点を同じ方向に同じ距離だけ移動させる変換です。
平行移動の視覚化:
移動前: 移動後(tx=3, ty=2):
◇ ◇
╱ ╲ ╱ ╲
◇───◇ → ◇───◇
(0,0)-(2,0)-(1,2) (3,2)-(5,2)-(4,4)
平行移動行列(4×4同次座標):
T(tx, ty, tz) = ┌ ┐
│ 1 0 0 tx │
│ 0 1 0 ty │
│ 0 0 1 tz │
│ 0 0 0 1 │
└ ┘
適用:
┌ ┐ ┌ ┐ ┌ ┐
│ 1 0 0 tx │ │ x │ │ x + tx │
│ 0 1 0 ty │ │ y │ = │ y + ty │
│ 0 0 1 tz │ │ z │ │ z + tz │
│ 0 0 0 1 │ │ 1 │ │ 1 │
└ ┘ └ ┘ └ ┘
1.4.2 スケーリング(Scaling)
スケーリングは、オブジェクトの大きさを変更する変換です。
スケーリングの視覚化:
原図形: 2倍スケール:
◇ ◇
│ ╱│
◇─◇ → ╱ │
◇ │
│ │
◇──◇
スケーリング行列:
S(sx, sy, sz) = ┌ ┐
│ sx 0 0 0 │
│ 0 sy 0 0 │
│ 0 0 sz 0 │
│ 0 0 0 1 │
└ ┘
均一スケーリング: sx = sy = sz
非均一スケーリング: 軸ごとに異なる倍率
適用:
┌ ┐ ┌ ┐ ┌ ┐
│ sx 0 0 0 │ │ x │ │ sx·x │
│ 0 sy 0 0 │ │ y │ = │ sy·y │
│ 0 0 sz 0 │ │ z │ │ sz·z │
│ 0 0 0 1 │ │ 1 │ │ 1 │
└ ┘ └ ┘ └ ┘
1.4.3 回転(Rotation)
回転は、ある軸を中心にオブジェクトを回転させる変換です。
2D回転:
点P(x,y)を原点中心にθ度回転:
y y
↑ ↑ P'
│ P │ ╱
│ ╱ │ ╱ θ
│ ╱ → │ ╱
│ ╱ │╱────P
└───────x └───────x
P' = (x·cos(θ) - y·sin(θ), x·sin(θ) + y·cos(θ))
2D回転行列:
R(θ) = ┌ ┐
│ cos(θ) -sin(θ) │
│ sin(θ) cos(θ) │
└ ┘
3D回転(各軸周り):
X軸周りの回転:
y y'
↑ θ ╱
│ ╱ ╱
│ ╱ ╱
│╱ → ╱
└───────z └───────z'
Rx(θ) = ┌ ┐
│ 1 0 0 0 │
│ 0 cos(θ) -sin(θ) 0 │
│ 0 sin(θ) cos(θ) 0 │
│ 0 0 0 1 │
└ ┘
Y軸周りの回転:
Ry(θ) = ┌ ┐
│ cos(θ) 0 sin(θ) 0 │
│ 0 1 0 0 │
│ -sin(θ) 0 cos(θ) 0 │
│ 0 0 0 1 │
└ ┘
Z軸周りの回転:
Rz(θ) = ┌ ┐
│ cos(θ) -sin(θ) 0 0 │
│ sin(θ) cos(θ) 0 0 │
│ 0 0 1 0 │
│ 0 0 0 1 │
└ ┘
1.4.4 変換の合成
複数の変換を一つの行列にまとめることができます。
変換の合成:
「スケール → 回転 → 平行移動」の順序で変換する場合:
M = T × R × S
注意: 行列の乗算は右から左に適用される!
計算例:
v' = T × R × S × v
= T × R × (S × v) ← まずスケーリング
= T × (R × ...) ← 次に回転
= T × ... ← 最後に平行移動
なぜこの順序が重要か?
パターン1: 平行移動 → 回転
◇ ◇
│ 移動→ ←───→│ 回転→ ◇──
◇─◇ ◇─◇ ╲
◇
パターン2: 回転 → 平行移動
◇ ◇
│ 回転→ ╲ 移動→ ╲
◇─◇ ◇── ◇──
↗ ↗
◇ ◇
→ 結果が異なる!
1.5 投影変換
1.5.1 投影の種類
3D空間の物体を2D画面に表示するには、投影変換が必要です。
投影の2大分類:
1. 平行投影(Parallel Projection)
- 投影線が平行
- 遠近感なし
- 製図、CADで使用
光源(無限遠)
↓ ↓ ↓ ↓
┌─────────────────┐
│ 立方体 │
└─────────────────┘
↓ ↓ ↓ ↓
══════════════════ ← 投影面
2. 透視投影(Perspective Projection)
- 投影線が一点に収束
- 遠近感あり
- 写真、3Dゲームで使用
視点
╲ │ ╱
╲│╱
┌─────────────────┐
│ 立方体 │
└─────────────────┘
╱│╲
╱ │ ╲
════════════════════ ← 投影面
1.5.2 アイソメトリック投影
FdFで最もよく使われる投影方式がアイソメトリック(等角)投影です。
アイソメトリック投影の特徴:
3つの軸が等しい角度(120°)で交わる:
y
↑
╱│╲
╱ │ ╲
╱ 120° ╲
↙─────────↘
z x
視覚的効果:
- 立方体が対称に見える
- 全ての軸方向の長さが同じ比率
- 42のマップを見やすく表示できる
アイソメトリック投影の行列:
標準的なアイソメトリック投影:
1. X軸周りに約35.264°(arctan(1/√2))回転
2. Z軸周りに45°回転
または、直接計算式を使用:
screen_x = (x - z) × cos(30°)
screen_y = y + (x + z) × sin(30°)
30°の三角関数:
cos(30°) = √3/2 ≈ 0.866
sin(30°) = 1/2 = 0.5
実装例(擬似コード):
iso_x = (x - z) * 0.866
iso_y = y * -1 + (x + z) * 0.5 // yを反転(画面座標系)
アイソメトリック投影の視覚化:
入力(3D地形): 出力(2D画面):
z ╱╲
↑ ╱ ╲
│ ╱╲ ╱ ╲
│ ╱ ╲ ╱ ╲
│ ╱────╲ ╱ ◆ ╲
│ ╱──────╲ ╱ ╱╲╲ ╲
└─────────→ x ╱ ╱ ╲╲ ╲
╱ ╱───╱────╲╲───╲
↙ ╱───╱──────╲╲───╲
y ╱───────────────╲
1.5.3 投影パラメータの調整
FdFでは、ユーザーが視点を変更できるようにすることが求められます。
調整可能なパラメータ:
1. 回転角度
- X軸回転: 俯瞰角度の変更
- Y軸回転: 地形を横から見る
- Z軸回転: 地形を回す
2. ズーム(スケール)
- 拡大/縮小
3. 平行移動
- 画面上での位置調整
4. 投影タイプ
- アイソメトリック
- 平行(上面図、側面図)
- パースペクティブ(オプション)
キーボード操作の例:
W/S: X軸回転
A/D: Y軸回転
Q/E: Z軸回転
+/-: ズーム
矢印キー: 平行移動
I/P: 投影切り替え
1.6 色彩理論の基礎
1.6.1 RGB色空間
RGB(加法混色):
光の三原色を組み合わせて色を作る:
Red (255,0,0)
╱╲
╱ ╲
╱ M ╲ ← Magenta (255,0,255)
╱──────╲
╱ W ╲ ← White (255,255,255)
╱──────────╲
╱ C Y ╲
╱──────────────╲
Green (0,255,0) Blue (0,0,255)
C = Cyan (0,255,255)
Y = Yellow (255,255,0)
1.6.2 高さに基づく色付け
FdFでは、地形の高さに応じて色を変えることで、立体感を強調できます。
高さと色のマッピング:
低い ───────────────────→ 高い
z=0 z=max
青 赤
┃━━━━━━━━━━━━━━━━━━━━━━━━┃
🔵 → 🟢 → 🟡 → 🟠 → 🔴
グラデーション計算:
ratio = z / z_max // 0.0 〜 1.0
// 線形補間
r = low_r + (high_r - low_r) * ratio
g = low_g + (high_g - low_g) * ratio
b = low_b + (high_b - low_b) * ratio
color = (r << 16) | (g << 8) | b
標高カラーマップの例:
高度 色 RGB値
─────────────────────────────
< 0 深海青 (0, 0, 128)
0 海青 (0, 0, 255)
1-3 砂浜黄 (255, 255, 128)
4-10 草原緑 (0, 255, 0)
11-20 森林緑 (0, 128, 0)
21-40 岩山灰 (128, 128, 128)
41-60 山頂茶 (139, 90, 43)
> 60 雪白 (255, 255, 255)
1.7 プログラミングへの橋渡し
1.7.1 数学とコードの対応
ここまで学んだ数学的概念を、C言語のコードにどう対応させるか見ていきましょう。
// ベクトルの表現
typedef struct s_vec3
{
float x;
float y;
float z;
} t_vec3;
// 4×4行列の表現
typedef struct s_mat4
{
float m[4][4];
} t_mat4;
// ベクトル加算
t_vec3 vec3_add(t_vec3 a, t_vec3 b)
{
return ((t_vec3){a.x + b.x, a.y + b.y, a.z + b.z});
}
// ベクトルのスカラー倍
t_vec3 vec3_scale(t_vec3 v, float k)
{
return ((t_vec3){v.x * k, v.y * k, v.z * k});
}
// 内積
float vec3_dot(t_vec3 a, t_vec3 b)
{
return (a.x * b.x + a.y * b.y + a.z * b.z);
}
// 行列×ベクトル(同次座標)
t_vec3 mat4_transform(t_mat4 m, t_vec3 v)
{
float w;
t_vec3 result;
result.x = m.m[0][0] * v.x + m.m[0][1] * v.y
+ m.m[0][2] * v.z + m.m[0][3];
result.y = m.m[1][0] * v.x + m.m[1][1] * v.y
+ m.m[1][2] * v.z + m.m[1][3];
result.z = m.m[2][0] * v.x + m.m[2][1] * v.y
+ m.m[2][2] * v.z + m.m[2][3];
w = m.m[3][0] * v.x + m.m[3][1] * v.y
+ m.m[3][2] * v.z + m.m[3][3];
// 同次座標の正規化(wで割る)
if (w != 1.0f && w != 0.0f)
{
result.x /= w;
result.y /= w;
result.z /= w;
}
return (result);
}
1.7.2 データ構造の設計
FdFのデータ構造を設計する際の考え方:
// 点(頂点)の表現
typedef struct s_point
{
int x; // グリッド上のx座標
int y; // グリッド上のy座標
int z; // 高さ
int color; // 色(RGBパック)
} t_point;
// 変換後の画面座標
typedef struct s_screen_point
{
int x; // 画面上のx座標
int y; // 画面上のy座標
int color; // 色
} t_screen_point;
// マップ全体
typedef struct s_map
{
int width; // マップの幅
int height; // マップの高さ
t_point **points; // 2次元配列
int z_min; // 最小高度
int z_max; // 最大高度
} t_map;
// カメラ(視点)設定
typedef struct s_camera
{
float rot_x; // X軸回転角
float rot_y; // Y軸回転角
float rot_z; // Z軸回転角
float zoom; // ズーム倍率
int offset_x; // 画面オフセットX
int offset_y; // 画面オフセットY
int projection; // 投影タイプ
} t_camera;
1.8 本章のまとめ
この章では、FdFプロジェクトの数学的基礎を学びました。
学習内容の階層構造:
┌─────────────────────────────────────────────────────┐
│ FdFプロジェクト │
├─────────────────────────────────────────────────────┤
│ ワイヤーフレーム描画 │ ユーザーインタラクション │
├─────────────────────────────────────────────────────┤
│ 投影変換 │ 色彩計算 │
│ (アイソメトリック) │ (グラデーション) │
├─────────────────────────────────────────────────────┤
│ 座標変換(回転・スケール・移動) │
├─────────────────────────────────────────────────────┤
│ 行列演算・ベクトル演算 │
├─────────────────────────────────────────────────────┤
│ 座標系・同次座標の理解 │
├─────────────────────────────────────────────────────┤
│ コンピュータグラフィックス基礎 │
└─────────────────────────────────────────────────────┘
重要ポイント:
- 座標系の理解: 3D座標、画面座標、同次座標の違いと変換
- ベクトルと行列: 座標変換の数学的基盤
- 変換の合成: 複数の変換を一つの行列にまとめる効率性
- 投影変換: 3D→2Dへの変換方法
- 色彩理論: 高さ情報の視覚化
次章では、MiniLibXライブラリを使ったグラフィックスプログラミングの実践に進みます。