phanerozoic commited on
Commit
5b3a263
·
verified ·
1 Parent(s): fa6efb6

Card: standardized form

Browse files
Files changed (2) hide show
  1. CARD.md +108 -182
  2. README.md +108 -182
CARD.md CHANGED
@@ -6,42 +6,38 @@ license: apache-2.0
6
  # pathtracer-diff
7
 
8
  A differentiable Monte Carlo path tracer as one kernel, loadable through
9
- `kernels`. The forward pass is a megakernel unidirectional path tracer:
10
- Lambertian diffuse, GGX conductor (VNDF-sampled, height-correlated Smith),
11
- smooth dielectric, rough plastic, and rough dielectric materials; area
12
- lights with per-texel emission and an importance-sampled equirectangular
13
- environment map; an optional homogeneous participating medium; multiple
14
- importance sampling (balance heuristic) by default; per-texel albedo with
15
- bilinear filtering; a binned-SAH BVH built in vectorized torch in
16
- fractions of a second at a million triangles. The backward pass replays
17
- the identical paths with the identical counter-based random draws and
18
- emits analytic gradients to albedo texels, emission texels, environment
19
- texels, and medium coefficients; a companion pass differentiates the
20
- direct-lighting image with respect to vertex positions, including shadow
21
- and camera silhouettes. Inverse rendering runs as a bare torch loop around
22
- the kernel, with no rendering framework in the loop: render, compare,
23
- `backward()`, step.
24
 
25
  <video src="https://huggingface.co/kernels/phanerozoic/pathtracer-diff/resolve/main/media/inverse.mp4" autoplay loop muted playsinline controls width="100%">
26
  <img src="https://huggingface.co/kernels/phanerozoic/pathtracer-diff/resolve/main/media/inverse.gif" alt="THE DRAGON'S HALL: one continuous inverse-rendering shot; a dark marble hall with a gold-trimmed pedestal, glowing rune band, warm sconces, and a levitating crystal Stanford dragon that slides onto its pedestal while fog fills the room, converging to the fixed target inset">
27
  </video>
28
 
29
  *THE DRAGON'S HALL (`media/make_hero.py`): one continuous shot, every
30
- gradient, optimized live through the kernel against the fixed TARGET
31
- inset, 30 fps with a progress bar. From a flat gray start, Adam through
32
- `render()` recovers the 128x128 marble floor albedo (49,152 unknowns),
33
- the gold trim's conductor tint, the pedestal's plastic albedo, the
34
- braziers' warm emission (max err 1.75 of 14), and a 16x64 per-texel
35
- emissive rune band (3,072 unknowns, mean err 0.15), around a smooth
36
- glass slab and the rough-dielectric crystal dragon. Mid-shot,
37
- `geometry_grad()` alone takes over the dragon's position: 5,205 vertices
38
- slide 4.60 scene units onto the pedestal by shadow and silhouette alone,
39
- final offset 0.004. In the last phase the participating medium rolls in:
40
- sigma_a and sigma_s recover from the hazy target to extinction error
41
- 0.014. Image loss falls 8.5e-1 to 1.9e-1 at spp 8; the dim-hall floor
42
- and trim retain residual texel error where the scene barely observes
43
- them, reported as measured. Stanford dragon courtesy of the Stanford
44
- Computer Graphics Laboratory.*
45
 
46
  ## Usage
47
 
@@ -52,146 +48,68 @@ from kernels import get_kernel
52
  ptd = get_kernel("phanerozoic/pathtracer-diff", version=1, trust_remote_code=True)
53
 
54
  wall_tex = torch.full((256, 256, 3), 0.5, device="cuda", requires_grad=True)
55
- panel_emi = torch.full((16, 32, 3), 5.0, device="cuda", requires_grad=True)
56
  scene = ptd.Scene(vertices, faces, material_ids,
57
- albedo=[torch.tensor([0.73] * 3), # constants are 1x1 textures
58
- wall_tex, # [H, W, 3] per-texel albedo
59
- torch.tensor([0.9, 0.6, 0.3])],
60
- emission=[torch.zeros(3), torch.zeros(3), panel_emi],
61
- uvs=uvs,
62
- material_types=[ptd.DIFFUSE, ptd.DIFFUSE, ptd.PLASTIC],
63
- roughness=[0.3, 0.3, 0.2])
64
  cam = ptd.Camera(position=(2.78, 2.73, -8.0), look_at=(2.78, 2.73, 2.8),
65
  vfov_deg=39.0)
66
 
67
- img = ptd.render(scene, cam, 512, 512, spp=64, max_bounces=4) # [H, W, 3] linear
68
  loss = (img - target).square().mean()
69
- loss.backward() # gradients on wall_tex and panel_emi
70
 
