これは Dartmouth Introductory Ray Tracer(DIRT)フレームワークに基づき C++ で書かれた物理ベースのオフラインレンダラーで、CMU の Physically Based Rendering コースのために開発しました。1 学期をかけて、Whitted スタイルのレイトレーシングから、Multiple Importance Sampling(MIS)付きモンテカルロパストレーシング、ボリュームレンダリング、そして最終プロジェクトとして balance heuristic による MIS 重み付けを備えた Bidirectional Path Tracing(BDPT)まで、光輸送のスタック全体を実装しました。
ソースコードについて: 学術倫理ポリシーの対象となる課題のためリポジトリは非公開であり、ソース全体は公開できません。コアアルゴリズム、コード抜粋、設計上の判断は本ページに記載しており、完全なレポートはページ末尾に添付しています。

なぜ Bidirectional Path Tracing なのか
単方向のパストレーサーは、すべてのパスをカメラから開始します。光が偶然見つかりやすいシーンでは有効ですが、間接的で到達しにくい照明が支配的なシーン——小さな光源、狭い開口部から入る光、specular-diffuse-specular のコースティクス連鎖——では破綻します。ほとんどのカメラパスは光源に触れる前に終了し、サンプルの大半が無駄になり、画像はノイズだらけのままです。
BDPT はこの問題を両端から攻めます:
- 視点からカメラサブパスをトレースする。
- 光源上の点をサンプリングしてライトサブパスをトレースする。
- 一方のサブパスの各プレフィックスをもう一方の各プレフィックスと接続し、推定量のファミリー全体を生成する——各 (s, t) ペアは同じパス長に対する異なるサンプリング戦略。
- すべての戦略を MIS 重みで結合し、各戦略が分散の低い領域でのみ寄与するようにする。
MIS のステップは省略できません。すべての接続戦略を素朴に合計すると、同じ光輸送を複数回カウントしてしまい、深刻な firefly ノイズが発生します。以下の比較が MIS の重要性を最も明確に示しています:
| 素朴な接続(MIS なし) | MIS 重み付けあり | パストレーシング参照画像 |
|---|---|---|
![]() | ![]() | ![]() |
レンダラーのアーキテクチャ
レンダラーは JSON シーン記述で駆動されるモジュラーな C++ システムで、各サブシステムはファクトリーベースのパーサーでプラグイン可能です:
- Integrator —— 法線 / AO デバッグビュー、BSDF のみのパストレーシング、Next Event Estimation、MIS パストレーシング(power heuristic)、ボリュームパストレーシング(uniform と NEE+MIS)、そして BDPT(balance heuristic)。
- BSDF —— Lambertian、金属(ラフ反射)、Fresnel 付き誘電体、Phong / Blinn-Phong ローブ、法線を重点サンプリングする Beckmann マイクロファセット、Oren-Nayar ラフディフューズ、テクスチャ駆動のブレンドマテリアル。すべての BSDF は specular フラグ付きの統一
sample / eval / pdfインターフェースを公開しており、この設計によって同一のマテリアルライブラリが PT、MIS-PT、BDPT のすべてで使えます。 - 光源 ——
sampleOn / pdfOnの立体角サンプリングを備えた矩形・球面エリアライト、および最終シーン用に追加したデルタポイントライトとスポットライト(コーン減衰付き)。 - 加速構造 —— 再帰的中点分割による Bounding Volume Hierarchy(BVH)。
- メディア —— Henyey-Greenstein 位相関数を備えた均質ボリュームと Perlin ノイズの不均質ボリューム。free-flight 距離サンプリングと ratio tracking で解く。
- サンプラー —— 独立、層化、Halton 低食い違い量列。
- カメラ —— 絞りと焦点距離を設定できる薄レンズモデルによる被写界深度。
単方向パストレーサーにおける MIS
BDPT の前に、MIS パストレーサーは各バウンスで BSDF サンプリングと **Next Event Estimation(光源サンプリング)**を power heuristic で結合します:
C++float powerHeuristic(float pdf1, float pdf2, float power) {
float pow1 = pow(pdf1, power);
float pow2 = pow(pdf2, power);
return pow1 / (pow1 + pow2);
}
各戦略は自分がサンプリングした方向についてもう一方の戦略の PDF を評価し、それに応じて寄与を重み付けします。重みは再帰を通して引き継がれ、放射輝度が二重にカウントされることはありません。Specular バウンスは重み付けを完全にバイパスします(デルタ分布はもう一方の戦略ではサンプリングできないため、重みは 1)。古典的な Veach シーンは全ラフネス範囲で正しくレンダリングされます:


