title: "3Dシーン" description: "複数のオブジェクト、マテリアル、カメラ制御を組み合わせて、完成度の高い3Dシーンを構築します。これまで学んだテクニックの集大成です。" chapter: "09-advanced" order: 4 challenge: description: "オリジナルの3Dシーンを作りましょう!これまで学んだテクニックを自由に組み合わせて、独自の3Dシーンを作成してください。複数のオブジェクト、ライティング、色使いを工夫しましょう。" hints:
- "SDFの基本形状を組み合わせましょう。min()で合体、max()で交差、max(a,-b)で切り抜きができます。"
- "カメラを時間で回転させると、シーンを様々な角度から見られます。"
- "ボックスSDFのsdBox関数も使えます。abs(p)-bで各軸の距離を求め、lengthとmin/maxで外部・内部の距離を計算します。"
3Dシーン
シーンの構成要素
レイマーチングで本格的な3Dシーンを作るために、いくつかの要素を組み合わせます:
- 複数のオブジェクト — SDF の組み合わせ
- マテリアル — オブジェクトごとの色と質感
- カメラ制御 — 視点の位置と向き
- ライティング — 光と影
基本的なSDF形状
球体以外にも様々なSDF形状があります:
ボックス
float sdBox(vec3 p, vec3 b) {
vec3 d = abs(p) - b;
return length(max(d, 0.0)) + min(max(d.x, max(d.y, d.z)), 0.0);
}
トーラス
float sdTorus(vec3 p, vec2 t) {
vec2 q = vec2(length(p.xz) - t.x, p.y);
return length(q) - t.y;
}
平面
float sdPlane(vec3 p, float h) {
return p.y - h;
}
SDFの組み合わせ
SDFの強力な点は、ブーリアン演算で複雑な形状を作れることです:
// 合体(Union)
float opUnion(float d1, float d2) {
return min(d1, d2);
}
// 交差(Intersection)
float opIntersection(float d1, float d2) {
return max(d1, d2);
}
// 切り抜き(Subtraction)
float opSubtraction(float d1, float d2) {
return max(d1, -d2);
}
滑らかな合体
min の代わりに smooth min を使うと、オブジェクト同士が滑らかに融合します:
float opSmoothUnion(float d1, float d2, float k) {
float h = clamp(0.5 + 0.5 * (d2 - d1) / k, 0.0, 1.0);
return mix(d2, d1, h) - k * h * (1.0 - h);
}
k が大きいほど融合が滑らかになります。有機的なオブジェクトの表現に最適です。
マテリアルの切り替え
レイがどのオブジェクトにヒットしたかを判別するために、SDF値と一緒にマテリアルIDを返す方法があります:
vec2 scene(vec3 p) {
float d1 = sdSphere(p, 0.5); // ID = 1.0
float d2 = sdPlane(p, -1.0); // ID = 2.0
if (d1 < d2) return vec2(d1, 1.0);
return vec2(d2, 2.0);
}
vec2 の x に距離、y にマテリアルIDを格納します。
カメラの制御
カメラを自由に動かすには、ルックアット行列を使います:
mat3 lookAt(vec3 eye, vec3 target, vec3 up) {
vec3 f = normalize(target - eye);
vec3 r = normalize(cross(f, up));
vec3 u = cross(r, f);
return mat3(r, u, f);
}
void main() {
vec2 uv = (gl_FragCoord.xy - u_resolution * 0.5) / u_resolution.y;
// カメラ位置を時間で回転
float angle = u_time * 0.3;
vec3 ro = vec3(sin(angle) * 4.0, 2.0, cos(angle) * 4.0);
vec3 target = vec3(0.0, 0.0, 0.0);
mat3 cam = lookAt(ro, target, vec3(0.0, 1.0, 0.0));
vec3 rd = cam * normalize(vec3(uv, 1.0));
}
カメラの位置を sin / cos で円軌道上に動かし、常にシーンの中心を向くようにします。
3Dシーンの完成
右のエディタでは、球体と床のある3Dシーンを構築しています。カメラが回転し、ライティングとシャドウが適用された完成度の高いシーンです。
これまで学んだすべてのテクニック——SDF、法線計算、ランバート照明、フォン照明、シャドウ、カメラ制御——が組み合わさっています。
さらに先へ
レイマーチングの世界は奥深く、ここで紹介したのは入り口にすぎません:
- ソフトシャドウ — 影の境界を滑らかに
- AO(Ambient Occlusion) — 隙間や角の暗さ
- フォグ — 距離に応じた空気の濃さ
- リフレクション — 反射レイを飛ばして映り込み
- 繰り返し —
modで無限にオブジェクトを繰り返す
まとめ
- ボックス、トーラスなど様々なSDF形状がある
- ブーリアン演算で複雑な形状を構築
- smooth min で有機的な融合
- マテリアルIDでオブジェクトごとに色を変える
- ルックアット行列でカメラを自由に制御
- これまでの全テクニックの集大成