@meimu/mutracer 0.3.0 → 0.3.1
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/CHANGELOG.md +222 -0
- package/LICENSES/FidelityFX-FSR.txt +19 -0
- package/LICENSES/pbrt-v4-Apache-2.0.txt +202 -0
- package/THIRD_PARTY_NOTICES.md +18 -4
- package/dist/assets/bvh.worker.js +1 -1
- package/dist/mutracer.umd.js +6260 -5580
- package/docs/api.md +45 -18
- package/docs/materials.md +4 -2
- package/docs/performance.md +7 -0
- package/docs/post-processing.md +62 -9
- package/docs/progressive-rendering.md +3 -0
- package/docs/sampling.md +4 -0
- package/docs/technical/acceleration.md +52 -0
- package/docs/technical/aovs.md +79 -0
- package/docs/technical/caustics.md +109 -0
- package/docs/technical/continuous-spectrum.md +29 -0
- package/docs/technical/display-upscale.md +82 -0
- package/docs/technical/openpbr.md +99 -0
- package/docs/technical/pbrt-import.md +168 -0
- package/docs/technical/tiling.md +74 -0
- package/docs/technical/webgl.md +53 -0
- package/docs/viewer.md +11 -1
- package/lib/Display.d.ts +19 -5
- package/lib/Display.js +10 -7
- package/lib/Display.js.map +1 -1
- package/lib/Engine.d.ts +2 -1
- package/lib/Engine.js +30 -2
- package/lib/Engine.js.map +1 -1
- package/lib/Render.d.ts +20 -11
- package/lib/Render.js +32 -7
- package/lib/Render.js.map +1 -1
- package/lib/main.d.ts +3 -1
- package/lib/main.js +1 -0
- package/lib/main.js.map +1 -1
- package/lib/oidn.d.ts +1 -1
- package/lib/renderer/AovPrimitiveCache.js +39 -8
- package/lib/renderer/AovPrimitiveCache.js.map +1 -1
- package/lib/renderer/DisplayPostProcess.d.ts +8 -3
- package/lib/renderer/DisplayPostProcess.js +51 -18
- package/lib/renderer/DisplayPostProcess.js.map +1 -1
- package/lib/renderer/MaterialData.js +7 -2
- package/lib/renderer/MaterialData.js.map +1 -1
- package/lib/renderer/RenderingPipeline.d.ts +7 -1
- package/lib/renderer/RenderingPipeline.js.map +1 -1
- package/lib/renderer/TileRender.d.ts +2 -2
- package/lib/renderer/TileRender.js +128 -7
- package/lib/renderer/TileRender.js.map +1 -1
- package/lib/renderer/WGPUAov.d.ts +6 -1
- package/lib/renderer/WGPUAov.js +108 -14
- package/lib/renderer/WGPUAov.js.map +1 -1
- package/lib/renderer/WGPUCanvasSurface.js +12 -1
- package/lib/renderer/WGPUCanvasSurface.js.map +1 -1
- package/lib/renderer/WGPUEnvMapDistribution.js +19 -19
- package/lib/renderer/WGPUFullQuadPass.js +3 -3
- package/lib/renderer/WGPURayTraceOutput.js +12 -12
- package/lib/renderer/WGPURayTracePass.d.ts +17 -4
- package/lib/renderer/WGPURayTracePass.js +194 -71
- package/lib/renderer/WGPURayTracePass.js.map +1 -1
- package/lib/renderer/WGPURenderingPipeline.js +46 -40
- package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLAov.js +3 -3
- package/lib/renderer/WebGLAov.js.map +1 -1
- package/lib/renderer/WebGLFullQuadPass.d.ts +1 -1
- package/lib/renderer/WebGLFullQuadPass.js +8 -2
- package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
- package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
- package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
- package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
- package/lib/renderer/WebGLRayTracePass.d.ts +14 -10
- package/lib/renderer/WebGLRayTracePass.js +115 -111
- package/lib/renderer/WebGLRayTracePass.js.map +1 -1
- package/lib/renderer/WebGLRenderingPipeline.js +10 -3
- package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLSkyBaker.js +57 -57
- package/lib/renderer/aov.d.ts +13 -1
- package/lib/renderer/aov.js +21 -2
- package/lib/renderer/aov.js.map +1 -1
- package/lib/renderer/aovDisplay.js +2 -2
- package/lib/renderer/aovDisplay.js.map +1 -1
- package/lib/renderer/binnedSahBvh.d.ts +2 -1
- package/lib/renderer/binnedSahBvh.js +24 -17
- package/lib/renderer/binnedSahBvh.js.map +1 -1
- package/lib/renderer/bvhAccel.d.ts +3 -1
- package/lib/renderer/bvhAccel.js +36 -6
- package/lib/renderer/bvhAccel.js.map +1 -1
- package/lib/renderer/cameraUniform.d.ts +1 -1
- package/lib/renderer/cameraUniform.js +6 -2
- package/lib/renderer/cameraUniform.js.map +1 -1
- package/lib/renderer/compactVertices.d.ts +14 -0
- package/lib/renderer/compactVertices.js +124 -0
- package/lib/renderer/compactVertices.js.map +1 -0
- package/lib/renderer/easu.d.ts +11 -0
- package/lib/renderer/easu.js +156 -0
- package/lib/renderer/easu.js.map +1 -0
- package/lib/renderer/geometryUpdateState.d.ts +1 -0
- package/lib/renderer/geometryUpdateState.js.map +1 -1
- package/lib/renderer/geometryUpdates.js +3 -2
- package/lib/renderer/geometryUpdates.js.map +1 -1
- package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
- package/lib/renderer/glsl/aovReflectance.glsl.js +35 -35
- package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
- package/lib/renderer/glsl/aovTrace.frag.js +44 -36
- package/lib/renderer/glsl/aovTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/chunks/cameraProjection.glsl.d.ts +2 -0
- package/lib/renderer/glsl/chunks/cameraProjection.glsl.js +16 -0
- package/lib/renderer/glsl/chunks/cameraProjection.glsl.js.map +1 -0
- package/lib/renderer/glsl/chunks/cameraRay.glsl.d.ts +1 -1
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -16
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +8 -8
- package/lib/renderer/glsl/chunks/envMap.glsl.js +67 -67
- package/lib/renderer/glsl/chunks/intersect.glsl.js +390 -375
- package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +120 -120
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +562 -562
- package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
- package/lib/renderer/glsl/chunks/random.glsl.js +4 -4
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +37 -35
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js +70 -74
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -1
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +107 -88
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +11 -11
- package/lib/renderer/glsl/chunks/sampling.glsl.js +77 -77
- package/lib/renderer/glsl/chunks/sobol.glsl.js +70 -70
- package/lib/renderer/glsl/chunks/spectralPrefix.glsl.d.ts +6 -0
- package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js +47 -0
- package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js.map +1 -0
- package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
- package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
- package/lib/renderer/glsl/depthQuery.frag.js +12 -6
- package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
- package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
- package/lib/renderer/glsl/rayTrace.frag.js +233 -170
- package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/shadowCatcher.frag.js +10 -9
- package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
- package/lib/renderer/glsl/webglPipeline.frag.js +58 -58
- package/lib/renderer/gpuMemory.js +1 -1
- package/lib/renderer/gpuMemory.js.map +1 -1
- package/lib/renderer/openPbrEnergyLut.d.ts +2 -1
- package/lib/renderer/openPbrEnergyLut.js +18 -1
- package/lib/renderer/openPbrEnergyLut.js.map +1 -1
- package/lib/renderer/pathLimits.d.ts +5 -4
- package/lib/renderer/pathLimits.js +6 -5
- package/lib/renderer/pathLimits.js.map +1 -1
- package/lib/renderer/prepareMeshBVH.d.ts +2 -0
- package/lib/renderer/prepareMeshBVH.js +5 -3
- package/lib/renderer/prepareMeshBVH.js.map +1 -1
- package/lib/renderer/progressiveScheduler.js +1 -1
- package/lib/renderer/progressiveScheduler.js.map +1 -1
- package/lib/renderer/renderPipeline.d.ts +4 -0
- package/lib/renderer/renderPipeline.js +1 -0
- package/lib/renderer/renderPipeline.js.map +1 -1
- package/lib/renderer/researchTransport.d.ts +1 -1
- package/lib/renderer/researchTransport.js.map +1 -1
- package/lib/renderer/sceneAssembly.d.ts +1 -1
- package/lib/renderer/sceneAssembly.js +4 -3
- package/lib/renderer/sceneAssembly.js.map +1 -1
- package/lib/renderer/sceneChangeDetector.d.ts +1 -1
- package/lib/renderer/sceneChangeDetector.js +3 -1
- package/lib/renderer/sceneChangeDetector.js.map +1 -1
- package/lib/renderer/sceneLayoutSchema.d.ts +1 -0
- package/lib/renderer/sceneLayoutSchema.js +2 -2
- package/lib/renderer/sceneLayoutSchema.js.map +1 -1
- package/lib/renderer/scenePreparation.d.ts +3 -2
- package/lib/renderer/scenePreparation.js +18 -2
- package/lib/renderer/scenePreparation.js.map +1 -1
- package/lib/renderer/scratchSampling.js +6 -6
- package/lib/renderer/spectrum.js +2 -2
- package/lib/renderer/staticBvhPreparation.d.ts +9 -0
- package/lib/renderer/staticBvhPreparation.js +8 -2
- package/lib/renderer/staticBvhPreparation.js.map +1 -1
- package/lib/renderer/surfaceHit.d.ts +10 -0
- package/lib/renderer/surfaceHit.js +22 -0
- package/lib/renderer/surfaceHit.js.map +1 -0
- package/lib/renderer/tangentRequirements.d.ts +2 -0
- package/lib/renderer/tangentRequirements.js +7 -0
- package/lib/renderer/tangentRequirements.js.map +1 -0
- package/lib/renderer/textureAtlas.d.ts +4 -0
- package/lib/renderer/textureAtlas.js +48 -2
- package/lib/renderer/textureAtlas.js.map +1 -1
- package/lib/renderer/webgl.js +4 -0
- package/lib/renderer/webgl.js.map +1 -1
- package/lib/renderer/webglVertexAttributes.d.ts +17 -0
- package/lib/renderer/webglVertexAttributes.js +44 -0
- package/lib/renderer/webglVertexAttributes.js.map +1 -0
- package/lib/renderer/wgpuBindlessSource.js +2 -2
- package/lib/renderer/wgpuMaterialConstants.js +4 -3
- package/lib/renderer/wgpuMaterialConstants.js.map +1 -1
- package/lib/renderer/wgpuSceneUniform.js +2 -2
- package/lib/renderer/wgpuSceneUniform.js.map +1 -1
- package/lib/renderer/wgpuShaderSource.d.ts +15 -0
- package/lib/renderer/wgpuShaderSource.js +1 -1
- package/lib/renderer/wgpuShaderSource.js.map +1 -1
- package/lib/renderer/wgsl/aovCompact.wgsl.d.ts +4 -0
- package/lib/renderer/wgsl/aovCompact.wgsl.js +26 -0
- package/lib/renderer/wgsl/aovCompact.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
- package/lib/renderer/wgsl/aovReflectance.wgsl.js +39 -39
- package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
- package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +4 -0
- package/lib/renderer/wgsl/aovResolve.wgsl.js +21 -2
- package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/aovSnapshot.wgsl.js +2 -2
- package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/aovTrace.wgsl.js +31 -31
- package/lib/renderer/wgsl/aovTraceDisplay.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js +56 -0
- package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
- package/lib/renderer/wgsl/causticLight.compute.d.ts +1 -1
- package/lib/renderer/wgsl/causticLight.compute.js +121 -119
- package/lib/renderer/wgsl/causticLight.compute.js.map +1 -1
- package/lib/renderer/wgsl/chunks/background.wgsl.js +46 -46
- package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +4 -4
- package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/caustics.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js +30 -30
- package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +6 -2
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js +550 -542
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +18 -16
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +2 -1
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +70 -69
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +593 -593
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +118 -118
- package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +4 -1
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +32 -26
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +114 -114
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +159 -137
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +8 -8
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js +74 -74
- package/lib/renderer/wgsl/chunks/sobol.wgsl.js +74 -74
- package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.d.ts +6 -0
- package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js +66 -0
- package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
- package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js +61 -0
- package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/compose.frag.wgsl.js +99 -97
- package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
- package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/depthQuery.wgsl.js +11 -2
- package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/easu.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/easu.wgsl.js +80 -0
- package/lib/renderer/wgsl/easu.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/easuDetail.wgsl.d.ts +21 -0
- package/lib/renderer/wgsl/easuDetail.wgsl.js +230 -0
- package/lib/renderer/wgsl/easuDetail.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/jnlm.compute.js +74 -74
- package/lib/renderer/wgsl/jnlm.compute.js.map +1 -1
- package/lib/renderer/wgsl/offlineAdaptive.compute.js +26 -26
- package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
- package/lib/renderer/wgsl/photonMap.compute.js +89 -89
- package/lib/renderer/wgsl/proceduralSky.compute.js +61 -61
- package/lib/renderer/wgsl/rayTrace.compute.d.ts +14 -0
- package/lib/renderer/wgsl/rayTrace.compute.js +395 -333
- package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
- package/lib/scene/Scene.js.map +1 -1
- package/lib/settings/render.d.ts +11 -0
- package/lib/settings/render.js +1 -0
- package/lib/settings/render.js.map +1 -1
- package/lib/util/fromTHREE.js +21 -14
- package/lib/util/fromTHREE.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/package.json +5 -2
- package/lib/oidnBundledWeights.cdn.d.ts +0 -2
- package/lib/oidnBundledWeights.cdn.js +0 -5
- package/lib/oidnBundledWeights.cdn.js.map +0 -1
- package/lib/renderer/bvh.worker.d.ts +0 -1
- package/lib/renderer/bvh.worker.js +0 -16
- package/lib/renderer/bvh.worker.js.map +0 -1
- package/lib/renderer/bvhWorkerBuild.d.ts +0 -13
- package/lib/renderer/bvhWorkerBuild.js +0 -17
- package/lib/renderer/bvhWorkerBuild.js.map +0 -1
- package/lib/renderer/lightCore.d.ts +0 -12
- package/lib/renderer/lightCore.js +0 -13
- package/lib/renderer/lightCore.js.map +0 -1
- package/lib/renderer/materialCore.d.ts +0 -8
- package/lib/renderer/materialCore.js +0 -9
- package/lib/renderer/materialCore.js.map +0 -1
- package/lib/scene/PixelTexture.d.ts +0 -6
- package/lib/scene/PixelTexture.js +0 -10
- package/lib/scene/PixelTexture.js.map +0 -1
- package/lib/util/studioLighting.d.ts +0 -18
- package/lib/util/studioLighting.js +0 -36
- package/lib/util/studioLighting.js.map +0 -1
|
@@ -7,7 +7,7 @@ function guideBinding(guides, binding, name) {
|
|
|
7
7
|
: `@group(0) @binding(${binding}) var<storage, read> ${name}: array<vec4f>;`;
|
|
8
8
|
}
|
|
9
9
|
function loadGuide(guides, name, position) {
|
|
10
|
-
return guides === 'textures' ? `textureLoad(${name}, ${position}, 0)` : `${name}[
|
|
10
|
+
return guides === 'textures' ? `textureLoad(${name}, ${position}, 0)` : `${name}[gx(${position})]`;
|
|
11
11
|
}
|
|
12
12
|
// Shared uniform packing also fixes sensitivity and dilation for native callers.
|
|
13
13
|
export function packJNLMParameters(width, height, strength, pass) {
|
|
@@ -43,9 +43,9 @@ ${shadowCatcherAccumulationWGSL}
|
|
|
43
43
|
fn main(@builtin(global_invocation_id) id: vec3u) {
|
|
44
44
|
let size = params.size;
|
|
45
45
|
if (id.x >= size.x || id.y >= size.y) { return; }
|
|
46
|
-
let
|
|
47
|
-
let n = normalBuffer[
|
|
48
|
-
let moments = accumulation[
|
|
46
|
+
let rJ = id.x + (size.y - 1u - id.y) * size.x;
|
|
47
|
+
let n = normalBuffer[rJ].a;
|
|
48
|
+
let moments = accumulation[rJ].moments;
|
|
49
49
|
let mean = moments.x / max(n, 1.0);
|
|
50
50
|
|
|
51
51
|
var variance = -1.0;
|
|
@@ -69,28 +69,28 @@ fn main(@builtin(global_invocation_id) id: vec3u) {
|
|
|
69
69
|
let size = params.size;
|
|
70
70
|
if (id.x >= size.x || id.y >= size.y) { return; }
|
|
71
71
|
let center = rawGuide[id.x + id.y * size.x];
|
|
72
|
-
var
|
|
73
|
-
var
|
|
74
|
-
var
|
|
75
|
-
var
|
|
72
|
+
var mF = 0.0;
|
|
73
|
+
var jI = 0.0;
|
|
74
|
+
var oL = 0.0;
|
|
75
|
+
var jB = 0.0;
|
|
76
76
|
for (var dy = -1; dy <= 1; dy++) {
|
|
77
77
|
for (var dx = -1; dx <= 1; dx++) {
|
|
78
78
|
let q = clamp(vec2i(id.xy) + vec2i(dx, dy), vec2i(0), vec2i(size) - 1);
|
|
79
79
|
let value = rawGuide[u32(q.x) + u32(q.y) * size.x];
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
oL += value.x;
|
|
81
|
+
jB += value.x * value.x;
|
|
82
82
|
if (value.y >= 0.0) {
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
mF += value.y;
|
|
84
|
+
jI += 1.0;
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
var variance: f32;
|
|
89
89
|
if (center.y >= 0.0) {
|
|
90
|
-
variance =
|
|
90
|
+
variance = mF / jI;
|
|
91
91
|
} else {
|
|
92
|
-
let
|
|
93
|
-
variance = max(
|
|
92
|
+
let mE = oL / 9.0;
|
|
93
|
+
variance = max(jB / 9.0 - mE * mE, 0.0) * 9.0 / 8.0;
|
|
94
94
|
}
|
|
95
95
|
guide[id.x + id.y * size.x] = vec2f(center.x, variance);
|
|
96
96
|
}
|
|
@@ -114,11 +114,11 @@ fn main(@builtin(global_invocation_id) id: vec3u) {
|
|
|
114
114
|
if (id.x >= params.size.x || id.y >= params.size.y) { return; }
|
|
115
115
|
outColor[id.x + id.y * params.size.x] = textureLoad(colorTex, vec2i(id.xy), 0);
|
|
116
116
|
${guides === 'buffers' ? ` let index = id.x + id.y * params.size.x;
|
|
117
|
-
let
|
|
118
|
-
let n = normalAccumulation[
|
|
117
|
+
let rJ = id.x + (params.size.y - 1u - id.y) * params.size.x;
|
|
118
|
+
let n = normalAccumulation[rJ];
|
|
119
119
|
let samples = max(n.a, 1.0);
|
|
120
120
|
normal[index] = vec4f(n.rgb / samples * 0.5 + 0.5, select(0.0, 1.0, dot(n.rgb, n.rgb) > 0.1));
|
|
121
|
-
shadow[index] = shadowAccumulation[
|
|
121
|
+
shadow[index] = shadowAccumulation[rJ].mask / samples;
|
|
122
122
|
albedo[index] = textureLoad(albedoTex, vec2i(id.xy), 0);` : ''}
|
|
123
123
|
}
|
|
124
124
|
`;
|
|
@@ -155,23 +155,23 @@ const INV_NORMAL_BANDWIDTH2 = ${(1 / normalBandwidth ** 2).toFixed(6)};
|
|
|
155
155
|
|
|
156
156
|
const CENTER_WEIGHT_MIN = 0.01;
|
|
157
157
|
|
|
158
|
-
fn
|
|
158
|
+
fn gx(p: vec2i) -> u32 {
|
|
159
159
|
let q = clamp(p, vec2i(0), vec2i(params.size) - 1);
|
|
160
160
|
return u32(q.x) + u32(q.y) * params.size.x;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
fn
|
|
163
|
+
fn mJ(q: vec2i, bp: vec4f, hk: vec3f) -> f32 {
|
|
164
164
|
let albedo = ${loadGuide(guides, 'albedoTex', 'q')};
|
|
165
165
|
|
|
166
166
|
|
|
167
|
-
let
|
|
168
|
-
let
|
|
169
|
-
let
|
|
170
|
-
let
|
|
167
|
+
let rc = max((albedo.rgb + bp.rgb) * 0.5, vec3f(${JNLM_ALBEDO_FLOOR}));
|
|
168
|
+
let mn = (albedo.rgb - bp.rgb) / rc;
|
|
169
|
+
let jv = albedo.a - bp.a;
|
|
170
|
+
let my = ${loadGuide(guides, 'normalTex', 'q')}.rgb - hk;
|
|
171
171
|
return max(
|
|
172
|
-
dot(
|
|
173
|
-
+
|
|
174
|
-
dot(
|
|
172
|
+
dot(mn, mn) * INV_ALBEDO_BANDWIDTH2
|
|
173
|
+
+ jv * jv * INV_COVERAGE_BANDWIDTH2,
|
|
174
|
+
dot(my, my) * INV_NORMAL_BANDWIDTH2
|
|
175
175
|
);
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -185,37 +185,37 @@ let size = vec2i(params.size);
|
|
|
185
185
|
let p = vec2i(id.xy);
|
|
186
186
|
if (p.x >= size.x || p.y >= size.y) { return; }
|
|
187
187
|
let step = i32(params.dilation);
|
|
188
|
-
let
|
|
189
|
-
let
|
|
190
|
-
let
|
|
191
|
-
let index =
|
|
188
|
+
let bp = ${loadGuide(guides, 'albedoTex', 'p')};
|
|
189
|
+
let fO = ${loadGuide(guides, 'normalTex', 'p')};
|
|
190
|
+
let hk = fO.rgb;
|
|
191
|
+
let index = gx(p);
|
|
192
192
|
|
|
193
193
|
|
|
194
|
-
if (
|
|
194
|
+
if (fO.a <= 0.0 && ${loadGuide(guides, 'shadowCatcherTex', 'p')}.r <= 0.0) {
|
|
195
195
|
outColor[index] = color[index];
|
|
196
196
|
outGuide[index] = guide[index];
|
|
197
197
|
return;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
var
|
|
200
|
+
var mq: array<vec2f, PATCH_SIZE>;
|
|
201
201
|
var k = 0;
|
|
202
202
|
for (var dy = -PATCH_RADIUS; dy <= PATCH_RADIUS; dy++) {
|
|
203
203
|
for (var dx = -PATCH_RADIUS; dx <= PATCH_RADIUS; dx++) {
|
|
204
|
-
|
|
204
|
+
mq[k] = guide[gx(p + vec2i(dx, dy) * step)];
|
|
205
205
|
k++;
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
let k2 = params.colorSensitivity * params.colorSensitivity;
|
|
209
209
|
|
|
210
210
|
var sum = vec4f(0.0);
|
|
211
|
-
let
|
|
212
|
-
var
|
|
213
|
-
var
|
|
214
|
-
var
|
|
215
|
-
var
|
|
216
|
-
var
|
|
217
|
-
var
|
|
218
|
-
var
|
|
211
|
+
let hn = max(bp.rgb, vec3f(${JNLM_ALBEDO_FLOOR}));
|
|
212
|
+
var nS = vec3f(0.0);
|
|
213
|
+
var gP = vec3f(0.0);
|
|
214
|
+
var hH = vec3f(0.0);
|
|
215
|
+
var rn = 0.0;
|
|
216
|
+
var gR = 0.0;
|
|
217
|
+
var pK = 0.0;
|
|
218
|
+
var lR = 0.0;
|
|
219
219
|
for (var sy = -SEARCH_RADIUS; sy <= SEARCH_RADIUS; sy++) {
|
|
220
220
|
for (var sx = -SEARCH_RADIUS; sx <= SEARCH_RADIUS; sx++) {
|
|
221
221
|
let q = p + vec2i(sx, sy) * step;
|
|
@@ -224,63 +224,63 @@ if (
|
|
|
224
224
|
q.x < 0 || q.y < 0 || q.x >= size.x || q.y >= size.y
|
|
225
225
|
) { continue; }
|
|
226
226
|
|
|
227
|
-
let features =
|
|
227
|
+
let features = mJ(q, bp, hk);
|
|
228
228
|
if (features > 16.0) { continue; }
|
|
229
229
|
|
|
230
|
-
var
|
|
230
|
+
var gn = 0.0;
|
|
231
231
|
k = 0;
|
|
232
232
|
for (var dy = -PATCH_RADIUS; dy <= PATCH_RADIUS; dy++) {
|
|
233
233
|
for (var dx = -PATCH_RADIUS; dx <= PATCH_RADIUS; dx++) {
|
|
234
|
-
let a =
|
|
235
|
-
let b = guide[
|
|
236
|
-
let
|
|
237
|
-
|
|
234
|
+
let a = mq[k];
|
|
235
|
+
let b = guide[gx(q + vec2i(dx, dy) * step)];
|
|
236
|
+
let tn = a.x - b.x;
|
|
237
|
+
gn += (tn * tn
|
|
238
238
|
- VARIANCE_CANCELLATION * (a.y + min(a.y, b.y)))
|
|
239
239
|
/ (1e-10 + k2 * (a.y + b.y));
|
|
240
240
|
k++;
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
|
-
|
|
243
|
+
gn /= f32(PATCH_SIZE);
|
|
244
244
|
|
|
245
|
-
let weight = exp(-max(max(
|
|
246
|
-
let index =
|
|
245
|
+
let weight = exp(-max(max(gn, 0.0), features));
|
|
246
|
+
let index = gx(q);
|
|
247
247
|
let c = color[index];
|
|
248
248
|
let g = guide[index];
|
|
249
249
|
|
|
250
|
-
let a = (${loadGuide(guides, 'albedoTex', 'q')}.rgb -
|
|
250
|
+
let a = (${loadGuide(guides, 'albedoTex', 'q')}.rgb - bp.rgb) / hn;
|
|
251
251
|
let value = premultiplied(c);
|
|
252
252
|
sum += value * weight;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
253
|
+
nS += a * weight;
|
|
254
|
+
gP += a * a * weight;
|
|
255
|
+
hH += a * value.rgb * weight;
|
|
256
|
+
rn += g.x * weight;
|
|
257
|
+
gR += g.y * weight * weight;
|
|
258
|
+
pK += weight;
|
|
259
|
+
lR = max(lR, weight);
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
|
|
264
264
|
|
|
265
265
|
let center = color[index];
|
|
266
|
-
let
|
|
267
|
-
let
|
|
268
|
-
let totalWeight =
|
|
269
|
-
let
|
|
270
|
-
let mean = (sum +
|
|
266
|
+
let mp = guide[index];
|
|
267
|
+
let dG = max(lR, CENTER_WEIGHT_MIN);
|
|
268
|
+
let totalWeight = pK + dG;
|
|
269
|
+
let xj = premultiplied(center);
|
|
270
|
+
let mean = (sum + xj * dG) / totalWeight;
|
|
271
271
|
|
|
272
272
|
|
|
273
|
-
let
|
|
274
|
-
let
|
|
275
|
-
let
|
|
273
|
+
let eA = nS / totalWeight;
|
|
274
|
+
let mK = max(gP / totalWeight - eA * eA, vec3f(0.0));
|
|
275
|
+
let sH = hH / totalWeight - eA * mean.rgb;
|
|
276
276
|
|
|
277
277
|
|
|
278
278
|
|
|
279
|
-
let
|
|
280
|
-
let
|
|
281
|
-
let
|
|
282
|
-
let
|
|
283
|
-
let filtered = vec4f(max(
|
|
279
|
+
let gt = bp.rgb + eA * hn;
|
|
280
|
+
let qm = mean.rgb * hn / max(gt, vec3f(${JNLM_ALBEDO_FLOOR}));
|
|
281
|
+
let slope = clamp(sH / (mK + vec3f(${JNLM_ALBEDO_REGULARIZATION})), vec3f(0.0), qm);
|
|
282
|
+
let tY = mean.rgb - slope * eA;
|
|
283
|
+
let filtered = vec4f(max(tY, vec3f(0.0)), max(mean.a, 0.0));
|
|
284
284
|
let rgb = select(
|
|
285
285
|
center.rgb,
|
|
286
286
|
filtered.rgb / max(filtered.a, 1e-6),
|
|
@@ -288,8 +288,8 @@ filtered.a > 1e-6
|
|
|
288
288
|
);
|
|
289
289
|
outColor[index] = vec4f(rgb, filtered.a);
|
|
290
290
|
outGuide[index] = vec2f(
|
|
291
|
-
(
|
|
292
|
-
(
|
|
291
|
+
(rn + mp.x * dG) / totalWeight,
|
|
292
|
+
(gR + mp.y * dG * dG)
|
|
293
293
|
/ (totalWeight * totalWeight)
|
|
294
294
|
);
|
|
295
295
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jnlm.compute.js","sourceRoot":"","sources":["../../../src/renderer/wgsl/jnlm.compute.ts"],"names":[],"mappings":"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","sourcesContent":["import shadowCatcherAccumulationWGSL from './chunks/shadowCatcherAccumulation.wgsl.js';\nimport {linearToSrgbDefine} from './compose.frag.wgsl.js';\nimport {\n JNLM_DEFAULT_COLOR_SENSITIVITY, JNLM_DEFAULT_SHAPE, JNLM_ALBEDO_FLOOR,\n JNLM_ALBEDO_REGULARIZATION, JNLM_COVERAGE_BANDWIDTH, jnlmPassDilation,\n type JNLMFilterShape\n} from '../jnlm.js';\n\n// Web textures and native normalized guide buffers use the same filter body.\nexport type JNLMGuideBindings = 'textures' | 'buffers';\nfunction guideBinding(guides: JNLMGuideBindings, binding: number, name: string): string {\n return guides === 'textures'\n ? `@group(0) @binding(${binding}) var ${name}: texture_2d<f32>;`\n : `@group(0) @binding(${binding}) var<storage, read> ${name}: array<vec4f>;`;\n}\nfunction loadGuide(guides: JNLMGuideBindings, name: string, position: string): string {\n return guides === 'textures' ? `textureLoad(${name}, ${position}, 0)` : `${name}[pixelIndex(${position})]`;\n}\n\n// Shared uniform packing also fixes sensitivity and dilation for native callers.\nexport function packJNLMParameters(width: number, height: number, strength: number, pass: number): ArrayBuffer {\n const bytes = new ArrayBuffer(16);\n new Uint32Array(bytes, 0, 2).set([width, height]);\n new Float32Array(bytes, 8, 2).set([\n JNLM_DEFAULT_COLOR_SENSITIVITY * Math.max(strength, 1e-3), jnlmPassDilation(pass)\n ]);\n return bytes;\n}\n\nexport const JNLM_WORKGROUP_SIZE = 8;\n\nconst paramsWGSL = /* wgsl */ `\nstruct JNLMParams {\n size: vec2u,\n colorSensitivity: f32,\n // Pixel spacing of the search window and patches in this pass.\n dilation: f32,\n}\n@group(0) @binding(0) var<uniform> params: JNLMParams;\n`;\n\n/**\n * Pass 1: the guide mean and the variance of that mean from the accumulated\n * moments. Ray buffers are stored bottom-up; guides use texture rows.\n */\nexport const jnlmGuideWGSL = /* wgsl */ `\n${paramsWGSL}\n${shadowCatcherAccumulationWGSL}\n@group(0) @binding(1) var<storage, read> accumulation: array<ShadowCatcherAccumulation>;\n@group(0) @binding(2) var<storage, read> normalBuffer: array<vec4f>;\n@group(0) @binding(3) var<storage, read_write> rawGuide: array<vec2f>;\n\n@compute @workgroup_size(${JNLM_WORKGROUP_SIZE}, ${JNLM_WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n let size = params.size;\n if (id.x >= size.x || id.y >= size.y) { return; }\n let rayIndex = id.x + (size.y - 1u - id.y) * size.x;\n let n = normalBuffer[rayIndex].a;\n let moments = accumulation[rayIndex].moments;\n let mean = moments.x / max(n, 1.0);\n // Variance of the mean, or -1 when one sample cannot estimate it.\n var variance = -1.0;\n if (n >= 2.0) {\n variance = max(moments.y - n * mean * mean, 0.0) / (n * (n - 1.0));\n }\n rawGuide[id.x + id.y * size.x] = vec2f(mean, variance);\n}\n`;\n\n/**\n * Pass 2: smooth the noisy per-pixel variance over 3 x 3 pixels. Pixels with\n * a single sample take the spatial variance of their neighborhood instead.\n */\nexport const jnlmVarianceWGSL = /* wgsl */ `\n${paramsWGSL}\n@group(0) @binding(1) var<storage, read> rawGuide: array<vec2f>;\n@group(0) @binding(2) var<storage, read_write> guide: array<vec2f>;\n\n@compute @workgroup_size(${JNLM_WORKGROUP_SIZE}, ${JNLM_WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n let size = params.size;\n if (id.x >= size.x || id.y >= size.y) { return; }\n let center = rawGuide[id.x + id.y * size.x];\n var varianceSum = 0.0;\n var varianceCount = 0.0;\n var meanSum = 0.0;\n var meanSquareSum = 0.0;\n for (var dy = -1; dy <= 1; dy++) {\n for (var dx = -1; dx <= 1; dx++) {\n let q = clamp(vec2i(id.xy) + vec2i(dx, dy), vec2i(0), vec2i(size) - 1);\n let value = rawGuide[u32(q.x) + u32(q.y) * size.x];\n meanSum += value.x;\n meanSquareSum += value.x * value.x;\n if (value.y >= 0.0) {\n varianceSum += value.y;\n varianceCount += 1.0;\n }\n }\n }\n var variance: f32;\n if (center.y >= 0.0) {\n variance = varianceSum / varianceCount;\n } else {\n let spatialMean = meanSum / 9.0;\n variance = max(meanSquareSum / 9.0 - spatialMean * spatialMean, 0.0) * 9.0 / 8.0;\n }\n guide[id.x + id.y * size.x] = vec2f(center.x, variance);\n}\n`;\n\n/** Copies the composed image into the filter's color buffer. */\nexport function createJNLMLoadColorWGSL(guides: JNLMGuideBindings = 'textures'): string {\n return /* wgsl */ `\n${paramsWGSL}\n@group(0) @binding(1) var colorTex: texture_2d<f32>;\n@group(0) @binding(2) var<storage, read_write> outColor: array<vec4f>;\n${guides === 'buffers' ? `${shadowCatcherAccumulationWGSL}\n@group(0) @binding(3) var<storage, read> normalAccumulation: array<vec4f>;\n@group(0) @binding(4) var<storage, read_write> normal: array<vec4f>;\n@group(0) @binding(5) var<storage, read> shadowAccumulation: array<ShadowCatcherAccumulation>;\n@group(0) @binding(6) var<storage, read_write> shadow: array<vec4f>;\n@group(0) @binding(7) var albedoTex: texture_2d<f32>;\n@group(0) @binding(8) var<storage, read_write> albedo: array<vec4f>;` : ''}\n\n@compute @workgroup_size(${JNLM_WORKGROUP_SIZE}, ${JNLM_WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n if (id.x >= params.size.x || id.y >= params.size.y) { return; }\n outColor[id.x + id.y * params.size.x] = textureLoad(colorTex, vec2i(id.xy), 0);\n${guides === 'buffers' ? ` let index = id.x + id.y * params.size.x;\n let rayIndex = id.x + (params.size.y - 1u - id.y) * params.size.x;\n let n = normalAccumulation[rayIndex];\n let samples = max(n.a, 1.0);\n normal[index] = vec4f(n.rgb / samples * 0.5 + 0.5, select(0.0, 1.0, dot(n.rgb, n.rgb) > 0.1));\n shadow[index] = shadowAccumulation[rayIndex].mask / samples;\n albedo[index] = textureLoad(albedoTex, vec2i(id.xy), 0);` : ''}\n}\n`;\n}\nexport const jnlmLoadColorWGSL = createJNLMLoadColorWGSL();\n\n/**\n * Pass 3: the joint NL-means filter of the composed image. It also filters\n * the guide with the same weights, with the variance of the weighted mean,\n * for a following pass. The albedo regression corrects color only, keeping\n * uncertain fitted texture responses out of the matching guide.\n */\nexport function createJNLMFilterWGSL({\n searchRadius, patchRadius, albedoBandwidth, normalBandwidth\n}: JNLMFilterShape = JNLM_DEFAULT_SHAPE, guides: JNLMGuideBindings = 'textures') {\n const patchSize = (2 * patchRadius + 1) ** 2;\n return /* wgsl */ `\n${paramsWGSL}\n@group(0) @binding(1) var<storage, read> guide: array<vec2f>;\n@group(0) @binding(2) var<storage, read> color: array<vec4f>;\n${guideBinding(guides, 3, 'albedoTex')}\n${guideBinding(guides, 4, 'normalTex')}\n@group(0) @binding(5) var<storage, read_write> outColor: array<vec4f>;\n@group(0) @binding(6) var<storage, read_write> outGuide: array<vec2f>;\n${guideBinding(guides, 7, 'shadowCatcherTex')}\n\nconst SEARCH_RADIUS = ${searchRadius};\nconst PATCH_RADIUS = ${patchRadius};\nconst PATCH_SIZE = ${patchSize};\n// Variance cancellation strength alpha of the paper.\nconst VARIANCE_CANCELLATION = 1.0;\n// Inverse squared feature bandwidths. Normals are encoded as n * 0.5 + 0.5.\nconst INV_ALBEDO_BANDWIDTH2 = ${(1 / albedoBandwidth ** 2).toFixed(6)};\nconst INV_COVERAGE_BANDWIDTH2 = ${(1 / JNLM_COVERAGE_BANDWIDTH ** 2).toFixed(6)};\nconst INV_NORMAL_BANDWIDTH2 = ${(1 / normalBandwidth ** 2).toFixed(6)};\n// Lower bound of the center weight, so a pixel unlike all of its neighbors\n// (a thin highlight) is kept rather than replaced by them.\nconst CENTER_WEIGHT_MIN = 0.01;\n\nfn pixelIndex(p: vec2i) -> u32 {\n let q = clamp(p, vec2i(0), vec2i(params.size) - 1);\n return u32(q.x) + u32(q.y) * params.size.x;\n}\n\nfn featureDistance(q: vec2i, centerAlbedo: vec4f, centerNormal: vec3f) -> f32 {\n let albedo = ${loadGuide(guides, 'albedoTex', 'q')};\n // Absolute differences in linear albedo erase low-contrast dark textures.\n // Measure RGB contrast relative to both pixels; keep coverage absolute.\n let albedoScale = max((albedo.rgb + centerAlbedo.rgb) * 0.5, vec3f(${JNLM_ALBEDO_FLOOR}));\n let albedoDelta = (albedo.rgb - centerAlbedo.rgb) / albedoScale;\n let coverageDelta = albedo.a - centerAlbedo.a;\n let normalDelta = ${loadGuide(guides, 'normalTex', 'q')}.rgb - centerNormal;\n return max(\n dot(albedoDelta, albedoDelta) * INV_ALBEDO_BANDWIDTH2\n + coverageDelta * coverageDelta * INV_COVERAGE_BANDWIDTH2,\n dot(normalDelta, normalDelta) * INV_NORMAL_BANDWIDTH2\n );\n}\n\nfn premultiplied(c: vec4f) -> vec4f {\n return vec4f(c.rgb * c.a, c.a);\n}\n\n@compute @workgroup_size(${JNLM_WORKGROUP_SIZE}, ${JNLM_WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n let size = vec2i(params.size);\n let p = vec2i(id.xy);\n if (p.x >= size.x || p.y >= size.y) { return; }\n let step = i32(params.dilation);\n let centerAlbedo = ${loadGuide(guides, 'albedoTex', 'p')};\n let centerNormalTexel = ${loadGuide(guides, 'normalTex', 'p')};\n let centerNormal = centerNormalTexel.rgb;\n let index = pixelIndex(p);\n // Primary misses show the background directly. It has no features that\n // protect its detail and no noise beyond antialiasing, so keep it.\n if (centerNormalTexel.a <= 0.0 && ${loadGuide(guides, 'shadowCatcherTex', 'p')}.r <= 0.0) {\n outColor[index] = color[index];\n outGuide[index] = guide[index];\n return;\n }\n\n var centerPatch: array<vec2f, PATCH_SIZE>;\n var k = 0;\n for (var dy = -PATCH_RADIUS; dy <= PATCH_RADIUS; dy++) {\n for (var dx = -PATCH_RADIUS; dx <= PATCH_RADIUS; dx++) {\n centerPatch[k] = guide[pixelIndex(p + vec2i(dx, dy) * step)];\n k++;\n }\n }\n let k2 = params.colorSensitivity * params.colorSensitivity;\n\n var sum = vec4f(0.0);\n let featureScale = max(centerAlbedo.rgb, vec3f(${JNLM_ALBEDO_FLOOR}));\n var featureSum = vec3f(0.0);\n var featureSquareSum = vec3f(0.0);\n var featureColorSum = vec3f(0.0);\n var guideSum = 0.0;\n var guideVarianceSum = 0.0;\n var weightSum = 0.0;\n var maxWeight = 0.0;\n for (var sy = -SEARCH_RADIUS; sy <= SEARCH_RADIUS; sy++) {\n for (var sx = -SEARCH_RADIUS; sx <= SEARCH_RADIUS; sx++) {\n let q = p + vec2i(sx, sy) * step;\n if (\n (sx == 0 && sy == 0) ||\n q.x < 0 || q.y < 0 || q.x >= size.x || q.y >= size.y\n ) { continue; }\n\n let features = featureDistance(q, centerAlbedo, centerNormal);\n if (features > 16.0) { continue; }\n\n var colorDistance = 0.0;\n k = 0;\n for (var dy = -PATCH_RADIUS; dy <= PATCH_RADIUS; dy++) {\n for (var dx = -PATCH_RADIUS; dx <= PATCH_RADIUS; dx++) {\n let a = centerPatch[k];\n let b = guide[pixelIndex(q + vec2i(dx, dy) * step)];\n let delta = a.x - b.x;\n colorDistance += (delta * delta\n - VARIANCE_CANCELLATION * (a.y + min(a.y, b.y)))\n / (1e-10 + k2 * (a.y + b.y));\n k++;\n }\n }\n colorDistance /= f32(PATCH_SIZE);\n\n let weight = exp(-max(max(colorDistance, 0.0), features));\n let index = pixelIndex(q);\n let c = color[index];\n let g = guide[index];\n // Filter premultiplied color so coverage and color stay consistent.\n let a = (${loadGuide(guides, 'albedoTex', 'q')}.rgb - centerAlbedo.rgb) / featureScale;\n let value = premultiplied(c);\n sum += value * weight;\n featureSum += a * weight;\n featureSquareSum += a * a * weight;\n featureColorSum += a * value.rgb * weight;\n guideSum += g.x * weight;\n guideVarianceSum += g.y * weight * weight;\n weightSum += weight;\n maxWeight = max(maxWeight, weight);\n }\n }\n\n // Weight the center like its most similar neighbor (Buades et al. 2005)\n // instead of 1, so isolated outliers are not kept unfiltered.\n let center = color[index];\n let centerGuide = guide[index];\n let centerWeight = max(maxWeight, CENTER_WEIGHT_MIN);\n let totalWeight = weightSum + centerWeight;\n let centerValue = premultiplied(center);\n let mean = (sum + centerValue * centerWeight) / totalWeight;\n // Fit each color channel against relative albedo with the same JNLM weights.\n // Centering at this pixel avoids cancellation when the feature is constant.\n let meanFeature = featureSum / totalWeight;\n let featureVariance = max(featureSquareSum / totalWeight - meanFeature * meanFeature, vec3f(0.0));\n let covariance = featureColorSum / totalWeight - meanFeature * mean.rgb;\n // A 1% contrast ridge stabilizes flat guides. Bound the response between no\n // texture dependence and multiplicative albedo modulation; specular color\n // with an unrelated albedo falls back to the weighted mean.\n let meanAlbedo = centerAlbedo.rgb + meanFeature * featureScale;\n let maximumSlope = mean.rgb * featureScale / max(meanAlbedo, vec3f(${JNLM_ALBEDO_FLOOR}));\n let slope = clamp(covariance / (featureVariance + vec3f(${JNLM_ALBEDO_REGULARIZATION})), vec3f(0.0), maximumSlope);\n let corrected = mean.rgb - slope * meanFeature;\n let filtered = vec4f(max(corrected, vec3f(0.0)), max(mean.a, 0.0));\n let rgb = select(\n center.rgb,\n filtered.rgb / max(filtered.a, 1e-6),\n filtered.a > 1e-6\n );\n outColor[index] = vec4f(rgb, filtered.a);\n outGuide[index] = vec2f(\n (guideSum + centerGuide.x * centerWeight) / totalWeight,\n (guideVarianceSum + centerGuide.y * centerWeight * centerWeight)\n / (totalWeight * totalWeight)\n );\n}\n`;\n}\n\n// Compute presentation for hosts that consume the filtered buffer directly.\n// Use the same sRGB transfer as browser display composition.\nexport const jnlmEncodeWGSL = /* wgsl */ `\nstruct OutputParams { size: vec2u, premultiply: u32, reserved: u32 }\n@group(0) @binding(0) var<uniform> params: OutputParams;\n@group(0) @binding(1) var<storage, read> color: array<vec4f>;\n@group(0) @binding(2) var output: texture_storage_2d<rgba8unorm, write>;\n${linearToSrgbDefine}\n@compute @workgroup_size(${JNLM_WORKGROUP_SIZE}, ${JNLM_WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n if (any(id.xy >= params.size)) { return; }\n let c = color[id.y * params.size.x + id.x];\n var rgb = linearToSrgb(c.rgb);\n if (params.premultiply != 0u) { rgb *= c.a; }\n textureStore(output, id.xy, vec4f(rgb, c.a));\n}\n`;\n"]}
|
|
1
|
+
{"version":3,"file":"jnlm.compute.js","sourceRoot":"","sources":["../../../src/renderer/wgsl/jnlm.compute.ts"],"names":[],"mappings":"AAAA,OAAO,6BAA6B,MAAM,4CAA4C,CAAC;AACvF,OAAO,EAAC,kBAAkB,EAAC,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EACL,8BAA8B,EAAE,kBAAkB,EAAE,iBAAiB,EACrE,0BAA0B,EAAE,uBAAuB,EAAE,gBAAgB,EAEtE,MAAM,YAAY,CAAC;AAIpB,SAAS,YAAY,CAAC,MAAyB,EAAE,OAAe,EAAE,IAAY;IAC5E,OAAO,MAAM,KAAK,UAAU;QAC1B,CAAC,CAAC,sBAAsB,OAAO,SAAS,IAAI,oBAAoB;QAChE,CAAC,CAAC,sBAAsB,OAAO,wBAAwB,IAAI,iBAAiB,CAAC;AACjF,CAAC;AACD,SAAS,SAAS,CAAC,MAAyB,EAAE,IAAY,EAAE,QAAgB;IAC1E,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,eAAe,IAAI,KAAK,QAAQ,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,OAAe,QAAQ,IAAI,CAAC;AAC7G,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,kBAAkB,CAAC,KAAa,EAAE,MAAc,EAAE,QAAgB,EAAE,IAAY;IAC9F,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC;IAClC,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,IAAI,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;QAChC,8BAA8B,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,gBAAgB,CAAC,IAAI,CAAC;KAClF,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,MAAM,UAAU,GAAG,UAAU,CAAC;;;;;;;;CAQ7B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC;EACtC,UAAU;EACV,6BAA6B;;;;;2BAKJ,mBAAmB,KAAK,mBAAmB;;;;;;;;;;;;;;;CAerE,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;EACzC,UAAU;;;;2BAIe,mBAAmB,KAAK,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BrE,CAAC;AAEF,gEAAgE;AAChE,MAAM,UAAU,uBAAuB,CAAC,SAA4B,UAAU;IAC5E,OAAO,UAAU,CAAC;EAClB,UAAU;;;EAGV,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,6BAA6B;;;;;;qEAMY,CAAC,CAAC,CAAC,EAAE;;2BAE/C,mBAAmB,KAAK,mBAAmB;;;;EAIpE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC;;;;;;yDAMkC,CAAC,CAAC,CAAC,EAAE;;CAE/D,CAAC;AACF,CAAC;AACD,MAAM,CAAC,MAAM,iBAAiB,GAAG,uBAAuB,EAAE,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,EACnC,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,KACxC,kBAAkB,EAAE,SAA4B,UAAU;IAC7E,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,UAAU,CAAC;EAClB,UAAU;;;EAGV,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,WAAW,CAAC;EACpC,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,WAAW,CAAC;;;EAGpC,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,kBAAkB,CAAC;;wBAErB,YAAY;uBACb,WAAW;qBACb,SAAS;;;;gCAIE,CAAC,CAAC,GAAG,eAAe,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;kCACnC,CAAC,CAAC,GAAG,uBAAuB,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;gCAC/C,CAAC,CAAC,GAAG,eAAe,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;;;;;;;;;;;eAWpD,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC;;;kDAGmB,iBAAiB;;;WAGlE,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC;;;;;;;;;;;;2BAY9B,mBAAmB,KAAK,mBAAmB;;;;;;WAM/C,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC;WAC9B,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC;;;;;qBAKzB,SAAS,CAAC,MAAM,EAAE,kBAAkB,EAAE,GAAG,CAAC;;;;;;;;;;;;;;;;;6BAiB7B,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAuCnD,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yCA8BmB,iBAAiB;qCAC5B,0BAA0B;;;;;;;;;;;;;;;CAerF,CAAC;AACF,CAAC;AAED,4EAA4E;AAC5E,6DAA6D;AAC7D,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;;;;;EAKvC,kBAAkB;2BACO,mBAAmB,KAAK,mBAAmB;;;;;;;;CAQrE,CAAC","sourcesContent":["import shadowCatcherAccumulationWGSL from './chunks/shadowCatcherAccumulation.wgsl.js';\nimport {linearToSrgbDefine} from './compose.frag.wgsl.js';\nimport {\n JNLM_DEFAULT_COLOR_SENSITIVITY, JNLM_DEFAULT_SHAPE, JNLM_ALBEDO_FLOOR,\n JNLM_ALBEDO_REGULARIZATION, JNLM_COVERAGE_BANDWIDTH, jnlmPassDilation,\n type JNLMFilterShape\n} from '../jnlm.js';\n\n// Web textures and native normalized guide buffers use the same filter body.\nexport type JNLMGuideBindings = 'textures' | 'buffers';\nfunction guideBinding(guides: JNLMGuideBindings, binding: number, name: string): string {\n return guides === 'textures'\n ? `@group(0) @binding(${binding}) var ${name}: texture_2d<f32>;`\n : `@group(0) @binding(${binding}) var<storage, read> ${name}: array<vec4f>;`;\n}\nfunction loadGuide(guides: JNLMGuideBindings, name: string, position: string): string {\n return guides === 'textures' ? `textureLoad(${name}, ${position}, 0)` : `${name}[pixelIndex(${position})]`;\n}\n\n// Shared uniform packing also fixes sensitivity and dilation for native callers.\nexport function packJNLMParameters(width: number, height: number, strength: number, pass: number): ArrayBuffer {\n const bytes = new ArrayBuffer(16);\n new Uint32Array(bytes, 0, 2).set([width, height]);\n new Float32Array(bytes, 8, 2).set([\n JNLM_DEFAULT_COLOR_SENSITIVITY * Math.max(strength, 1e-3), jnlmPassDilation(pass)\n ]);\n return bytes;\n}\n\nexport const JNLM_WORKGROUP_SIZE = 8;\n\nconst paramsWGSL = /* wgsl */ `\nstruct JNLMParams {\n size: vec2u,\n colorSensitivity: f32,\n // Pixel spacing of the search window and patches in this pass.\n dilation: f32,\n}\n@group(0) @binding(0) var<uniform> params: JNLMParams;\n`;\n\n/**\n * Pass 1: the guide mean and the variance of that mean from the accumulated\n * moments. Ray buffers are stored bottom-up; guides use texture rows.\n */\nexport const jnlmGuideWGSL = /* wgsl */ `\n${paramsWGSL}\n${shadowCatcherAccumulationWGSL}\n@group(0) @binding(1) var<storage, read> accumulation: array<ShadowCatcherAccumulation>;\n@group(0) @binding(2) var<storage, read> normalBuffer: array<vec4f>;\n@group(0) @binding(3) var<storage, read_write> rawGuide: array<vec2f>;\n\n@compute @workgroup_size(${JNLM_WORKGROUP_SIZE}, ${JNLM_WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n let size = params.size;\n if (id.x >= size.x || id.y >= size.y) { return; }\n let rayIndex = id.x + (size.y - 1u - id.y) * size.x;\n let n = normalBuffer[rayIndex].a;\n let moments = accumulation[rayIndex].moments;\n let mean = moments.x / max(n, 1.0);\n // Variance of the mean, or -1 when one sample cannot estimate it.\n var variance = -1.0;\n if (n >= 2.0) {\n variance = max(moments.y - n * mean * mean, 0.0) / (n * (n - 1.0));\n }\n rawGuide[id.x + id.y * size.x] = vec2f(mean, variance);\n}\n`;\n\n/**\n * Pass 2: smooth the noisy per-pixel variance over 3 x 3 pixels. Pixels with\n * a single sample take the spatial variance of their neighborhood instead.\n */\nexport const jnlmVarianceWGSL = /* wgsl */ `\n${paramsWGSL}\n@group(0) @binding(1) var<storage, read> rawGuide: array<vec2f>;\n@group(0) @binding(2) var<storage, read_write> guide: array<vec2f>;\n\n@compute @workgroup_size(${JNLM_WORKGROUP_SIZE}, ${JNLM_WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n let size = params.size;\n if (id.x >= size.x || id.y >= size.y) { return; }\n let center = rawGuide[id.x + id.y * size.x];\n var varianceSum = 0.0;\n var varianceCount = 0.0;\n var meanSum = 0.0;\n var meanSquareSum = 0.0;\n for (var dy = -1; dy <= 1; dy++) {\n for (var dx = -1; dx <= 1; dx++) {\n let q = clamp(vec2i(id.xy) + vec2i(dx, dy), vec2i(0), vec2i(size) - 1);\n let value = rawGuide[u32(q.x) + u32(q.y) * size.x];\n meanSum += value.x;\n meanSquareSum += value.x * value.x;\n if (value.y >= 0.0) {\n varianceSum += value.y;\n varianceCount += 1.0;\n }\n }\n }\n var variance: f32;\n if (center.y >= 0.0) {\n variance = varianceSum / varianceCount;\n } else {\n let spatialMean = meanSum / 9.0;\n variance = max(meanSquareSum / 9.0 - spatialMean * spatialMean, 0.0) * 9.0 / 8.0;\n }\n guide[id.x + id.y * size.x] = vec2f(center.x, variance);\n}\n`;\n\n/** Copies the composed image into the filter's color buffer. */\nexport function createJNLMLoadColorWGSL(guides: JNLMGuideBindings = 'textures'): string {\n return /* wgsl */ `\n${paramsWGSL}\n@group(0) @binding(1) var colorTex: texture_2d<f32>;\n@group(0) @binding(2) var<storage, read_write> outColor: array<vec4f>;\n${guides === 'buffers' ? `${shadowCatcherAccumulationWGSL}\n@group(0) @binding(3) var<storage, read> normalAccumulation: array<vec4f>;\n@group(0) @binding(4) var<storage, read_write> normal: array<vec4f>;\n@group(0) @binding(5) var<storage, read> shadowAccumulation: array<ShadowCatcherAccumulation>;\n@group(0) @binding(6) var<storage, read_write> shadow: array<vec4f>;\n@group(0) @binding(7) var albedoTex: texture_2d<f32>;\n@group(0) @binding(8) var<storage, read_write> albedo: array<vec4f>;` : ''}\n\n@compute @workgroup_size(${JNLM_WORKGROUP_SIZE}, ${JNLM_WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n if (id.x >= params.size.x || id.y >= params.size.y) { return; }\n outColor[id.x + id.y * params.size.x] = textureLoad(colorTex, vec2i(id.xy), 0);\n${guides === 'buffers' ? ` let index = id.x + id.y * params.size.x;\n let rayIndex = id.x + (params.size.y - 1u - id.y) * params.size.x;\n let n = normalAccumulation[rayIndex];\n let samples = max(n.a, 1.0);\n normal[index] = vec4f(n.rgb / samples * 0.5 + 0.5, select(0.0, 1.0, dot(n.rgb, n.rgb) > 0.1));\n shadow[index] = shadowAccumulation[rayIndex].mask / samples;\n albedo[index] = textureLoad(albedoTex, vec2i(id.xy), 0);` : ''}\n}\n`;\n}\nexport const jnlmLoadColorWGSL = createJNLMLoadColorWGSL();\n\n/**\n * Pass 3: the joint NL-means filter of the composed image. It also filters\n * the guide with the same weights, with the variance of the weighted mean,\n * for a following pass. The albedo regression corrects color only, keeping\n * uncertain fitted texture responses out of the matching guide.\n */\nexport function createJNLMFilterWGSL({\n searchRadius, patchRadius, albedoBandwidth, normalBandwidth\n}: JNLMFilterShape = JNLM_DEFAULT_SHAPE, guides: JNLMGuideBindings = 'textures') {\n const patchSize = (2 * patchRadius + 1) ** 2;\n return /* wgsl */ `\n${paramsWGSL}\n@group(0) @binding(1) var<storage, read> guide: array<vec2f>;\n@group(0) @binding(2) var<storage, read> color: array<vec4f>;\n${guideBinding(guides, 3, 'albedoTex')}\n${guideBinding(guides, 4, 'normalTex')}\n@group(0) @binding(5) var<storage, read_write> outColor: array<vec4f>;\n@group(0) @binding(6) var<storage, read_write> outGuide: array<vec2f>;\n${guideBinding(guides, 7, 'shadowCatcherTex')}\n\nconst SEARCH_RADIUS = ${searchRadius};\nconst PATCH_RADIUS = ${patchRadius};\nconst PATCH_SIZE = ${patchSize};\n// Variance cancellation strength alpha of the paper.\nconst VARIANCE_CANCELLATION = 1.0;\n// Inverse squared feature bandwidths. Normals are encoded as n * 0.5 + 0.5.\nconst INV_ALBEDO_BANDWIDTH2 = ${(1 / albedoBandwidth ** 2).toFixed(6)};\nconst INV_COVERAGE_BANDWIDTH2 = ${(1 / JNLM_COVERAGE_BANDWIDTH ** 2).toFixed(6)};\nconst INV_NORMAL_BANDWIDTH2 = ${(1 / normalBandwidth ** 2).toFixed(6)};\n// Lower bound of the center weight, so a pixel unlike all of its neighbors\n// (a thin highlight) is kept rather than replaced by them.\nconst CENTER_WEIGHT_MIN = 0.01;\n\nfn pixelIndex(p: vec2i) -> u32 {\n let q = clamp(p, vec2i(0), vec2i(params.size) - 1);\n return u32(q.x) + u32(q.y) * params.size.x;\n}\n\nfn featureDistance(q: vec2i, centerAlbedo: vec4f, centerNormal: vec3f) -> f32 {\n let albedo = ${loadGuide(guides, 'albedoTex', 'q')};\n // Absolute differences in linear albedo erase low-contrast dark textures.\n // Measure RGB contrast relative to both pixels; keep coverage absolute.\n let albedoScale = max((albedo.rgb + centerAlbedo.rgb) * 0.5, vec3f(${JNLM_ALBEDO_FLOOR}));\n let albedoDelta = (albedo.rgb - centerAlbedo.rgb) / albedoScale;\n let coverageDelta = albedo.a - centerAlbedo.a;\n let normalDelta = ${loadGuide(guides, 'normalTex', 'q')}.rgb - centerNormal;\n return max(\n dot(albedoDelta, albedoDelta) * INV_ALBEDO_BANDWIDTH2\n + coverageDelta * coverageDelta * INV_COVERAGE_BANDWIDTH2,\n dot(normalDelta, normalDelta) * INV_NORMAL_BANDWIDTH2\n );\n}\n\nfn premultiplied(c: vec4f) -> vec4f {\n return vec4f(c.rgb * c.a, c.a);\n}\n\n@compute @workgroup_size(${JNLM_WORKGROUP_SIZE}, ${JNLM_WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n let size = vec2i(params.size);\n let p = vec2i(id.xy);\n if (p.x >= size.x || p.y >= size.y) { return; }\n let step = i32(params.dilation);\n let centerAlbedo = ${loadGuide(guides, 'albedoTex', 'p')};\n let centerNormalTexel = ${loadGuide(guides, 'normalTex', 'p')};\n let centerNormal = centerNormalTexel.rgb;\n let index = pixelIndex(p);\n // Primary misses show the background directly. It has no features that\n // protect its detail and no noise beyond antialiasing, so keep it.\n if (centerNormalTexel.a <= 0.0 && ${loadGuide(guides, 'shadowCatcherTex', 'p')}.r <= 0.0) {\n outColor[index] = color[index];\n outGuide[index] = guide[index];\n return;\n }\n\n var centerPatch: array<vec2f, PATCH_SIZE>;\n var k = 0;\n for (var dy = -PATCH_RADIUS; dy <= PATCH_RADIUS; dy++) {\n for (var dx = -PATCH_RADIUS; dx <= PATCH_RADIUS; dx++) {\n centerPatch[k] = guide[pixelIndex(p + vec2i(dx, dy) * step)];\n k++;\n }\n }\n let k2 = params.colorSensitivity * params.colorSensitivity;\n\n var sum = vec4f(0.0);\n let featureScale = max(centerAlbedo.rgb, vec3f(${JNLM_ALBEDO_FLOOR}));\n var featureSum = vec3f(0.0);\n var featureSquareSum = vec3f(0.0);\n var featureColorSum = vec3f(0.0);\n var guideSum = 0.0;\n var guideVarianceSum = 0.0;\n var weightSum = 0.0;\n var maxWeight = 0.0;\n for (var sy = -SEARCH_RADIUS; sy <= SEARCH_RADIUS; sy++) {\n for (var sx = -SEARCH_RADIUS; sx <= SEARCH_RADIUS; sx++) {\n let q = p + vec2i(sx, sy) * step;\n if (\n (sx == 0 && sy == 0) ||\n q.x < 0 || q.y < 0 || q.x >= size.x || q.y >= size.y\n ) { continue; }\n\n let features = featureDistance(q, centerAlbedo, centerNormal);\n if (features > 16.0) { continue; }\n\n var colorDistance = 0.0;\n k = 0;\n for (var dy = -PATCH_RADIUS; dy <= PATCH_RADIUS; dy++) {\n for (var dx = -PATCH_RADIUS; dx <= PATCH_RADIUS; dx++) {\n let a = centerPatch[k];\n let b = guide[pixelIndex(q + vec2i(dx, dy) * step)];\n let delta = a.x - b.x;\n colorDistance += (delta * delta\n - VARIANCE_CANCELLATION * (a.y + min(a.y, b.y)))\n / (1e-10 + k2 * (a.y + b.y));\n k++;\n }\n }\n colorDistance /= f32(PATCH_SIZE);\n\n let weight = exp(-max(max(colorDistance, 0.0), features));\n let index = pixelIndex(q);\n let c = color[index];\n let g = guide[index];\n // Filter premultiplied color so coverage and color stay consistent.\n let a = (${loadGuide(guides, 'albedoTex', 'q')}.rgb - centerAlbedo.rgb) / featureScale;\n let value = premultiplied(c);\n sum += value * weight;\n featureSum += a * weight;\n featureSquareSum += a * a * weight;\n featureColorSum += a * value.rgb * weight;\n guideSum += g.x * weight;\n guideVarianceSum += g.y * weight * weight;\n weightSum += weight;\n maxWeight = max(maxWeight, weight);\n }\n }\n\n // Weight the center like its most similar neighbor (Buades et al. 2005)\n // instead of 1, so isolated outliers are not kept unfiltered.\n let center = color[index];\n let centerGuide = guide[index];\n let centerWeight = max(maxWeight, CENTER_WEIGHT_MIN);\n let totalWeight = weightSum + centerWeight;\n let centerValue = premultiplied(center);\n let mean = (sum + centerValue * centerWeight) / totalWeight;\n // Fit each color channel against relative albedo with the same JNLM weights.\n // Centering at this pixel avoids cancellation when the feature is constant.\n let meanFeature = featureSum / totalWeight;\n let featureVariance = max(featureSquareSum / totalWeight - meanFeature * meanFeature, vec3f(0.0));\n let covariance = featureColorSum / totalWeight - meanFeature * mean.rgb;\n // A 1% contrast ridge stabilizes flat guides. Bound the response between no\n // texture dependence and multiplicative albedo modulation; specular color\n // with an unrelated albedo falls back to the weighted mean.\n let meanAlbedo = centerAlbedo.rgb + meanFeature * featureScale;\n let maximumSlope = mean.rgb * featureScale / max(meanAlbedo, vec3f(${JNLM_ALBEDO_FLOOR}));\n let slope = clamp(covariance / (featureVariance + vec3f(${JNLM_ALBEDO_REGULARIZATION})), vec3f(0.0), maximumSlope);\n let corrected = mean.rgb - slope * meanFeature;\n let filtered = vec4f(max(corrected, vec3f(0.0)), max(mean.a, 0.0));\n let rgb = select(\n center.rgb,\n filtered.rgb / max(filtered.a, 1e-6),\n filtered.a > 1e-6\n );\n outColor[index] = vec4f(rgb, filtered.a);\n outGuide[index] = vec2f(\n (guideSum + centerGuide.x * centerWeight) / totalWeight,\n (guideVarianceSum + centerGuide.y * centerWeight * centerWeight)\n / (totalWeight * totalWeight)\n );\n}\n`;\n}\n\n// Compute presentation for hosts that consume the filtered buffer directly.\n// Use the same sRGB transfer as browser display composition.\nexport const jnlmEncodeWGSL = /* wgsl */ `\nstruct OutputParams { size: vec2u, premultiply: u32, reserved: u32 }\n@group(0) @binding(0) var<uniform> params: OutputParams;\n@group(0) @binding(1) var<storage, read> color: array<vec4f>;\n@group(0) @binding(2) var output: texture_storage_2d<rgba8unorm, write>;\n${linearToSrgbDefine}\n@compute @workgroup_size(${JNLM_WORKGROUP_SIZE}, ${JNLM_WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) id: vec3u) {\n if (any(id.xy >= params.size)) { return; }\n let c = color[id.y * params.size.x + id.x];\n var rgb = linearToSrgb(c.rgb);\n if (params.premultiply != 0u) { rgb *= c.a; }\n textureStore(output, id.xy, vec4f(rgb, c.a));\n}\n`;\n"]}
|
|
@@ -27,8 +27,8 @@ _padding: f32,
|
|
|
27
27
|
const LUMINANCE = vec3f(0.2126, 0.7152, 0.0722);
|
|
28
28
|
|
|
29
29
|
@compute @workgroup_size(64)
|
|
30
|
-
fn main(@builtin(global_invocation_id)
|
|
31
|
-
let tile =
|
|
30
|
+
fn main(@builtin(global_invocation_id) eJ: vec3u) {
|
|
31
|
+
let tile = eJ.x;
|
|
32
32
|
let tileCount = params.columns * params.rows;
|
|
33
33
|
if (tile >= tileCount) {
|
|
34
34
|
return;
|
|
@@ -36,16 +36,16 @@ return;
|
|
|
36
36
|
|
|
37
37
|
let tileX = tile % params.columns;
|
|
38
38
|
let tileY = tile / params.columns;
|
|
39
|
-
let
|
|
40
|
-
let
|
|
41
|
-
let
|
|
42
|
-
let
|
|
43
|
-
var
|
|
44
|
-
var
|
|
39
|
+
let yp = tileX * params.tileSize;
|
|
40
|
+
let yq = tileY * params.tileSize;
|
|
41
|
+
let AD = min(yp + params.tileSize, params.width);
|
|
42
|
+
let AE = min(yq + params.tileSize, params.height);
|
|
43
|
+
var oL = 0.0;
|
|
44
|
+
var jL = 0.0;
|
|
45
45
|
var count = 0u;
|
|
46
46
|
|
|
47
|
-
for (var y =
|
|
48
|
-
for (var x =
|
|
47
|
+
for (var y = yq; y < AE; y++) {
|
|
48
|
+
for (var x = yp; x < AD; x++) {
|
|
49
49
|
let pixel = y * params.width + x;
|
|
50
50
|
let offset = pixel * 4u;
|
|
51
51
|
let samples = pilotMoments[offset + 3u];
|
|
@@ -60,28 +60,28 @@ pilotColor[offset + 2u]
|
|
|
60
60
|
),
|
|
61
61
|
LUMINANCE
|
|
62
62
|
) / samples;
|
|
63
|
-
let
|
|
64
|
-
let
|
|
65
|
-
var variance =
|
|
63
|
+
let wX = pilotMoments[offset] / samples;
|
|
64
|
+
let rj = max(wX - mean * mean, 0.0);
|
|
65
|
+
var variance = rj;
|
|
66
66
|
if (samples > 1.0) {
|
|
67
|
-
variance =
|
|
67
|
+
variance = rj * samples / (samples - 1.0);
|
|
68
68
|
}
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
oL += mean;
|
|
70
|
+
jL = max(jL, variance);
|
|
71
71
|
count += 1u;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
let mean =
|
|
76
|
-
var
|
|
75
|
+
let mean = oL / f32(max(count, 1u));
|
|
76
|
+
var qO = 0.0;
|
|
77
77
|
if (params.hasPrevious != 0u) {
|
|
78
|
-
|
|
78
|
+
qO = previousVariance[tile];
|
|
79
79
|
}
|
|
80
|
-
let
|
|
81
|
-
let
|
|
80
|
+
let uX = max(jL, qO);
|
|
81
|
+
let sf = params.absoluteError + params.relativeError * abs(mean);
|
|
82
82
|
let required = ceil(
|
|
83
|
-
params.confidence * params.confidence *
|
|
84
|
-
max(
|
|
83
|
+
params.confidence * params.confidence * uX /
|
|
84
|
+
max(sf * sf, 1e-16)
|
|
85
85
|
);
|
|
86
86
|
let targetSamples = u32(min(
|
|
87
87
|
f32(params.maxSamples),
|
|
@@ -91,11 +91,11 @@ max(f32(params.minSamples), required)
|
|
|
91
91
|
tileMeans[tile] = mean;
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
tileVariances[tile] =
|
|
94
|
+
tileVariances[tile] = jL;
|
|
95
95
|
tileTargets[tile] = targetSamples;
|
|
96
96
|
|
|
97
|
-
for (var y =
|
|
98
|
-
for (var x =
|
|
97
|
+
for (var y = yq; y < AE; y++) {
|
|
98
|
+
for (var x = yp; x < AD; x++) {
|
|
99
99
|
textureStore(
|
|
100
100
|
targetTexture,
|
|
101
101
|
vec2i(i32(x), i32(y)),
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
// OIDN's LDR auxiliary networks use sRGB albedo and normals in [0,1].
|
|
2
2
|
export const oidnAuxiliaryDefine = /* wgsl */ `
|
|
3
|
-
fn
|
|
3
|
+
fn uq(value: vec3f) -> vec3f {
|
|
4
4
|
let linear = clamp(value, vec3f(0.0), vec3f(1.0));
|
|
5
5
|
return select(1.055 * pow(linear, vec3f(1.0 / 2.4)) - 0.055,
|
|
6
6
|
12.92 * linear, linear <= vec3f(0.0031308));
|
|
7
7
|
}
|
|
8
|
-
fn
|
|
8
|
+
fn uo(value: vec3f) -> vec3f {
|
|
9
9
|
let encoded = clamp(value, vec3f(0.0), vec3f(1.0));
|
|
10
10
|
return select(pow((encoded + 0.055) / 1.055, vec3f(2.4)),
|
|
11
11
|
encoded / 12.92, encoded <= vec3f(0.04045));
|
|
12
12
|
}
|
|
13
|
-
fn
|
|
13
|
+
fn sW(value: vec3f) -> vec3f {
|
|
14
14
|
return clamp(value, vec3f(0.0), vec3f(1.0));
|
|
15
15
|
}
|
|
16
16
|
`;
|