Slide 1

Slide 1 text

ReSTIRの数理と実装 @yumcyawiz レイトレ合宿10 セミナー

Slide 2

Slide 2 text

ReSTIRとは? Resampled Importance Sampling (RIS) 好きな分布からの重点的サンプリングを行う Spatio Temporal Resamping 前フレーム (Temporal), 周辺ピクセル (Spatial)の情報を活用 最近のリアルタイムレイトレの流行り ReSTIR DI + ReSTIR GI + Radiance Cache + Denoiser + Upscaler

Slide 3

Slide 3 text

ReSTIRの流れ Generate Candidate Temporal Resampling Spatial Resampling Resolve Generate Candidate: 光源サンプリング(DI), パスト レーシング(GI)によって を生成 Temporal resampling: 前フレームと現在フレームの サンプル集合からリサンプリング Spatial resampling: 現在フレームの周辺ピクセルの サンプル集合からリサンプリング Resolve: リサンプリング後の結果を使って放射輝 度を評価 (x ​ , L ​ ) i i

Slide 4

Slide 4 text

Resampled Importance Sampling (RIS) 提案分布から生成した 個のサンプルの中から1つを選び直し(Resampling), 目標分布 (Target function) に従うようなサンプルを得る アルゴリズム 1. 提案分布 から 個のサンプル を生成 2. 各サンプルの重み を計算 3. 確率 に従って を選び直してサンプル を生成 M ​ (x) p ^ p(x) M (x ​ , x ​ , ⋯ , x ​ ) 1 2 M w ​ = i ​ ​ M 1 p(x ​ ) i ​ (x ​ ) p ^ i P ​ = i ​ ​ w ​ ∑i=1 M i w ​ i x ​ i y

Slide 5

Slide 5 text

RISの性質 ならRISによって得られるサンプルは目標分布に 分布収束 (Convergence in law) する サンプル列 に自己相関が出てくる 極端なケースだと同じサンプルが選ばれ続ける RISから得られたサンプルを入力とする統計的な推定において問題が起こる可能性 (後述) lim ​ V[ ​ w ​ ] → M→∞ ∑i=1 M i 0 (y ​ , y ​ , ⋯ ) 1 2

Slide 6

Slide 6 text

Target function レンダリング方程式(経路積分形式)の被積分関数 を目標分布とする RISにおいては正規化されている必要はない (分布ではなくTarget functionと呼ぶ理由) 可視項の扱い Unshadowed target function: Shadowed target function: f × G × V × L ​ i f × G × L ​ i f × G × L ​ × i V

Slide 7

Slide 7 text

