@tinypoly/gpu-renderer 0.0.5 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +59 -3
- package/README.md +52 -4
- package/dist/atmosphere.d.ts +21 -0
- package/dist/atmosphere.d.ts.map +1 -0
- package/dist/atmosphere.js +72 -0
- package/dist/atmosphere.js.map +1 -0
- package/dist/denoise.d.ts +11 -1
- package/dist/denoise.d.ts.map +1 -1
- package/dist/denoise.js +34 -6
- package/dist/denoise.js.map +1 -1
- package/dist/frameLayout.d.ts.map +1 -1
- package/dist/frameLayout.js +15 -0
- package/dist/frameLayout.js.map +1 -1
- package/dist/geometry.d.ts.map +1 -1
- package/dist/geometry.js +6 -3
- package/dist/geometry.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lensPass.d.ts +60 -0
- package/dist/lensPass.d.ts.map +1 -0
- package/dist/lensPass.js +235 -0
- package/dist/lensPass.js.map +1 -0
- package/dist/lightTree.d.ts +43 -0
- package/dist/lightTree.d.ts.map +1 -0
- package/dist/lightTree.js +107 -0
- package/dist/lightTree.js.map +1 -0
- package/dist/lights.d.ts +10 -0
- package/dist/lights.d.ts.map +1 -1
- package/dist/lights.js +54 -1
- package/dist/lights.js.map +1 -1
- package/dist/materialLayout.d.ts +24 -0
- package/dist/materialLayout.d.ts.map +1 -1
- package/dist/materialLayout.js +36 -1
- package/dist/materialLayout.js.map +1 -1
- package/dist/pathTracePass.d.ts +6 -3
- package/dist/pathTracePass.d.ts.map +1 -1
- package/dist/pathTracePass.js +16 -10
- package/dist/pathTracePass.js.map +1 -1
- package/dist/renderer.d.ts +19 -7
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +118 -14
- package/dist/renderer.js.map +1 -1
- package/dist/sceneUpload.d.ts +5 -0
- package/dist/sceneUpload.d.ts.map +1 -1
- package/dist/sceneUpload.js +4 -0
- package/dist/sceneUpload.js.map +1 -1
- package/dist/settings.d.ts +136 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +100 -0
- package/dist/settings.js.map +1 -0
- package/dist/subsurface.d.ts +76 -0
- package/dist/subsurface.d.ts.map +1 -0
- package/dist/subsurface.js +70 -0
- package/dist/subsurface.js.map +1 -0
- package/dist/volumeContainment.d.ts +17 -0
- package/dist/volumeContainment.d.ts.map +1 -0
- package/dist/volumeContainment.js +61 -0
- package/dist/volumeContainment.js.map +1 -0
- package/dist/wgsl/bsdf.wgsl.d.ts +1 -1
- package/dist/wgsl/bsdf.wgsl.d.ts.map +1 -1
- package/dist/wgsl/bsdf.wgsl.js +35 -3
- package/dist/wgsl/bsdf.wgsl.js.map +1 -1
- package/dist/wgsl/common.wgsl.d.ts.map +1 -1
- package/dist/wgsl/common.wgsl.js +4 -0
- package/dist/wgsl/common.wgsl.js.map +1 -1
- package/dist/wgsl/lens.wgsl.d.ts +17 -0
- package/dist/wgsl/lens.wgsl.d.ts.map +1 -0
- package/dist/wgsl/lens.wgsl.js +315 -0
- package/dist/wgsl/lens.wgsl.js.map +1 -0
- package/dist/wgsl/lights.wgsl.d.ts +1 -1
- package/dist/wgsl/lights.wgsl.d.ts.map +1 -1
- package/dist/wgsl/lights.wgsl.js +203 -31
- package/dist/wgsl/lights.wgsl.js.map +1 -1
- package/dist/wgsl/pathTrace.wgsl.d.ts +1 -1
- package/dist/wgsl/pathTrace.wgsl.d.ts.map +1 -1
- package/dist/wgsl/pathTrace.wgsl.js +396 -25
- package/dist/wgsl/pathTrace.wgsl.js.map +1 -1
- package/dist/wgsl/sampling.wgsl.d.ts +1 -1
- package/dist/wgsl/sampling.wgsl.d.ts.map +1 -1
- package/dist/wgsl/sampling.wgsl.js +25 -0
- package/dist/wgsl/sampling.wgsl.js.map +1 -1
- package/dist/wgsl/surface.wgsl.d.ts +1 -1
- package/dist/wgsl/surface.wgsl.d.ts.map +1 -1
- package/dist/wgsl/surface.wgsl.js +21 -1
- package/dist/wgsl/surface.wgsl.js.map +1 -1
- package/package.json +1 -1
- package/src/atmosphere.ts +95 -0
- package/src/denoise.ts +44 -6
- package/src/frameLayout.ts +15 -0
- package/src/geometry.ts +6 -2
- package/src/index.ts +13 -0
- package/src/lensPass.ts +299 -0
- package/src/lightTree.ts +172 -0
- package/src/lights.ts +71 -1
- package/src/materialLayout.ts +52 -1
- package/src/pathTracePass.ts +19 -10
- package/src/renderer.ts +166 -22
- package/src/sceneUpload.ts +9 -0
- package/src/settings.ts +234 -0
- package/src/subsurface.ts +153 -0
- package/src/volumeContainment.ts +81 -0
- package/src/wgsl/bsdf.wgsl.ts +35 -3
- package/src/wgsl/common.wgsl.ts +4 -0
- package/src/wgsl/lens.wgsl.ts +314 -0
- package/src/wgsl/lights.wgsl.ts +203 -31
- package/src/wgsl/pathTrace.wgsl.ts +396 -25
- package/src/wgsl/sampling.wgsl.ts +25 -0
- package/src/wgsl/surface.wgsl.ts +21 -1
package/ARCHITECTURE.md
CHANGED
|
@@ -146,6 +146,57 @@ frame traces (at the end of a pass, at most every 200 ms) and on the image `rend
|
|
|
146
146
|
holds six vec4 per pixel for all of this, 96 bytes, and the filter six more buffers of 16 and one of 4, the last two
|
|
147
147
|
the guides each tap compares, worked out once per pixel: a 2000 x 1350 image takes some 530 MB on the device.
|
|
148
148
|
|
|
149
|
+
**Light inside materials and volumes is a walk, compiled in only where a scene has it.** Both are media the path
|
|
150
|
+
crosses instead of surfaces it bounces off, and both are written into the bounce loop rather than as loops of their
|
|
151
|
+
own, so the one traversal call in the loop serves them.
|
|
152
|
+
|
|
153
|
+
- **Subsurface scattering** is a random walk, as in Cycles. A vertex on such a material takes the walk instead of its
|
|
154
|
+
surface lobes by chance, in proportion to the light each accounts for, so neither is weighted against the other; the
|
|
155
|
+
lobes it keeps lose that share of the diffuse layer. Inside, each colour has its own mean free path (`radius`) and the
|
|
156
|
+
single scattering albedo that the material's colour asks for (van de Hulst's relation, inverted); distances are drawn
|
|
157
|
+
for a colour picked by what the path still carries of it and weighed over all three (spectral MIS). Where the walk
|
|
158
|
+
reaches a surface it leaves through a white Lambert surface facing out, which takes direct light there.
|
|
159
|
+
- **Volumes** are homogeneous media bounded by an invisible mesh. A medium of one grey density lets the distance be
|
|
160
|
+
drawn after the segment is traced: the next hit is found, then a collision before it is drawn, and either way the
|
|
161
|
+
weight is one. A collision is a vertex whose lobe is the phase function (Henyey-Greenstein), with light sampled from
|
|
162
|
+
there. Shadow rays fade through every boundary they cross, and through the volume they start in; a light that casts no
|
|
163
|
+
shadow still fades, since the medium is not geometry. Whether the camera stands inside one is found on the CPU, by the
|
|
164
|
+
parity of a ray's crossings, once per frame.
|
|
165
|
+
- `SUBSURFACE_WALK` and `VOLUMES` are WGSL constants the pipeline is compiled with, true only for a scene with such a
|
|
166
|
+
material. Carrying the walk's code in every scene measured 2 to 5 percent slower on scenes that never run it (see
|
|
167
|
+
the next decision); folded away, a plain scene renders the same image as before, byte for byte.
|
|
168
|
+
- The snapshot has no field for either medium, so `serializeLive` marks the material's name while the CPU renderer
|
|
169
|
+
serializes it, as it already renames a shader material's type, and attaches the medium after. A snapshot built
|
|
170
|
+
elsewhere can carry `subsurface` or `volume` on its materials itself.
|
|
171
|
+
|
|
172
|
+
**The lens and the sensor are a pass of their own, after the radiance.** Bloom (Jimenez's 13 tap pyramid, folded
|
|
173
|
+
back with a tent), the aperture's rays (gathered along each ray from a quarter sized level), lateral chromatic
|
|
174
|
+
aberration and the white balance (a von Kries scaling in Bradford's cone space, exactly the identity at 6500 K) work on
|
|
175
|
+
linear light between the denoiser and the display transform, into a buffer the presentation and the image resolve read
|
|
176
|
+
in its place. Bloom and rays only move light, with weights that sum to one, so a flat image keeps its mean. Aerial
|
|
177
|
+
perspective is not part of it: it needs each sample's distance, so the kernel lays it over the camera's first surface,
|
|
178
|
+
as it lays Three's fog, with the extinction Koschmieder's law gives each colour and the glow of the surroundings and of
|
|
179
|
+
the brightest directional light scattered by the air's phase function. Lens distortion bends the camera rays, so it
|
|
180
|
+
needs no resampling.
|
|
181
|
+
|
|
182
|
+
**Lights are picked through a tree, by what they can deliver to the shaded point.** Picking by power alone sends a
|
|
183
|
+
small lamp beside a surface as few samples as its power earns against a large panel across the room, and its light
|
|
184
|
+
then arrives through bounce rays as fireflies. The lights that sit in the scene (emissive triangles, point, spot and
|
|
185
|
+
rectangular lights) lead the light buffer and are bound by a tree built on the CPU (`lightTree.ts`): each node keeps
|
|
186
|
+
its box, its power and a cone of the directions its lights face, and the kernel walks down choosing a child by its
|
|
187
|
+
importance from the point, power over squared distance times how well the cone and the receiving normal face each other,
|
|
188
|
+
widened by the angle the box takes (Conty Estevez and Kulla, 2018, without the splitting). The same walk, taken up from
|
|
189
|
+
a leaf, gives the chance a bounce ray's light was picked from the vertex it left, so the heuristic stays exact. The
|
|
190
|
+
directional lights and the environment follow, picked as before by the irradiance they deliver; the share between the
|
|
191
|
+
two groups is what the old distribution gave them. The nodes live in the texture buffer's extra words, sixteen a node,
|
|
192
|
+
because the kernel's eight storage bindings are taken.
|
|
193
|
+
|
|
194
|
+
**Bevel rounds edges at shading time, by one probe a sample.** As Cycles' bevel node, but stochastic: a point on a disk
|
|
195
|
+
of the radius around the shaded point, across an axis picked along the normal half the time and each tangent a
|
|
196
|
+
quarter, and a short ray along it that finds the same material there. Its normal turns the shading normal by as much
|
|
197
|
+
as it differs from the normal here, which keeps a normal map, and the pixel's samples average the turn into a rounded
|
|
198
|
+
edge. It costs a traversal a vertex on such materials, so `BEVEL` is compiled in only for a scene that has one.
|
|
199
|
+
|
|
149
200
|
**The kernel is bound by its size, not by its arithmetic.** Measured on an Apple GPU (`scripts/bench.html`, every
|
|
150
201
|
build timed in turns against the other), the nodes that hold their children's boxes made whole renders 16 to 21 percent
|
|
151
202
|
faster, but only once the traversal loop tested leaves in one place: with the leaf test written out wherever a leaf
|
|
@@ -256,7 +307,7 @@ it over one pixel.
|
|
|
256
307
|
| `SpotLight` | Cone and penumbra as Three computes them, and its projected `map` |
|
|
257
308
|
| `RectAreaLight` | A real area light, sampled over its rectangle, so its penumbra is traced rather than approximated |
|
|
258
309
|
| `AmbientLight`, `HemisphereLight` | See the divergence below |
|
|
259
|
-
| Emissive geometry | Any triangle whose material emits,
|
|
310
|
+
| Emissive geometry | Any triangle whose material emits, picked through the light tree by what it delivers to the shaded point |
|
|
260
311
|
|
|
261
312
|
`light.castShadow` gates the occlusion ray, as does `settings.shadows.enabled`. A `RectAreaLight` always occludes,
|
|
262
313
|
because Three has no shadow switch for one and an area light that cast none would look broken once traced. A toon
|
|
@@ -286,7 +337,10 @@ covers every surface instead.
|
|
|
286
337
|
|
|
287
338
|
Read: `samples`, `renderScale`, `exposure`, `toneMapping`, `vignette`, `grain`, `background`, `fog`, `depthOfField`
|
|
288
339
|
(the CPU renderer's 35 mm lens, `focusDistance`, `aperture` as an f-number, `blades` for polygonal bokeh),
|
|
289
|
-
`globalIllumination.bounces`, `globalIllumination.clamp`, `shadows.enabled`, `shadows.softness
|
|
340
|
+
`globalIllumination.bounces`, `globalIllumination.clamp`, `shadows.enabled`, `shadows.softness`, and the groups only this
|
|
341
|
+
renderer has: `globalIllumination.russianRoulette` and `rouletteStart`, `bloom`, `glare`, `lens`, `camera`,
|
|
342
|
+
`atmosphere`, `subsurface` and `volumes`. Its defaults differ from the CPU renderer's in `toneMapping` (AgX),
|
|
343
|
+
`globalIllumination.bounces` (12) and `globalIllumination.clamp` (10).
|
|
290
344
|
|
|
291
345
|
Ignored: `tileSize`, `ambientOcclusion`, and everything else under `globalIllumination` (`enabled`, `samples`,
|
|
292
346
|
`intensity`, `cache`, `cacheSpacing`, `denoise`).
|
|
@@ -322,7 +376,9 @@ Ignored: `tileSize`, `ambientOcclusion`, and everything else under `globalIllumi
|
|
|
322
376
|
- **Transparency is stochastic.** A surface with an opacity below one lets that share of rays straight through, which is
|
|
323
377
|
what makes a cutout cast a cutout shadow, and what makes the result converge rather than composite.
|
|
324
378
|
- **Unlit surfaces emit.** A basic material, a shader material, a sprite: whatever colour Three would draw, the surface
|
|
325
|
-
radiates, so it also lights its surroundings
|
|
379
|
+
radiates, so it also lights its surroundings. An opaque `MeshBasicMaterial` of a fixed colour (normal blending, no
|
|
380
|
+
alpha, light or environment map) is aimed at as a light, its colour times its map and vertex colours read at the
|
|
381
|
+
sampled point; anything else unlit is only found by bounce rays, since what it shows is not known until it runs.
|
|
326
382
|
- Global illumination is always on. There is no flag for it: tracing paths is what this renderer does.
|
|
327
383
|
- **Glass refracts** where Three's transmission samples a blurred copy of the opaque scene behind it. A volume bends what
|
|
328
384
|
is behind it and reflects its surroundings properly; a thin wall is exact. Neither is what Three draws.
|
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ It is the GPU sibling of [@tinypoly/cpu-renderer](https://github.com/tinypoly/cp
|
|
|
18
18
|
- Perspective and orthographic cameras, any projection Three builds; meshes, lines (`Line`, `LineSegments`, `LineLoop`), dashed lines, fat lines (`Line2`), sprites and `PointsMaterial`.
|
|
19
19
|
- Your custom `ShaderMaterial` GLSL, `RawShaderMaterial` and `onBeforeCompile` hooks: fragment shaders are translated to WGSL by [naga](https://github.com/gfx-rs/wgpu/tree/trunk/naga) and run in the kernel, with real derivatives; vertex shaders run on the CPU renderer's interpreter.
|
|
20
20
|
- HDR, gradient or procedural sky environments, light probes, depth of field with polygonal bokeh, fog, volumetric lights, vignette and grain, and ACES, AgX or Neutral tone mapping.
|
|
21
|
+
- What a photograph adds, on by default: subsurface scattering, volumes of fog and smoke, aerial perspective, a lens's bloom, aperture rays, chromatic aberration and distortion, a camera's white balance and exposure triangle.
|
|
21
22
|
- Adaptive sampling: pixels whose noise is gone stop taking paths, and the work they leave goes to the dark, glossy and out of focus areas that still need it.
|
|
22
23
|
- A variance-guided denoiser (the spatial filter of SVGF, with diffuse and specular light filtered apart as NVIDIA's RELAX does): each signal is separated from its albedo, filtered by an edge-avoiding wavelet that stops at normals, depths and roughness and trusts each pixel as far as its own variance says, and multiplied back, so textures stay sharp, a sharp reflection stays sharp and flat areas keep their exact mean.
|
|
23
24
|
- Progressive output: the canvas refines sample by sample, and the render can be paused, resumed and cancelled.
|
|
@@ -152,20 +153,23 @@ The renderer never falls back on its own. Choosing another renderer when WebGPU
|
|
|
152
153
|
|
|
153
154
|
### Settings
|
|
154
155
|
|
|
155
|
-
`RenderSettings`, grouped by feature, the CPU renderer
|
|
156
|
+
`GpuRenderSettings`: the CPU renderer's `RenderSettings`, grouped by feature, with the [groups only this renderer reads](#what-a-photograph-adds) beside them in the same object, which the CPU renderer leaves alone. Defaults are `DEFAULT_GPU_RENDER_SETTINGS`, the CPU renderer's own with what a photograph has turned on; `mergeGpuSettings(base, input)` applies a partial `GpuRenderSettingsInput` the way `setSettings` does. A path tracer reads some of the shared ones differently:
|
|
156
157
|
|
|
157
158
|
| Field | Default | |
|
|
158
159
|
| --- | --- | --- |
|
|
159
160
|
| `renderScale` | `1` | Multiplier on the image size. |
|
|
160
161
|
| `samples` | `4` | **Paths per pixel**, on average: one number does the antialiasing and the global illumination in a path tracer, and a few hundred is a clean image. Above 32, a pixel that has converged stops early and the noisiest ones take up to four times as many. |
|
|
161
|
-
| `toneMapping` | `"
|
|
162
|
+
| `toneMapping` | `"agx"` | `"aces"`, `"agx"`, `"neutral"` or `"linear"`. AgX rolls highlights off as film does; the CPU renderer defaults to ACES. |
|
|
162
163
|
| `exposure` | `1` | |
|
|
163
164
|
| `background.mode` | `"environment"` | `"environment"`, `"color"` or `"transparent"`, which makes the image's alpha the coverage of every pixel. |
|
|
164
165
|
| `background.color` | `"#1a1a1a"` | Used by `mode: "color"`. |
|
|
165
166
|
| `shadows.enabled` | `true` | Whether Three's lights are occlusion tested. Emissive geometry always is. |
|
|
166
167
|
| `shadows.softness` | `1` | Angular diameter of the lights in degrees. `0` gives hard shadows. |
|
|
167
|
-
| `globalIllumination.bounces` | `
|
|
168
|
-
| `globalIllumination.clamp` | `
|
|
168
|
+
| `globalIllumination.bounces` | `12` | How far a path goes: enough for light to fill a room. The roulette below keeps it affordable. |
|
|
169
|
+
| `globalIllumination.clamp` | `10` | Maximum luminance one indirect contribution may add, which trades a little bias for fewer fireflies, as Cycles' indirect light clamp does. `0` disables clamping. |
|
|
170
|
+
| `globalIllumination.russianRoulette` | `true` | Ends a path that carries little light by chance, weighting the survivors up so the mean stays exact. |
|
|
171
|
+
| `globalIllumination.rouletteStart` | `3` | Bounces every path takes before the roulette may end it. |
|
|
172
|
+
| `globalIllumination.lightTree` | `true` | Picks the light to aim at by what it delivers to the shaded point (power, distance, facing) through a tree over the scene's lights, rather than by power alone: a small lamp near a surface gets the samples it needs. |
|
|
169
173
|
| `depthOfField.enabled` | `false` | |
|
|
170
174
|
| `depthOfField.focusDistance` | `10` | World units. |
|
|
171
175
|
| `depthOfField.aperture` | `2.8` | f-number. |
|
|
@@ -176,6 +180,36 @@ The renderer never falls back on its own. Choosing another renderer when WebGPU
|
|
|
176
180
|
| `vignette` | `0` | `0` to `1`. |
|
|
177
181
|
| `grain` | `0` | `0` to `1`. |
|
|
178
182
|
|
|
183
|
+
#### What a photograph adds
|
|
184
|
+
|
|
185
|
+
Groups only this renderer reads, all on by default except the physical exposure, which needs a scene lit in physical units. Changing the lens, the camera's white balance or exposure, the bloom or the glare shows the finished image again without tracing it.
|
|
186
|
+
|
|
187
|
+
| Field | Default | |
|
|
188
|
+
| --- | --- | --- |
|
|
189
|
+
| `bloom.enabled` | `true` | The halo a lens and sensor scatter around bright light. Energy conserving and without a threshold: it only shows where something is far brighter than its surroundings. |
|
|
190
|
+
| `bloom.intensity` | `0.04` | Share of the light the halo takes, `0` to `1`. |
|
|
191
|
+
| `bloom.radius` | `0.65` | How far it reaches, `0` to `1`. |
|
|
192
|
+
| `glare.enabled` | `true` | The rays a bright light throws through the aperture's blades. Energy conserving like the bloom. |
|
|
193
|
+
| `glare.intensity` | `0.06` | Share of the light the rays take, `0` to `1`. |
|
|
194
|
+
| `glare.blades` | `6` | Blades of the aperture: an even count makes as many rays, an odd count twice as many. |
|
|
195
|
+
| `glare.length`, `glare.rotation` | `0.25`, `15` | How far the rays reach, as a share of the half diagonal, and the aperture's turn in degrees. |
|
|
196
|
+
| `lens.chromaticAberration` | `0.2` | Lateral colour fringes towards the corners, `0` to `1`. |
|
|
197
|
+
| `lens.distortion` | `0` | Radial distortion of the camera rays: positive is barrel, negative pincushion. |
|
|
198
|
+
| `camera.whiteBalance` | `6500` | Kelvin the camera takes as white; `6500` is neutral, lower cools the image. |
|
|
199
|
+
| `camera.physicalExposure` | `false` | Exposes from `iso`, `shutterSpeed` and `depthOfField.aperture`, with `exposure` as compensation. |
|
|
200
|
+
| `camera.iso`, `camera.shutterSpeed` | `100`, `1/125` | Seconds for the shutter. |
|
|
201
|
+
| `atmosphere.enabled` | `true` | Aerial perspective: the air fades distant surfaces towards the light it scatters, from the surroundings and the brightest directional light. Scenes in metres. |
|
|
202
|
+
| `atmosphere.visibility` | `20000` | Scene units at which a dark object keeps 2 percent contrast (Koschmieder): 20 km is a clear day. |
|
|
203
|
+
| `atmosphere.haze` | `0.6` | `0` clean air, blue (Rayleigh); `1` haze, grey and bright towards the sun (Mie). |
|
|
204
|
+
| `atmosphere.color` | `""` | The air's glow as `#rrggbb`; empty takes the surroundings' average. |
|
|
205
|
+
| `subsurface.enabled` | `true` | Materials with a [`subsurface`](#object-options) medium scatter light inside. |
|
|
206
|
+
| `volumes.enabled` | `true` | Meshes with a [`volume`](#object-options) are filled with their medium; off leaves them out. |
|
|
207
|
+
| `bevel.enabled` | `true` | Materials with a [`bevel`](#object-options) radius round their edges at shading time. |
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
renderer.setSettings({ glare: { blades: 7 }, camera: { whiteBalance: 3200 }, atmosphere: { visibility: 3000 } });
|
|
211
|
+
```
|
|
212
|
+
|
|
179
213
|
`globalIllumination.denoise` (default `true`) turns the denoiser on: on the canvas a few times a second while the frame traces, and on the image `render()` resolves with. Switching it alone filters the finished image again without tracing.
|
|
180
214
|
|
|
181
215
|
### Denoiser
|
|
@@ -189,6 +223,8 @@ A variance-guided wavelet filter, the spatial half of SVGF, with the diffuse and
|
|
|
189
223
|
| `edge` | `2` | How large a luminance difference still counts as noise, in standard deviations of the pixel's own noise. Lower keeps more detail and removes less noise. |
|
|
190
224
|
| `onlyNoisy` | `true` | Leaves the pixels that have converged as they were traced, and filters only where noise is still visible. |
|
|
191
225
|
| `antiFirefly` | `true` | Before filtering, clamps a pixel brighter than every one of its eight neighbours to the brightest of them, in each signal. |
|
|
226
|
+
| `subsurface` | `1` | How far the diffuse filter lets go of its normal and depth tests over a material that scatters light inside, `0` to `1`: such light leaves the surface after wandering under it. |
|
|
227
|
+
| `mirrorRadius` | `3` | The smallest footprint a smooth reflection (roughness under 0.3) is filtered over, in pixels, by taps that see the reflected thing at the same distance, which keeps its edges. `0` leaves a mirror alone. |
|
|
192
228
|
|
|
193
229
|
A denoiser trades noise for detail. What it smooths most is what its guides cannot see: the relief of a lacquer, an out of focus area. With `onlyNoisy` it leaves alone whatever has already converged, so more samples always mean less filtering.
|
|
194
230
|
|
|
@@ -227,6 +263,18 @@ The renderer reads the CPU renderer's optional settings from `userData.cpuRender
|
|
|
227
263
|
light.userData.cpuRenderer = { volumetric: { density: 0.05 } };
|
|
228
264
|
```
|
|
229
265
|
|
|
266
|
+
Two more on materials, which only this renderer reads:
|
|
267
|
+
|
|
268
|
+
- **`subsurface`** (standard and physical materials): light enters the material and leaves it somewhere else, by a random walk inside it, as skin, wax, marble and milk do. `true` takes Blender's defaults. The material's colour, maps included, is what the medium reflects in all; `radius` is how far red, green and blue travel inside, as multiples of `scale` in scene units; `weight` is the share of the diffuse layer that scatters inside; `anisotropy` the phase function's.
|
|
269
|
+
- **`bevel`** (lit materials): a radius in scene units, over which the shading normal turns from one face to the next, as Cycles' bevel node rounds edges without changing the geometry. Sharp CG corners catch light like real, slightly rounded ones. The normal map still applies.
|
|
270
|
+
- **`volume`** (any material): the mesh only bounds a medium, which scatters and absorbs light in the space it encloses: fog, smoke, a beam of sunlight through dust, murky water. Its surface is invisible. `density` is per scene unit, `color` what each scattering keeps (white for fog, dark for soot), `anisotropy` how forward it scatters (about `0.7` for haze). The mesh has to be closed, with its normals out; the camera may stand inside one, and objects inside are lit through it.
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
skin.userData.cpuRenderer = { subsurface: { radius: [1, 0.4, 0.25], scale: 0.01 } };
|
|
274
|
+
table.userData.cpuRenderer = { bevel: 0.003 };
|
|
275
|
+
haze.material.userData.cpuRenderer = { volume: { density: 0.05, color: "#ffffff", anisotropy: 0.6 } };
|
|
276
|
+
```
|
|
277
|
+
|
|
230
278
|
### Helpers
|
|
231
279
|
|
|
232
280
|
| Function | |
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PackedEnvironment } from "./environment.js";
|
|
2
|
+
import type { GpuRenderSettings } from "./settings.js";
|
|
3
|
+
import type { SerializedLight } from "./upstream.js";
|
|
4
|
+
/** Extinction per scene unit of red, green and blue for the atmosphere setting, or nothing when it is off. */
|
|
5
|
+
export declare function atmosphereExtinction(atmosphere: GpuRenderSettings["atmosphere"]): [number, number, number];
|
|
6
|
+
/** The brightest directional light of a scene, which lights the air: its direction towards the light and irradiance. */
|
|
7
|
+
export declare function brightestSun(lights: readonly SerializedLight[]): {
|
|
8
|
+
direction: number[];
|
|
9
|
+
irradiance: number[];
|
|
10
|
+
} | null;
|
|
11
|
+
/**
|
|
12
|
+
* The radiance of an image averaged over the whole sphere: what air scattering every direction alike sends towards the
|
|
13
|
+
* camera. Each row weighs by the sine of its colatitude, the share of the sphere it covers.
|
|
14
|
+
*/
|
|
15
|
+
export declare function averageRadiance(environment: PackedEnvironment): [number, number, number];
|
|
16
|
+
/**
|
|
17
|
+
* A vertical gradient averaged over the sphere. Directions are spread evenly in height over a sphere, so the share of
|
|
18
|
+
* the top colour is the mean of h^exponent for h even in [0, 1]: one over exponent plus one.
|
|
19
|
+
*/
|
|
20
|
+
export declare function gradientAverage(top: number[], bottom: number[], exponent: number): [number, number, number];
|
|
21
|
+
//# sourceMappingURL=atmosphere.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atmosphere.d.ts","sourceRoot":"","sources":["../src/atmosphere.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAYrD,8GAA8G;AAC9G,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,iBAAiB,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAS1G;AAED,wHAAwH;AACxH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,GAAG;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAmBrH;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,iBAAiB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CA8BxF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAI3G"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How much more each colour scatters than green, at 680, 550 and 440 nm. Clean air scatters as the inverse fourth
|
|
3
|
+
* power of the wavelength (Rayleigh), haze nearly alike (Mie, an Angstrom exponent of about 1.3).
|
|
4
|
+
*/
|
|
5
|
+
const RAYLEIGH = [(550 / 680) ** 4, 1, (550 / 440) ** 4];
|
|
6
|
+
const MIE = [(550 / 680) ** 1.3, 1, (550 / 440) ** 1.3];
|
|
7
|
+
/** Koschmieder: a dark object fades to 2 percent contrast over the visibility, so green's extinction is ln 50 over it. */
|
|
8
|
+
const KOSCHMIEDER = Math.log(50);
|
|
9
|
+
/** Extinction per scene unit of red, green and blue for the atmosphere setting, or nothing when it is off. */
|
|
10
|
+
export function atmosphereExtinction(atmosphere) {
|
|
11
|
+
const visibility = Number(atmosphere.visibility);
|
|
12
|
+
if (!atmosphere.enabled || !(visibility > 0))
|
|
13
|
+
return [0, 0, 0];
|
|
14
|
+
const haze = Math.min(1, Math.max(0, Number(atmosphere.haze) || 0));
|
|
15
|
+
const scale = KOSCHMIEDER / visibility;
|
|
16
|
+
return [0, 1, 2].map(k => scale * (RAYLEIGH[k] + (MIE[k] - RAYLEIGH[k]) * haze));
|
|
17
|
+
}
|
|
18
|
+
/** The brightest directional light of a scene, which lights the air: its direction towards the light and irradiance. */
|
|
19
|
+
export function brightestSun(lights) {
|
|
20
|
+
let best = null;
|
|
21
|
+
let strongest = 0;
|
|
22
|
+
for (const light of lights) {
|
|
23
|
+
if (light.type !== "DirectionalLight" || !(light.intensity > 0))
|
|
24
|
+
continue;
|
|
25
|
+
const irradiance = light.color.map(channel => channel * light.intensity);
|
|
26
|
+
const strength = 0.2126 * irradiance[0] + 0.7152 * irradiance[1] + 0.0722 * irradiance[2];
|
|
27
|
+
const axis = [0, 1, 2].map(k => light.position[k] - light.target[k]);
|
|
28
|
+
const size = Math.hypot(...axis);
|
|
29
|
+
if (strength > strongest && size > 0) {
|
|
30
|
+
strongest = strength;
|
|
31
|
+
best = { direction: axis.map(value => value / size), irradiance };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return best;
|
|
35
|
+
}
|
|
36
|
+
const averages = new WeakMap();
|
|
37
|
+
/**
|
|
38
|
+
* The radiance of an image averaged over the whole sphere: what air scattering every direction alike sends towards the
|
|
39
|
+
* camera. Each row weighs by the sine of its colatitude, the share of the sphere it covers.
|
|
40
|
+
*/
|
|
41
|
+
export function averageRadiance(environment) {
|
|
42
|
+
const known = averages.get(environment);
|
|
43
|
+
if (known)
|
|
44
|
+
return known;
|
|
45
|
+
const { width, height, data } = environment.texture;
|
|
46
|
+
const sum = [0, 0, 0];
|
|
47
|
+
let weight = 0;
|
|
48
|
+
// A byte image is stored as it was encoded; only a float one holds linear light, as an HDR image always does.
|
|
49
|
+
const scale = data instanceof Float32Array ? 1 : 1 / 255;
|
|
50
|
+
for (let y = 0; y < height; y++) {
|
|
51
|
+
const sine = Math.sin((y + 0.5) / height * Math.PI);
|
|
52
|
+
for (let x = 0; x < width; x++) {
|
|
53
|
+
const at = (y * width + x) * 4;
|
|
54
|
+
sum[0] += data[at] * scale * sine;
|
|
55
|
+
sum[1] += data[at + 1] * scale * sine;
|
|
56
|
+
sum[2] += data[at + 2] * scale * sine;
|
|
57
|
+
}
|
|
58
|
+
weight += sine * width;
|
|
59
|
+
}
|
|
60
|
+
const average = sum.map(channel => channel / Math.max(weight, 1e-12));
|
|
61
|
+
averages.set(environment, average);
|
|
62
|
+
return average;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A vertical gradient averaged over the sphere. Directions are spread evenly in height over a sphere, so the share of
|
|
66
|
+
* the top colour is the mean of h^exponent for h even in [0, 1]: one over exponent plus one.
|
|
67
|
+
*/
|
|
68
|
+
export function gradientAverage(top, bottom, exponent) {
|
|
69
|
+
const share = 1 / (Math.max(exponent, 0) + 1);
|
|
70
|
+
return [0, 1, 2].map(k => bottom[k] + (top[k] - bottom[k]) * share);
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=atmosphere.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atmosphere.js","sourceRoot":"","sources":["../src/atmosphere.ts"],"names":[],"mappings":"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"}
|
package/dist/denoise.d.ts
CHANGED
|
@@ -13,6 +13,16 @@ export interface DenoiserSettings {
|
|
|
13
13
|
onlyNoisy: boolean;
|
|
14
14
|
/** Clamp a pixel brighter than every one of its neighbours to the brightest of them, before filtering. */
|
|
15
15
|
antiFirefly: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* How far the filter lets go of the normal and depth tests over a material that scatters light inside, from 0 to 1.
|
|
18
|
+
* Light leaves such a surface after wandering under it, so its shading hardly follows the normal at each point.
|
|
19
|
+
*/
|
|
20
|
+
subsurface: number;
|
|
21
|
+
/**
|
|
22
|
+
* The smallest footprint a smooth reflection is filtered over, in pixels; 0 leaves a mirror alone. Within it, taps
|
|
23
|
+
* also have to agree on how far away the reflected thing is, so the edge of a reflected object stays sharp.
|
|
24
|
+
*/
|
|
25
|
+
mirrorRadius: number;
|
|
16
26
|
}
|
|
17
27
|
export declare const DEFAULT_DENOISER: Readonly<DenoiserSettings>;
|
|
18
28
|
/** The settings with `input` applied, each field kept in range. */
|
|
@@ -43,7 +53,7 @@ export interface DenoiseView {
|
|
|
43
53
|
* The weights are normalized, which keeps every flat area at exactly its mean, and each signal is mixed with what was
|
|
44
54
|
* traced by how noisy it still is, so what has converged keeps exactly its traced value.
|
|
45
55
|
*/
|
|
46
|
-
export declare const DENOISE_WGSL = "\nstruct Params {\n size: vec2<u32>,\n step: u32,\n count: u32,\n edge: f32,\n strength: f32,\n onlyNoisy: u32,\n converged: f32,\n pixelScale: f32,\n orthographic: u32,\n padding: vec2<u32>,\n};\n\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read> accumulation: array<vec4<f32>>;\n@group(0) @binding(2) var<storage, read> diffuseIn: array<vec4<f32>>;\n@group(0) @binding(3) var<storage, read> specularIn: array<vec4<f32>>;\n@group(0) @binding(4) var<storage, read_write> diffuseOut: array<vec4<f32>>;\n@group(0) @binding(5) var<storage, read_write> specularOut: array<vec4<f32>>;\n// The guides each tap compares, worked out once per pixel by prepare: the normal and the depth, then the roughness, or\n// -1 where no sample has landed. The same numbers the accumulation gives, two reads a tap instead of four.\n@group(0) @binding(6) var<storage, read_write> guides: array<vec4<f32>>;\n@group(0) @binding(7) var<storage, read_write> roughnesses: array<f32>;\n\nfn luminance(color: vec3<f32>) -> f32 {\n return dot(color, vec3<f32>(0.2126, 0.7152, 0.0722));\n}\n\nfn samples(index: u32) -> f32 {\n return max(accumulation[index].w, 1.0);\n}\n\n/** Each signal's mean radiance: the diffuse one stored apart, the specular one the rest. */\nfn diffuseMean(index: u32) -> vec3<f32> {\n return accumulation[4u * params.count + index].rgb / samples(index);\n}\n\nfn specularMean(index: u32) -> vec3<f32> {\n return accumulation[index].rgb / samples(index) - diffuseMean(index);\n}\n\n/** The albedos the light is divided by, kept off zero so the division never explodes. */\nfn diffuseAlbedo(index: u32) -> vec3<f32> {\n return max(accumulation[2u * params.count + index].rgb / samples(index), vec3<f32>(0.01));\n}\n\nfn specularAlbedo(index: u32) -> vec3<f32> {\n return max(accumulation[5u * params.count + index].rgb / samples(index), vec3<f32>(0.01));\n}\n\nfn guideNormal(index: u32) -> vec3<f32> {\n let sum = accumulation[3u * params.count + index].xyz;\n let size = length(sum);\n\n return select(vec3<f32>(0.0), sum / size, size > 1e-6);\n}\n\nfn guideDepth(index: u32) -> f32 {\n return accumulation[3u * params.count + index].w / samples(index);\n}\n\nfn guideRoughness(index: u32) -> f32 {\n return accumulation[2u * params.count + index].w / samples(index);\n}\n\nfn specularDistance(index: u32) -> f32 {\n return accumulation[4u * params.count + index].w / samples(index);\n}\n\n/** The variance of a signal's mean, in the space of its demodulated light. */\nfn variance(mean: vec3<f32>, square: f32, count: f32, albedo: vec3<f32>) -> f32 {\n let m = luminance(mean);\n let a = max(luminance(albedo), 0.01);\n\n return max(square / count - m * m, 0.0) / count / (a * a);\n}\n\n/** Both signals, divided by their albedos, with the variance of their means. */\n@compute @workgroup_size(8, 8, 1)\nfn prepare(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= params.size.x || id.y >= params.size.y) {\n return;\n }\n\n let index = id.y * params.size.x + id.x;\n let count = accumulation[index].w;\n\n if (count <= 0.0) {\n diffuseOut[index] = vec4<f32>(0.0);\n specularOut[index] = vec4<f32>(0.0);\n guides[index] = vec4<f32>(0.0);\n roughnesses[index] = -1.0;\n\n return;\n }\n\n guides[index] = vec4<f32>(guideNormal(index), guideDepth(index));\n roughnesses[index] = guideRoughness(index);\n\n let squares = accumulation[params.count + index];\n let diffuse = diffuseMean(index);\n let specular = specularMean(index);\n\n diffuseOut[index] = vec4<f32>(diffuse / diffuseAlbedo(index), variance(diffuse, squares.z, count, diffuseAlbedo(index)));\n specularOut[index] = vec4<f32>(\n specular / specularAlbedo(index), variance(specular, squares.w, count, specularAlbedo(index)));\n}\n\nfn inside(q: vec2<i32>) -> bool {\n return q.x >= 0 && q.y >= 0 && q.x < i32(params.size.x) && q.y < i32(params.size.y);\n}\n\n/** A value brighter than every neighbour, scaled down to the brightest of them: a firefly, not a feature. */\nfn clampFirefly(value: vec4<f32>, brightest: f32) -> vec4<f32> {\n let own = luminance(value.rgb);\n\n if (own <= brightest || own <= 0.0) {\n return value;\n }\n\n return vec4<f32>(value.rgb * (brightest / own), value.w);\n}\n\n@compute @workgroup_size(8, 8, 1)\nfn antiFirefly(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= params.size.x || id.y >= params.size.y) {\n return;\n }\n\n let pixel = vec2<i32>(id.xy);\n let index = id.y * params.size.x + id.x;\n var diffuse = 0.0;\n var specular = 0.0;\n\n for (var dy = -1; dy <= 1; dy = dy + 1) {\n for (var dx = -1; dx <= 1; dx = dx + 1) {\n let q = pixel + vec2<i32>(dx, dy);\n\n if ((dx == 0 && dy == 0) || !inside(q)) {\n continue;\n }\n\n let other = u32(q.y) * params.size.x + u32(q.x);\n diffuse = max(diffuse, luminance(diffuseIn[other].rgb));\n specular = max(specular, luminance(specularIn[other].rgb));\n }\n }\n\n diffuseOut[index] = clampFirefly(diffuseIn[index], diffuse);\n specularOut[index] = clampFirefly(specularIn[index], specular);\n}\n\nconst KERNEL = array<f32, 5>(0.0625, 0.25, 0.375, 0.25, 0.0625);\n\n/** The variance around a pixel, blurred over 3 x 3, which steadies the luminance test on a few samples. */\nfn localVariance(pixel: vec2<i32>, specular: bool) -> f32 {\n var sum = 0.0;\n var weight = 0.0;\n\n for (var dy = -1; dy <= 1; dy = dy + 1) {\n for (var dx = -1; dx <= 1; dx = dx + 1) {\n let q = pixel + vec2<i32>(dx, dy);\n\n if (!inside(q)) {\n continue;\n }\n\n let other = u32(q.y) * params.size.x + u32(q.x);\n let w = select(0.5, 1.0, dx == 0) * select(0.5, 1.0, dy == 0);\n sum = sum + select(diffuseIn[other].w, specularIn[other].w, specular) * w;\n weight = weight + w;\n }\n }\n\n return sum / max(weight, 1e-6);\n}\n\n/**\n * How many pixels the specular lobe's footprint covers: the lobe's angle times the distance to what it reflects, seen\n * from the camera across the distance to the surface plus that.\n */\nfn specularRadius(index: u32, depth: f32) -> f32 {\n let alpha = guideRoughness(index) * guideRoughness(index);\n let reflected = specularDistance(index);\n\n if (params.orthographic != 0u) {\n return min(alpha * reflected / params.pixelScale, 64.0);\n }\n\n return min(alpha * reflected / max(depth + reflected, 1e-6) / params.pixelScale, 64.0);\n}\n\n/** One a-trous pass over both signals, its taps params.step pixels apart. */\n@compute @workgroup_size(8, 8, 1)\nfn atrous(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= params.size.x || id.y >= params.size.y) {\n return;\n }\n\n let pixel = vec2<i32>(id.xy);\n let index = id.y * params.size.x + id.x;\n let diffuseCenter = diffuseIn[index];\n let specularCenter = specularIn[index];\n\n if (accumulation[index].w <= 0.0) {\n diffuseOut[index] = diffuseCenter;\n specularOut[index] = specularCenter;\n\n return;\n }\n\n let guide = guides[index];\n let normal = guide.xyz;\n let depth = guide.w;\n let roughness = roughnesses[index];\n let diffuseSigma = params.edge * sqrt(max(localVariance(pixel, false), 0.0)) + 1e-6;\n let specularSigma = params.edge * sqrt(max(localVariance(pixel, true), 0.0)) + 1e-6;\n let radius = specularRadius(index, depth);\n // A smoother surface's reflection turns faster with its normal: the normal test sharpens with it.\n let specularPower = min(128.0 / max(roughness * roughness, 0.03), 4096.0);\n let step = i32(params.step);\n var diffuse = vec4<f32>(0.0);\n var specular = vec4<f32>(0.0);\n var diffuseTotal = 0.0;\n var specularTotal = 0.0;\n\n for (var dy = -2; dy <= 2; dy = dy + 1) {\n for (var dx = -2; dx <= 2; dx = dx + 1) {\n let q = pixel + vec2<i32>(dx, dy) * step;\n\n if (!inside(q)) {\n continue;\n }\n\n let other = u32(q.y) * params.size.x + u32(q.x);\n let d = diffuseIn[other];\n let s = specularIn[other];\n let base = KERNEL[dx + 2] * KERNEL[dy + 2];\n var wd = base;\n var ws = base;\n\n if (dx != 0 || dy != 0) {\n let otherRoughness = roughnesses[other];\n\n // No sample has landed there.\n if (otherRoughness < 0.0) {\n continue;\n }\n\n let otherGuide = guides[other];\n let offset = length(vec2<f32>(f32(dx * step), f32(dy * step)));\n let alignment = max(dot(normal, otherGuide.xyz), 0.0);\n let depthWeight = exp(-abs(depth - otherGuide.w) / (0.02 * depth * offset + 1e-3));\n\n wd = wd * pow(alignment, 128.0) * depthWeight\n * exp(-abs(luminance(d.rgb) - luminance(diffuseCenter.rgb)) / diffuseSigma);\n\n ws = ws * pow(alignment, specularPower) * depthWeight\n * exp(-abs(otherRoughness - roughness) * 10.0)\n * exp(-abs(luminance(s.rgb) - luminance(specularCenter.rgb)) / specularSigma)\n * select(0.0, exp(-offset * offset / (radius * radius)), radius >= 0.5);\n }\n\n diffuse = diffuse + vec4<f32>(d.rgb * wd, d.w * wd * wd);\n specular = specular + vec4<f32>(s.rgb * ws, s.w * ws * ws);\n diffuseTotal = diffuseTotal + wd;\n specularTotal = specularTotal + ws;\n }\n }\n\n diffuseOut[index] = vec4<f32>(diffuse.rgb / diffuseTotal, diffuse.w / (diffuseTotal * diffuseTotal));\n specularOut[index] = vec4<f32>(specular.rgb / specularTotal, specular.w / (specularTotal * specularTotal));\n}\n\n/** How much of the filtered signal replaces the traced one: by how noisy the signal still is, when asked. */\nfn amount(mean: vec3<f32>, square: f32, count: f32) -> f32 {\n if (params.onlyNoisy == 0u || count <= 1.0) {\n return params.strength;\n }\n\n let m = luminance(mean);\n let error = sqrt(max(square / count - m * m, 0.0) / count) / sqrt(max(m, 0.0) + 1e-3);\n\n return params.strength * smoothstep(params.converged, 4.0 * params.converged, error);\n}\n\n/** Both filtered signals, times the albedos they were divided by, each mixed with what was traced. */\n@compute @workgroup_size(8, 8, 1)\nfn finish(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= params.size.x || id.y >= params.size.y) {\n return;\n }\n\n let index = id.y * params.size.x + id.x;\n let count = accumulation[index].w;\n\n if (count <= 0.0) {\n diffuseOut[index] = vec4<f32>(0.0, 0.0, 0.0, 1.0);\n\n return;\n }\n\n let squares = accumulation[params.count + index];\n let diffuse = diffuseMean(index);\n let specular = specularMean(index);\n let filteredDiffuse = diffuseIn[index].rgb * diffuseAlbedo(index);\n let filteredSpecular = specularIn[index].rgb * specularAlbedo(index);\n\n diffuseOut[index] = vec4<f32>(\n mix(diffuse, filteredDiffuse, amount(diffuse, squares.z, count))\n + mix(specular, filteredSpecular, amount(specular, squares.w, count)), 1.0);\n}\n";
|
|
56
|
+
export declare const DENOISE_WGSL = "\nstruct Params {\n size: vec2<u32>,\n step: u32,\n count: u32,\n edge: f32,\n strength: f32,\n onlyNoisy: u32,\n converged: f32,\n pixelScale: f32,\n orthographic: u32,\n subsurface: f32,\n mirrorRadius: f32,\n};\n\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read> accumulation: array<vec4<f32>>;\n@group(0) @binding(2) var<storage, read> diffuseIn: array<vec4<f32>>;\n@group(0) @binding(3) var<storage, read> specularIn: array<vec4<f32>>;\n@group(0) @binding(4) var<storage, read_write> diffuseOut: array<vec4<f32>>;\n@group(0) @binding(5) var<storage, read_write> specularOut: array<vec4<f32>>;\n// The guides each tap compares, worked out once per pixel by prepare: the normal and the depth, then the roughness, or\n// -1 where no sample has landed. The same numbers the accumulation gives, two reads a tap instead of four.\n@group(0) @binding(6) var<storage, read_write> guides: array<vec4<f32>>;\n@group(0) @binding(7) var<storage, read_write> roughnesses: array<f32>;\n\nfn luminance(color: vec3<f32>) -> f32 {\n return dot(color, vec3<f32>(0.2126, 0.7152, 0.0722));\n}\n\nfn samples(index: u32) -> f32 {\n return max(accumulation[index].w, 1.0);\n}\n\n/** Each signal's mean radiance: the diffuse one stored apart, the specular one the rest. */\nfn diffuseMean(index: u32) -> vec3<f32> {\n return accumulation[4u * params.count + index].rgb / samples(index);\n}\n\nfn specularMean(index: u32) -> vec3<f32> {\n return accumulation[index].rgb / samples(index) - diffuseMean(index);\n}\n\n/** The albedos the light is divided by, kept off zero so the division never explodes. */\nfn diffuseAlbedo(index: u32) -> vec3<f32> {\n return max(accumulation[2u * params.count + index].rgb / samples(index), vec3<f32>(0.01));\n}\n\nfn specularAlbedo(index: u32) -> vec3<f32> {\n return max(accumulation[5u * params.count + index].rgb / samples(index), vec3<f32>(0.01));\n}\n\nfn guideNormal(index: u32) -> vec3<f32> {\n let sum = accumulation[3u * params.count + index].xyz;\n let size = length(sum);\n\n return select(vec3<f32>(0.0), sum / size, size > 1e-6);\n}\n\nfn guideDepth(index: u32) -> f32 {\n return accumulation[3u * params.count + index].w / samples(index);\n}\n\nfn guideRoughness(index: u32) -> f32 {\n return accumulation[2u * params.count + index].w / samples(index);\n}\n\n/** How much of the first surface scatters light inside, averaged over the pixel's samples. */\nfn guideSubsurface(index: u32) -> f32 {\n return accumulation[5u * params.count + index].w / samples(index);\n}\n\nfn specularDistance(index: u32) -> f32 {\n return accumulation[4u * params.count + index].w / samples(index);\n}\n\n/** The variance of a signal's mean, in the space of its demodulated light. */\nfn variance(mean: vec3<f32>, square: f32, count: f32, albedo: vec3<f32>) -> f32 {\n let m = luminance(mean);\n let a = max(luminance(albedo), 0.01);\n\n return max(square / count - m * m, 0.0) / count / (a * a);\n}\n\n/** Both signals, divided by their albedos, with the variance of their means. */\n@compute @workgroup_size(8, 8, 1)\nfn prepare(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= params.size.x || id.y >= params.size.y) {\n return;\n }\n\n let index = id.y * params.size.x + id.x;\n let count = accumulation[index].w;\n\n if (count <= 0.0) {\n diffuseOut[index] = vec4<f32>(0.0);\n specularOut[index] = vec4<f32>(0.0);\n guides[index] = vec4<f32>(0.0);\n roughnesses[index] = -1.0;\n\n return;\n }\n\n guides[index] = vec4<f32>(guideNormal(index), guideDepth(index));\n roughnesses[index] = guideRoughness(index);\n\n let squares = accumulation[params.count + index];\n let diffuse = diffuseMean(index);\n let specular = specularMean(index);\n\n diffuseOut[index] = vec4<f32>(diffuse / diffuseAlbedo(index), variance(diffuse, squares.z, count, diffuseAlbedo(index)));\n specularOut[index] = vec4<f32>(\n specular / specularAlbedo(index), variance(specular, squares.w, count, specularAlbedo(index)));\n}\n\nfn inside(q: vec2<i32>) -> bool {\n return q.x >= 0 && q.y >= 0 && q.x < i32(params.size.x) && q.y < i32(params.size.y);\n}\n\n/** A value brighter than every neighbour, scaled down to the brightest of them: a firefly, not a feature. */\nfn clampFirefly(value: vec4<f32>, brightest: f32) -> vec4<f32> {\n let own = luminance(value.rgb);\n\n if (own <= brightest || own <= 0.0) {\n return value;\n }\n\n return vec4<f32>(value.rgb * (brightest / own), value.w);\n}\n\n@compute @workgroup_size(8, 8, 1)\nfn antiFirefly(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= params.size.x || id.y >= params.size.y) {\n return;\n }\n\n let pixel = vec2<i32>(id.xy);\n let index = id.y * params.size.x + id.x;\n var diffuse = 0.0;\n var specular = 0.0;\n\n for (var dy = -1; dy <= 1; dy = dy + 1) {\n for (var dx = -1; dx <= 1; dx = dx + 1) {\n let q = pixel + vec2<i32>(dx, dy);\n\n if ((dx == 0 && dy == 0) || !inside(q)) {\n continue;\n }\n\n let other = u32(q.y) * params.size.x + u32(q.x);\n diffuse = max(diffuse, luminance(diffuseIn[other].rgb));\n specular = max(specular, luminance(specularIn[other].rgb));\n }\n }\n\n diffuseOut[index] = clampFirefly(diffuseIn[index], diffuse);\n specularOut[index] = clampFirefly(specularIn[index], specular);\n}\n\nconst KERNEL = array<f32, 5>(0.0625, 0.25, 0.375, 0.25, 0.0625);\n\n/** The variance around a pixel, blurred over 3 x 3, which steadies the luminance test on a few samples. */\nfn localVariance(pixel: vec2<i32>, specular: bool) -> f32 {\n var sum = 0.0;\n var weight = 0.0;\n\n for (var dy = -1; dy <= 1; dy = dy + 1) {\n for (var dx = -1; dx <= 1; dx = dx + 1) {\n let q = pixel + vec2<i32>(dx, dy);\n\n if (!inside(q)) {\n continue;\n }\n\n let other = u32(q.y) * params.size.x + u32(q.x);\n let w = select(0.5, 1.0, dx == 0) * select(0.5, 1.0, dy == 0);\n sum = sum + select(diffuseIn[other].w, specularIn[other].w, specular) * w;\n weight = weight + w;\n }\n }\n\n return sum / max(weight, 1e-6);\n}\n\n/**\n * How many pixels the specular lobe's footprint covers: the lobe's angle times the distance to what it reflects, seen\n * from the camera across the distance to the surface plus that.\n */\nfn specularRadius(index: u32, depth: f32) -> f32 {\n let alpha = guideRoughness(index) * guideRoughness(index);\n let reflected = specularDistance(index);\n\n if (params.orthographic != 0u) {\n return min(alpha * reflected / params.pixelScale, 64.0);\n }\n\n return min(alpha * reflected / max(depth + reflected, 1e-6) / params.pixelScale, 64.0);\n}\n\n/** One a-trous pass over both signals, its taps params.step pixels apart. */\n@compute @workgroup_size(8, 8, 1)\nfn atrous(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= params.size.x || id.y >= params.size.y) {\n return;\n }\n\n let pixel = vec2<i32>(id.xy);\n let index = id.y * params.size.x + id.x;\n let diffuseCenter = diffuseIn[index];\n let specularCenter = specularIn[index];\n\n if (accumulation[index].w <= 0.0) {\n diffuseOut[index] = diffuseCenter;\n specularOut[index] = specularCenter;\n\n return;\n }\n\n let guide = guides[index];\n let normal = guide.xyz;\n let depth = guide.w;\n let roughness = roughnesses[index];\n let diffuseSigma = params.edge * sqrt(max(localVariance(pixel, false), 0.0))\n * (1.0 + guideSubsurface(index) * params.subsurface) + 1e-6;\n let specularSigma = params.edge * sqrt(max(localVariance(pixel, true), 0.0)) + 1e-6;\n // A smooth reflection is filtered over at least the mirror radius, by taps that see the reflected thing at the same\n // distance: that test keeps the edges of what it reflects, which lets the normal test relax over a curved mirror,\n // where neighbouring normals part fast and would otherwise leave the reflection unfiltered.\n let mirror = params.mirrorRadius > 0.0 && roughness < 0.3;\n let radius = select(specularRadius(index, depth), max(specularRadius(index, depth), params.mirrorRadius), mirror);\n let reflected = specularDistance(index);\n // A smoother surface's reflection turns faster with its normal: the normal test sharpens with it, short of shutting\n // out the neighbours of a curved mirror altogether once the reflected distance guards it.\n let specularPower = min(128.0 / max(roughness * roughness, 0.03), select(4096.0, 64.0, mirror));\n // Under a surface that scatters light inside, the normal and the depth say little about the light that leaves it.\n let scattering = guideSubsurface(index) * params.subsurface;\n let diffusePower = mix(128.0, 1.0, scattering);\n let depthSlack = mix(1.0, 4.0, scattering);\n let step = i32(params.step);\n var diffuse = vec4<f32>(0.0);\n var specular = vec4<f32>(0.0);\n var diffuseTotal = 0.0;\n var specularTotal = 0.0;\n\n for (var dy = -2; dy <= 2; dy = dy + 1) {\n for (var dx = -2; dx <= 2; dx = dx + 1) {\n let q = pixel + vec2<i32>(dx, dy) * step;\n\n if (!inside(q)) {\n continue;\n }\n\n let other = u32(q.y) * params.size.x + u32(q.x);\n let d = diffuseIn[other];\n let s = specularIn[other];\n let base = KERNEL[dx + 2] * KERNEL[dy + 2];\n var wd = base;\n var ws = base;\n\n if (dx != 0 || dy != 0) {\n let otherRoughness = roughnesses[other];\n\n // No sample has landed there.\n if (otherRoughness < 0.0) {\n continue;\n }\n\n let otherGuide = guides[other];\n let offset = length(vec2<f32>(f32(dx * step), f32(dy * step)));\n let alignment = max(dot(normal, otherGuide.xyz), 0.0);\n let depthWeight = exp(-abs(depth - otherGuide.w) / (0.02 * depth * offset + 1e-3));\n let diffuseDepth = exp(-abs(depth - otherGuide.w) / (0.02 * depthSlack * depth * offset + 1e-3));\n\n wd = wd * pow(alignment, diffusePower) * diffuseDepth\n * exp(-abs(luminance(d.rgb) - luminance(diffuseCenter.rgb)) / diffuseSigma);\n\n ws = ws * pow(alignment, specularPower) * depthWeight\n * exp(-abs(otherRoughness - roughness) * 10.0)\n * exp(-abs(luminance(s.rgb) - luminance(specularCenter.rgb)) / specularSigma)\n * select(0.0, exp(-offset * offset / (radius * radius)), radius >= 0.5);\n\n if (mirror) {\n ws = ws * exp(-abs(specularDistance(other) - reflected) / (0.05 * reflected + 1e-3));\n }\n }\n\n diffuse = diffuse + vec4<f32>(d.rgb * wd, d.w * wd * wd);\n specular = specular + vec4<f32>(s.rgb * ws, s.w * ws * ws);\n diffuseTotal = diffuseTotal + wd;\n specularTotal = specularTotal + ws;\n }\n }\n\n diffuseOut[index] = vec4<f32>(diffuse.rgb / diffuseTotal, diffuse.w / (diffuseTotal * diffuseTotal));\n specularOut[index] = vec4<f32>(specular.rgb / specularTotal, specular.w / (specularTotal * specularTotal));\n}\n\n/** How much of the filtered signal replaces the traced one: by how noisy the signal still is, when asked. */\nfn amount(mean: vec3<f32>, square: f32, count: f32) -> f32 {\n if (params.onlyNoisy == 0u || count <= 1.0) {\n return params.strength;\n }\n\n let m = luminance(mean);\n let error = sqrt(max(square / count - m * m, 0.0) / count) / sqrt(max(m, 0.0) + 1e-3);\n\n return params.strength * smoothstep(params.converged, 4.0 * params.converged, error);\n}\n\n/** Both filtered signals, times the albedos they were divided by, each mixed with what was traced. */\n@compute @workgroup_size(8, 8, 1)\nfn finish(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= params.size.x || id.y >= params.size.y) {\n return;\n }\n\n let index = id.y * params.size.x + id.x;\n let count = accumulation[index].w;\n\n if (count <= 0.0) {\n diffuseOut[index] = vec4<f32>(0.0, 0.0, 0.0, 1.0);\n\n return;\n }\n\n let squares = accumulation[params.count + index];\n let diffuse = diffuseMean(index);\n let specular = specularMean(index);\n let filteredDiffuse = diffuseIn[index].rgb * diffuseAlbedo(index);\n let filteredSpecular = specularIn[index].rgb * specularAlbedo(index);\n\n diffuseOut[index] = vec4<f32>(\n mix(diffuse, filteredDiffuse, amount(diffuse, squares.z, count))\n + mix(specular, filteredSpecular, amount(specular, squares.w, count)), 1.0);\n}\n";
|
|
47
57
|
export declare class DenoisePass {
|
|
48
58
|
private readonly device;
|
|
49
59
|
private readonly layout;
|
package/dist/denoise.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"denoise.d.ts","sourceRoot":"","sources":["../src/denoise.ts"],"names":[],"mappings":"AAEA,iHAAiH;AACjH,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,8GAA8G;IAC9G,SAAS,EAAE,OAAO,CAAC;IACnB,0GAA0G;IAC1G,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"denoise.d.ts","sourceRoot":"","sources":["../src/denoise.ts"],"names":[],"mappings":"AAEA,iHAAiH;AACjH,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,8GAA8G;IAC9G,SAAS,EAAE,OAAO,CAAC;IACnB,0GAA0G;IAC1G,WAAW,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,CAQtD,CAAC;AAEH,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,GAAE,OAAO,CAAC,gBAAgB,CAAM,GAAG,gBAAgB,CAY7G;AAED,uFAAuF;AACvF,eAAO,MAAM,eAAe,QAAQ,CAAC;AAErC,uGAAuG;AACvG,MAAM,WAAW,WAAW;IAC1B,6FAA6F;IAC7F,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY,2iYA+UxB,CAAC;AAQF,qBAAa,WAAW;IA6BV,OAAO,CAAC,QAAQ,CAAC,MAAM;IA5BnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,gGAAgG;IAChG,OAAO,CAAC,OAAO,CAAmB;IAClC,iHAAiH;IACjH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;IAC1D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoC;IACxD,OAAO,CAAC,MAAM,CAAK;IAEnB,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IAEnB,wEAAwE;IACxE,MAAM,EAAE,SAAS,GAAG,IAAI,CAAQ;gBAEH,MAAM,EAAE,SAAS;IA6B9C,OAAO,CAAC,MAAM;IAiBd,OAAO,CAAC,WAAW;IAqBnB,wDAAwD;IACxD,MAAM,CACJ,OAAO,EAAE,iBAAiB,EAC1B,YAAY,EAAE,SAAS,EACvB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,gBAAmC,EAC7C,IAAI,GAAE,WAA6D;IA6DrE,OAAO;CAOR"}
|
package/dist/denoise.js
CHANGED
|
@@ -5,6 +5,8 @@ export const DEFAULT_DENOISER = Object.freeze({
|
|
|
5
5
|
edge: 2,
|
|
6
6
|
onlyNoisy: true,
|
|
7
7
|
antiFirefly: true,
|
|
8
|
+
subsurface: 1,
|
|
9
|
+
mirrorRadius: 3,
|
|
8
10
|
});
|
|
9
11
|
/** The settings with `input` applied, each field kept in range. */
|
|
10
12
|
export function mergeDenoiser(base, input = {}) {
|
|
@@ -15,6 +17,8 @@ export function mergeDenoiser(base, input = {}) {
|
|
|
15
17
|
edge: Math.max(0.01, Number(merged.edge) || DEFAULT_DENOISER.edge),
|
|
16
18
|
onlyNoisy: Boolean(merged.onlyNoisy),
|
|
17
19
|
antiFirefly: Boolean(merged.antiFirefly),
|
|
20
|
+
subsurface: Math.min(1, Math.max(0, Number(merged.subsurface) || 0)),
|
|
21
|
+
mirrorRadius: Math.min(16, Math.max(0, Number(merged.mirrorRadius) || 0)),
|
|
18
22
|
};
|
|
19
23
|
}
|
|
20
24
|
/** The error a pixel counts as converged under, the one adaptive sampling stops at. */
|
|
@@ -48,7 +52,8 @@ struct Params {
|
|
|
48
52
|
converged: f32,
|
|
49
53
|
pixelScale: f32,
|
|
50
54
|
orthographic: u32,
|
|
51
|
-
|
|
55
|
+
subsurface: f32,
|
|
56
|
+
mirrorRadius: f32,
|
|
52
57
|
};
|
|
53
58
|
|
|
54
59
|
@group(0) @binding(0) var<uniform> params: Params;
|
|
@@ -103,6 +108,11 @@ fn guideRoughness(index: u32) -> f32 {
|
|
|
103
108
|
return accumulation[2u * params.count + index].w / samples(index);
|
|
104
109
|
}
|
|
105
110
|
|
|
111
|
+
/** How much of the first surface scatters light inside, averaged over the pixel's samples. */
|
|
112
|
+
fn guideSubsurface(index: u32) -> f32 {
|
|
113
|
+
return accumulation[5u * params.count + index].w / samples(index);
|
|
114
|
+
}
|
|
115
|
+
|
|
106
116
|
fn specularDistance(index: u32) -> f32 {
|
|
107
117
|
return accumulation[4u * params.count + index].w / samples(index);
|
|
108
118
|
}
|
|
@@ -253,11 +263,22 @@ fn atrous(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
253
263
|
let normal = guide.xyz;
|
|
254
264
|
let depth = guide.w;
|
|
255
265
|
let roughness = roughnesses[index];
|
|
256
|
-
let diffuseSigma = params.edge * sqrt(max(localVariance(pixel, false), 0.0))
|
|
266
|
+
let diffuseSigma = params.edge * sqrt(max(localVariance(pixel, false), 0.0))
|
|
267
|
+
* (1.0 + guideSubsurface(index) * params.subsurface) + 1e-6;
|
|
257
268
|
let specularSigma = params.edge * sqrt(max(localVariance(pixel, true), 0.0)) + 1e-6;
|
|
258
|
-
|
|
259
|
-
//
|
|
260
|
-
|
|
269
|
+
// A smooth reflection is filtered over at least the mirror radius, by taps that see the reflected thing at the same
|
|
270
|
+
// distance: that test keeps the edges of what it reflects, which lets the normal test relax over a curved mirror,
|
|
271
|
+
// where neighbouring normals part fast and would otherwise leave the reflection unfiltered.
|
|
272
|
+
let mirror = params.mirrorRadius > 0.0 && roughness < 0.3;
|
|
273
|
+
let radius = select(specularRadius(index, depth), max(specularRadius(index, depth), params.mirrorRadius), mirror);
|
|
274
|
+
let reflected = specularDistance(index);
|
|
275
|
+
// A smoother surface's reflection turns faster with its normal: the normal test sharpens with it, short of shutting
|
|
276
|
+
// out the neighbours of a curved mirror altogether once the reflected distance guards it.
|
|
277
|
+
let specularPower = min(128.0 / max(roughness * roughness, 0.03), select(4096.0, 64.0, mirror));
|
|
278
|
+
// Under a surface that scatters light inside, the normal and the depth say little about the light that leaves it.
|
|
279
|
+
let scattering = guideSubsurface(index) * params.subsurface;
|
|
280
|
+
let diffusePower = mix(128.0, 1.0, scattering);
|
|
281
|
+
let depthSlack = mix(1.0, 4.0, scattering);
|
|
261
282
|
let step = i32(params.step);
|
|
262
283
|
var diffuse = vec4<f32>(0.0);
|
|
263
284
|
var specular = vec4<f32>(0.0);
|
|
@@ -291,14 +312,19 @@ fn atrous(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
291
312
|
let offset = length(vec2<f32>(f32(dx * step), f32(dy * step)));
|
|
292
313
|
let alignment = max(dot(normal, otherGuide.xyz), 0.0);
|
|
293
314
|
let depthWeight = exp(-abs(depth - otherGuide.w) / (0.02 * depth * offset + 1e-3));
|
|
315
|
+
let diffuseDepth = exp(-abs(depth - otherGuide.w) / (0.02 * depthSlack * depth * offset + 1e-3));
|
|
294
316
|
|
|
295
|
-
wd = wd * pow(alignment,
|
|
317
|
+
wd = wd * pow(alignment, diffusePower) * diffuseDepth
|
|
296
318
|
* exp(-abs(luminance(d.rgb) - luminance(diffuseCenter.rgb)) / diffuseSigma);
|
|
297
319
|
|
|
298
320
|
ws = ws * pow(alignment, specularPower) * depthWeight
|
|
299
321
|
* exp(-abs(otherRoughness - roughness) * 10.0)
|
|
300
322
|
* exp(-abs(luminance(s.rgb) - luminance(specularCenter.rgb)) / specularSigma)
|
|
301
323
|
* select(0.0, exp(-offset * offset / (radius * radius)), radius >= 0.5);
|
|
324
|
+
|
|
325
|
+
if (mirror) {
|
|
326
|
+
ws = ws * exp(-abs(specularDistance(other) - reflected) / (0.05 * reflected + 1e-3));
|
|
327
|
+
}
|
|
302
328
|
}
|
|
303
329
|
|
|
304
330
|
diffuse = diffuse + vec4<f32>(d.rgb * wd, d.w * wd * wd);
|
|
@@ -431,6 +457,8 @@ export class DenoisePass {
|
|
|
431
457
|
floats[7] = CONVERGED_ERROR;
|
|
432
458
|
floats[8] = view.pixelScale;
|
|
433
459
|
words[9] = view.orthographic ? 1 : 0;
|
|
460
|
+
floats[10] = settings.subsurface;
|
|
461
|
+
floats[11] = settings.mirrorRadius;
|
|
434
462
|
this.device.queue.writeBuffer(this.params[k], 0, values);
|
|
435
463
|
});
|
|
436
464
|
}
|
package/dist/denoise.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"denoise.js","sourceRoot":"","sources":["../src/denoise.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"denoise.js","sourceRoot":"","sources":["../src/denoise.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frameLayout.d.ts","sourceRoot":"","sources":["../src/frameLayout.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,eAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"frameLayout.d.ts","sourceRoot":"","sources":["../src/frameLayout.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,eAAO,MAAM,YAAY,0CAuEvB,CAAC;AAEH,eAAO,MAAM,WAAW,QAAqB,CAAC"}
|
package/dist/frameLayout.js
CHANGED
|
@@ -31,6 +31,10 @@ export const FRAME_LAYOUT = layoutStruct("Frame", [
|
|
|
31
31
|
{ name: "areaLightCount", type: "u32", doc: "How many leading lights are emissive triangles." },
|
|
32
32
|
{ name: "lightCdf", type: "u32", doc: "Where every light's cumulative probability starts in the texture buffer." },
|
|
33
33
|
{ name: "lightTriangles", type: "u32", doc: "Where every emissive light's triangle starts in the texture buffer." },
|
|
34
|
+
{ name: "localLightCount", type: "u32", doc: "How many leading lights sit in the scene, which the light tree picks." },
|
|
35
|
+
{ name: "localLightPick", type: "f32", doc: "The chance of picking one of them, the last one's cumulative probability." },
|
|
36
|
+
{ name: "lightTree", type: "u32", doc: "Where the light tree's nodes start in the texture buffer; zero picks by power alone." },
|
|
37
|
+
{ name: "lightLeaves", type: "u32", doc: "Where the leaf of every local light starts in the texture buffer." },
|
|
34
38
|
{ name: "clampIndirect", type: "f32", doc: "Maximum luminance one indirect contribution may add; zero leaves it." },
|
|
35
39
|
{ name: "shadowSpread", type: "f32", doc: "Tangent of half the angular diameter shadow rays spread over." },
|
|
36
40
|
{ name: "shadowsEnabled", type: "u32" },
|
|
@@ -55,6 +59,17 @@ export const FRAME_LAYOUT = layoutStruct("Frame", [
|
|
|
55
59
|
{ name: "volumetricData", type: "u32" },
|
|
56
60
|
{ name: "adaptiveMin", type: "u32", doc: "Samples a pixel takes before it may stop as converged; 0 never stops." },
|
|
57
61
|
{ name: "adaptiveThreshold", type: "f32", doc: "The error under which a pixel and its neighbours count as converged." },
|
|
62
|
+
{ name: "lensDistortion", type: "f32", doc: "Radial distortion of the camera ray: positive is barrel, negative pincushion." },
|
|
63
|
+
{ name: "rouletteThreshold", type: "f32", doc: "Throughput under which a path may be ended by chance; zero never." },
|
|
64
|
+
{ name: "rouletteStart", type: "u32", doc: "Bounces every path takes before the roulette may end it." },
|
|
65
|
+
{ name: "subsurfaceEnabled", type: "u32", doc: "Whether materials that scatter light inside do; 0 renders them opaque." },
|
|
66
|
+
{ name: "bevelEnabled", type: "u32", doc: "Whether materials with a bevel radius round their edges." },
|
|
67
|
+
{ name: "volumesEnabled", type: "u32", doc: "Whether volumes scatter light; 0 leaves their meshes out altogether." },
|
|
68
|
+
{ name: "cameraVolume", type: "u32", doc: "The volume material the camera stands inside, or NO_MATERIAL." },
|
|
69
|
+
{ name: "atmosphereColor", type: "vec4f", doc: "rgb is the radiance the air scatters towards the camera, w 1 when it is on." },
|
|
70
|
+
{ name: "atmosphereExtinction", type: "vec4f", doc: "rgb is how much of each colour the air takes per scene unit, w the haze." },
|
|
71
|
+
{ name: "atmosphereSun", type: "vec4f", doc: "xyz is the direction towards the brightest directional light." },
|
|
72
|
+
{ name: "atmosphereSunColor", type: "vec4f", doc: "rgb is that light's irradiance, which the air scatters." },
|
|
58
73
|
{ name: "clipPlane", type: "vec4f", doc: "A plane the camera ray drops surfaces behind, for planar captures; w = 0 and xyz = 0 is none." },
|
|
59
74
|
]);
|
|
60
75
|
export const FRAME_WORDS = FRAME_LAYOUT.words;
|
package/dist/frameLayout.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frameLayout.js","sourceRoot":"","sources":["../src/frameLayout.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"frameLayout.js","sourceRoot":"","sources":["../src/frameLayout.ts"],"names":[],"mappings":"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"}
|