BDPT 詳解
サブパスの生成
両方のサブパスはランダムウォークであり、各頂点で MIS が後に必要とするデータを記録します:forward PDF(このウォークが実際にこの頂点を生成した確率)と reverse PDF(逆方向のウォークがこの頂点を生成したであろう確率)、そして累積 throughput です。
C++struct Vertex {
Vec3f wi, wo; // 入射 / 出射方向
HitInfo hit; // サーフェス交差情報
Color3f emitted; // ウォークに沿って累積された発光
Color3f throughput; // この頂点までのパス throughput
Color3f nextThroughput; // この頂点で散乱した後の throughput
float pdfFwd; // 累積 forward サンプリング PDF
float pdfRev; // 累積 reverse サンプリング PDF
};
カメラウォークは各サーフェスで BSDF の重点サンプリングにより散乱し、両方向の PDF の積を累積していきます。Specular 頂点は PDF に触れずにデルタ減衰を throughput に乗じるだけで、ディフューズ頂点は両方向を追跡します:
C++// traceCameraPath() 内部:バウンスごとの PDF 記録
float pdfMat = hit.mat->pdf(currentRay.d, normalize(srec.scattered), hit);
vertex.pdfFwd = pdfFwd;
pdfFwd *= pdfMat; // forward:視点 -> シーン
pdfRev *= hit.mat->pdf(-srec.scattered, normalize(-currentRay.d), hit); // reverse ウォーク
vertex.pdfRev = pdfRev;
Color3f evalMat = hit.mat->eval(currentRay.d, normalize(srec.scattered), hit);
throughput *= evalMat / pdfMat;
ライトウォークは、光源上の位置と出射方向を同時にサンプリングして開始し(initVertex.pdfFwd = pdfDir * lightPDF)、その後は同様に延長されます。両方のウォークは throughput の輝度に基づくロシアンルーレットで終了し、パス長を制限しながら推定量の不偏性を保ちます。
サブパスの接続
メインループはバウンス予算内のすべての有効な (s, t) 戦略を列挙します:
C++Color3f BDPTIntegrator::Li(const Scene &scene, Sampler &sampler, const Ray3f &ray) const {
Color3f L(0.f);
vector<Vertex> lightPath = traceLightPath(scene, sampler);
vector<Vertex> cameraPath = traceCameraPath(scene, sampler, ray);
for (int s = 1; s <= cameraPath.size(); ++s) {
for (int t = 1; t <= lightPath.size(); ++t) {
int depth = t + s - 2;
if ((s == 1 && t == 1) || depth < 0 || depth > m_maxBounces) continue;
Color3f contribution = connect(scene, cameraPath, lightPath, s - 1, t - 1);
float misWeight = calculateMIS(cameraPath, lightPath, s, t);
L += contribution * misWeight;
}
}
return L;
}
接続では 2 つの端点頂点間に shadow ray を飛ばして可視性テストを行い、その後、2 つのサブパスの throughput、各端点で接続方向に向けて評価した BSDF、光源項、そして立体角と面積の測度を変換する幾何項を乗算します:
C++float geometryTerm(const Vec3f &nx, const Vec3f &x, const Vec3f &ny, const Vec3f &y) {
Vec3f d = y - x;
float cosThetaI = abs(dot(normalize(nx), normalize(d)));
float cosThetaO = abs(dot(normalize(ny), normalize(-d)));
return cosThetaI * cosThetaO / length2(d);
}
// connect():一般的な (s, t) 戦略
Color3f bsdfCam = cameraVertex.hit.mat->eval(cameraVertex.wi, normalize(dir), cameraVertex.hit);
Color3f bsdfLight = lightVertex.hit.mat->eval(lightVertex.wo, normalize(dir), lightVertex.hit);
Color3f result = lightVertex.throughput * cameraVertex.throughput;
result *= initLight.emitted / initLight.pdfFwd; // 光源の放射輝度 / サンプリング PDF
result *= bsdfCam * bsdfLight;
result *= geometryTerm(cameraVertex.hit.sn, cameraVertex.hit.p,
lightVertex.hit.sn, lightVertex.hit.p);
戦略間の MIS 重み
ある長さの完全なパスに対して、各 (s, t) の分割は同じパスをサンプリングする異なる方法です。選択された戦略の balance heuristic 重みは、その PDF をすべての戦略の PDF の合計で割ったものです:
実装では、仮想的な戦略ごとの PDF をすべて具体化する代わりに、Veach の学位論文(PBRT で広まった形)の漸化式を使います。各サブパスに沿って reverse PDF と forward PDF の比を累積することで、「逆方向のウォークがこの頂点を生成する可能性」を実際の生成方法との相対値として表現します:
C++float BDPTIntegrator::calculateMIS(const vector<Vertex> &camPath,
const vector<Vertex> &lightPath,
size_t s, size_t t) const {
if (s == 1 && t == 1) return 1.f;
float sumCamera = 1.f, sumLight = 1.f;
for (int i = 1; i <= s; i++)
sumCamera = (remap0(camPath[i].pdfRev) / remap0(camPath[i].pdfFwd)) * (sumCamera + 1.f);
for (int i = 1; i <= t; i++)
sumLight = (remap0(lightPath[i].pdfRev) / remap0(lightPath[i].pdfFwd)) * (sumLight + 1.f);
return 1.f / (sumLight + 1.f + sumCamera);
}
remap0() はゼロに近い PDF(デルタ交差)を 1 にクランプし、ゼロ除算ではなく比から消去します——漸化式で specular 頂点を扱う標準的な方法です。
デルタ光源
ポイントライトとスポットライト(コーン角と減衰を設定可能)は最終シーン用に追加しました。デルタ光源はランダムウォークで命中させることができないため、その寄与は明示的な接続戦略のみを通じて流れます——MIS の記録が「サンプリング不可能な戦略」を正しく扱えているかの良いストレステストです。
| ポイントライト | 減衰付きスポットライト |
|---|---|
![]() | ![]() |
BDPT への道のり:機能の進化
最終プロジェクトは 1 学期にわたるレンダラー開発の上に成り立っています:
基礎 —— レイとプリミティブの交差(球、矩形、Möller-Trumbore 三角形、OBJ メッシュ)、インスタンス変換、再帰的 Whitted レイトレーシング、プロシージャル・画像テクスチャ、BVH 構築。