71
- grad_verts = ptd.geometry_grad(scene, cam, dloss_dimage) # [V, 3] vertex grads
72
  ```
73
 
74
  `version` selects the release branch; `trust_remote_code` is required by
75
- `kernels` for publishers without the trusted-publisher mark.
76
-
77
- A fixed `seed` renders the same paths every call, so the Monte Carlo
78
- objective is a deterministic function of the parameters and gradient
79
- descent sees a smooth landscape at any spp; that is the configuration an
80
- inverse-rendering loop wants. `estimator` selects `"mis"` (default),
81
- `"nee"`, or `"brdf"`.
82
 
83
  ## API
84
 
85
  | Symbol | Purpose |
86
  |---|---|
87
- | `Scene(vertices, faces, material_ids, albedo, emission, uvs, material_types, roughness, ior, env, medium)` | triangle-mesh scene; BVH, light list, and the detached environment CDF / medium rate are built at construction; `albedo` and `emission` are `[M, 3]` constants or lists of `[Hm, Wm, 3]` textures; `env` is an optional `[Eh, Ew, 3]` equirect map; `medium=(sigma_a, sigma_s)` fills the scene with a homogeneous absorbing/scattering medium (mutually exclusive with `env`); texture/env shapes are fixed at construction |
88
  | `Camera(position, look_at, up, vfov_deg)` | pinhole camera |
89
- | `render(scene, camera, height, width, spp, max_bounces, estimator, seed)` | differentiable render to `[H, W, 3]` f32 linear radiance; autograd to albedo texels, emission texels, env texels, and medium sigmas |
90
- | `geometry_grad(scene, camera, grad_image, spp, edge_samples, seed)` | d(loss)/d(vertex positions) `[V, 3]` for the direct-lighting transport term: dual-number interior derivative + edge-sampled shadow-silhouette and camera-silhouette boundary terms |
91
  | `DIFFUSE`, `CONDUCTOR`, `DIELECTRIC`, `PLASTIC`, `ROUGH_DIELECTRIC` | material type constants |
92
  | `ops.pt_forward` / `ops.pt_backward` / `ops.pt_geometry_grad` | raw kernel launches |
93
 
94
- ## How it works
95
 
96
  One thread owns one pixel and accumulates its spp samples serially, so the
97
- image is bitwise deterministic with no atomics in the forward pass.
98
- Diffuse vertices sample the cosine hemisphere (the throughput multiplier
99
- is then exactly the albedo); conductors sample the GGX visible-normal
100
- distribution (Heitz 2018) with the height-correlated Smith term, so the
101
- continuation weight is `F * G2/G1`; smooth dielectrics branch on the
102
- Fresnel term; rough plastic mixes a diffuse base with a GGX coat
103
- (F0 = 0.04) under a 50/50 lobe-mixture pdf; rough dielectrics sample GGX
104
- transmission. With a medium, propagation distances are drawn from a
105
- detached exponential rate frozen at Scene construction, and each segment
106
- carries the transmittance ratio `exp(-sigma_t d) / exp(-sbar d)`. Direct
107
- light is estimated at every NEE-capable vertex (and at medium scattering
108
- vertices) by area sampling over the emissive faces and, when an
109
- environment map is present, by sampling a detached mixture of its
110
- luminance CDF and the uniform sphere; BSDF-sampled hits on emitters and
111
- environment misses are combined with the balance heuristic, so all three
112
- estimators agree in expectation at any bounce budget.
113
-
114
- The backward pass is exact path replay. Sampling distributions depend only
115
- on geometry, frozen material parameters (roughness, ior), the
116
- construction-time environment CDF and medium rate, and the counter-based
117
- Philox stream keyed by `(seed, pixel, sample)`, never on the
118
- differentiable parameters, so the backward kernel re-traces identical
119
- paths with identical draws and no stored path state. Every radiance term
120
- is a product of per-bounce factors, each affine in its vertex's albedo
121
- texels (Schlick Fresnel is affine in F0; dielectric factors are constant),
122
- times a linear emission or environment texel; the replay differentiates
123
- the product in closed form with prefix/suffix exclusion products (no
124
- division, so zero albedos are safe) and scatters each factor's gradient
125
- into the four texels of its bilinear footprint with the sampling weights,
126
- the exact adjoint of the fetch. Medium sigma-gradients are the closed-form
127
- log-derivatives `-(D_total + d_term)` for `sigma_a`, plus
128
- `N_scatter/sigma_s` for `sigma_s`. Albedo gradients up to 2048 texels and
129
- emission gradients up to 512 texels accumulate in per-block shared memory
130
- and flush once; larger textures and environment gradients use direct
131
- global atomics.
132
-
133
- Geometry gradients are a separate pass over the direct-lighting transport
134
- term. The interior (smooth) part re-traces camera rays and light samples
135
- under detached sampling and pushes forward-mode dual numbers through the
136
- receiver hit, the shading geometry, and the light-area measure, one pass
137
- per perturbed vertex coordinate. The boundary (visibility) part is
138
- redner-style edge sampling: shadow silhouettes are sampled as (edge point,
139
- light point) pairs, projected to the receiver, tested for a clean
140
- lit/blocked crossing, and accumulated with the exact projective sweep
141
- velocity, the edge-to-curve arc stretch, and the receiver-area-to-pixel
142
- measure; camera silhouettes are sampled on the image plane, where the
143
- projected edge sweeps front-minus-background direct radiance across
144
- pixels.
145
-
146
- ## Correctness
147
-
148
- Measured on RTX 6000 Ada (sm89) from a source build; the published `v1`
149
- variants additionally pass the full 22-test suite through `get_kernel` on
150
- a GeForce RTX 3070 Ti (Ampere sm86, Linux):
151
-
152
- - **Analytic furnace, zero variance.** Closed uniform diffuse box under
153
- BRDF sampling: every path returns exactly `E * sum a^k`; the analytic
154
- value 0.498046875 is met with worst pixel deviation 3.4e-3. A diffuse
155
- plane under a constant environment reproduces `albedo * c` the same way.
156
- - **Fresnel slabs.** A smooth glass slab (ior 1.5) at normal incidence
157
- transmits the internal-bounce series `(1-R)/(1+R)`: measured 0.923141
158
- against 0.923077 analytic (0.007%). A rough-dielectric slab at
159
- alpha = 0.02 transmits the same series within 0.05%.
160
- - **Beer-Lambert.** A purely absorbing medium attenuates a uniform emitter
161
- to `E * exp(-sigma_a d)` within 0.035%; with scattering on, MIS and
162
- BRDF-only means agree on a foggy Cornell box.
163
- - **Estimator agreement.** MIS, NEE-only, and BRDF-only means agree within
164
- Monte Carlo tolerance on a Cornell box; MIS vs BRDF-only agree for a GGX
165
- conductor under an environment map and for rough plastic, which jointly
166
- exercises the VNDF weight, the GGX pdf, the mixed-lobe pdf, and the
167
- environment pdf.
168
- - **Gradients match finite differences along the same paths.** Constants
169
- 2.2e-4 max relative error over nine entries; wall-texture texels 2.6e-3;
170
- conductor F0 through GGX sampling and MIS 9.9e-4; plastic albedo through
171
- both lobes 2.8e-4; environment texels 2.1e-4 (detached CDF); emission
172
- texels linear-exact; medium sigmas 2e-2 gates. Texels no path can see
173
- get exactly zero from both the replay and the differences.
174
- - **Geometry gradients match seed-averaged finite differences.** Interior:
175
- translating an area light matches central differences of the direct
176
- image within 15%. Boundary: each occluder vertex's gradient (shadow
177
- sweep + its own image silhouette) matches an 8-seed-averaged FD within
178
- 0.23 worst-case (6-8% on three of four vertices; a single-seed FD of a
179
- visibility discontinuity is flip noise at these scales).
180
- - **Constants are 1x1 textures.** Both albedo forms render equal images to
181
- float rounding and receive matching gradients.
182
- - **Inverse rendering.** A wall color (3 unknowns) recovers to max error
183
- 0.004 in 80 Adam steps; an 8x8 wall texture (192 unknowns) drives the
184
- image loss from 1.8e-4 to 7.2e-14; a 4x8 emissive panel texture recovers
185
- from the room it lights; all through the kernel alone.
186
- - **Deterministic.** Repeated forward renders are bitwise identical;
187
- gradient buffers accumulate through float atomics and are deterministic
188
- in expectation but not bitwise.
189
 
190
  ## Measured
191
 
192
- RTX 6000 Ada, 512x512, MIS, 4 bounces. The terrain scenes are displaced
193
- grids with a 256x256 checker albedo texture, a GGX conductor block, and a
194
- constant environment map; the SAH build is the vectorized torch builder.
195
 
196
  | scene | build | forward | backward (replay) |
197
  |---|---|---|---|
@@ -199,47 +117,55 @@ constant environment map; the SAH build is the vectorized torch builder.
199
  | terrain, 522,254 tris, 16 spp | 0.2 s | 18.3 ms (229 Mpaths/s) | 39.0 ms (2.1x) |
200
  | terrain, 1,045,470 tris, 16 spp | 0.3 s | 20.3 ms (207 Mpaths/s) | 40.8 ms (2.0x) |
201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  ## Requirements and limits
203
 
204
  - NVIDIA GPU with compute capability 8.0+; float32 throughout.
205
- - Materials: Lambertian diffuse, GGX conductor, smooth dielectric, rough
206
- plastic (F0 = 0.04 coat), rough dielectric (BSDF-sampled only; GGX
207
- roughness clamped to >= 0.01); per-texel emission; pinhole camera; at
208
- most 64 materials and 16 bounces; two-sided surfaces; no Russian
209
- roulette (fixed bounce budget keeps the replay exact).
210
- - `render()` differentiates albedo texels/constants (all material types;
211
- conductor F0 tint), emission texels, environment texels, and medium
212
- sigmas. Camera, UVs, roughness, and ior are frozen.
213
- - `geometry_grad()` differentiates vertex positions for the
214
- direct-lighting term only: diffuse receivers and emitters carry the
215
- radiance jumps, indirect bounces are not differentiated, and uv motion
216
- across textures is not differentiated. Boundary terms cover shadow
217
- silhouettes and camera silhouettes.
218
- - The medium is homogeneous (isotropic phase), fills the scene, and is
219
- mutually exclusive with an environment map; its sampling rate is
220
- detached at Scene construction (`scene.med_sbar`).
221
- - The environment sampling CDF is detached at Scene construction (a 0.5
222
- uniform-sphere mixture keeps every direction reachable, so optimizing an
223
- env map from an arbitrary initialization stays unbiased); rebuild the
224
- Scene to re-importance-sample a changed map.
225
- - Texture and env shapes are fixed at Scene construction; UVs wrap.
226
- - Published variants are Linux x86_64; on Windows `load_local.py`
227
- JIT-builds the same source and exposes the identical API.
228
 
229
  ## References
230
 
231
- Kajiya, "The Rendering Equation" (SIGGRAPH 1986); Vicini, Speierer, Jakob,
232
- "Path Replay Backpropagation" (SIGGRAPH 2021); Nimier-David, Speierer,
233
- Ruiz, Jakob, "Radiative Backpropagation" (SIGGRAPH 2020); Li, Aittala,
234
- Durand, Lehtinen, "Differentiable Monte Carlo Ray Tracing through Edge
235
- Sampling" (SIGGRAPH Asia 2018); Heitz, "Sampling the GGX Distribution of
236
- Visible Normals" (JCGT 2018); Heitz, "Understanding the Masking-Shadowing
237
- Function" (JCGT 2014); Walter et al., "Microfacet Models for Refraction
238
- through Rough Surfaces" (EGSR 2007); Veach and Guibas, "Optimally
239
- Combining Sampling Techniques" (SIGGRAPH 1995); Möller and Trumbore,
240
- "Fast, Minimum Storage Ray-Triangle Intersection" (1997); Duff et al.,
241
- "Building an Orthonormal Basis, Revisited" (JCGT 2017); Wald, "On fast
242
- Construction of SAH-based Bounding Volume Hierarchies" (2007).
243
 
244
  ## License
245
 
 
6
  # pathtracer-diff
7
 
8
  A differentiable Monte Carlo path tracer as one kernel, loadable through
9
+ `kernels`. The forward pass is a megakernel unidirectional path tracer with
10
+ five materials, area lights, an importance-sampled environment map, an
11
+ optional homogeneous medium, and multiple importance sampling; the backward
12
+ pass replays the identical paths with identical counter-based draws and
13
+ emits analytic gradients. The reference standards are analytic transport
14
+ results and finite differences along the same paths.
15
+
16
+ Rendering is a simulation of light, and making it differentiable turns it
17
+ into an instrument: give it a photograph and unknown scene parameters, and
18
+ gradient descent recovers the materials, the lights, the fog, and the
19
+ geometry that produced the image. That normally requires a research
20
+ framework; here it is one loadable kernel, and inverse rendering is a bare
21
+ torch loop: render, compare, `backward()`, step.
 
 
22
 
23
  <video src="https://huggingface.co/kernels/phanerozoic/pathtracer-diff/resolve/main/media/inverse.mp4" autoplay loop muted playsinline controls width="100%">
24
  <img src="https://huggingface.co/kernels/phanerozoic/pathtracer-diff/resolve/main/media/inverse.gif" alt="THE DRAGON'S HALL: one continuous inverse-rendering shot; a dark marble hall with a gold-trimmed pedestal, glowing rune band, warm sconces, and a levitating crystal Stanford dragon that slides onto its pedestal while fog fills the room, converging to the fixed target inset">
25
  </video>
26
 
27
  *THE DRAGON'S HALL (`media/make_hero.py`): one continuous shot, every
28
+ gradient, optimized live through the kernel against the fixed TARGET inset.
29
+ From a flat gray start, Adam through `render()` recovers the 128x128 marble
30
+ floor albedo (49,152 unknowns), the gold trim's conductor tint, the
31
+ pedestal's plastic albedo, the braziers' warm emission (max err 1.75 of 14),
32
+ and a 16x64 per-texel emissive rune band (3,072 unknowns, mean err 0.15),
33
+ around a smooth glass slab and the rough-dielectric crystal dragon.
34
+ Mid-shot, `geometry_grad()` alone takes over the dragon's position: 5,205
35
+ vertices slide 4.60 scene units onto the pedestal by shadow and silhouette
36
+ alone, final offset 0.004. In the last phase the participating medium rolls
37
+ in: sigma_a and sigma_s recover from the hazy target to extinction error
38
+ 0.014. Image loss falls 8.5e-1 to 1.9e-1 at spp 8; the dim-hall floor and
39
+ trim retain residual texel error where the scene barely observes them.
40
+ Stanford dragon courtesy of the Stanford Computer Graphics Laboratory.*
 
 
41
 
42
  ## Usage
43
 
 
48
  ptd = get_kernel("phanerozoic/pathtracer-diff", version=1, trust_remote_code=True)
49
 
50
  wall_tex = torch.full((256, 256, 3), 0.5, device="cuda", requires_grad=True)
 
51
  scene = ptd.Scene(vertices, faces, material_ids,
52
+ albedo=[torch.tensor([0.73] * 3), wall_tex],
53
+ emission=[torch.zeros(3), torch.zeros(3)],
54
+ uvs=uvs, material_types=[ptd.DIFFUSE, ptd.DIFFUSE],
55
+ roughness=[0.3, 0.3])
 
 
 
56
  cam = ptd.Camera(position=(2.78, 2.73, -8.0), look_at=(2.78, 2.73, 2.8),
57
  vfov_deg=39.0)
58
 
59
+ img = ptd.render(scene, cam, 512, 512, spp=64, max_bounces=4)
60
  loss = (img - target).square().mean()
61
+ loss.backward() # gradients on wall_tex
62
 
63
+ grad_verts = ptd.geometry_grad(scene, cam, dloss_dimage)
64
  ```
