Tutorial

WebGL 2.0 レイマーチングレンダリングの原理

WebGL 2.0 レイマーチング技術を深く掘り下げ、SDF 符号付き距離場を使用してリアルタイムボリュームレンダリングを実現する方法と、この技術が GPU 性能を限界まで引き出す仕組みを解説します。

🚀 volumeshadertest
12月3日
10分

WebGL 2.0 レイマーチング、SDF、リアルタイムボリュームレンダリングの深掘り

WebGL 2.0 は強力な GPU 機能セットを解き放ち、従来ネイティブ API が必要だった高度なレンダリング技術を可能にします。その中でも、レイマーチングは、最小限のジオメトリで複雑なシーンをレンダリングする最も表現力豊かで数学的に優雅な方法の一つとして際立っています。

この記事では、WebGL 2.0 レイマーチングの核心概念を解説し、**符号距離場(SDF)**がどのように効率的なレンダリングを可能にするかを説明し、この技術が GPU から驚異的な性能を引き出す方法を示します。


1. レイマーチングとは?

レイマーチングは、ジオメトリを画面に投影する代わりに、カメラからピクセルごとに光線を発射し、仮想シーンを段階的に移動し、光線が表面に当たるか最大範囲に達するまで距離を評価するレンダリング方法です。

1.1 レイマーチングの仕組み(視覚図)

カメラ
  |
  | 光線
  v
  +--------------------------------- 画面ピクセル
  |
  | マーチステップ 1      (距離 d1)
  |
  |---------> マーチステップ 2      (距離 d2)
  |
  |-----------------------> マーチステップ 3 ...
  |
距離 < ε で表面に到達

レイマーチングは本質的にループです:

for (int i = 0; i < MAX_STEPS; i++) {
    float dist = sdf(currentPos);
    if (dist < EPSILON) hitSurface();
    currentPos += rayDir * dist;
}

魔法の要素は、私たちが渡す SDF 関数です。


2. 符号距離場(SDF)とは?

符号距離場は、以下を返す関数です:

> 0  オブジェクト外部の距離  
= 0  表面に正確に  
< 0  オブジェクト内部の距離  

2.1 SDF 図

           外部
        +------------+
        |   d = 0.5  |
        |            |
内部    |     SDF    | 表面 d = 0
d < 0   |            |
        |   d = -0.3 |
        +------------+

2.2 例:球体 SDF

float sdSphere(vec3 p, float r) {
    return length(p) - r;
}

これらの基本形状をブール演算(和集合、差集合、交集合)と組み合わせることで、従来のジオメトリなしで複雑なシーンを構築できます。


3. なぜ WebGL 2.0 で SDF レイマーチングを使用するのか?

3.1 利点

  • 頂点バッファやメッシュが不要 すべてがフラグメントシェーダーで数学的に計算されます。

  • 無限の幾何学的詳細 SDF は滑らかで連続的な表面を定義します。

  • 動的で変形可能なオブジェクト 数学で形状を簡単にアニメーション化できます。

  • コンパクトなシーン 完全な 3D シーンは、わずか数行の GLSL で定義できます。

3.2 これを可能にする WebGL 2.0 の機能

  • 高精度浮動小数点
  • 完全な GLSL ES 3.0 シェーダーサポート
  • UBO(ユニフォームバッファ)
  • 浮動小数点テクスチャ
  • より柔軟なループと分岐

これにより、WebGL 1.0 では困難だった安定した高性能レイマーチングが可能になります。


4. WebGL 2.0 レイマーチャーの段階的構築

4.1 シーン光線の設定

各ピクセルに対して光線を計算:

vec3 rayOrigin = cameraPos;
vec3 rayDir = normalize(uv.x * camRight + uv.y * camUp + camForward);

4.2 レイマーチループ

float marchRay(vec3 ro, vec3 rd) {
    float totalDist = 0.0;

    for (int i = 0; i < MAX_STEPS; i++) {
        vec3 p = ro + rd * totalDist;
        float d = map(p);     // あなたの SDF 関数

        if (d < EPSILON) break;    // ヒット
        if (totalDist > MAX_DISTANCE) break;  // ミス

        totalDist += d;
    }
    return totalDist;
}

4.3 SDF シーンの定義

float map(vec3 p) {
    float s = sdSphere(p, 1.0);
    float box = sdBox(p - vec3(2.0, 0.0, 0.0), vec3(0.7));
    return min(s, box); // 和集合
}

4.4 法線の計算

勾配近似を使用:

vec3 calcNormal(vec3 p) {
    const vec2 e = vec2(0.001, 0.0);
    return normalize(vec3(
        map(p + e.xyy) - map(p - e.xyy),
        map(p + e.yxy) - map(p - e.yxy),
        map(p + e.yyx) - map(p - e.yyx)
    ));
}

4.5 ライティングモデル

シンプルな拡散シェーディング:

float diffuse = max(dot(normal, lightDir), 0.0);

5. ボリュームのレンダリング(霧、雲、煙)

SDF レイマーチングはボリュームレンダリングに拡張でき、表面で停止する代わりに、各ステップが色と密度を貢献します。

