Card: standardized form
Browse files
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 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 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 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 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),
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 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)
|
| 68 |
loss = (img - target).square().mean()
|
| 69 |
-
loss.backward()
|
| 70 |
|
| 71 |
-
grad_verts = ptd.geometry_grad(scene, cam, dloss_dimage)
|
| 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 |
-
|
| 78 |
-
|
| 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
|
| 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
|
| 90 |
-
| `geometry_grad(scene, camera, grad_image, spp, edge_samples, seed)` | d(loss)/d(vertex positions)
|
| 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 |
-
##
|
| 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 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 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
|
| 193 |
-
|
| 194 |
-
|
| 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 |
-
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 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
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 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 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 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 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 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),
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 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)
|
| 68 |
loss = (img - target).square().mean()
|
| 69 |
-
loss.backward()
|
| 70 |
|
| 71 |
-
grad_verts = ptd.geometry_grad(scene, cam, dloss_dimage)
|
| 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 |
-
|
| 78 |
-
|
| 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
|
| 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
|
| 90 |
-
| `geometry_grad(scene, camera, grad_image, spp, edge_samples, seed)` | d(loss)/d(vertex positions)
|
| 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 |
-
##
|
| 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 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 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
|
| 193 |
-
|
| 194 |
-
|
| 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 |
-
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 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
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 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 |
|