65
 
66
  `version` selects the release branch; `trust_remote_code` is required by
67
+ `kernels` for publishers without the trusted-publisher mark. A fixed `seed`
68
+ renders the same paths every call, so the Monte Carlo objective is a
69
+ deterministic function of the parameters and gradient descent sees a smooth
70
+ landscape at any spp.
 
 
 
71
 
72
  ## API
73
 
74
  | Symbol | Purpose |
75
  |---|---|
76
+ | `Scene(vertices, faces, material_ids, albedo, emission, uvs, material_types, roughness, ior, env, medium)` | triangle-mesh scene; BVH, light list, and the detached environment CDF / medium rate built at construction |
77
  | `Camera(position, look_at, up, vfov_deg)` | pinhole camera |
78
+ | `render(scene, camera, height, width, spp, max_bounces, estimator, seed)` | differentiable render to `[H, W, 3]` f32 linear; autograd to albedo, emission, environment texels, and medium sigmas |
79
+ | `geometry_grad(scene, camera, grad_image, spp, edge_samples, seed)` | d(loss)/d(vertex positions) for the direct-lighting term: dual-number interior + edge-sampled shadow and camera silhouettes |
80
  | `DIFFUSE`, `CONDUCTOR`, `DIELECTRIC`, `PLASTIC`, `ROUGH_DIELECTRIC` | material type constants |