モンテカルロと BSDF —— cosine 加重、Phong ローブ、Beckmann 法線の重点サンプリング。それぞれヒストグラム比較により解析的 PDF に対して検証済み。層化・Halton サンプラー、薄レンズ被写界深度。
| Beckmann マイクロファセット | 被写界深度 |
|---|---|
![]() | ![]() |
直接照明と MIS —— 立体角 PDF によるエリアライトサンプリング、Next Event Estimation、power heuristic MIS、HDR 環境照明。
ボリュームレンダリング —— 解析的逆変換サンプリングによる Henyey-Greenstein 位相関数、指数 free-flight サンプリングによる均質メディア、ratio tracking による不均質 Perlin メディア、メディア交差点での位相関数サンプリングと光源サンプリング間の MIS。
| 等方性ボリューム、NEE | 関与媒質を通るスポットライト |
|---|---|
![]() | ![]() |
このプロジェクトが示すもの
- 光輸送理論の実践 —— レンダリング方程式に対する 4 つの解法戦略(Whitted、PT、MIS-PT、BDPT)の推定量を導出・実装し、それぞれの分散の由来を正確に理解。
- 2 つの MIS 体系 —— 2 戦略(BSDF × NEE)上の power heuristic と、forward/reverse PDF の漸化式による戦略ファミリー全体の balance heuristic。
- サンプリングの技巧 —— すべての BSDF ローブの重点サンプリング、立体角光源サンプリング、低食い違い量列、不偏ロシアンルーレット、真値に対する PDF 検証。
- レンダラーエンジニアリング —— 1 つのマテリアルライブラリをすべての integrator で使えるようにする統一 BSDF インターフェース、ファクトリー駆動のシーンパース、最終シーンを現実的にする BVH。
完全な技術レポート(導出と追加の比較を含む):
BDPT 最終レポートOpen ↗