Weighted Reservoir Sampling (WRS) RISを逐次的なアルゴリズムで置き換えたもの Reservoirという構造体に現在のサンプルだけを保存しておく アルゴリズム 1. 新たなサンプル とそのp.d.f が与えられる 2. 重み を計算 3. Reservoirのこれまでの重みの総和を更新: 4. サンプル数も更新: 5. の一様乱数 が ならReservoirのサンプルを で置き換える struct Reservoir { ReservoirSample x; // サンプル float w_sum; // 重みの総和 float c; // サンプル数 float ucw; // Unbiased Contribution Weight }; x ​ next p(x ) next w ​ = next ​ ​ M 1 p(x ​ ) next ​ (x ​ ) p ^ next w ​ + sum = w ​ next c += 1 [0, 1) u u < ​ w ​ sum w ​ next x ​ next

Slide 8

Slide 8 text

実装例 void updateReservoir(inout Reservoir reservoir, vec3 hit_position, vec3 hit_normal, vec3 radiance, float weight, float rv0) { reservoir.w_sum += weight; reservoir.c += 1; if (rv0 < weight / reservoir.w_sum) { reservoir.hit_position = hit_position; reservoir.hit_normal = hit_normal; reservoir.radiance = radiance; } }

Slide 9

Slide 9 text

Unbiased Contribution Weight (UCW) モンテカルロ積分を行うためにはRISによって得られたサンプルのp.d.fが必要 問題: どうやって計算? p.d.fは厳密に計算出来る必要はなく, ある確率変数 が以下を満たせば良い を Unbiased Contribution Weight (UCW) という 上の式は と等価 RISのUCWは で与えられる W ​ X E f(X)W = [ X] ​ f(x)dx ∫ Ω W ​ X E[W ​ ∣X] = X ​ p ​ (X) X 1 W ​ = X ​ ​ (X) p ^ ​ w ​ ∑i=1 M i

Slide 10

Slide 10 text

RISの性質2 かつ, ある について を満たすなら 特に となっている場合 この式から言えること は の良い近似になった方が分散が減る が出来るだけ変動しないようにすることが分散を減らすために重要 lim ​ V[ ​ w ​ ] → M→∞ ∑i=1 M i 0 C ​ > f 0 0 ≤ f ≤ C ​ ​ f p ^ V[f(X)W ​ ] ≤ X V ​ + [ ​ (X) p ^ f(X) ] C ​ ​ ​ ∥ ​ ∥ + 2 ​ ​ f 2 V ​ w ​ [ i=1 ∑ M i] p ^ 2 V ​ w ​ [ i=1 ∑ M i] f(x) = C ​ ​ (x) f p ^ V[f(X)W ​ ] = X C ​ V ​ w ​ f 2 [ i=1 ∑ M i] ​ p ^ f w ​ i

Slide 11

Slide 11 text

Reservoirのmerge Reservoir 1とReservoir 2を用いてWRSすることで, あたかもReservoir 1とReservoir 2の両方のサンプル集 合からRISしたようなサンプルを得ることが出来る ReSTIRにおいて最も重要な部分 アルゴリズム : Reservoir 1, Reservoir 2のサンプル : Reservoir 1, Reservoir 2のサンプル数 Reservoir 1, Reservoir 2のUCW 1. 重み を計算 2. Reservoir 1の重みの総和を更新: 3. Reservoir 1のサンプル数を更新: 4. の一様乱数 が ならReservoir 1のサンプルをReservoir 2で置き換える x ​ , x ​ 1 2 c ​ , c ​ 1 2 W , W ​ X ​ 1 X ​ 2 w ​ = 2 ​ ​ (x ​ )W ​ c ​ +c ​ 1 2 c ​ 2 p ^ 2 X ​ 2 w ​ + sum = w ​ 2 c ​ + 1 = c ​ 2 [0, 1) u u < ​ w ​ sum w ​ 2

Slide 12

Slide 12 text

実装例 void mergeReservoir(inout Reservoir reservoir0, Reservoir reservoir1, float weight, float rv0) { reservoir0.w_sum += weight; reservoir0.c += reservoir1.c; if (rv0 < weight / reservoir0.w_sum) { reservoir0.hit_position = reservoir1.hit_position; reservoir0.hit_normal = reservoir1.hit_normal; reservoir0.radiance = reservoir1.radiance; } }

Slide 13

Slide 13 text

MIS Weight RISにおいても Multiple Importance Sampling (MIS) を考えることが出来る MIS weight を用いて重みの計算を と置き換える. Constant MIS: Balance Heuristics: Generalized Balance Heuristics m ​ (x) i w ​ (x ) = i i m ​ (x ​ ) ​ (x )W ​ i i p ^ i X ​ i m ​ (x ) = i i ​ ​ c ​ ∑j=1 K j c ​ i m ​ (x ) = i i ​ ​ c ​ p ​ (x ​ ) ∑j=1 K j j i c ​ p ​ (x ​ ) i i i m ​ (x ) = i i ​ ​ c ​ ​ ​ (x ​ ) ∑j=1 K j p ^j i c ​ ​ ​ (x ​ ) ip ^i i

Slide 14

Slide 14 text

Confidence weight Reservoirのサンプル数 はmergeにおいて信頼度のような役割を果たす Confidence weight という別名が与えられている Reservoirのmergeの前にConfidence weightを減らすことで, mergeの仕方を操作できる 例: Reservoir 2は信頼度が低いのでConfidence weightに0.1をかける c

Slide 15

Slide 15 text

Temporal Resampling 各ピクセルにReservoirがあると仮定 前フレームとReservoirと現在フレームのReservoirをmergeすることで有効サンプル数を増やす戦略 PT Temporal Resampling

Slide 16

Slide 16 text

M-Capping ナイーブにTemporal resamplingを実装すると絵が壊れることがある が大きくなりすぎて同じサンプルが使われ続ける Biasが蓄積し過ぎて絵が壊れる 実用上はConfidence weightを一定の値で抑える必要がある 論文では 初期サンプル数 * 20 が推奨されている M-Capping Off M-Capping On w ​ sum

Slide 17

Slide 17 text

実装例 // M-capping { previous_reservoir.c = min(previous_reservoir.c, g_ConfidenceCap * g_CandidateSamples); } // temporal resampling { const float p_12 = evaluateTargetFunction( origin_position, origin_normal, previous_reservoir.hit_position, previous_reservoir.hit_normal, previous_reservoir.radiance); // rejection heuristics previous_reservoir.c *= candidate_score; const float w = p_12 * previous_reservoir.ucw * previous_reservoir.c; mergeReservoir(reservoir, previous_reservoir, w, sample1D(rng)); }

Slide 18

Slide 18 text

Spatial Resampling 周辺ピクセルに存在するReservoirをmergeすることで有効サンプル数を増やす戦略 Temporal resamplingによる相関を取り除く役割もある 出来るだけランダム性を高めたほうが良いので2次元ガウス分布に従う乱数で周辺ピクセルを選ぶのが おすすめ PT Spatial Resampling

Slide 19

Slide 19 text

実装例 // spatial resampling for (int i = 0; i < g_SpatialSamples; ++i) { // sample neighbor pixel vec2 gaussian = sample2DGaussian(sample2D(rng)); ivec2 neighbor_pixel = ivec2(pixel + g_SpatialRadius / 1.96 * gaussian); if (neighbor_pixel.x < 0 || neighbor_pixel.y < 0 || neighbor_pixel.x >= g_Extent.x || neighbor_pixel.y >= g_Extent.y) { // out of bounds continue; } Reservoir neighbor_reservoir = g_PreviousReservoirs[neighbor_pixel.y * g_Extent.x + neighbor_pixel.x]; const float p_12 = evaluateTargetFunction( origin_position, origin_normal, neighbor_reservoir.hit_position, neighbor_reservoir.hit_normal, neighbor_reservoir.radiance);

Slide 20

Slide 20 text

if (g_UseRejectionHeuristics != 0) { neighbor_reservoir.c *= rejectionHeuristics( origin_position, origin_normal, neighbor_hit_info.position, neighbor_hit_info.normal); } if (g_UseJacobianRejectionHeuristics != 0) { neighbor_reservoir.c *= reconnectionShiftJacobian( neighbor_hit_info.position, neighbor_reservoir.hit_position, neighbor_reservoir.hit_normal, origin_position) > g_JacobianRejectionHeuristicsThreshold ? 0.0 : 1.0; } const float w = p_12 * neighbor_reservoir.ucw * neighbor_reservoir.c; mergeReservoir(reservoir, neighbor_reservoir, w, sample1D(rng)); }