81
  | `ops.pt_forward` / `ops.pt_backward` / `ops.pt_geometry_grad` | raw kernel launches |
82
 
83
+ ## Method
84
 
85
  One thread owns one pixel and accumulates its spp samples serially, so the
86
+ image is bitwise deterministic with no atomics in the forward pass. Diffuse
87
+ vertices sample the cosine hemisphere; conductors sample the GGX
88
+ visible-normal distribution with the height-correlated Smith term; smooth
89
+ dielectrics branch on Fresnel; rough plastic mixes a diffuse base with a GGX
90
+ coat under a 50/50 lobe-mixture pdf; rough dielectrics sample GGX
91
+ transmission. Direct light is estimated at every NEE-capable vertex by area
92
+ sampling over emissive faces and, with an environment map, by a detached
93
+ mixture of its luminance CDF and the uniform sphere; BSDF-sampled hits
94
+ combine by the balance heuristic.
95
+
96
+ The backward pass is exact path replay: sampling distributions depend only
97
+ on geometry, frozen parameters, and the counter-based Philox stream, never
98
+ on the differentiable parameters, so the backward kernel re-traces identical
99
+ paths with no stored path state. Every radiance term is a product of
100
+ per-bounce factors, each affine in its vertex's albedo texels, times a
101
+ linear emission or environment texel; the replay differentiates the product
102
+ in closed form with prefix/suffix exclusion products and scatters each
103
+ factor's gradient into the four texels of its bilinear footprint. Geometry
104
+ gradients run a separate pass over the direct-lighting term: forward-mode
105
+ duals for the smooth part, redner-style edge sampling for shadow and camera
106
+ silhouettes.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
  ## Measured
109
 
110
+ RTX 6000 Ada, 512x512, MIS, 4 bounces; terrain scenes are displaced grids
111
+ with a 256x256 checker albedo, a GGX conductor block, and a constant
112
+ environment, built by the vectorized torch SAH builder:
113
 
114
  | scene | build | forward | backward (replay) |
115
  |---|---|---|---|
 
117
  | terrain, 522,254 tris, 16 spp | 0.2 s | 18.3 ms (229 Mpaths/s) | 39.0 ms (2.1x) |
118
  | terrain, 1,045,470 tris, 16 spp | 0.3 s | 20.3 ms (207 Mpaths/s) | 40.8 ms (2.0x) |
119
 
120
+ ## Correctness
121
+
122
+ Measured on RTX 6000 Ada from source; the published `v1` variants pass the
123
+ full 22-test suite through `get_kernel` on a GeForce RTX 3070 Ti (sm86):
124
+
125
+ - Analytic furnace, zero variance: a closed uniform diffuse box returns
126
+ exactly `E * sum a^k` per path; the analytic 0.498046875 is met with worst
127
+ pixel deviation 3.4e-3.
128
+ - Fresnel slabs: a smooth glass slab at normal incidence transmits the
129
+ internal-bounce series `(1-R)/(1+R)`, measured 0.923141 against 0.923077
130
+ (0.007%); a rough-dielectric slab at alpha 0.02 within 0.05%.
131
+ - Beer-Lambert: a purely absorbing medium attenuates a uniform emitter to
132
+ `E * exp(-sigma_a d)` within 0.035%.
133
+ - Estimator agreement: MIS, NEE-only, and BRDF-only means agree within Monte
134
+ Carlo tolerance across Cornell, GGX-under-environment, and rough-plastic
135
+ scenes.
136
+ - Gradients match finite differences along the same paths: constants 2.2e-4
137
+ max relative over nine entries; wall texels 2.6e-3; conductor F0 through
138
+ GGX and MIS 9.9e-4; environment texels 2.1e-4; emission linear-exact;
139
+ medium sigmas within 2e-2 gates. Texels no path can see get exactly zero
140
+ from both sides.
141
+ - Geometry gradients match seed-averaged finite differences: light
142
+ translation within 15%; occluder vertices within 0.23 worst case.
143
+ - Inverse rendering: a wall color recovers to max error 0.004 in 80 Adam
144
+ steps; an 8x8 texture drives the loss from 1.8e-4 to 7.2e-14.
145
+ - Deterministic: repeated forward renders are bitwise identical; gradient
146
+ buffers accumulate through float atomics and are deterministic in
147
+ expectation but not bitwise.
148
+
149
  ## Requirements and limits
150
 
151
  - NVIDIA GPU with compute capability 8.0+; float32 throughout.
152
+ - At most 64 materials and 16 bounces; two-sided surfaces; no Russian
153
+ roulette (the fixed bounce budget keeps the replay exact); pinhole camera.
154
+ - `render()` differentiates albedo, emission, environment texels, and medium
155
+ sigmas; camera, UVs, roughness, and ior are frozen. `geometry_grad()`
156
+ covers the direct-lighting term only.
157
+ - The medium is homogeneous, fills the scene, and is mutually exclusive with
158
+ an environment map; the environment CDF is detached at Scene construction.
159
+ - Published variants are Linux x86_64; on Windows `load_local.py` JIT-builds
160
+ the same source.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
  ## References
163
 
164
+ Kajiya (1986); Vicini et al., "Path Replay Backpropagation" (2021);
165
+ Nimier-David et al., "Radiative Backpropagation" (2020); Li et al.,
166
+ "Differentiable Monte Carlo Ray Tracing through Edge Sampling" (2018);
167
+ Heitz (2014, 2018) on GGX and VNDF; Walter et al. (2007); Veach and Guibas
168
+ (1995); Möller and Trumbore (1997); Wald (2007) on SAH construction.
 
 
 
 
 
 
 
