@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.
Files changed (304) hide show
  1. package/CHANGELOG.md +222 -0
  2. package/LICENSES/FidelityFX-FSR.txt +19 -0
  3. package/LICENSES/pbrt-v4-Apache-2.0.txt +202 -0
  4. package/THIRD_PARTY_NOTICES.md +18 -4
  5. package/dist/assets/bvh.worker.js +1 -1
  6. package/dist/mutracer.umd.js +6260 -5580
  7. package/docs/api.md +45 -18
  8. package/docs/materials.md +4 -2
  9. package/docs/performance.md +7 -0
  10. package/docs/post-processing.md +62 -9
  11. package/docs/progressive-rendering.md +3 -0
  12. package/docs/sampling.md +4 -0
  13. package/docs/technical/acceleration.md +52 -0
  14. package/docs/technical/aovs.md +79 -0
  15. package/docs/technical/caustics.md +109 -0
  16. package/docs/technical/continuous-spectrum.md +29 -0
  17. package/docs/technical/display-upscale.md +82 -0
  18. package/docs/technical/openpbr.md +99 -0
  19. package/docs/technical/pbrt-import.md +168 -0
  20. package/docs/technical/tiling.md +74 -0
  21. package/docs/technical/webgl.md +53 -0
  22. package/docs/viewer.md +11 -1
  23. package/lib/Display.d.ts +19 -5
  24. package/lib/Display.js +10 -7
  25. package/lib/Display.js.map +1 -1
  26. package/lib/Engine.d.ts +2 -1
  27. package/lib/Engine.js +30 -2
  28. package/lib/Engine.js.map +1 -1
  29. package/lib/Render.d.ts +20 -11
  30. package/lib/Render.js +32 -7
  31. package/lib/Render.js.map +1 -1
  32. package/lib/main.d.ts +3 -1
  33. package/lib/main.js +1 -0
  34. package/lib/main.js.map +1 -1
  35. package/lib/oidn.d.ts +1 -1
  36. package/lib/renderer/AovPrimitiveCache.js +39 -8
  37. package/lib/renderer/AovPrimitiveCache.js.map +1 -1
  38. package/lib/renderer/DisplayPostProcess.d.ts +8 -3
  39. package/lib/renderer/DisplayPostProcess.js +51 -18
  40. package/lib/renderer/DisplayPostProcess.js.map +1 -1
  41. package/lib/renderer/MaterialData.js +7 -2
  42. package/lib/renderer/MaterialData.js.map +1 -1
  43. package/lib/renderer/RenderingPipeline.d.ts +7 -1
  44. package/lib/renderer/RenderingPipeline.js.map +1 -1
  45. package/lib/renderer/TileRender.d.ts +2 -2
  46. package/lib/renderer/TileRender.js +128 -7
  47. package/lib/renderer/TileRender.js.map +1 -1
  48. package/lib/renderer/WGPUAov.d.ts +6 -1
  49. package/lib/renderer/WGPUAov.js +108 -14
  50. package/lib/renderer/WGPUAov.js.map +1 -1
  51. package/lib/renderer/WGPUCanvasSurface.js +12 -1
  52. package/lib/renderer/WGPUCanvasSurface.js.map +1 -1
  53. package/lib/renderer/WGPUEnvMapDistribution.js +19 -19
  54. package/lib/renderer/WGPUFullQuadPass.js +3 -3
  55. package/lib/renderer/WGPURayTraceOutput.js +12 -12
  56. package/lib/renderer/WGPURayTracePass.d.ts +17 -4
  57. package/lib/renderer/WGPURayTracePass.js +194 -71
  58. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  59. package/lib/renderer/WGPURenderingPipeline.js +46 -40
  60. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  61. package/lib/renderer/WebGLAov.js +3 -3
  62. package/lib/renderer/WebGLAov.js.map +1 -1
  63. package/lib/renderer/WebGLFullQuadPass.d.ts +1 -1
  64. package/lib/renderer/WebGLFullQuadPass.js +8 -2
  65. package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
  66. package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
  67. package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
  68. package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
  69. package/lib/renderer/WebGLRayTracePass.d.ts +14 -10
  70. package/lib/renderer/WebGLRayTracePass.js +115 -111
  71. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  72. package/lib/renderer/WebGLRenderingPipeline.js +10 -3
  73. package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
  74. package/lib/renderer/WebGLSkyBaker.js +57 -57
  75. package/lib/renderer/aov.d.ts +13 -1
  76. package/lib/renderer/aov.js +21 -2
  77. package/lib/renderer/aov.js.map +1 -1
  78. package/lib/renderer/aovDisplay.js +2 -2
  79. package/lib/renderer/aovDisplay.js.map +1 -1
  80. package/lib/renderer/binnedSahBvh.d.ts +2 -1
  81. package/lib/renderer/binnedSahBvh.js +24 -17
  82. package/lib/renderer/binnedSahBvh.js.map +1 -1
  83. package/lib/renderer/bvhAccel.d.ts +3 -1
  84. package/lib/renderer/bvhAccel.js +36 -6
  85. package/lib/renderer/bvhAccel.js.map +1 -1
  86. package/lib/renderer/cameraUniform.d.ts +1 -1
  87. package/lib/renderer/cameraUniform.js +6 -2
  88. package/lib/renderer/cameraUniform.js.map +1 -1
  89. package/lib/renderer/compactVertices.d.ts +14 -0
  90. package/lib/renderer/compactVertices.js +124 -0
  91. package/lib/renderer/compactVertices.js.map +1 -0
  92. package/lib/renderer/easu.d.ts +11 -0
  93. package/lib/renderer/easu.js +156 -0
  94. package/lib/renderer/easu.js.map +1 -0
  95. package/lib/renderer/geometryUpdateState.d.ts +1 -0
  96. package/lib/renderer/geometryUpdateState.js.map +1 -1
  97. package/lib/renderer/geometryUpdates.js +3 -2
  98. package/lib/renderer/geometryUpdates.js.map +1 -1
  99. package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
  100. package/lib/renderer/glsl/aovReflectance.glsl.js +35 -35
  101. package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
  102. package/lib/renderer/glsl/aovTrace.frag.js +44 -36
  103. package/lib/renderer/glsl/aovTrace.frag.js.map +1 -1
  104. package/lib/renderer/glsl/chunks/cameraProjection.glsl.d.ts +2 -0
  105. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js +16 -0
  106. package/lib/renderer/glsl/chunks/cameraProjection.glsl.js.map +1 -0
  107. package/lib/renderer/glsl/chunks/cameraRay.glsl.d.ts +1 -1
  108. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -16
  109. package/lib/renderer/glsl/chunks/cameraRay.glsl.js.map +1 -1
  110. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +8 -8
  111. package/lib/renderer/glsl/chunks/envMap.glsl.js +67 -67
  112. package/lib/renderer/glsl/chunks/intersect.glsl.js +390 -375
  113. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
  114. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +120 -120
  115. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +562 -562
  116. package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
  117. package/lib/renderer/glsl/chunks/random.glsl.js +4 -4
  118. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +37 -35
  119. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
  120. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +70 -74
  121. package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
  122. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -1
  123. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +107 -88
  124. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
  125. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +11 -11
  126. package/lib/renderer/glsl/chunks/sampling.glsl.js +77 -77
  127. package/lib/renderer/glsl/chunks/sobol.glsl.js +70 -70
  128. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.d.ts +6 -0
  129. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js +47 -0
  130. package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js.map +1 -0
  131. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
  132. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
  133. package/lib/renderer/glsl/depthQuery.frag.js +12 -6
  134. package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
  135. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
  136. package/lib/renderer/glsl/rayTrace.frag.js +233 -170
  137. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  138. package/lib/renderer/glsl/shadowCatcher.frag.js +10 -9
  139. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
  140. package/lib/renderer/glsl/webglPipeline.frag.js +58 -58
  141. package/lib/renderer/gpuMemory.js +1 -1
  142. package/lib/renderer/gpuMemory.js.map +1 -1
  143. package/lib/renderer/openPbrEnergyLut.d.ts +2 -1
  144. package/lib/renderer/openPbrEnergyLut.js +18 -1
  145. package/lib/renderer/openPbrEnergyLut.js.map +1 -1
  146. package/lib/renderer/pathLimits.d.ts +5 -4
  147. package/lib/renderer/pathLimits.js +6 -5
  148. package/lib/renderer/pathLimits.js.map +1 -1
  149. package/lib/renderer/prepareMeshBVH.d.ts +2 -0
  150. package/lib/renderer/prepareMeshBVH.js +5 -3
  151. package/lib/renderer/prepareMeshBVH.js.map +1 -1
  152. package/lib/renderer/progressiveScheduler.js +1 -1
  153. package/lib/renderer/progressiveScheduler.js.map +1 -1
  154. package/lib/renderer/renderPipeline.d.ts +4 -0
  155. package/lib/renderer/renderPipeline.js +1 -0
  156. package/lib/renderer/renderPipeline.js.map +1 -1
  157. package/lib/renderer/researchTransport.d.ts +1 -1
  158. package/lib/renderer/researchTransport.js.map +1 -1
  159. package/lib/renderer/sceneAssembly.d.ts +1 -1
  160. package/lib/renderer/sceneAssembly.js +4 -3
  161. package/lib/renderer/sceneAssembly.js.map +1 -1
  162. package/lib/renderer/sceneChangeDetector.d.ts +1 -1
  163. package/lib/renderer/sceneChangeDetector.js +3 -1
  164. package/lib/renderer/sceneChangeDetector.js.map +1 -1
  165. package/lib/renderer/sceneLayoutSchema.d.ts +1 -0
  166. package/lib/renderer/sceneLayoutSchema.js +2 -2
  167. package/lib/renderer/sceneLayoutSchema.js.map +1 -1
  168. package/lib/renderer/scenePreparation.d.ts +3 -2
  169. package/lib/renderer/scenePreparation.js +18 -2
  170. package/lib/renderer/scenePreparation.js.map +1 -1
  171. package/lib/renderer/scratchSampling.js +6 -6
  172. package/lib/renderer/spectrum.js +2 -2
  173. package/lib/renderer/staticBvhPreparation.d.ts +9 -0
  174. package/lib/renderer/staticBvhPreparation.js +8 -2
  175. package/lib/renderer/staticBvhPreparation.js.map +1 -1
  176. package/lib/renderer/surfaceHit.d.ts +10 -0
  177. package/lib/renderer/surfaceHit.js +22 -0
  178. package/lib/renderer/surfaceHit.js.map +1 -0
  179. package/lib/renderer/tangentRequirements.d.ts +2 -0
  180. package/lib/renderer/tangentRequirements.js +7 -0
  181. package/lib/renderer/tangentRequirements.js.map +1 -0
  182. package/lib/renderer/textureAtlas.d.ts +4 -0
  183. package/lib/renderer/textureAtlas.js +48 -2
  184. package/lib/renderer/textureAtlas.js.map +1 -1
  185. package/lib/renderer/webgl.js +4 -0
  186. package/lib/renderer/webgl.js.map +1 -1
  187. package/lib/renderer/webglVertexAttributes.d.ts +17 -0
  188. package/lib/renderer/webglVertexAttributes.js +44 -0
  189. package/lib/renderer/webglVertexAttributes.js.map +1 -0
  190. package/lib/renderer/wgpuBindlessSource.js +2 -2
  191. package/lib/renderer/wgpuMaterialConstants.js +4 -3
  192. package/lib/renderer/wgpuMaterialConstants.js.map +1 -1
  193. package/lib/renderer/wgpuSceneUniform.js +2 -2
  194. package/lib/renderer/wgpuSceneUniform.js.map +1 -1
  195. package/lib/renderer/wgpuShaderSource.d.ts +15 -0
  196. package/lib/renderer/wgpuShaderSource.js +1 -1
  197. package/lib/renderer/wgpuShaderSource.js.map +1 -1
  198. package/lib/renderer/wgsl/aovCompact.wgsl.d.ts +4 -0
  199. package/lib/renderer/wgsl/aovCompact.wgsl.js +26 -0
  200. package/lib/renderer/wgsl/aovCompact.wgsl.js.map +1 -0
  201. package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
  202. package/lib/renderer/wgsl/aovReflectance.wgsl.js +39 -39
  203. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
  204. package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +4 -0
  205. package/lib/renderer/wgsl/aovResolve.wgsl.js +21 -2
  206. package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -1
  207. package/lib/renderer/wgsl/aovSnapshot.wgsl.js +2 -2
  208. package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -1
  209. package/lib/renderer/wgsl/aovTrace.wgsl.js +31 -31
  210. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.d.ts +2 -0
  211. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js +56 -0
  212. package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js.map +1 -0
  213. package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
  214. package/lib/renderer/wgsl/causticLight.compute.d.ts +1 -1
  215. package/lib/renderer/wgsl/causticLight.compute.js +121 -119
  216. package/lib/renderer/wgsl/causticLight.compute.js.map +1 -1
  217. package/lib/renderer/wgsl/chunks/background.wgsl.js +46 -46
  218. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +4 -4
  219. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +3 -3
  220. package/lib/renderer/wgsl/chunks/caustics.wgsl.js +3 -3
  221. package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
  222. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +30 -30
  223. package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +6 -2
  224. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +550 -542
  225. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  226. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.d.ts +1 -1
  227. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +18 -16
  228. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
  229. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +2 -1
  230. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +70 -69
  231. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
  232. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +593 -593
  233. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +118 -118
  234. package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
  235. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +4 -1
  236. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +32 -26
  237. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
  238. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +114 -114
  239. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
  240. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +159 -137
  241. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
  242. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +8 -8
  243. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +74 -74
  244. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +74 -74
  245. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.d.ts +6 -0
  246. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js +66 -0
  247. package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js.map +1 -0
  248. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
  249. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.d.ts +2 -0
  250. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js +61 -0
  251. package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js.map +1 -0
  252. package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +2 -0
  253. package/lib/renderer/wgsl/compose.frag.wgsl.js +99 -97
  254. package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
  255. package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
  256. package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
  257. package/lib/renderer/wgsl/depthQuery.wgsl.js +11 -2
  258. package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
  259. package/lib/renderer/wgsl/easu.wgsl.d.ts +2 -0
  260. package/lib/renderer/wgsl/easu.wgsl.js +80 -0
  261. package/lib/renderer/wgsl/easu.wgsl.js.map +1 -0
  262. package/lib/renderer/wgsl/easuDetail.wgsl.d.ts +21 -0
  263. package/lib/renderer/wgsl/easuDetail.wgsl.js +230 -0
  264. package/lib/renderer/wgsl/easuDetail.wgsl.js.map +1 -0
  265. package/lib/renderer/wgsl/jnlm.compute.js +74 -74
  266. package/lib/renderer/wgsl/jnlm.compute.js.map +1 -1
  267. package/lib/renderer/wgsl/offlineAdaptive.compute.js +26 -26
  268. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
  269. package/lib/renderer/wgsl/photonMap.compute.js +89 -89
  270. package/lib/renderer/wgsl/proceduralSky.compute.js +61 -61
  271. package/lib/renderer/wgsl/rayTrace.compute.d.ts +14 -0
  272. package/lib/renderer/wgsl/rayTrace.compute.js +395 -333
  273. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  274. package/lib/scene/Scene.js.map +1 -1
  275. package/lib/settings/render.d.ts +11 -0
  276. package/lib/settings/render.js +1 -0
  277. package/lib/settings/render.js.map +1 -1
  278. package/lib/util/fromTHREE.js +21 -14
  279. package/lib/util/fromTHREE.js.map +1 -1
  280. package/lib/version.d.ts +1 -1
  281. package/lib/version.js +1 -1
  282. package/lib/version.js.map +1 -1
  283. package/package.json +5 -2
  284. package/lib/oidnBundledWeights.cdn.d.ts +0 -2
  285. package/lib/oidnBundledWeights.cdn.js +0 -5
  286. package/lib/oidnBundledWeights.cdn.js.map +0 -1
  287. package/lib/renderer/bvh.worker.d.ts +0 -1
  288. package/lib/renderer/bvh.worker.js +0 -16
  289. package/lib/renderer/bvh.worker.js.map +0 -1
  290. package/lib/renderer/bvhWorkerBuild.d.ts +0 -13
  291. package/lib/renderer/bvhWorkerBuild.js +0 -17
  292. package/lib/renderer/bvhWorkerBuild.js.map +0 -1
  293. package/lib/renderer/lightCore.d.ts +0 -12
  294. package/lib/renderer/lightCore.js +0 -13
  295. package/lib/renderer/lightCore.js.map +0 -1
  296. package/lib/renderer/materialCore.d.ts +0 -8
  297. package/lib/renderer/materialCore.js +0 -9
  298. package/lib/renderer/materialCore.js.map +0 -1
  299. package/lib/scene/PixelTexture.d.ts +0 -6
  300. package/lib/scene/PixelTexture.js +0 -10
  301. package/lib/scene/PixelTexture.js.map +0 -1
  302. package/lib/util/studioLighting.d.ts +0 -18
  303. package/lib/util/studioLighting.js +0 -36
  304. 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}[gf(${position})]`;
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 qw = id.x + (size.y - 1u - id.y) * size.x;
47
- let n = normalBuffer[qw].a;
48
- let moments = accumulation[qw].moments;
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 lM = 0.0;
73
- var je = 0.0;
74
- var nM = 0.0;
75
- var iV = 0.0;
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
- nM += value.x;
81
- iV += value.x * value.x;
80
+ oL += value.x;
81
+ jB += value.x * value.x;
82
82
  if (value.y >= 0.0) {
83
- lM += value.y;
84
- je += 1.0;
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 = lM / je;
90
+ variance = mF / jI;
91
91
  } else {
92
- let lL = nM / 9.0;
93
- variance = max(iV / 9.0 - lL * lL, 0.0) * 9.0 / 8.0;
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 qw = id.x + (params.size.y - 1u - id.y) * params.size.x;
118
- let n = normalAccumulation[qw];
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[qw].mask / samples;
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 gf(p: vec2i) -> u32 {
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 lP(q: vec2i, bo: vec4f, gO: vec3f) -> f32 {
163
+ fn mJ(q: vec2i, bp: vec4f, hk: vec3f) -> f32 {
164
164
  let albedo = ${loadGuide(guides, 'albedoTex', 'q')};
165
165
 
166
166
 
167
- let pY = max((albedo.rgb + bo.rgb) * 0.5, vec3f(${JNLM_ALBEDO_FLOOR}));
168
- let lu = (albedo.rgb - bo.rgb) / pY;
169
- let iQ = albedo.a - bo.a;
170
- let lG = ${loadGuide(guides, 'normalTex', 'q')}.rgb - gO;
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(lu, lu) * INV_ALBEDO_BANDWIDTH2
173
- + iQ * iQ * INV_COVERAGE_BANDWIDTH2,
174
- dot(lG, lG) * INV_NORMAL_BANDWIDTH2
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 bo = ${loadGuide(guides, 'albedoTex', 'p')};
189
- let fC = ${loadGuide(guides, 'normalTex', 'p')};
190
- let gO = fC.rgb;
191
- let index = gf(p);
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 (fC.a <= 0.0 && ${loadGuide(guides, 'shadowCatcherTex', 'p')}.r <= 0.0) {
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 lA: array<vec2f, PATCH_SIZE>;
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
- lA[k] = guide[gf(p + vec2i(dx, dy) * step)];
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 gR = max(bo.rgb, vec3f(${JNLM_ALBEDO_FLOOR}));
212
- var mQ = vec3f(0.0);
213
- var gv = vec3f(0.0);
214
- var hm = vec3f(0.0);
215
- var qf = 0.0;
216
- var gx = 0.0;
217
- var oE = 0.0;
218
- var kX = 0.0;
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 = lP(q, bo, gO);
227
+ let features = mJ(q, bp, hk);
228
228
  if (features > 16.0) { continue; }
229
229
 
230
- var fW = 0.0;
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 = lA[k];
235
- let b = guide[gf(q + vec2i(dx, dy) * step)];
236
- let rV = a.x - b.x;
237
- fW += (rV * rV
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
- fW /= f32(PATCH_SIZE);
243
+ gn /= f32(PATCH_SIZE);
244
244
 
245
- let weight = exp(-max(max(fW, 0.0), features));
246
- let index = gf(q);
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 - bo.rgb) / gR;
250
+ let a = (${loadGuide(guides, 'albedoTex', 'q')}.rgb - bp.rgb) / hn;
251
251
  let value = premultiplied(c);
252
252
  sum += value * weight;
253
- mQ += a * weight;
254
- gv += a * a * weight;
255
- hm += a * value.rgb * weight;
256
- qf += g.x * weight;
257
- gx += g.y * weight * weight;
258
- oE += weight;
259
- kX = max(kX, weight);
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 lw = guide[index];
267
- let dE = max(kX, CENTER_WEIGHT_MIN);
268
- let totalWeight = oE + dE;
269
- let vs = premultiplied(center);
270
- let mean = (sum + vs * dE) / totalWeight;
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 er = mQ / totalWeight;
274
- let lQ = max(gv / totalWeight - er * er, vec3f(0.0));
275
- let rq = hm / totalWeight - er * mean.rgb;
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 rD = bo.rgb + er * gR;
280
- let pd = mean.rgb * gR / max(rD, vec3f(${JNLM_ALBEDO_FLOOR}));
281
- let xI = clamp(rq / (lQ + vec3f(${JNLM_ALBEDO_REGULARIZATION})), vec3f(0.0), pd);
282
- let sz = mean.rgb - xI * er;
283
- let filtered = vec4f(max(sz, vec3f(0.0)), max(mean.a, 0.0));
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
- (qf + lw.x * dE) / totalWeight,
292
- (gx + lw.y * dE * dE)
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":"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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"]}
@@ -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) eA: vec3u) {
31
- let tile = eA.x;
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 ws = tileX * params.tileSize;
40
- let wt = tileY * params.tileSize;
41
- let yF = min(ws + params.tileSize, params.width);
42
- let yG = min(wt + params.tileSize, params.height);
43
- var nM = 0.0;
44
- var jh = 0.0;
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 = wt; y < yG; y++) {
48
- for (var x = ws; x < yF; 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 vj = pilotMoments[offset] / samples;
64
- let qb = max(vj - mean * mean, 0.0);
65
- var variance = qb;
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 = qb * samples / (samples - 1.0);
67
+ variance = rj * samples / (samples - 1.0);
68
68
  }
69
- nM += mean;
70
- jh = max(jh, variance);
69
+ oL += mean;
70
+ jL = max(jL, variance);
71
71
  count += 1u;
72
72
  }
73
73
  }
74
74
 
75
- let mean = nM / f32(max(count, 1u));
76
- var pK = 0.0;
75
+ let mean = oL / f32(max(count, 1u));
76
+ var qO = 0.0;
77
77
  if (params.hasPrevious != 0u) {
78
- pK = previousVariance[tile];
78
+ qO = previousVariance[tile];
79
79
  }
80
- let tu = max(jh, pK);
81
- let uH = params.absoluteError + params.relativeError * abs(mean);
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 * tu /
84
- max(uH * uH, 1e-16)
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] = jh;
94
+ tileVariances[tile] = jL;
95
95
  tileTargets[tile] = targetSamples;
96
96
 
97
- for (var y = wt; y < yG; y++) {
98
- for (var x = ws; x < yF; 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 sQ(value: vec3f) -> vec3f {
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 sP(value: vec3f) -> vec3f {
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 rG(value: vec3f) -> vec3f {
13
+ fn sW(value: vec3f) -> vec3f {
14
14
  return clamp(value, vec3f(0.0), vec3f(1.0));
15
15
  }
16
16
  `;