@tinypoly/gpu-renderer 0.0.4 → 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 +81 -10
- package/README.md +53 -5
- 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/bvh.d.ts +11 -6
- package/dist/bvh.d.ts.map +1 -1
- package/dist/bvh.js +64 -11
- package/dist/bvh.js.map +1 -1
- package/dist/denoise.d.ts +17 -2
- package/dist/denoise.d.ts.map +1 -1
- package/dist/denoise.js +95 -27
- package/dist/denoise.js.map +1 -1
- package/dist/dispatchScheduler.d.ts +11 -4
- package/dist/dispatchScheduler.d.ts.map +1 -1
- package/dist/dispatchScheduler.js +14 -7
- package/dist/dispatchScheduler.js.map +1 -1
- package/dist/frameLayout.d.ts.map +1 -1
- package/dist/frameLayout.js +18 -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 +19 -1
- package/dist/lights.d.ts.map +1 -1
- package/dist/lights.js +63 -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 +198 -36
- package/dist/renderer.js.map +1 -1
- package/dist/sceneUpload.d.ts +19 -1
- package/dist/sceneUpload.d.ts.map +1 -1
- package/dist/sceneUpload.js +25 -3
- 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/texturePack.d.ts +16 -4
- package/dist/texturePack.d.ts.map +1 -1
- package/dist/texturePack.js +61 -8
- package/dist/texturePack.js.map +1 -1
- 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/bvh.wgsl.d.ts +8 -2
- package/dist/wgsl/bvh.wgsl.d.ts.map +1 -1
- package/dist/wgsl/bvh.wgsl.js +167 -96
- package/dist/wgsl/bvh.wgsl.js.map +1 -1
- package/dist/wgsl/common.wgsl.d.ts.map +1 -1
- package/dist/wgsl/common.wgsl.js +24 -7
- 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 +206 -33
- 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 +410 -25
- package/dist/wgsl/pathTrace.wgsl.js.map +1 -1
- package/dist/wgsl/random.wgsl.d.ts +1 -1
- package/dist/wgsl/random.wgsl.d.ts.map +1 -1
- package/dist/wgsl/random.wgsl.js +9 -17
- package/dist/wgsl/random.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 +47 -4
- package/dist/wgsl/surface.wgsl.js.map +1 -1
- package/dist/wgsl/texture.wgsl.d.ts +1 -1
- package/dist/wgsl/texture.wgsl.d.ts.map +1 -1
- package/dist/wgsl/texture.wgsl.js +10 -11
- package/dist/wgsl/texture.wgsl.js.map +1 -1
- package/package.json +1 -1
- package/src/atmosphere.ts +95 -0
- package/src/bvh.ts +81 -12
- package/src/denoise.ts +110 -27
- package/src/dispatchScheduler.ts +16 -7
- package/src/frameLayout.ts +18 -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 +90 -2
- package/src/materialLayout.ts +52 -1
- package/src/pathTracePass.ts +19 -10
- package/src/renderer.ts +258 -43
- package/src/sceneUpload.ts +41 -4
- package/src/settings.ts +234 -0
- package/src/subsurface.ts +153 -0
- package/src/texturePack.ts +80 -13
- package/src/volumeContainment.ts +81 -0
- package/src/wgsl/bsdf.wgsl.ts +35 -3
- package/src/wgsl/bvh.wgsl.ts +167 -96
- package/src/wgsl/common.wgsl.ts +24 -7
- package/src/wgsl/lens.wgsl.ts +314 -0
- package/src/wgsl/lights.wgsl.ts +206 -33
- package/src/wgsl/pathTrace.wgsl.ts +410 -25
- package/src/wgsl/random.wgsl.ts +9 -17
- package/src/wgsl/sampling.wgsl.ts +25 -0
- package/src/wgsl/surface.wgsl.ts +47 -4
- package/src/wgsl/texture.wgsl.ts +10 -11
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\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\n return;\n }\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 normal = guideNormal(index);\n let depth = guideDepth(index);\n let roughness = guideRoughness(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 if (accumulation[other].w <= 0.0) {\n continue;\n }\n\n let offset = length(vec2<f32>(f32(dx * step), f32(dy * step)));\n let alignment = max(dot(normal, guideNormal(other)), 0.0);\n let depthWeight = exp(-abs(depth - guideDepth(other)) / (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(guideRoughness(other) - 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;
|
|
@@ -53,8 +63,13 @@ export declare class DenoisePass {
|
|
|
53
63
|
private readonly finish;
|
|
54
64
|
/** One uniform per stage, so a whole denoise is a single submit. */
|
|
55
65
|
private readonly params;
|
|
56
|
-
/** Two ping-pong pairs, diffuse and specular, and the
|
|
66
|
+
/** Two ping-pong pairs, diffuse and specular, the output, then the guides and the roughness. */
|
|
57
67
|
private buffers;
|
|
68
|
+
/** Bind groups by the buffers they bind, and a number for each buffer seen, which is what the key is made of. */
|
|
69
|
+
private readonly groups;
|
|
70
|
+
private readonly ids;
|
|
71
|
+
private nextId;
|
|
72
|
+
private idOf;
|
|
58
73
|
private width;
|
|
59
74
|
private height;
|
|
60
75
|
/** The denoised radiance, one vec4 per pixel, once `encode` has run. */
|
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;
|
|
@@ -57,6 +62,10 @@ struct Params {
|
|
|
57
62
|
@group(0) @binding(3) var<storage, read> specularIn: array<vec4<f32>>;
|
|
58
63
|
@group(0) @binding(4) var<storage, read_write> diffuseOut: array<vec4<f32>>;
|
|
59
64
|
@group(0) @binding(5) var<storage, read_write> specularOut: array<vec4<f32>>;
|
|
65
|
+
// The guides each tap compares, worked out once per pixel by prepare: the normal and the depth, then the roughness, or
|
|
66
|
+
// -1 where no sample has landed. The same numbers the accumulation gives, two reads a tap instead of four.
|
|
67
|
+
@group(0) @binding(6) var<storage, read_write> guides: array<vec4<f32>>;
|
|
68
|
+
@group(0) @binding(7) var<storage, read_write> roughnesses: array<f32>;
|
|
60
69
|
|
|
61
70
|
fn luminance(color: vec3<f32>) -> f32 {
|
|
62
71
|
return dot(color, vec3<f32>(0.2126, 0.7152, 0.0722));
|
|
@@ -99,6 +108,11 @@ fn guideRoughness(index: u32) -> f32 {
|
|
|
99
108
|
return accumulation[2u * params.count + index].w / samples(index);
|
|
100
109
|
}
|
|
101
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
|
+
|
|
102
116
|
fn specularDistance(index: u32) -> f32 {
|
|
103
117
|
return accumulation[4u * params.count + index].w / samples(index);
|
|
104
118
|
}
|
|
@@ -124,10 +138,15 @@ fn prepare(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
124
138
|
if (count <= 0.0) {
|
|
125
139
|
diffuseOut[index] = vec4<f32>(0.0);
|
|
126
140
|
specularOut[index] = vec4<f32>(0.0);
|
|
141
|
+
guides[index] = vec4<f32>(0.0);
|
|
142
|
+
roughnesses[index] = -1.0;
|
|
127
143
|
|
|
128
144
|
return;
|
|
129
145
|
}
|
|
130
146
|
|
|
147
|
+
guides[index] = vec4<f32>(guideNormal(index), guideDepth(index));
|
|
148
|
+
roughnesses[index] = guideRoughness(index);
|
|
149
|
+
|
|
131
150
|
let squares = accumulation[params.count + index];
|
|
132
151
|
let diffuse = diffuseMean(index);
|
|
133
152
|
let specular = specularMean(index);
|
|
@@ -240,14 +259,26 @@ fn atrous(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
240
259
|
return;
|
|
241
260
|
}
|
|
242
261
|
|
|
243
|
-
let
|
|
244
|
-
let
|
|
245
|
-
let
|
|
246
|
-
let
|
|
262
|
+
let guide = guides[index];
|
|
263
|
+
let normal = guide.xyz;
|
|
264
|
+
let depth = guide.w;
|
|
265
|
+
let roughness = roughnesses[index];
|
|
266
|
+
let diffuseSigma = params.edge * sqrt(max(localVariance(pixel, false), 0.0))
|
|
267
|
+
* (1.0 + guideSubsurface(index) * params.subsurface) + 1e-6;
|
|
247
268
|
let specularSigma = params.edge * sqrt(max(localVariance(pixel, true), 0.0)) + 1e-6;
|
|
248
|
-
|
|
249
|
-
//
|
|
250
|
-
|
|
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);
|
|
251
282
|
let step = i32(params.step);
|
|
252
283
|
var diffuse = vec4<f32>(0.0);
|
|
253
284
|
var specular = vec4<f32>(0.0);
|
|
@@ -270,21 +301,30 @@ fn atrous(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
270
301
|
var ws = base;
|
|
271
302
|
|
|
272
303
|
if (dx != 0 || dy != 0) {
|
|
273
|
-
|
|
304
|
+
let otherRoughness = roughnesses[other];
|
|
305
|
+
|
|
306
|
+
// No sample has landed there.
|
|
307
|
+
if (otherRoughness < 0.0) {
|
|
274
308
|
continue;
|
|
275
309
|
}
|
|
276
310
|
|
|
311
|
+
let otherGuide = guides[other];
|
|
277
312
|
let offset = length(vec2<f32>(f32(dx * step), f32(dy * step)));
|
|
278
|
-
let alignment = max(dot(normal,
|
|
279
|
-
let depthWeight = exp(-abs(depth -
|
|
313
|
+
let alignment = max(dot(normal, otherGuide.xyz), 0.0);
|
|
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));
|
|
280
316
|
|
|
281
|
-
wd = wd * pow(alignment,
|
|
317
|
+
wd = wd * pow(alignment, diffusePower) * diffuseDepth
|
|
282
318
|
* exp(-abs(luminance(d.rgb) - luminance(diffuseCenter.rgb)) / diffuseSigma);
|
|
283
319
|
|
|
284
320
|
ws = ws * pow(alignment, specularPower) * depthWeight
|
|
285
|
-
* exp(-abs(
|
|
321
|
+
* exp(-abs(otherRoughness - roughness) * 10.0)
|
|
286
322
|
* exp(-abs(luminance(s.rgb) - luminance(specularCenter.rgb)) / specularSigma)
|
|
287
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
|
+
}
|
|
288
328
|
}
|
|
289
329
|
|
|
290
330
|
diffuse = diffuse + vec4<f32>(d.rgb * wd, d.w * wd * wd);
|
|
@@ -350,8 +390,18 @@ export class DenoisePass {
|
|
|
350
390
|
finish;
|
|
351
391
|
/** One uniform per stage, so a whole denoise is a single submit. */
|
|
352
392
|
params;
|
|
353
|
-
/** Two ping-pong pairs, diffuse and specular, and the
|
|
393
|
+
/** Two ping-pong pairs, diffuse and specular, the output, then the guides and the roughness. */
|
|
354
394
|
buffers = [];
|
|
395
|
+
/** Bind groups by the buffers they bind, and a number for each buffer seen, which is what the key is made of. */
|
|
396
|
+
groups = new Map();
|
|
397
|
+
ids = new WeakMap();
|
|
398
|
+
nextId = 0;
|
|
399
|
+
idOf(buffer) {
|
|
400
|
+
let id = this.ids.get(buffer);
|
|
401
|
+
if (id === undefined)
|
|
402
|
+
this.ids.set(buffer, id = this.nextId++);
|
|
403
|
+
return id;
|
|
404
|
+
}
|
|
355
405
|
width = 0;
|
|
356
406
|
height = 0;
|
|
357
407
|
/** The denoised radiance, one vec4 per pixel, once `encode` has run. */
|
|
@@ -369,6 +419,8 @@ export class DenoisePass {
|
|
|
369
419
|
{ binding: 3, visibility, buffer: storage("read-only-storage") },
|
|
370
420
|
{ binding: 4, visibility, buffer: storage("storage") },
|
|
371
421
|
{ binding: 5, visibility, buffer: storage("storage") },
|
|
422
|
+
{ binding: 6, visibility, buffer: storage("storage") },
|
|
423
|
+
{ binding: 7, visibility, buffer: storage("storage") },
|
|
372
424
|
],
|
|
373
425
|
});
|
|
374
426
|
const layout = device.createPipelineLayout({ bindGroupLayouts: [this.layout] });
|
|
@@ -385,8 +437,10 @@ export class DenoisePass {
|
|
|
385
437
|
for (const buffer of this.buffers)
|
|
386
438
|
buffer.destroy();
|
|
387
439
|
const size = Math.max(16, width * height * 16);
|
|
388
|
-
this.buffers = Array.from({ length:
|
|
440
|
+
this.buffers = Array.from({ length: 6 }, () => this.device.createBuffer({ size, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC }));
|
|
441
|
+
this.buffers.push(this.device.createBuffer({ size: Math.max(16, width * height * 4), usage: GPUBufferUsage.STORAGE }));
|
|
389
442
|
this.output = this.buffers[4];
|
|
443
|
+
this.groups.clear();
|
|
390
444
|
this.width = width;
|
|
391
445
|
this.height = height;
|
|
392
446
|
}
|
|
@@ -403,6 +457,8 @@ export class DenoisePass {
|
|
|
403
457
|
floats[7] = CONVERGED_ERROR;
|
|
404
458
|
floats[8] = view.pixelScale;
|
|
405
459
|
words[9] = view.orthographic ? 1 : 0;
|
|
460
|
+
floats[10] = settings.subsurface;
|
|
461
|
+
floats[11] = settings.mirrorRadius;
|
|
406
462
|
this.device.queue.writeBuffer(this.params[k], 0, values);
|
|
407
463
|
});
|
|
408
464
|
}
|
|
@@ -410,20 +466,31 @@ export class DenoisePass {
|
|
|
410
466
|
encode(encoder, accumulation, width, height, settings = DEFAULT_DENOISER, view = { pixelScale: 1 / height, orthographic: false }) {
|
|
411
467
|
this.resize(width, height);
|
|
412
468
|
this.writeParams(width, height, settings, view);
|
|
413
|
-
const [diffuseA, diffuseB, specularA, specularB, output] = this.buffers;
|
|
469
|
+
const [diffuseA, diffuseB, specularA, specularB, output, guides, roughnesses] = this.buffers;
|
|
414
470
|
const groups = [Math.ceil(width / 8), Math.ceil(height / 8)];
|
|
415
471
|
const run = (pipeline, params, sources, targets) => {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
472
|
+
// The same few groups serve every denoise of an image size: they are made once and kept.
|
|
473
|
+
const key = [params, accumulation, ...sources, ...targets].map(buffer => this.idOf(buffer)).join();
|
|
474
|
+
let group = this.groups.get(key);
|
|
475
|
+
if (!group) {
|
|
476
|
+
group = this.device.createBindGroup({
|
|
477
|
+
layout: this.layout,
|
|
478
|
+
entries: [
|
|
479
|
+
{ binding: 0, resource: { buffer: params } },
|
|
480
|
+
{ binding: 1, resource: { buffer: accumulation } },
|
|
481
|
+
{ binding: 2, resource: { buffer: sources[0] } },
|
|
482
|
+
{ binding: 3, resource: { buffer: sources[1] } },
|
|
483
|
+
{ binding: 4, resource: { buffer: targets[0] } },
|
|
484
|
+
{ binding: 5, resource: { buffer: targets[1] } },
|
|
485
|
+
{ binding: 6, resource: { buffer: guides } },
|
|
486
|
+
{ binding: 7, resource: { buffer: roughnesses } },
|
|
487
|
+
],
|
|
488
|
+
});
|
|
489
|
+
// A new accumulation buffer comes with a new size, which clears these; the cap is only a guard.
|
|
490
|
+
if (this.groups.size > 64)
|
|
491
|
+
this.groups.clear();
|
|
492
|
+
this.groups.set(key, group);
|
|
493
|
+
}
|
|
427
494
|
const pass = encoder.beginComputePass({ label: "denoise" });
|
|
428
495
|
pass.setPipeline(pipeline);
|
|
429
496
|
pass.setBindGroup(0, group);
|
|
@@ -449,6 +516,7 @@ export class DenoisePass {
|
|
|
449
516
|
for (const buffer of [...this.buffers, ...this.params])
|
|
450
517
|
buffer.destroy();
|
|
451
518
|
this.buffers = [];
|
|
519
|
+
this.groups.clear();
|
|
452
520
|
this.output = null;
|
|
453
521
|
}
|
|
454
522
|
}
|
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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"}
|
|
@@ -13,17 +13,24 @@ export interface DispatchSchedulerOptions {
|
|
|
13
13
|
initialRows?: number;
|
|
14
14
|
/** Rows in the whole image. */
|
|
15
15
|
maxRows: number;
|
|
16
|
+
/** The most paths per pixel one submit may take, which is how far past the whole image a band may grow. */
|
|
17
|
+
maxSamples?: number;
|
|
16
18
|
}
|
|
17
19
|
export declare class DispatchScheduler {
|
|
18
20
|
private readonly targetMs;
|
|
19
21
|
private readonly initialRows;
|
|
20
22
|
private readonly maxRows;
|
|
23
|
+
private readonly maxSamples;
|
|
24
|
+
/** The work of the next submit, in rows times paths per pixel. */
|
|
21
25
|
private rows;
|
|
22
|
-
/** Rows a millisecond, as far as anything has shown. Zero until the first submit says something. */
|
|
26
|
+
/** Rows a millisecond, one path each, as far as anything has shown. Zero until the first submit says something. */
|
|
23
27
|
private throughput;
|
|
24
28
|
constructor(options: DispatchSchedulerOptions);
|
|
25
|
-
/**
|
|
26
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Rows the next submit should cover, never more than what is left. A submit that takes several paths per pixel
|
|
31
|
+
* covers that many times fewer rows, so it still takes about the target time.
|
|
32
|
+
*/
|
|
33
|
+
next(remaining: number, samples?: number): number;
|
|
27
34
|
/**
|
|
28
35
|
* Feeds back how long the last submit took, which moves the band towards the target time.
|
|
29
36
|
*
|
|
@@ -36,7 +43,7 @@ export declare class DispatchScheduler {
|
|
|
36
43
|
* The rate is measured from the rows actually submitted rather than from the current guess, so one band clamped by
|
|
37
44
|
* what was left of the image does not stop the next one from growing.
|
|
38
45
|
*/
|
|
39
|
-
record(durationMs: number, rows: number): void;
|
|
46
|
+
record(durationMs: number, rows: number, samples?: number): void;
|
|
40
47
|
/** Back to the unmeasured band, for a new scene or a new size. */
|
|
41
48
|
reset(): void;
|
|
42
49
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatchScheduler.d.ts","sourceRoot":"","sources":["../src/dispatchScheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,wBAAwB;IACvC,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"dispatchScheduler.d.ts","sourceRoot":"","sources":["../src/dispatchScheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,wBAAwB;IACvC,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,2GAA2G;IAC3G,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,kEAAkE;IAClE,OAAO,CAAC,IAAI,CAAS;IACrB,mHAAmH;IACnH,OAAO,CAAC,UAAU,CAAK;gBAEX,OAAO,EAAE,wBAAwB;IAQ7C;;;OAGG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,SAAI,GAAG,MAAM;IAI5C;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,SAAI;IAYpD,kEAAkE;IAClE,KAAK;CAIN"}
|
|
@@ -6,18 +6,24 @@ export class DispatchScheduler {
|
|
|
6
6
|
targetMs;
|
|
7
7
|
initialRows;
|
|
8
8
|
maxRows;
|
|
9
|
+
maxSamples;
|
|
10
|
+
/** The work of the next submit, in rows times paths per pixel. */
|
|
9
11
|
rows;
|
|
10
|
-
/** Rows a millisecond, as far as anything has shown. Zero until the first submit says something. */
|
|
12
|
+
/** Rows a millisecond, one path each, as far as anything has shown. Zero until the first submit says something. */
|
|
11
13
|
throughput = 0;
|
|
12
14
|
constructor(options) {
|
|
13
15
|
this.targetMs = options.targetMs ?? 12;
|
|
14
16
|
this.maxRows = Math.max(1, Math.floor(options.maxRows));
|
|
17
|
+
this.maxSamples = Math.max(1, Math.floor(options.maxSamples ?? 1));
|
|
15
18
|
this.initialRows = Math.max(1, Math.min(this.maxRows, Math.floor(options.initialRows ?? 16)));
|
|
16
19
|
this.rows = this.initialRows;
|
|
17
20
|
}
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Rows the next submit should cover, never more than what is left. A submit that takes several paths per pixel
|
|
23
|
+
* covers that many times fewer rows, so it still takes about the target time.
|
|
24
|
+
*/
|
|
25
|
+
next(remaining, samples = 1) {
|
|
26
|
+
return Math.max(1, Math.min(Math.floor(this.rows / Math.max(1, samples)), Math.floor(remaining)));
|
|
21
27
|
}
|
|
22
28
|
/**
|
|
23
29
|
* Feeds back how long the last submit took, which moves the band towards the target time.
|
|
@@ -31,14 +37,15 @@ export class DispatchScheduler {
|
|
|
31
37
|
* The rate is measured from the rows actually submitted rather than from the current guess, so one band clamped by
|
|
32
38
|
* what was left of the image does not stop the next one from growing.
|
|
33
39
|
*/
|
|
34
|
-
record(durationMs, rows) {
|
|
40
|
+
record(durationMs, rows, samples = 1) {
|
|
35
41
|
if (!Number.isFinite(durationMs) || durationMs <= 0 || rows <= 0)
|
|
36
42
|
return;
|
|
37
|
-
const
|
|
43
|
+
const work = rows * Math.max(1, samples);
|
|
44
|
+
const observed = work / durationMs;
|
|
38
45
|
// Good news is believed at once, since it can only mean the band was cheaper than the clock suggested. Bad news
|
|
39
46
|
// is taken gradually, so one submit that happened to land behind something else does not collapse the band.
|
|
40
47
|
this.throughput = observed > this.throughput ? observed : this.throughput * DECAY + observed * (1 - DECAY);
|
|
41
|
-
this.rows = Math.max(1, Math.min(this.maxRows,
|
|
48
|
+
this.rows = Math.max(1, Math.min(this.maxRows * this.maxSamples, work * GROW, this.targetMs * this.throughput));
|
|
42
49
|
}
|
|
43
50
|
/** Back to the unmeasured band, for a new scene or a new size. */
|
|
44
51
|
reset() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatchScheduler.js","sourceRoot":"","sources":["../src/dispatchScheduler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dispatchScheduler.js","sourceRoot":"","sources":["../src/dispatchScheduler.ts"],"names":[],"mappings":"AAmBA,0GAA0G;AAC1G,MAAM,IAAI,GAAG,CAAC,CAAC;AACf,6FAA6F;AAC7F,MAAM,KAAK,GAAG,GAAG,CAAC;AAElB,MAAM,OAAO,iBAAiB;IACX,QAAQ,CAAS;IACjB,WAAW,CAAS;IACpB,OAAO,CAAS;IAChB,UAAU,CAAS;IACpC,kEAAkE;IAC1D,IAAI,CAAS;IACrB,mHAAmH;IAC3G,UAAU,GAAG,CAAC,CAAC;IAEvB,YAAY,OAAiC;QAC3C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9F,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,IAAI,CAAC,SAAiB,EAAE,OAAO,GAAG,CAAC;QACjC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACpG,CAAC;IAED;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,UAAkB,EAAE,IAAY,EAAE,OAAO,GAAG,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;YAAE,OAAO;QAEzE,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,IAAI,GAAG,UAAU,CAAC;QAEnC,gHAAgH;QAChH,4GAA4G;QAC5G,IAAI,CAAC,UAAU,GAAG,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QAC3G,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,GAAG,IAAI,EAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAClH,CAAC;IAED,kEAAkE;IAClE,KAAK;QACH,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;IACtB,CAAC;CACF"}
|
|
@@ -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
|
@@ -29,11 +29,18 @@ export const FRAME_LAYOUT = layoutStruct("Frame", [
|
|
|
29
29
|
{ name: "triangleCount", type: "u32" },
|
|
30
30
|
{ name: "lightCount", type: "u32" },
|
|
31
31
|
{ name: "areaLightCount", type: "u32", doc: "How many leading lights are emissive triangles." },
|
|
32
|
+
{ name: "lightCdf", type: "u32", doc: "Where every light's cumulative probability starts in the texture buffer." },
|
|
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." },
|
|
32
38
|
{ name: "clampIndirect", type: "f32", doc: "Maximum luminance one indirect contribution may add; zero leaves it." },
|
|
33
39
|
{ name: "shadowSpread", type: "f32", doc: "Tangent of half the angular diameter shadow rays spread over." },
|
|
34
40
|
{ name: "shadowsEnabled", type: "u32" },
|
|
35
41
|
{ name: "uvOffset", type: "u32", doc: "Where the UV sets start inside the attribute buffer." },
|
|
36
42
|
{ name: "orderOffset", type: "u32", doc: "Where the hierarchy's triangle order starts inside the index buffer." },
|
|
43
|
+
{ name: "leafOffset", type: "u32", doc: "Where each triangle's position in that order starts inside the index buffer." },
|
|
37
44
|
{ name: "colorOffset", type: "u32", doc: "Where vertex colours start inside the attribute buffer." },
|
|
38
45
|
{ name: "hasColors", type: "u32" },
|
|
39
46
|
{ name: "varyingOffset", type: "u32", doc: "Where shader varyings start inside the attribute buffer." },
|
|
@@ -52,6 +59,17 @@ export const FRAME_LAYOUT = layoutStruct("Frame", [
|
|
|
52
59
|
{ name: "volumetricData", type: "u32" },
|
|
53
60
|
{ name: "adaptiveMin", type: "u32", doc: "Samples a pixel takes before it may stop as converged; 0 never stops." },
|
|
54
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." },
|
|
55
73
|
{ name: "clipPlane", type: "vec4f", doc: "A plane the camera ray drops surfaces behind, for planar captures; w = 0 and xyz = 0 is none." },
|
|
56
74
|
]);
|
|
57
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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"}
|
package/dist/geometry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EAML,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,KAAK,EAAsB,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE1H,wHAAwH;AACxH,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,SAAS,EAAE,YAAY,CAAC;IACxB,2DAA2D;IAC3D,OAAO,EAAE,YAAY,CAAC;IACtB,gHAAgH;IAChH,GAAG,EAAE,YAAY,CAAC;IAClB,qGAAqG;IACrG,MAAM,EAAE,YAAY,CAAC;IACrB,uGAAuG;IACvG,QAAQ,EAAE,YAAY,CAAC;IACvB,uCAAuC;IACvC,SAAS,EAAE,WAAW,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,cAAc,EAAE,YAAY,CAAC;IAC7B,uEAAuE;IACvE,gBAAgB,EAAE,YAAY,CAAC;IAC/B,8FAA8F;IAC9F,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;IACzC,qDAAqD;IACrD,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,2FAA2F;IAC3F,UAAU,EAAE,WAAW,CAAC;IACxB;;;OAGG;IACH,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,wGAAwG;IACxG,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACrD,yFAAyF;IACzF,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,6GAA6G;IAC7G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,GAAG,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;CAC9B;AAiGD,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE,EAChG,GAAG,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAmB5D;AAsBD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,eAAe,EAAE,OAAO,GAAE,WAAW,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,cAAc,CAuUjG;AAqfD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,GAAG;IAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAetG;AAED,wGAAwG;AACxG,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAKrG;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/geometry.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { clipToWorld, transform4, worldToClip } from "./camera.js";
|
|
2
|
-
import { isMaterialVisible, MATERIAL_KIND, materialProperties, NO_PLACEMENT, NO_PROGRAM, } from "./materialLayout.js";
|
|
2
|
+
import { isMaterialVisible, MATERIAL_KIND, MATERIAL_UNLIT_LIGHT, materialProperties, NO_PLACEMENT, NO_PROGRAM, } from "./materialLayout.js";
|
|
3
3
|
import { normalMatrix, transformNormal, transformPoint } from "./math.js";
|
|
4
4
|
import { matrixValue, sampleTexture } from "./upstream.js";
|
|
5
5
|
/** Three's wireframe and line width: WebGL draws every line one pixel wide whatever the material asks. */
|
|
@@ -368,8 +368,11 @@ export function packGeometry(scene, options = {}) {
|
|
|
368
368
|
materials: new Uint32Array(triangleMaterials),
|
|
369
369
|
triangleCount: triangleMaterials.length,
|
|
370
370
|
materialColors: new Float32Array(properties.flatMap(m => [...m.color, m.opacity])),
|
|
371
|
-
// Only a lit or plainly emissive surface is a light: a shader's colour is not known until it runs.
|
|
372
|
-
|
|
371
|
+
// Only a lit or plainly emissive surface is a light: a shader's colour is not known until it runs. An unlit
|
|
372
|
+
// material of a fixed colour emits that colour, so it is one too.
|
|
373
|
+
materialEmissive: new Float32Array(properties.flatMap(m => m.kind === MATERIAL_KIND.shader ? [0, 0, 0, 0]
|
|
374
|
+
: (m.flags & MATERIAL_UNLIT_LIGHT) !== 0 ? [...m.color, 1]
|
|
375
|
+
: [...m.emissive, m.emissiveIntensity])),
|
|
373
376
|
materialProperties: properties,
|
|
374
377
|
materialPlacements: entries.map(entry => entry.placement),
|
|
375
378
|
materialCount: properties.length,
|