169
 
170
  ## License
171
 
README.md CHANGED
@@ -6,42 +6,38 @@ license: apache-2.0
6
  # pathtracer-diff
7
 
8
  A differentiable Monte Carlo path tracer as one kernel, loadable through
9
- `kernels`. The forward pass is a megakernel unidirectional path tracer:
10
- Lambertian diffuse, GGX conductor (VNDF-sampled, height-correlated Smith),
11
- smooth dielectric, rough plastic, and rough dielectric materials; area
12
- lights with per-texel emission and an importance-sampled equirectangular
13
- environment map; an optional homogeneous participating medium; multiple
14
- importance sampling (balance heuristic) by default; per-texel albedo with
15
- bilinear filtering; a binned-SAH BVH built in vectorized torch in
16
- fractions of a second at a million triangles. The backward pass replays
17
- the identical paths with the identical counter-based random draws and
18
- emits analytic gradients to albedo texels, emission texels, environment
19
- texels, and medium coefficients; a companion pass differentiates the
20
- direct-lighting image with respect to vertex positions, including shadow
21
- and camera silhouettes. Inverse rendering runs as a bare torch loop around
22
- the kernel, with no rendering framework in the loop: render, compare,
23
- `backward()`, step.
24
 
25
  <video src="https://huggingface.co/kernels/phanerozoic/pathtracer-diff/resolve/main/media/inverse.mp4" autoplay loop muted playsinline controls width="100%">
26
  <img src="https://huggingface.co/kernels/phanerozoic/pathtracer-diff/resolve/main/media/inverse.gif" alt="THE DRAGON'S HALL: one continuous inverse-rendering shot; a dark marble hall with a gold-trimmed pedestal, glowing rune band, warm sconces, and a levitating crystal Stanford dragon that slides onto its pedestal while fog fills the room, converging to the fixed target inset">
27
  </video>
28
 
29
  *THE DRAGON'S HALL (`media/make_hero.py`): one continuous shot, every
30
- gradient, optimized live through the kernel against the fixed TARGET
31
- inset, 30 fps with a progress bar. From a flat gray start, Adam through
32
- `render()` recovers the 128x128 marble floor albedo (49,152 unknowns),
33
- the gold trim's conductor tint, the pedestal's plastic albedo, the
34
- braziers' warm emission (max err 1.75 of 14), and a 16x64 per-texel
35
- emissive rune band (3,072 unknowns, mean err 0.15), around a smooth
36
- glass slab and the rough-dielectric crystal dragon. Mid-shot,
37
- `geometry_grad()` alone takes over the dragon's position: 5,205 vertices
38
- slide 4.60 scene units onto the pedestal by shadow and silhouette alone,
39
- final offset 0.004. In the last phase the participating medium rolls in:
40
- sigma_a and sigma_s recover from the hazy target to extinction error
41
- 0.014. Image loss falls 8.5e-1 to 1.9e-1 at spp 8; the dim-hall floor
42
- and trim retain residual texel error where the scene barely observes
43
- them, reported as measured. Stanford dragon courtesy of the Stanford
44
- Computer Graphics Laboratory.*
45
 
46
  ## Usage
47
 
@@ -52,146 +48,68 @@ from kernels import get_kernel
52
  ptd = get_kernel("phanerozoic/pathtracer-diff", version=1, trust_remote_code=True)
53
 
54
  wall_tex = torch.full((256, 256, 3), 0.5, device="cuda", requires_grad=True)
55
- panel_emi = torch.full((16, 32, 3), 5.0, device="cuda", requires_grad=True)
56
  scene = ptd.Scene(vertices, faces, material_ids,
57
- albedo=[torch.tensor([0.73] * 3), # constants are 1x1 textures
58
- wall_tex, # [H, W, 3] per-texel albedo
59
- torch.tensor([0.9, 0.6, 0.3])],
60
- emission=[torch.zeros(3), torch.zeros(3), panel_emi],
61
- uvs=uvs,
62
- material_types=[ptd.DIFFUSE, ptd.DIFFUSE, ptd.PLASTIC],
63
- roughness=[0.3, 0.3, 0.2])
64
  cam = ptd.Camera(position=(2.78, 2.73, -8.0), look_at=(2.78, 2.73, 2.8),
65
  vfov_deg=39.0)
66
 
67
- img = ptd.render(scene, cam, 512, 512, spp=64, max_bounces=4) # [H, W, 3] linear
68
  loss = (img - target).square().mean()
69
- loss.backward() # gradients on wall_tex and panel_emi
70
 
