3Dシーン


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シーンを作るために、いくつかの要素を組み合わせます:

  1. 複数のオブジェクト — SDF の組み合わせ
  2. マテリアル — オブジェクトごとの色と質感
  3. カメラ制御 — 視点の位置と向き
  4. ライティング — 光と影

基本的な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でオブジェクトごとに色を変える
  • ルックアット行列でカメラを自由に制御
  • これまでの全テクニックの集大成