5.1 ボリューム図

光線 →
[低密度] [中密度] [高密度] [不透明]
   +--------+---------+---------+-------+
   サンプル1  サンプル2  サンプル3  サンプル4

5.2 ボリューム累積モデル

vec3 color = vec3(0);
float absorption = 1.0;

for (int i = 0; i < STEPS; i++) {
    float density = computeDensity(p);
    vec3 sampleColor = density * vec3(0.6, 0.7, 1.0);

    color += sampleColor * absorption;
    absorption *= (1.0 - density * 0.1);

    p += rd * STEP_SIZE;
}

レイマーチングボリュームはより高コストですが、WebGL 2.0 の性能向上(特にモバイル GPU 上)により、リアルタイムの雲、霧、星雲が実現可能になります。


6. 性能最適化技術

レイマーチングは重い場合があるため、最適化が不可欠です。

6.1 主要な技術

  1. 距離カリング 正確な SDF 距離に依存して、大きな空の領域をスキップします。

  2. 境界ボリューム 光線が SDF が存在する領域に入るかどうかをテストします。

  3. 適応ステップサイズ 表面近くでは小さなステップを使用し、開放空間では大きなステップを使用します。

  4. 早期終了 表面に当たったらすぐにマーチングを停止します。

  5. MAX_STEPS の削減 リアルタイムレンダリングの典型的な値は 64–128 の範囲です。

  6. WebGL 2.0 ユニフォームとバッファの効率的な使用 変化しない値をループの外に移動します。


7. WebGL 2.0 プロジェクトのコード構造

ディレクトリレイアウトの例:

shader/
  raymarch.vert
  raymarch.frag
src/
  webgl2-context.js
  camera.js
  renderer.js
index.html

典型的なフラグメントシェーダーセクション:

// 1. カメラ/光線の設定
// 2. SDF 定義
// 3. マテリアルとライティング関数
// 4. レイマーチループ
// 5. 最終色出力

8. いつレイマーチングを使用すべきか?

レイマーチングは以下に優れています:

  • 無限の滑らかな形状
  • フラクタル
  • サイファイシーン
  • 符号距離ソフトシャドウ
  • 手続き型ジオメトリ
  • ボリューム効果(雲、霧、ゴッドレイ)

レイマーチングを避けるべき場合:

  • 多くの詳細な複雑なメッシュ
  • リアルなスキンキャラクター
  • 数千のオブジェクトを持つ大規模な動的シーン

レイマーチングは手続き型世界スタイライズされた視覚効果に最適です。


9. 結論

WebGL 2.0 レイマーチングは、数学と GLSL だけで、高度なリアルタイムレンダリングをブラウザに持ち込みます。SDF を使用すると、単一の頂点バッファなしで複雑な 3D シーンを作成できます。この技術は計算集約的ですが、慎重な最適化により、ネイティブ GPU アプリケーションに匹敵する表現力豊かな視覚効果が可能になります。

レイマーチングは、数学、芸術、GPU プログラミングの独特な交差点を表し、現代の WebGL 開発で探索する最もエキサイティングな技術の一つとなっています。


このベンチマーク(Volume Shader BM Test)との関係

理論を読み終えたら、ツール本体に戻りましょう。上の説明を実際の Volume Shader BM Test と結びつける重要なポイントです。

なぜこのテストは普通の 3D デモより重いのか

通常の WebGL デモは数千ポリゴンを描画するだけ。このベンチマークは GPU に対し、ピクセルごとにレイマーチングループを 1,000 回以上回させ、ジオメトリでショートカットすることもできません。ゲームで 60 FPS 出る中位 GPU でも、Heavy モードでは 30 FPS を下回ることがよくあります。

なぜモバイルデバイスでは FPS が落ちるのか

スマホは SoC が熱を持つと即座にスロットリングします。最初の 10 秒は高 FPS でも、その後 GPU クロックが下がります。充電器に接続し、Light か Medium で測定し、安定性で比較するのがフェアです。詳しくはスマホ GPU テストガイド

最初の 1 回が遅い理由(シェーダーコンパイル)

初回ロード時、ブラウザは GLSL をドライバ固有のバイナリにコンパイルする必要があり、これだけで数百ミリ秒かかることもあります。冒頭の小さなカクつきはこれが原因。1 回目は捨てて 2 回目を採用しましょう。

公平な比較の仕方

同じモード、ブラウザタブを前面、同じ電源状態で測定。Light / Medium / Heavy は別ワークロードなので直接比較できません。詳細は FPS 結果ガイド

ブラウザが内蔵 GPU を使っているとき

ノート PC で iGPU と dGPU の両方がある場合、Chrome / Edge は省電力のため iGPU を選びがちです。chrome://gpu を開き GL_RENDERER を確認。iGPU が表示されていたら、OS のグラフィック設定で専用 GPU を強制してください。詳細はラグ解消ガイド


実際に試す

理論はこれで十分。実機ベンチマークを 1 分以内で。

Volume Shader BM Test を開始 →

関連ガイド

タグ
WebGLレイマーチングボリュームレンダリングGPU性能