71
- grad_verts = ptd.geometry_grad(scene, cam, dloss_dimage) # [V, 3] vertex grads
72
  ```
73
 
74
  `version` selects the release branch; `trust_remote_code` is required by
75
- `kernels` for publishers without the trusted-publisher mark.
76
-
77
- A fixed `seed` renders the same paths every call, so the Monte Carlo
78
- objective is a deterministic function of the parameters and gradient
79
- descent sees a smooth landscape at any spp; that is the configuration an
80
- inverse-rendering loop wants. `estimator` selects `"mis"` (default),
81
- `"nee"`, or `"brdf"`.
82
 
83
  ## API
84
 
85
  | Symbol | Purpose |
86
  |---|---|
87
- | `Scene(vertices, faces, material_ids, albedo, emission, uvs, material_types, roughness, ior, env, medium)` | triangle-mesh scene; BVH, light list, and the detached environment CDF / medium rate are built at construction; `albedo` and `emission` are `[M, 3]` constants or lists of `[Hm, Wm, 3]` textures; `env` is an optional `[Eh, Ew, 3]` equirect map; `medium=(sigma_a, sigma_s)` fills the scene with a homogeneous absorbing/scattering medium (mutually exclusive with `env`); texture/env shapes are fixed at construction |
88
  | `Camera(position, look_at, up, vfov_deg)` | pinhole camera |
89
- | `render(scene, camera, height, width, spp, max_bounces, estimator, seed)` | differentiable render to `[H, W, 3]` f32 linear radiance; autograd to albedo texels, emission texels, env texels, and medium sigmas |
90
- | `geometry_grad(scene, camera, grad_image, spp, edge_samples, seed)` | d(loss)/d(vertex positions) `[V, 3]` for the direct-lighting transport term: dual-number interior derivative + edge-sampled shadow-silhouette and camera-silhouette boundary terms |
91
  | `DIFFUSE`, `CONDUCTOR`, `DIELECTRIC`, `PLASTIC`, `ROUGH_DIELECTRIC` | material type constants |
92
  | `ops.pt_forward` / `ops.pt_backward` / `ops.pt_geometry_grad` | raw kernel launches |
93
 
94
- ## How it works
95
 
96
  One thread owns one pixel and accumulates its spp samples serially, so the
97
- image is bitwise deterministic with no atomics in the forward pass.
98
- Diffuse vertices sample the cosine hemisphere (the throughput multiplier
99
- is then exactly the albedo); conductors sample the GGX visible-normal
100
- distribution (Heitz 2018) with the height-correlated Smith term, so the
101
- continuation weight is `F * G2/G1`; smooth dielectrics branch on the
102
- Fresnel term; rough plastic mixes a diffuse base with a GGX coat
103
- (F0 = 0.04) under a 50/50 lobe-mixture pdf; rough dielectrics sample GGX
104
- transmission. With a medium, propagation distances are drawn from a
105
- detached exponential rate frozen at Scene construction, and each segment
106
- carries the transmittance ratio `exp(-sigma_t d) / exp(-sbar d)`. Direct
107
- light is estimated at every NEE-capable vertex (and at medium scattering
108
- vertices) by area sampling over the emissive faces and, when an
109
- environment map is present, by sampling a detached mixture of its
110
- luminance CDF and the uniform sphere; BSDF-sampled hits on emitters and
111
- environment misses are combined with the balance heuristic, so all three
112
- estimators agree in expectation at any bounce budget.
113
-
114
- The backward pass is exact path replay. Sampling distributions depend only
115
- on geometry, frozen material parameters (roughness, ior), the
116
- construction-time environment CDF and medium rate, and the counter-based
117
- Philox stream keyed by `(seed, pixel, sample)`, never on the
118
- differentiable parameters, so the backward kernel re-traces identical
119
- paths with identical draws and no stored path state. Every radiance term
120
- is a product of per-bounce factors, each affine in its vertex's albedo
121
- texels (Schlick Fresnel is affine in F0; dielectric factors are constant),
122
- times a linear emission or environment texel; the replay differentiates
123
- the product in closed form with prefix/suffix exclusion products (no
124
- division, so zero albedos are safe) and scatters each factor's gradient
125
- into the four texels of its bilinear footprint with the sampling weights,
126
- the exact adjoint of the fetch. Medium sigma-gradients are the closed-form
127
- log-derivatives `-(D_total + d_term)` for `sigma_a`, plus
128
- `N_scatter/sigma_s` for `sigma_s`. Albedo gradients up to 2048 texels and
129
- emission gradients up to 512 texels accumulate in per-block shared memory
130
- and flush once; larger textures and environment gradients use direct
131
- global atomics.
132
-
133
- Geometry gradients are a separate pass over the direct-lighting transport
134
- term. The interior (smooth) part re-traces camera rays and light samples
135
- under detached sampling and pushes forward-mode dual numbers through the
136
- receiver hit, the shading geometry, and the light-area measure, one pass
137
- per perturbed vertex coordinate. The boundary (visibility) part is
138
- redner-style edge sampling: shadow silhouettes are sampled as (edge point,
139
- light point) pairs, projected to the receiver, tested for a clean
140
- lit/blocked crossing, and accumulated with the exact projective sweep
141
- velocity, the edge-to-curve arc stretch, and the receiver-area-to-pixel
142
- measure; camera silhouettes are sampled on the image plane, where the
143
- projected edge sweeps front-minus-background direct radiance across
144
- pixels.
145
-
146
- ## Correctness
147
-
148
- Measured on RTX 6000 Ada (sm89) from a source build; the published `v1`
149
- variants additionally pass the full 22-test suite through `get_kernel` on
150
- a GeForce RTX 3070 Ti (Ampere sm86, Linux):
151
-
152
- - **Analytic furnace, zero variance.** Closed uniform diffuse box under
153
- BRDF sampling: every path returns exactly `E * sum a^k`; the analytic
154
- value 0.498046875 is met with worst pixel deviation 3.4e-3. A diffuse
155
- plane under a constant environment reproduces `albedo * c` the same way.
156
- - **Fresnel slabs.** A smooth glass slab (ior 1.5) at normal incidence
157
- transmits the internal-bounce series `(1-R)/(1+R)`: measured 0.923141
158
- against 0.923077 analytic (0.007%). A rough-dielectric slab at
159
- alpha = 0.02 transmits the same series within 0.05%.
160
- - **Beer-Lambert.** A purely absorbing medium attenuates a uniform emitter
161
- to `E * exp(-sigma_a d)` within 0.035%; with scattering on, MIS and
162
- BRDF-only means agree on a foggy Cornell box.
163
- - **Estimator agreement.** MIS, NEE-only, and BRDF-only means agree within
164
- Monte Carlo tolerance on a Cornell box; MIS vs BRDF-only agree for a GGX
165
- conductor under an environment map and for rough plastic, which jointly
166
- exercises the VNDF weight, the GGX pdf, the mixed-lobe pdf, and the
167
- environment pdf.
168
- - **Gradients match finite differences along the same paths.** Constants
169
- 2.2e-4 max relative error over nine entries; wall-texture texels 2.6e-3;
170
- conductor F0 through GGX sampling and MIS 9.9e-4; plastic albedo through
171
- both lobes 2.8e-4; environment texels 2.1e-4 (detached CDF); emission
172
- texels linear-exact; medium sigmas 2e-2 gates. Texels no path can see
173
- get exactly zero from both the replay and the differences.
174
- - **Geometry gradients match seed-averaged finite differences.** Interior:
175
- translating an area light matches central differences of the direct
176
- image within 15%. Boundary: each occluder vertex's gradient (shadow
177
- sweep + its own image silhouette) matches an 8-seed-averaged FD within
178
- 0.23 worst-case (6-8% on three of four vertices; a single-seed FD of a
179
- visibility discontinuity is flip noise at these scales).
180
- - **Constants are 1x1 textures.** Both albedo forms render equal images to
181
- float rounding and receive matching gradients.
182
- - **Inverse rendering.** A wall color (3 unknowns) recovers to max error
183
- 0.004 in 80 Adam steps; an 8x8 wall texture (192 unknowns) drives the
184
- image loss from 1.8e-4 to 7.2e-14; a 4x8 emissive panel texture recovers
185
- from the room it lights; all through the kernel alone.
186
- - **Deterministic.** Repeated forward renders are bitwise identical;
187
- gradient buffers accumulate through float atomics and are deterministic
188
- in expectation but not bitwise.
189
 
190
  ## Measured
191
 
192
- RTX 6000 Ada, 512x512, MIS, 4 bounces. The terrain scenes are displaced
193
- grids with a 256x256 checker albedo texture, a GGX conductor block, and a
194
- constant environment map; the SAH build is the vectorized torch builder.
195
 
196
  | scene | build | forward | backward (replay) |
197
  |---|---|---|---|
@@ -199,47 +117,55 @@ constant environment map; the SAH build is the vectorized torch builder.
199
  | terrain, 522,254 tris, 16 spp | 0.2 s | 18.3 ms (229 Mpaths/s) | 39.0 ms (2.1x) |
200
  | terrain, 1,045,470 tris, 16 spp | 0.3 s | 20.3 ms (207 Mpaths/s) | 40.8 ms (2.0x) |
201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  ## Requirements and limits
203
 
204
  - NVIDIA GPU with compute capability 8.0+; float32 throughout.
205
- - Materials: Lambertian diffuse, GGX conductor, smooth dielectric, rough
206
- plastic (F0 = 0.04 coat), rough dielectric (BSDF-sampled only; GGX
207
- roughness clamped to >= 0.01); per-texel emission; pinhole camera; at
208
- most 64 materials and 16 bounces; two-sided surfaces; no Russian
209
- roulette (fixed bounce budget keeps the replay exact).
210
- - `render()` differentiates albedo texels/constants (all material types;
211
- conductor F0 tint), emission texels, environment texels, and medium
212
- sigmas. Camera, UVs, roughness, and ior are frozen.
213
- - `geometry_grad()` differentiates vertex positions for the
214
- direct-lighting term only: diffuse receivers and emitters carry the
215
- radiance jumps, indirect bounces are not differentiated, and uv motion
216
- across textures is not differentiated. Boundary terms cover shadow
217
- silhouettes and camera silhouettes.
218
- - The medium is homogeneous (isotropic phase), fills the scene, and is
219
- mutually exclusive with an environment map; its sampling rate is
220
- detached at Scene construction (`scene.med_sbar`).
221
- - The environment sampling CDF is detached at Scene construction (a 0.5
222
- uniform-sphere mixture keeps every direction reachable, so optimizing an
223
- env map from an arbitrary initialization stays unbiased); rebuild the
224
- Scene to re-importance-sample a changed map.
225
- - Texture and env shapes are fixed at Scene construction; UVs wrap.
226
- - Published variants are Linux x86_64; on Windows `load_local.py`
227
- JIT-builds the same source and exposes the identical API.
228
 
229
  ## References
230
 
231
- Kajiya, "The Rendering Equation" (SIGGRAPH 1986); Vicini, Speierer, Jakob,
232
- "Path Replay Backpropagation" (SIGGRAPH 2021); Nimier-David, Speierer,
233
- Ruiz, Jakob, "Radiative Backpropagation" (SIGGRAPH 2020); Li, Aittala,
234
- Durand, Lehtinen, "Differentiable Monte Carlo Ray Tracing through Edge
235
- Sampling" (SIGGRAPH Asia 2018); Heitz, "Sampling the GGX Distribution of
236
- Visible Normals" (JCGT 2018); Heitz, "Understanding the Masking-Shadowing
237
- Function" (JCGT 2014); Walter et al., "Microfacet Models for Refraction
238
- through Rough Surfaces" (EGSR 2007); Veach and Guibas, "Optimally
239
- Combining Sampling Techniques" (SIGGRAPH 1995); Möller and Trumbore,
240
- "Fast, Minimum Storage Ray-Triangle Intersection" (1997); Duff et al.,
241
- "Building an Orthonormal Basis, Revisited" (JCGT 2017); Wald, "On fast
242
- Construction of SAH-based Bounding Volume Hierarchies" (2007).
243
 
244
  ## License
245
 
 
6
  # pathtracer-diff
7
 
8
  A differentiable Monte Carlo path tracer as one kernel, loadable through
9
+ `kernels`. The forward pass is a megakernel unidirectional path tracer with
10
+ five materials, area lights, an importance-sampled environment map, an
11
+ optional homogeneous medium, and multiple importance sampling; the backward
12
+ pass replays the identical paths with identical counter-based draws and
13
+ emits analytic gradients. The reference standards are analytic transport
14
+ results and finite differences along the same paths.
15
+
16
+ Rendering is a simulation of light, and making it differentiable turns it
17
+ into an instrument: give it a photograph and unknown scene parameters, and
18
+ gradient descent recovers the materials, the lights, the fog, and the
19
+ geometry that produced the image. That normally requires a research
20
+ framework; here it is one loadable kernel, and inverse rendering is a bare
21
+ torch loop: render, compare, `backward()`, step.
 
 
22
 
23
  <video src="https://huggingface.co/kernels/phanerozoic/pathtracer-diff/resolve/main/media/inverse.mp4" autoplay loop muted playsinline controls width="100%">
24
  <img src="https://huggingface.co/kernels/phanerozoic/pathtracer-diff/resolve/main/media/inverse.gif" alt="THE DRAGON'S HALL: one continuous inverse-rendering shot; a dark marble hall with a gold-trimmed pedestal, glowing rune band, warm sconces, and a levitating crystal Stanford dragon that slides onto its pedestal while fog fills the room, converging to the fixed target inset">
25
  </video>
26
 
27
  *THE DRAGON'S HALL (`media/make_hero.py`): one continuous shot, every
28
+ gradient, optimized live through the kernel against the fixed TARGET inset.
29
+ From a flat gray start, Adam through `render()` recovers the 128x128 marble
30
+ floor albedo (49,152 unknowns), the gold trim's conductor tint, the
31
+ pedestal's plastic albedo, the braziers' warm emission (max err 1.75 of 14),
32
+ and a 16x64 per-texel emissive rune band (3,072 unknowns, mean err 0.15),
33
+ around a smooth glass slab and the rough-dielectric crystal dragon.
34
+ Mid-shot, `geometry_grad()` alone takes over the dragon's position: 5,205
35
+ vertices slide 4.60 scene units onto the pedestal by shadow and silhouette
36
+ alone, final offset 0.004. In the last phase the participating medium rolls
37
+ in: sigma_a and sigma_s recover from the hazy target to extinction error
38
+ 0.014. Image loss falls 8.5e-1 to 1.9e-1 at spp 8; the dim-hall floor and
39
+ trim retain residual texel error where the scene barely observes them.
40
+ Stanford dragon courtesy of the Stanford Computer Graphics Laboratory.*
 
 
41
 
42
  ## Usage
43
 
 
48
  ptd = get_kernel("phanerozoic/pathtracer-diff", version=1, trust_remote_code=True)
49
 
50
  wall_tex = torch.full((256, 256, 3), 0.5, device="cuda", requires_grad=True)
 
51
  scene = ptd.Scene(vertices, faces, material_ids,
52
+ albedo=[torch.tensor([0.73] * 3), wall_tex],
53
+ emission=[torch.zeros(3), torch.zeros(3)],
54
+ uvs=uvs, material_types=[ptd.DIFFUSE, ptd.DIFFUSE],
55
+ roughness=[0.3, 0.3])
 
 
 
56
  cam = ptd.Camera(position=(2.78, 2.73, -8.0), look_at=(2.78, 2.73, 2.8),
57
  vfov_deg=39.0)
58
 
59
+ img = ptd.render(scene, cam, 512, 512, spp=64, max_bounces=4)
60
  loss = (img - target).square().mean()
61
+ loss.backward() # gradients on wall_tex
62
 
63
+ grad_verts = ptd.geometry_grad(scene, cam, dloss_dimage)
64
  ```