Slide 21

Slide 21 text

Rejection heuristic Reservoir同士をmergeする前にGeometry, Materialの類似度に応じてConfidence weightを減らす Edge stopping functionを利用 Depth similarity Normal similarity Albedo similarity Heuristic Off Heuristic On

Slide 22

Slide 22 text

実装例 float depthHeuristics(float depth, float previous_depth, float sigma) { const float diff = (depth - previous_depth) * (depth - previous_depth) / depth; return clamp(exp(-sigma * diff), 0.0, 1.0); } float normalHeuristics(vec3 normal, vec3 previous_normal, float sigma) { return clamp(pow(max(dot(normal, previous_normal), 0.0), sigma), 0.0, 1.0); } float rejectionHeuristics(vec3 p0, vec3 n0, vec3 p1, vec3 n1) { float weight = 1.0; weight *= depthHeuristics(distance(g_Eye, p0), distance(g_Eye, p1), g_DepthRejectionHeuristicsSigma); weight *= normalHeuristics(n0, n1, g_NormalRejectionHeuristicsSigma); return weight; }

Slide 23

Slide 23 text

Visibility Reuse Target functionがVisibilityを含んでいないと影のノイズが取れない 一方でVisibilityを毎回評価するのはパフォーマンス的に避けたい そこでTemporal resampling後だけにVisibilityを一回評価し、Visibility = 0ならUCW = 0とする 次のSpatial resampling, Temporal resamplingで再利用されなくなる 影のノイズ減少 (代わりに暗くなるBiasが増える) Visibility Reuse Off Visibility Reuse On

Slide 24

Slide 24 text

実装例 // visibility reuse if (g_UseVisibilityReuse != 0) { const float V = checkVisibility(origin_position, first_hit_info.geometricNormal, reservoir.hit_position); reservoir.ucw *= V; }

Slide 25

Slide 25 text

Spatial resamplingの結果のフィードバック ReSTIR DIではSpatial resamplingの結果を次のTemporal resamplingにフィードバック 有効サンプル数が更に大きくなりノイズが減る ReSTIR GIではBiasの増大によるArtifactを防ぐためにフィードバックしていない MIS weight, Rejection Heuristicsを適切に設計すればフィードバックしても大丈夫? Feedback Off Feedback On

Slide 26

Slide 26 text

ReSTIR DI 1. 光源サンプリングによって光源上の点サンプルを 個生成 2. RISによって1個のサンプルを選択 3. Temporal Resampling 4. Spatial Resampling 5. Reservoirに含まれているサンプルを利用して を評価 PT ReSTIR DI M f × G × L ​ × i V

Slide 27

Slide 27 text

ReSTIR GI 1. First hit pointの位置からBRDF samplingによってSecond hit pointを生成 2. Second hit pointからFirst hit pointに向かう放射輝度をパストレーシングで計算 3. Temporal Resampling 4. Spatial Resampling 5. Reservoirに含まれているサンプルを利用して を評価 PT ReSTIR GI f × G × L ​ × i V

Slide 28

Slide 28 text

ReSTIR GIの問題 Lightingが複雑になると光の球のようなアーティファクトが出る RTXGIでは Boiling filter で取り除く が一定以上の値を取るときにはUCW=0とする 壁際で光の球のようなアーティファクトが出る 立体角 <-> 面積の変換に伴うヤコビアンが原因 ヤコビアンが一定以上大きくなるときにはConfidence weightを0にする f(X)W ​ X

Slide 29

Slide 29 text

Jacobian rejection heuristics Off On

Slide 30

Slide 30 text

最後に ReSTIRを正しく実装するには理論的な理解はとても重要 間違った式を使うとBiasが発生する BiasはTemporal Resampling, Spatial Resamplingで容易に周辺ピクセルに伝播してしまう Visibility reuse, その他近似によって発生するBiasを適切にコントロールする必要 常にパストレーシングによるリファレンスとの比較を行うようにする 設定出来る項目が多いので, GUIで簡単に比較出来るようにしたほうが良い 例: 初期サンプルの数, Spatial resamplingのサンプル数, 半径, Rejection heuristicなど まだまだ色々と研究の余地はありそう