65
 
66
  `version` selects the release branch; `trust_remote_code` is required by
67
+ `kernels` for publishers without the trusted-publisher mark. A fixed `seed`
68
+ renders the same paths every call, so the Monte Carlo objective is a
69
+ deterministic function of the parameters and gradient descent sees a smooth
70
+ landscape at any spp.
 
 
 
71
 
72
  ## API
73
 
74
  | Symbol | Purpose |
75
  |---|---|
76
+ | `Scene(vertices, faces, material_ids, albedo, emission, uvs, material_types, roughness, ior, env, medium)` | triangle-mesh scene; BVH, light list, and the detached environment CDF / medium rate built at construction |
77
  | `Camera(position, look_at, up, vfov_deg)` | pinhole camera |
78
+ | `render(scene, camera, height, width, spp, max_bounces, estimator, seed)` | differentiable render to `[H, W, 3]` f32 linear; autograd to albedo, emission, environment texels, and medium sigmas |
79
+ | `geometry_grad(scene, camera, grad_image, spp, edge_samples, seed)` | d(loss)/d(vertex positions) for the direct-lighting term: dual-number interior + edge-sampled shadow and camera silhouettes |
80
  | `DIFFUSE`, `CONDUCTOR`, `DIELECTRIC`, `PLASTIC`, `ROUGH_DIELECTRIC` | material type constants |
81
  | `ops.pt_forward` / `ops.pt_backward` / `ops.pt_geometry_grad` | raw kernel launches |
82
 
83
+ ## Method
84
 
85
  One thread owns one pixel and accumulates its spp samples serially, so the
86
+ image is bitwise deterministic with no atomics in the forward pass. Diffuse
87
+ vertices sample the cosine hemisphere; conductors sample the GGX
88
+ visible-normal distribution with the height-correlated Smith term; smooth
89
+ dielectrics branch on Fresnel; rough plastic mixes a diffuse base with a GGX
90
+ coat under a 50/50 lobe-mixture pdf; rough dielectrics sample GGX
91
+ transmission. Direct light is estimated at every NEE-capable vertex by area
92
+ sampling over emissive faces and, with an environment map, by a detached
93
+ mixture of its luminance CDF and the uniform sphere; BSDF-sampled hits
94
+ combine by the balance heuristic.
95
+
96
+ The backward pass is exact path replay: sampling distributions depend only
97
+ on geometry, frozen parameters, and the counter-based Philox stream, never
98
+ on the differentiable parameters, so the backward kernel re-traces identical
99
+ paths with no stored path state. Every radiance term is a product of
100
+ per-bounce factors, each affine in its vertex's albedo texels, times a
101
+ linear emission or environment texel; the replay differentiates the product
102
+ in closed form with prefix/suffix exclusion products and scatters each
103
+ factor's gradient into the four texels of its bilinear footprint. Geometry
104
+ gradients run a separate pass over the direct-lighting term: forward-mode
105
+ duals for the smooth part, redner-style edge sampling for shadow and camera
106
+ silhouettes.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
  ## Measured
109
 
110
+ RTX 6000 Ada, 512x512, MIS, 4 bounces; terrain scenes are displaced grids
111
+ with a 256x256 checker albedo, a GGX conductor block, and a constant
112
+ environment, built by the vectorized torch SAH builder:
113
 
114
  | scene | build | forward | backward (replay) |
115
  |---|---|---|---|
 
117
  | terrain, 522,254 tris, 16 spp | 0.2 s | 18.3 ms (229 Mpaths/s) | 39.0 ms (2.1x) |
118
  | terrain, 1,045,470 tris, 16 spp | 0.3 s | 20.3 ms (207 Mpaths/s) | 40.8 ms (2.0x) |
119
 
120
+ ## Correctness
121
+
122
+ Measured on RTX 6000 Ada from source; the published `v1` variants pass the
123
+ full 22-test suite through `get_kernel` on a GeForce RTX 3070 Ti (sm86):
124
+
125
+ - Analytic furnace, zero variance: a closed uniform diffuse box returns
126
+ exactly `E * sum a^k` per path; the analytic 0.498046875 is met with worst
127
+ pixel deviation 3.4e-3.
128
+ - Fresnel slabs: a smooth glass slab at normal incidence transmits the
129
+ internal-bounce series `(1-R)/(1+R)`, measured 0.923141 against 0.923077
130
+ (0.007%); a rough-dielectric slab at alpha 0.02 within 0.05%.
131
+ - Beer-Lambert: a purely absorbing medium attenuates a uniform emitter to
132
+ `E * exp(-sigma_a d)` within 0.035%.
133
+ - Estimator agreement: MIS, NEE-only, and BRDF-only means agree within Monte
134
+ Carlo tolerance across Cornell, GGX-under-environment, and rough-plastic
135
+ scenes.
136
+ - Gradients match finite differences along the same paths: constants 2.2e-4
137
+ max relative over nine entries; wall texels 2.6e-3; conductor F0 through
138
+ GGX and MIS 9.9e-4; environment texels 2.1e-4; emission linear-exact;
139
+ medium sigmas within 2e-2 gates. Texels no path can see get exactly zero
140
+ from both sides.
141
+ - Geometry gradients match seed-averaged finite differences: light
142
+ translation within 15%; occluder vertices within 0.23 worst case.
143
+ - Inverse rendering: a wall color recovers to max error 0.004 in 80 Adam
144
+ steps; an 8x8 texture drives the loss from 1.8e-4 to 7.2e-14.
145
+ - Deterministic: repeated forward renders are bitwise identical; gradient
146
+ buffers accumulate through float atomics and are deterministic in
147
+ expectation but not bitwise.
148
+
149
  ## Requirements and limits
150
 
151
  - NVIDIA GPU with compute capability 8.0+; float32 throughout.
152
+ - At most 64 materials and 16 bounces; two-sided surfaces; no Russian
153
+ roulette (the fixed bounce budget keeps the replay exact); pinhole camera.
154
+ - `render()` differentiates albedo, emission, environment texels, and medium
155
+ sigmas; camera, UVs, roughness, and ior are frozen. `geometry_grad()`
156
+ covers the direct-lighting term only.
157
+ - The medium is homogeneous, fills the scene, and is mutually exclusive with
158
+ an environment map; the environment CDF is detached at Scene construction.
159
+ - Published variants are Linux x86_64; on Windows `load_local.py` JIT-builds
160
+ the same source.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
  ## References
163
 
164
+ Kajiya (1986); Vicini et al., "Path Replay Backpropagation" (2021);
165
+ Nimier-David et al., "Radiative Backpropagation" (2020); Li et al.,
166
+ "Differentiable Monte Carlo Ray Tracing through Edge Sampling" (2018);
167
+ Heitz (2014, 2018) on GGX and VNDF; Walter et al. (2007); Veach and Guibas
168
+ (1995); Möller and Trumbore (1997); Wald (2007) on SAH construction.
 
 
 
 
 
 
 
169
 
170
  ## License
171