@meimu/mutracer 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/README.md +3 -0
  2. package/dist/assets/bvh.worker.js +1 -1
  3. package/dist/mutracer.umd.js +8949 -7915
  4. package/docs/api.md +42 -21
  5. package/docs/caustics.md +80 -0
  6. package/docs/materials.md +5 -4
  7. package/docs/progressive-rendering.md +50 -9
  8. package/docs/technical/continuous-spectrum.md +93 -0
  9. package/docs/updates.md +3 -1
  10. package/docs/viewer-checklist.md +65 -0
  11. package/docs/viewer.md +7 -5
  12. package/lib/Display.js +1 -1
  13. package/lib/Display.js.map +1 -1
  14. package/lib/Engine.d.ts +2 -0
  15. package/lib/Engine.js +47 -12
  16. package/lib/Engine.js.map +1 -1
  17. package/lib/Render.d.ts +61 -13
  18. package/lib/Render.js +169 -19
  19. package/lib/Render.js.map +1 -1
  20. package/lib/RenderLoop.d.ts +7 -3
  21. package/lib/RenderLoop.js +68 -44
  22. package/lib/RenderLoop.js.map +1 -1
  23. package/lib/main.d.ts +1 -1
  24. package/lib/main.js.map +1 -1
  25. package/lib/oidn.d.ts +1 -1
  26. package/lib/oidnBundledWeights.cdn.d.ts +2 -2
  27. package/lib/renderer/GPUTraceQueue.d.ts +2 -1
  28. package/lib/renderer/GPUTraceQueue.js +3 -1
  29. package/lib/renderer/GPUTraceQueue.js.map +1 -1
  30. package/lib/renderer/RenderingPipeline.d.ts +4 -0
  31. package/lib/renderer/RenderingPipeline.js.map +1 -1
  32. package/lib/renderer/WGPUCausticLightPass.d.ts +48 -0
  33. package/lib/renderer/WGPUCausticLightPass.js +217 -0
  34. package/lib/renderer/WGPUCausticLightPass.js.map +1 -0
  35. package/lib/renderer/WGPUEnvMapDistribution.js +18 -18
  36. package/lib/renderer/WGPUFullQuadPass.js +3 -3
  37. package/lib/renderer/WGPUPhotonMap.d.ts +37 -0
  38. package/lib/renderer/WGPUPhotonMap.js +191 -0
  39. package/lib/renderer/WGPUPhotonMap.js.map +1 -0
  40. package/lib/renderer/WGPURayTraceOutput.d.ts +13 -1
  41. package/lib/renderer/WGPURayTraceOutput.js +122 -28
  42. package/lib/renderer/WGPURayTraceOutput.js.map +1 -1
  43. package/lib/renderer/WGPURayTracePass.d.ts +48 -17
  44. package/lib/renderer/WGPURayTracePass.js +538 -340
  45. package/lib/renderer/WGPURayTracePass.js.map +1 -1
  46. package/lib/renderer/WGPURenderingPipeline.js +83 -47
  47. package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
  48. package/lib/renderer/WebGLFullQuadPass.d.ts +2 -1
  49. package/lib/renderer/WebGLFullQuadPass.js +4 -0
  50. package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
  51. package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
  52. package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
  53. package/lib/renderer/WebGLRayTracePass.d.ts +6 -1
  54. package/lib/renderer/WebGLRayTracePass.js +43 -6
  55. package/lib/renderer/WebGLRayTracePass.js.map +1 -1
  56. package/lib/renderer/WebGLRenderingPipeline.js +10 -14
  57. package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
  58. package/lib/renderer/WebGLSkyBaker.js +60 -60
  59. package/lib/renderer/accumulationTarget.d.ts +2 -1
  60. package/lib/renderer/accumulationTarget.js.map +1 -1
  61. package/lib/renderer/causticDiagnostics.d.ts +31 -0
  62. package/lib/renderer/causticDiagnostics.js +5 -0
  63. package/lib/renderer/causticDiagnostics.js.map +1 -0
  64. package/lib/renderer/causticEmission.d.ts +10 -0
  65. package/lib/renderer/causticEmission.js +40 -0
  66. package/lib/renderer/causticEmission.js.map +1 -0
  67. package/lib/renderer/causticUniform.d.ts +12 -0
  68. package/lib/renderer/causticUniform.js +22 -0
  69. package/lib/renderer/causticUniform.js.map +1 -0
  70. package/lib/renderer/caustics.d.ts +16 -0
  71. package/lib/renderer/caustics.js +93 -0
  72. package/lib/renderer/caustics.js.map +1 -0
  73. package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
  74. package/lib/renderer/glsl/aovReflectance.glsl.js +53 -53
  75. package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
  76. package/lib/renderer/glsl/aovTrace.frag.js +33 -33
  77. package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -9
  78. package/lib/renderer/glsl/chunks/emissiveLight.glsl.d.ts +2 -0
  79. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +35 -0
  80. package/lib/renderer/glsl/chunks/emissiveLight.glsl.js.map +1 -0
  81. package/lib/renderer/glsl/chunks/envMap.glsl.js +73 -73
  82. package/lib/renderer/glsl/chunks/intersect.glsl.js +426 -428
  83. package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
  84. package/lib/renderer/glsl/chunks/materialBuffer.glsl.d.ts +1 -0
  85. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +156 -153
  86. package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
  87. package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +771 -771
  88. package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
  89. package/lib/renderer/glsl/chunks/random.glsl.js +6 -6
  90. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.d.ts +1 -0
  91. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +68 -63
  92. package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
  93. package/lib/renderer/glsl/chunks/sampleLight.glsl.js +94 -76
  94. package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
  95. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -0
  96. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +140 -117
  97. package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
  98. package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +14 -14
  99. package/lib/renderer/glsl/chunks/sampling.glsl.js +89 -92
  100. package/lib/renderer/glsl/chunks/sampling.glsl.js.map +1 -1
  101. package/lib/renderer/glsl/chunks/sobol.glsl.js +79 -79
  102. package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
  103. package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
  104. package/lib/renderer/glsl/depthQuery.frag.js +2 -2
  105. package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
  106. package/lib/renderer/glsl/rayTrace.frag.d.ts +1 -1
  107. package/lib/renderer/glsl/rayTrace.frag.js +260 -237
  108. package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
  109. package/lib/renderer/glsl/shadowCatcher.frag.js +26 -26
  110. package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
  111. package/lib/renderer/glsl/webglPipeline.frag.js +59 -59
  112. package/lib/renderer/lightSampling.d.ts +3 -2
  113. package/lib/renderer/lightSampling.js +14 -8
  114. package/lib/renderer/lightSampling.js.map +1 -1
  115. package/lib/renderer/progressiveScheduler.js +4 -3
  116. package/lib/renderer/progressiveScheduler.js.map +1 -1
  117. package/lib/renderer/renderPipeline.d.ts +1 -0
  118. package/lib/renderer/renderPipeline.js.map +1 -1
  119. package/lib/renderer/researchTransport.d.ts +31 -0
  120. package/lib/renderer/researchTransport.js +5 -0
  121. package/lib/renderer/researchTransport.js.map +1 -0
  122. package/lib/renderer/sceneLayoutSchema.d.ts +1 -0
  123. package/lib/renderer/sceneLayoutSchema.js +2 -2
  124. package/lib/renderer/sceneLayoutSchema.js.map +1 -1
  125. package/lib/renderer/scratchSampling.js +6 -6
  126. package/lib/renderer/spectrum.d.ts +21 -0
  127. package/lib/renderer/spectrum.js +154 -0
  128. package/lib/renderer/spectrum.js.map +1 -0
  129. package/lib/renderer/webglLightSampling.d.ts +39 -0
  130. package/lib/renderer/webglLightSampling.js +15 -0
  131. package/lib/renderer/webglLightSampling.js.map +1 -0
  132. package/lib/renderer/wgpuBindlessSource.js +3 -3
  133. package/lib/renderer/wgpuShaderSource.d.ts +7 -0
  134. package/lib/renderer/wgpuShaderSource.js +2 -0
  135. package/lib/renderer/wgpuShaderSource.js.map +1 -1
  136. package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
  137. package/lib/renderer/wgsl/aovReflectance.wgsl.js +55 -55
  138. package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
  139. package/lib/renderer/wgsl/aovResolve.wgsl.js +2 -2
  140. package/lib/renderer/wgsl/aovSnapshot.wgsl.js +1 -1
  141. package/lib/renderer/wgsl/aovTrace.wgsl.js +33 -33
  142. package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
  143. package/lib/renderer/wgsl/causticLight.compute.d.ts +6 -0
  144. package/lib/renderer/wgsl/causticLight.compute.js +537 -0
  145. package/lib/renderer/wgsl/causticLight.compute.js.map +1 -0
  146. package/lib/renderer/wgsl/chunks/background.wgsl.d.ts +2 -0
  147. package/lib/renderer/wgsl/chunks/background.wgsl.js +102 -0
  148. package/lib/renderer/wgsl/chunks/background.wgsl.js.map +1 -0
  149. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.d.ts +2 -0
  150. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +12 -0
  151. package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js.map +1 -0
  152. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.d.ts +2 -0
  153. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +16 -0
  154. package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js.map +1 -0
  155. package/lib/renderer/wgsl/chunks/caustics.wgsl.d.ts +1 -0
  156. package/lib/renderer/wgsl/chunks/caustics.wgsl.js +62 -0
  157. package/lib/renderer/wgsl/chunks/caustics.wgsl.js.map +1 -0
  158. package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
  159. package/lib/renderer/wgsl/chunks/envMap.wgsl.js +37 -37
  160. package/lib/renderer/wgsl/chunks/intersect.wgsl.js +598 -592
  161. package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
  162. package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +14 -14
  163. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +2 -1
  164. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +75 -73
  165. package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
  166. package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +794 -794
  167. package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +139 -139
  168. package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
  169. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +2 -1
  170. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +35 -25
  171. package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
  172. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.d.ts +1 -1
  173. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +142 -138
  174. package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js.map +1 -1
  175. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
  176. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +204 -187
  177. package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
  178. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.d.ts +1 -1
  179. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +11 -11
  180. package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js.map +1 -1
  181. package/lib/renderer/wgsl/chunks/sampling.wgsl.d.ts +1 -1
  182. package/lib/renderer/wgsl/chunks/sampling.wgsl.js +89 -93
  183. package/lib/renderer/wgsl/chunks/sampling.wgsl.js.map +1 -1
  184. package/lib/renderer/wgsl/chunks/sobol.wgsl.js +83 -83
  185. package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
  186. package/lib/renderer/wgsl/compose.frag.wgsl.js +98 -98
  187. package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
  188. package/lib/renderer/wgsl/depthQuery.wgsl.js +2 -2
  189. package/lib/renderer/wgsl/jnlm.compute.js +74 -74
  190. package/lib/renderer/wgsl/offlineAdaptive.compute.js +25 -25
  191. package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
  192. package/lib/renderer/wgsl/photonMap.compute.d.ts +29 -0
  193. package/lib/renderer/wgsl/photonMap.compute.js +352 -0
  194. package/lib/renderer/wgsl/photonMap.compute.js.map +1 -0
  195. package/lib/renderer/wgsl/proceduralSky.compute.js +64 -64
  196. package/lib/renderer/wgsl/rayTrace.compute.d.ts +9 -1
  197. package/lib/renderer/wgsl/rayTrace.compute.js +448 -522
  198. package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
  199. package/lib/scene/MaterialState.d.ts +2 -2
  200. package/lib/scene/MaterialState.js +2 -2
  201. package/lib/scene/MaterialState.js.map +1 -1
  202. package/lib/settings/render.d.ts +11 -0
  203. package/lib/settings/render.js +1 -0
  204. package/lib/settings/render.js.map +1 -1
  205. package/lib/util/fromTHREE.js +2 -2
  206. package/lib/util/fromTHREE.js.map +1 -1
  207. package/lib/version.d.ts +1 -1
  208. package/lib/version.js +1 -1
  209. package/lib/version.js.map +1 -1
  210. package/llms.txt +2 -1
  211. package/package.json +3 -3
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Continuous wavelength sampling shared by camera paths and caustic photons.
3
+ *
4
+ * CIE 1931 matching functions use the multi-lobe fit from Wyman, Sloan and
5
+ * Shirley, "Simple Analytic Approximations to the CIE XYZ Color Matching
6
+ * Functions", JCGT 2(2), 2013. Only the published formula and coefficients are
7
+ * used. Out-of-gamut negatives clamp to zero, and each channel is normalized so
8
+ * that a uniform wavelength keeps white light white.
9
+ */
10
+ export const SPECTRUM_WAVELENGTH_MIN = 400;
11
+ export const SPECTRUM_WAVELENGTH_RANGE = 300;
12
+ // Amplitude, mean, sigma below the mean and sigma above it.
13
+ const XYZ_LOBES = [
14
+ [[1.056, 599.8, 37.9, 31.0], [0.362, 442.0, 16.0, 26.7], [-0.065, 501.1, 20.4, 26.2]],
15
+ [[0.821, 568.8, 46.9, 40.5], [0.286, 530.9, 16.3, 31.1]],
16
+ [[1.217, 437.0, 11.8, 36.0], [0.681, 459.0, 26.0, 13.8]]
17
+ ];
18
+ // Linear sRGB primaries, D65.
19
+ const XYZ_TO_RGB = [
20
+ [3.2406, -1.5372, -0.4986],
21
+ [-0.9689, 1.8758, 0.0415],
22
+ [0.0557, -0.204, 1.057]
23
+ ];
24
+ function unnormalizedRgb(wavelength) {
25
+ const xyz = XYZ_LOBES.map((lobes) => {
26
+ let sum = 0;
27
+ for (const lobe of lobes) {
28
+ const t = (wavelength - lobe[1]) / (wavelength < lobe[1] ? lobe[2] : lobe[3]);
29
+ sum += lobe[0] * Math.exp(-0.5 * t * t);
30
+ }
31
+ return sum;
32
+ });
33
+ return XYZ_TO_RGB.map((row) => Math.max(0, row[0] * xyz[0] + row[1] * xyz[1] + row[2] * xyz[2]));
34
+ }
35
+ function channelScale() {
36
+ const steps = 3000;
37
+ const sum = [0, 0, 0];
38
+ for (let i = 0; i < steps; i++) {
39
+ const rgb = unnormalizedRgb(SPECTRUM_WAVELENGTH_MIN + (SPECTRUM_WAVELENGTH_RANGE * (i + 0.5)) / steps);
40
+ for (let c = 0; c < 3; c++)
41
+ sum[c] += rgb[c] / steps;
42
+ }
43
+ // Rounded so JavaScript and native builds generate identical shader text.
44
+ return sum.map((mean) => Number((1 / mean).toFixed(6)));
45
+ }
46
+ const SCALE = channelScale();
47
+ export function spectrumRgb(wavelength) {
48
+ return unnormalizedRgb(wavelength).map((value, c) => value * SCALE[c]);
49
+ }
50
+ // Inverse CDFs in 5 nm bins: one per RGB channel for camera paths, and r+g+b for
51
+ // photons. Narrow bins keep each sampled response close to the fit.
52
+ const SAMPLE_BINS = 60;
53
+ const SAMPLE_BIN_WIDTH = SPECTRUM_WAVELENGTH_RANGE / SAMPLE_BINS;
54
+ export const SPECTRUM_PHOTON_DISTRIBUTION = 3;
55
+ function sampleCdfs() {
56
+ const substeps = 20;
57
+ const cdfs = [];
58
+ for (let distribution = 0; distribution <= SPECTRUM_PHOTON_DISTRIBUTION; distribution++) {
59
+ const cdf = [0];
60
+ for (let bin = 0; bin < SAMPLE_BINS; bin++) {
61
+ let mass = 0;
62
+ for (let i = 0; i < substeps; i++) {
63
+ const rgb = spectrumRgb(SPECTRUM_WAVELENGTH_MIN + (bin + (i + 0.5) / substeps) * SAMPLE_BIN_WIDTH);
64
+ mass += distribution === SPECTRUM_PHOTON_DISTRIBUTION ? rgb[0] + rgb[1] + rgb[2] : rgb[distribution];
65
+ }
66
+ cdf.push(cdf[bin] + mass);
67
+ }
68
+ for (const value of cdf)
69
+ cdfs.push(Number((value / cdf[SAMPLE_BINS]).toFixed(7)));
70
+ }
71
+ return cdfs;
72
+ }
73
+ const SAMPLE_CDFS = sampleCdfs();
74
+ /** Maps a unit random number to a wavelength and its probability density. */
75
+ export function spectrumSample(distribution, u) {
76
+ const first = distribution * (SAMPLE_BINS + 1);
77
+ let low = 0;
78
+ let high = SAMPLE_BINS;
79
+ while (high - low > 1) {
80
+ const middle = (low + high) >> 1;
81
+ if (SAMPLE_CDFS[first + middle] <= u)
82
+ low = middle;
83
+ else
84
+ high = middle;
85
+ }
86
+ const mass = SAMPLE_CDFS[first + low + 1] - SAMPLE_CDFS[first + low];
87
+ return {
88
+ wavelength: SPECTRUM_WAVELENGTH_MIN + SAMPLE_BIN_WIDTH * (low + (u - SAMPLE_CDFS[first + low]) / mass),
89
+ pdf: mass / SAMPLE_BIN_WIDTH
90
+ };
91
+ }
92
+ // Cauchy IOR grows quickly at short wavelengths and strong dispersion scales turn
93
+ // violet light into total internal reflection. Below 500 nm the refracting
94
+ // wavelength eases toward 475 nm, while color still follows the sampled wavelength.
95
+ const IOR_KNEE = 500;
96
+ const IOR_KNEE_WIDTH = 25;
97
+ export function spectrumIorWavelength(wavelength) {
98
+ if (wavelength >= IOR_KNEE)
99
+ return wavelength;
100
+ return IOR_KNEE - IOR_KNEE_WIDTH * (1 - Math.exp((wavelength - IOR_KNEE) / IOR_KNEE_WIDTH));
101
+ }
102
+ const CDF_LITERALS = SAMPLE_CDFS.map((value) => value.toFixed(7)).join(',');
103
+ const lobeSum = (lobes) => lobes.map((lobe) => `${lobe[0]}f*spectrumLobe(wavelength,${lobe[1]}f,${lobe[2]}f,${lobe[3]}f)`).join('+');
104
+ export const spectrumWgsl = /* wgsl */ `
105
+ fn spectrumLobe(wavelength:f32,mean:f32,below:f32,above:f32)->f32 {
106
+ let t=(wavelength-mean)/select(above,below,wavelength<mean);
107
+ return exp(-0.5*t*t);
108
+ }
109
+ fn spectrumRgb(wavelength:f32)->vec3f {
110
+ let xyz=vec3f(${XYZ_LOBES.map(lobeSum).join(',')});
111
+ let rgb=mat3x3f(${[0, 1, 2].map((c) => XYZ_TO_RGB.map((row) => `${row[c]}f`).join(',')).join(',')})*xyz;
112
+ return max(rgb,vec3f(0.0))*vec3f(${SCALE.map((s) => `${s}f`).join(',')});
113
+ }
114
+ fn spectrumIorWavelength(wavelength:f32)->f32 {
115
+ if(wavelength>=${IOR_KNEE}.0) { return wavelength; }
116
+ return ${IOR_KNEE}.0-${IOR_KNEE_WIDTH}.0*(1.0-exp((wavelength-${IOR_KNEE}.0)/${IOR_KNEE_WIDTH}.0));
117
+ }
118
+ const SPECTRUM_CDF=array<f32,${(SAMPLE_BINS + 1) * 4}>(${CDF_LITERALS});
119
+ fn spectrumSample(distribution:u32,u:f32,pdf:ptr<function,f32>)->f32 {
120
+ let first=distribution*${SAMPLE_BINS + 1}u;
121
+ var low=0u;
122
+ var high=${SAMPLE_BINS}u;
123
+ while(high-low>1u) {
124
+ let middle=(low+high)/2u;
125
+ if(SPECTRUM_CDF[first+middle]<=u) { low=middle; } else { high=middle; }
126
+ }
127
+ let mass=SPECTRUM_CDF[first+low+1u]-SPECTRUM_CDF[first+low];
128
+ *pdf=mass/${SAMPLE_BIN_WIDTH}.0;
129
+ return ${SPECTRUM_WAVELENGTH_MIN}.0+${SAMPLE_BIN_WIDTH}.0*(f32(low)+(u-SPECTRUM_CDF[first+low])/mass);
130
+ }
131
+
132
+ fn cU(channel:u32,u:f32)->f32 {
133
+ var pdf=0.0;
134
+ return spectrumIorWavelength(spectrumSample(channel,u,&pdf));
135
+ }
136
+ `;
137
+ export const spectrumGlsl = /* glsl */ `
138
+ const float SPECTRUM_CDF[${(SAMPLE_BINS + 1) * 4}]=float[${(SAMPLE_BINS + 1) * 4}](${CDF_LITERALS});
139
+
140
+ float cU(int channel,float u) {
141
+ int first=channel*${SAMPLE_BINS + 1};
142
+ int low=0;
143
+ int high=${SAMPLE_BINS};
144
+ while(high-low>1) {
145
+ int middle=(low+high)/2;
146
+ if(SPECTRUM_CDF[first+middle]<=u) { low=middle; } else { high=middle; }
147
+ }
148
+ float start=SPECTRUM_CDF[first+low];
149
+ float wavelength=${SPECTRUM_WAVELENGTH_MIN}.0+${SAMPLE_BIN_WIDTH}.0*(float(low)+(u-start)/(SPECTRUM_CDF[first+low+1]-start));
150
+ if(wavelength>=${IOR_KNEE}.0) { return wavelength; }
151
+ return ${IOR_KNEE}.0-${IOR_KNEE_WIDTH}.0*(1.0-exp((wavelength-${IOR_KNEE}.0)/${IOR_KNEE_WIDTH}.0));
152
+ }
153
+ `;
154
+ //# sourceMappingURL=spectrum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spectrum.js","sourceRoot":"","sources":["../../src/renderer/spectrum.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Continuous wavelength sampling shared by camera paths and caustic photons.\n *\n * CIE 1931 matching functions use the multi-lobe fit from Wyman, Sloan and\n * Shirley, \"Simple Analytic Approximations to the CIE XYZ Color Matching\n * Functions\", JCGT 2(2), 2013. Only the published formula and coefficients are\n * used. Out-of-gamut negatives clamp to zero, and each channel is normalized so\n * that a uniform wavelength keeps white light white.\n */\nexport const SPECTRUM_WAVELENGTH_MIN = 400;\nexport const SPECTRUM_WAVELENGTH_RANGE = 300;\n\n// Amplitude, mean, sigma below the mean and sigma above it.\nconst XYZ_LOBES: number[][][] = [\n [[1.056, 599.8, 37.9, 31.0], [0.362, 442.0, 16.0, 26.7], [-0.065, 501.1, 20.4, 26.2]],\n [[0.821, 568.8, 46.9, 40.5], [0.286, 530.9, 16.3, 31.1]],\n [[1.217, 437.0, 11.8, 36.0], [0.681, 459.0, 26.0, 13.8]]\n];\n// Linear sRGB primaries, D65.\nconst XYZ_TO_RGB: number[][] = [\n [3.2406, -1.5372, -0.4986],\n [-0.9689, 1.8758, 0.0415],\n [0.0557, -0.204, 1.057]\n];\n\nfunction unnormalizedRgb(wavelength: number): number[] {\n const xyz = XYZ_LOBES.map((lobes) => {\n let sum = 0;\n for (const lobe of lobes) {\n const t = (wavelength - lobe[1]) / (wavelength < lobe[1] ? lobe[2] : lobe[3]);\n sum += lobe[0] * Math.exp(-0.5 * t * t);\n }\n return sum;\n });\n return XYZ_TO_RGB.map((row) => Math.max(0, row[0] * xyz[0] + row[1] * xyz[1] + row[2] * xyz[2]));\n}\n\nfunction channelScale(): number[] {\n const steps = 3000;\n const sum = [0, 0, 0];\n for (let i = 0; i < steps; i++) {\n const rgb = unnormalizedRgb(SPECTRUM_WAVELENGTH_MIN + (SPECTRUM_WAVELENGTH_RANGE * (i + 0.5)) / steps);\n for (let c = 0; c < 3; c++) sum[c] += rgb[c] / steps;\n }\n // Rounded so JavaScript and native builds generate identical shader text.\n return sum.map((mean) => Number((1 / mean).toFixed(6)));\n}\nconst SCALE = channelScale();\n\nexport function spectrumRgb(wavelength: number): number[] {\n return unnormalizedRgb(wavelength).map((value, c) => value * SCALE[c]);\n}\n\n// Inverse CDFs in 5 nm bins: one per RGB channel for camera paths, and r+g+b for\n// photons. Narrow bins keep each sampled response close to the fit.\nconst SAMPLE_BINS = 60;\nconst SAMPLE_BIN_WIDTH = SPECTRUM_WAVELENGTH_RANGE / SAMPLE_BINS;\nexport const SPECTRUM_PHOTON_DISTRIBUTION = 3;\nfunction sampleCdfs(): number[] {\n const substeps = 20;\n const cdfs: number[] = [];\n for (let distribution = 0; distribution <= SPECTRUM_PHOTON_DISTRIBUTION; distribution++) {\n const cdf = [0];\n for (let bin = 0; bin < SAMPLE_BINS; bin++) {\n let mass = 0;\n for (let i = 0; i < substeps; i++) {\n const rgb = spectrumRgb(SPECTRUM_WAVELENGTH_MIN + (bin + (i + 0.5) / substeps) * SAMPLE_BIN_WIDTH);\n mass += distribution === SPECTRUM_PHOTON_DISTRIBUTION ? rgb[0] + rgb[1] + rgb[2] : rgb[distribution];\n }\n cdf.push(cdf[bin] + mass);\n }\n for (const value of cdf) cdfs.push(Number((value / cdf[SAMPLE_BINS]).toFixed(7)));\n }\n return cdfs;\n}\nconst SAMPLE_CDFS = sampleCdfs();\n\n/** Maps a unit random number to a wavelength and its probability density. */\nexport function spectrumSample(distribution: number, u: number) {\n const first = distribution * (SAMPLE_BINS + 1);\n let low = 0;\n let high = SAMPLE_BINS;\n while (high - low > 1) {\n const middle = (low + high) >> 1;\n if (SAMPLE_CDFS[first + middle] <= u) low = middle;\n else high = middle;\n }\n const mass = SAMPLE_CDFS[first + low + 1] - SAMPLE_CDFS[first + low];\n return {\n wavelength: SPECTRUM_WAVELENGTH_MIN + SAMPLE_BIN_WIDTH * (low + (u - SAMPLE_CDFS[first + low]) / mass),\n pdf: mass / SAMPLE_BIN_WIDTH\n };\n}\n\n// Cauchy IOR grows quickly at short wavelengths and strong dispersion scales turn\n// violet light into total internal reflection. Below 500 nm the refracting\n// wavelength eases toward 475 nm, while color still follows the sampled wavelength.\nconst IOR_KNEE = 500;\nconst IOR_KNEE_WIDTH = 25;\n\nexport function spectrumIorWavelength(wavelength: number) {\n if (wavelength >= IOR_KNEE) return wavelength;\n return IOR_KNEE - IOR_KNEE_WIDTH * (1 - Math.exp((wavelength - IOR_KNEE) / IOR_KNEE_WIDTH));\n}\n\nconst CDF_LITERALS = SAMPLE_CDFS.map((value) => value.toFixed(7)).join(',');\nconst lobeSum = (lobes: number[][]) =>\n lobes.map((lobe) => `${lobe[0]}f*spectrumLobe(wavelength,${lobe[1]}f,${lobe[2]}f,${lobe[3]}f)`).join('+');\n\nexport const spectrumWgsl = /* wgsl */ `\nfn spectrumLobe(wavelength:f32,mean:f32,below:f32,above:f32)->f32 {\n let t=(wavelength-mean)/select(above,below,wavelength<mean);\n return exp(-0.5*t*t);\n}\nfn spectrumRgb(wavelength:f32)->vec3f {\n let xyz=vec3f(${XYZ_LOBES.map(lobeSum).join(',')});\n let rgb=mat3x3f(${[0, 1, 2].map((c) => XYZ_TO_RGB.map((row) => `${row[c]}f`).join(',')).join(',')})*xyz;\n return max(rgb,vec3f(0.0))*vec3f(${SCALE.map((s) => `${s}f`).join(',')});\n}\nfn spectrumIorWavelength(wavelength:f32)->f32 {\n if(wavelength>=${IOR_KNEE}.0) { return wavelength; }\n return ${IOR_KNEE}.0-${IOR_KNEE_WIDTH}.0*(1.0-exp((wavelength-${IOR_KNEE}.0)/${IOR_KNEE_WIDTH}.0));\n}\nconst SPECTRUM_CDF=array<f32,${(SAMPLE_BINS + 1) * 4}>(${CDF_LITERALS});\nfn spectrumSample(distribution:u32,u:f32,pdf:ptr<function,f32>)->f32 {\n let first=distribution*${SAMPLE_BINS + 1}u;\n var low=0u;\n var high=${SAMPLE_BINS}u;\n while(high-low>1u) {\n let middle=(low+high)/2u;\n if(SPECTRUM_CDF[first+middle]<=u) { low=middle; } else { high=middle; }\n }\n let mass=SPECTRUM_CDF[first+low+1u]-SPECTRUM_CDF[first+low];\n *pdf=mass/${SAMPLE_BIN_WIDTH}.0;\n return ${SPECTRUM_WAVELENGTH_MIN}.0+${SAMPLE_BIN_WIDTH}.0*(f32(low)+(u-SPECTRUM_CDF[first+low])/mass);\n}\n// Refracting wavelength of a camera path that carries one RGB channel.\nfn spectrumCameraWavelength(channel:u32,u:f32)->f32 {\n var pdf=0.0;\n return spectrumIorWavelength(spectrumSample(channel,u,&pdf));\n}\n`;\n\nexport const spectrumGlsl = /* glsl */ `\nconst float SPECTRUM_CDF[${(SAMPLE_BINS + 1) * 4}]=float[${(SAMPLE_BINS + 1) * 4}](${CDF_LITERALS});\n// Refracting wavelength of a camera path that carries one RGB channel.\nfloat spectrumCameraWavelength(int channel,float u) {\n int first=channel*${SAMPLE_BINS + 1};\n int low=0;\n int high=${SAMPLE_BINS};\n while(high-low>1) {\n int middle=(low+high)/2;\n if(SPECTRUM_CDF[first+middle]<=u) { low=middle; } else { high=middle; }\n }\n float start=SPECTRUM_CDF[first+low];\n float wavelength=${SPECTRUM_WAVELENGTH_MIN}.0+${SAMPLE_BIN_WIDTH}.0*(float(low)+(u-start)/(SPECTRUM_CDF[first+low+1]-start));\n if(wavelength>=${IOR_KNEE}.0) { return wavelength; }\n return ${IOR_KNEE}.0-${IOR_KNEE_WIDTH}.0*(1.0-exp((wavelength-${IOR_KNEE}.0)/${IOR_KNEE_WIDTH}.0));\n}\n`;\n"]}
@@ -0,0 +1,39 @@
1
+ import type { EmissiveTriangle } from './lightSampling.js';
2
+ import type { AnalyticLightState } from '../scene/lightState.js';
3
+ /** Alias probabilities are stored as numeric texels; indices retain their bit representation. */
4
+ export declare function prepareWebGLLightSampling(lights: AnalyticLightState[], triangles: EmissiveTriangle[], environmentPower: number): {
5
+ data: Float32Array<ArrayBuffer>;
6
+ pmfOffset: number;
7
+ lightData: Float32Array<ArrayBufferLike>;
8
+ lightWidth: number;
9
+ lightHeight: number;
10
+ analyticPmfs: number[];
11
+ profiles: {
12
+ data: Float32Array<ArrayBuffer>;
13
+ offsets: Int32Array<ArrayBuffer>;
14
+ texelCount: number;
15
+ };
16
+ environmentPmf: number;
17
+ choiceCount: number;
18
+ totalWeight: number;
19
+ emissiveTexels: number;
20
+ domains: {
21
+ environment: {
22
+ count: number;
23
+ weight: number;
24
+ pmf: number;
25
+ };
26
+ infinite: {
27
+ count: number;
28
+ weight: number;
29
+ pmf: number;
30
+ };
31
+ localAndEmissive: {
32
+ count: number;
33
+ weight: number;
34
+ pmf: number;
35
+ };
36
+ };
37
+ shadowCatcherChoiceOffset: number;
38
+ shadowCatcherChoiceCount: number;
39
+ };
@@ -0,0 +1,15 @@
1
+ import { prepareSceneLightSampling } from './sceneAssembly.js';
2
+ /** Alias probabilities are stored as numeric texels; indices retain their bit representation. */
3
+ export function prepareWebGLLightSampling(lights, triangles, environmentPower) {
4
+ const lighting = prepareSceneLightSampling(lights, triangles, environmentPower, 4096);
5
+ const pmfOffset = lighting.emissiveTexels + lighting.choiceCount;
6
+ const data = new Float32Array(Math.max(1, pmfOffset + lighting.choiceCount) * 4);
7
+ data.set(lighting.lightData.subarray(0, pmfOffset * 4));
8
+ const words = new Int32Array(data.buffer);
9
+ for (let i = 0; i < lighting.choiceCount; i++) {
10
+ const type = words[(lighting.emissiveTexels + i) * 4 + 2], source = words[(lighting.emissiveTexels + i) * 4 + 3];
11
+ data[(pmfOffset + i) * 4] = type === 1 ? lighting.environmentPmf : type === 2 ? lighting.analyticPmfs[source] : triangles[source].selectionPmf;
12
+ }
13
+ return { ...lighting, data, pmfOffset };
14
+ }
15
+ //# sourceMappingURL=webglLightSampling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webglLightSampling.js","sourceRoot":"","sources":["../../src/renderer/webglLightSampling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,yBAAyB,EAAC,MAAM,oBAAoB,CAAC;AAG7D,iGAAiG;AACjG,MAAM,UAAU,yBAAyB,CAAC,MAA4B,EAAE,SAA6B,EAAE,gBAAwB;IAC7H,MAAM,QAAQ,GAAC,yBAAyB,CAAC,MAAM,EAAC,SAAS,EAAC,gBAAgB,EAAC,IAAI,CAAC,CAAC;IACjF,MAAM,SAAS,GAAC,QAAQ,CAAC,cAAc,GAAC,QAAQ,CAAC,WAAW,CAAC;IAC7D,MAAM,IAAI,GAAC,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAC,SAAS,GAAC,QAAQ,CAAC,WAAW,CAAC,GAAC,CAAC,CAAC,CAAC;IAC1E,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAC,SAAS,GAAC,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,KAAK,GAAC,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxC,KAAI,IAAI,CAAC,GAAC,CAAC,EAAC,CAAC,GAAC,QAAQ,CAAC,WAAW,EAAC,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,GAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,cAAc,GAAC,CAAC,CAAC,GAAC,CAAC,GAAC,CAAC,CAAC,EAAC,MAAM,GAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,cAAc,GAAC,CAAC,CAAC,GAAC,CAAC,GAAC,CAAC,CAAC,CAAC;QAChG,IAAI,CAAC,CAAC,SAAS,GAAC,CAAC,CAAC,GAAC,CAAC,CAAC,GAAC,IAAI,KAAG,CAAC,CAAA,CAAC,CAAA,QAAQ,CAAC,cAAc,CAAA,CAAC,CAAA,IAAI,KAAG,CAAC,CAAA,CAAC,CAAA,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA,CAAC,CAAA,SAAS,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC;IAC/H,CAAC;IACD,OAAO,EAAC,GAAG,QAAQ,EAAC,IAAI,EAAC,SAAS,EAAC,CAAC;AACtC,CAAC","sourcesContent":["import {prepareSceneLightSampling} from './sceneAssembly.js';\nimport type {EmissiveTriangle} from './lightSampling.js';\nimport type {AnalyticLightState} from '../scene/lightState.js';\n/** Alias probabilities are stored as numeric texels; indices retain their bit representation. */\nexport function prepareWebGLLightSampling(lights: AnalyticLightState[], triangles: EmissiveTriangle[], environmentPower: number) {\n const lighting=prepareSceneLightSampling(lights,triangles,environmentPower,4096);\n const pmfOffset=lighting.emissiveTexels+lighting.choiceCount;\n const data=new Float32Array(Math.max(1,pmfOffset+lighting.choiceCount)*4);\n data.set(lighting.lightData.subarray(0,pmfOffset*4));\n const words=new Int32Array(data.buffer);\n for(let i=0;i<lighting.choiceCount;i++) {\n const type=words[(lighting.emissiveTexels+i)*4+2],source=words[(lighting.emissiveTexels+i)*4+3];\n data[(pmfOffset+i)*4]=type===1?lighting.environmentPmf:type===2?lighting.analyticPmfs[source]:triangles[source].selectionPmf;\n }\n return {...lighting,data,pmfOffset};\n}\n"]}
@@ -32,12 +32,12 @@ extent: vec4u,
32
32
  @group(1) @binding(3) var<storage, read> materialTextureInfo: array<MaterialTextureInfo>;`;
33
33
  const sampling = /* wgsl */ `fn decodeMapColor(idx: i32, c: vec3f) -> vec3f {
34
34
  if (materialTextureInfo[idx].resources.w != 0u) { return c; }
35
- return mG(c);
35
+ return mq(c);
36
36
  }
37
- fn ah(val: i32) -> vec2f {
37
+ fn af(val: i32) -> vec2f {
38
38
  return vec2f(f32(val >> 8), f32(val & 0xff) / 255.0);
39
39
  }
40
- fn jO(idx: i32) -> vec2f { return vec2f(materialTextureInfo[idx].extent.xy); }
40
+ fn jv(idx: i32) -> vec2f { return vec2f(materialTextureInfo[idx].extent.xy); }
41
41
  fn getMapUvSet(idx: i32, uv: vec2f, uv1: vec2f) -> vec2f {
42
42
  return select(uv, uv1, materialTextureInfo[idx].resources.z != 0u);
43
43
  }
@@ -1,6 +1,13 @@
1
1
  import { type RayTraceInputHooks } from './wgsl/rayTrace.compute.js';
2
2
  export interface WGPUShaderFeatures {
3
3
  hasDisplacement: boolean;
4
+ debugGroup?: number;
5
+ /** Invocation block edge; the native host matches dispatch to this value. Default 8. */
6
+ workgroupSize?: number;
7
+ lightTracing?: boolean;
8
+ photonMapping?: boolean;
9
+ photonMaxDepth?: number;
10
+ photonDirectSubset?: boolean;
4
11
  hasAlpha: boolean;
5
12
  hasSceneAlpha: boolean;
6
13
  hasAnisotropyDirections: boolean;
@@ -1,3 +1,4 @@
1
+ import { researchTransport } from './researchTransport.js';
1
2
  import rayTraceCompute from './wgsl/rayTrace.compute.js';
2
3
  import { WGPU_SCENE_FEATURE_BITS } from './WGPUSceneFeatures.js';
3
4
  import { MATERIAL_TEXTURE_DEFINES } from './materialTextureSlots.js';
@@ -20,6 +21,7 @@ export function wgpuShaderPreamble(constants, bake = false) {
20
21
  return `\n${defineCode}\n${constantsCode}\n`;
21
22
  }
22
23
  export function wgpuShaderSource(constants, features, bake = false, sceneIntersections = '', pathDiagnostic = false, inputHooks) {
24
+ constants = researchTransport()?.constants(constants, features) ?? constants;
23
25
  return wgpuShaderPreamble(constants, bake) + rayTraceCompute({
24
26
  ...features,
25
27
  spectral: bake ? Boolean(constants.SPECTRAL) : undefined,
@@ -1 +1 @@
1
- {"version":3,"file":"wgpuShaderSource.js","sourceRoot":"","sources":["../../src/renderer/wgpuShaderSource.ts"],"names":[],"mappings":"AAAA,OAAO,eAA0C,MAAM,4BAA4B,CAAC;AACpF,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAYrE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,oBAAoB,CAAC,CAAC;AAChH,MAAM,gBAAgB,GAAG;IACvB,GAAG,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,EAAE,gBAAgB,EAAE,oBAAoB;IAC/E,yBAAyB,EAAE,GAAG,wBAAwB,EAAE,2BAA2B;IACnF,wBAAwB,EAAE,iBAAiB,EAAE,gBAAgB;CAC9D,CAAC;AACF,MAAM,UAAU,wBAAwB,CAAC,SAA2C;IAClF,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,uBAAuB,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;AAChI,CAAC;AACD,MAAM,UAAU,kBAAkB,CAAC,SAA2C,EAAE,IAAI,GAAG,KAAK;IAC1F,MAAM,UAAU,GAAG,uBAAuB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtG,MAAM,IAAI,GAAG,wBAAwB,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QACnC,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QAC7B,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,IAAI,GAAG,MAAM,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC;IAClI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO,KAAK,UAAU,KAAK,aAAa,IAAI,CAAC;AAC/C,CAAC;AACD,MAAM,UAAU,gBAAgB,CAAC,SAA2C,EAAE,QAA4B,EAAE,IAAI,GAAG,KAAK,EAAE,kBAAkB,GAAG,EAAE,EAAE,cAAc,GAAG,KAAK,EAAE,UAA+B;IACxM,OAAO,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,eAAe,CAAC;QAC3D,GAAG,QAAQ;QACX,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;QACxD,iBAAiB,EAAE,IAAI,IAAI,SAAS,CAAC,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,SAAS;QACxH,oBAAoB,EAAE,IAAI,IAAI,SAAS,CAAC,qBAAqB,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,SAAS;QACjI,iBAAiB,EAAE,IAAI,IAAI,SAAS,CAAC,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,SAAS;KACzH,EAAE,kBAAkB,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import rayTraceCompute, {type RayTraceInputHooks} from './wgsl/rayTrace.compute.js';\nimport { WGPU_SCENE_FEATURE_BITS } from './WGPUSceneFeatures.js';\nimport { MATERIAL_TEXTURE_DEFINES } from './materialTextureSlots.js';\n\nexport interface WGPUShaderFeatures {\n hasDisplacement: boolean;\n hasAlpha: boolean;\n hasSceneAlpha: boolean;\n hasAnisotropyDirections: boolean;\n hasCoatNormals: boolean;\n blasLayoutMask: number;\n /** Environment presence changes background/MIS semantics independently of packed material data. */\n dynamicEnvironment?: boolean;\n}\nexport const WGPU_SHADER_DEFINE_KEYS = ['NUM_MAPS', 'TLAS_STACK_SIZE', 'BLAS_STACK_SIZE', 'NUM_UNIFORM_LIGHTS'];\nconst constantsNotUsed = [\n ...Object.keys(WGPU_SCENE_FEATURE_BITS), 'USE_BACKGROUND', 'USE_BACKGROUND_MAP',\n 'USE_GROUNDED_BACKGROUND', ...MATERIAL_TEXTURE_DEFINES, 'USE_ANISOTROPY_DIRECTIONS',\n 'USE_COAT_NORMAL_FRAMES', 'HAS_ALPHA_BLEND', 'HAS_ALPHA_MASK'\n];\nexport function wgpuPipelineConstantKeys(constants: Record<string, boolean | number>) {\n return Object.keys(constants).sort().filter(key => !WGPU_SHADER_DEFINE_KEYS.includes(key) && !constantsNotUsed.includes(key));\n}\nexport function wgpuShaderPreamble(constants: Record<string, boolean | number>, bake = false): string {\n const defineCode = WGPU_SHADER_DEFINE_KEYS.map(key => `const ${key} = ${constants[key]};`).join('\\n');\n const keys = wgpuPipelineConstantKeys(constants);\n const constantsCode = keys.map(key => {\n const value = constants[key];\n return `${bake ? 'const' : 'override'} ${key} = ${typeof value === 'boolean' ? (value ? 'true' : 'false') : value.toString()};`;\n }).join('\\n');\n return `\\n${defineCode}\\n${constantsCode}\\n`;\n}\nexport function wgpuShaderSource(constants: Record<string, boolean | number>, features: WGPUShaderFeatures, bake = false, sceneIntersections = '', pathDiagnostic = false, inputHooks?: RayTraceInputHooks): string {\n return wgpuShaderPreamble(constants, bake) + rayTraceCompute({\n ...features,\n spectral: bake ? Boolean(constants.SPECTRAL) : undefined,\n sceneCapabilities: bake && constants.SCENE_CAPABILITIES !== undefined ? Number(constants.SCENE_CAPABILITIES) : undefined,\n materialCapabilities: bake && constants.MATERIAL_CAPABILITIES !== undefined ? Number(constants.MATERIAL_CAPABILITIES) : undefined,\n lightCapabilities: bake && constants.LIGHT_CAPABILITIES !== undefined ? Number(constants.LIGHT_CAPABILITIES) : undefined\n }, sceneIntersections, pathDiagnostic, inputHooks);\n}\n"]}
1
+ {"version":3,"file":"wgpuShaderSource.js","sourceRoot":"","sources":["../../src/renderer/wgpuShaderSource.ts"],"names":[],"mappings":"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","sourcesContent":["import {researchTransport} from './researchTransport.js';\nimport rayTraceCompute, {type RayTraceInputHooks} from './wgsl/rayTrace.compute.js';\nimport { WGPU_SCENE_FEATURE_BITS } from './WGPUSceneFeatures.js';\nimport { MATERIAL_TEXTURE_DEFINES } from './materialTextureSlots.js';\n\nexport interface WGPUShaderFeatures {\n hasDisplacement: boolean;\n debugGroup?: number;\n /** Invocation block edge; the native host matches dispatch to this value. Default 8. */\n workgroupSize?:number;\n lightTracing?: boolean;\n photonMapping?:boolean;\n photonMaxDepth?:number;\n photonDirectSubset?:boolean;\n hasAlpha: boolean;\n hasSceneAlpha: boolean;\n hasAnisotropyDirections: boolean;\n hasCoatNormals: boolean;\n blasLayoutMask: number;\n /** Environment presence changes background/MIS semantics independently of packed material data. */\n dynamicEnvironment?: boolean;\n}\nexport const WGPU_SHADER_DEFINE_KEYS = ['NUM_MAPS', 'TLAS_STACK_SIZE', 'BLAS_STACK_SIZE', 'NUM_UNIFORM_LIGHTS'];\nconst constantsNotUsed = [\n ...Object.keys(WGPU_SCENE_FEATURE_BITS), 'USE_BACKGROUND', 'USE_BACKGROUND_MAP',\n 'USE_GROUNDED_BACKGROUND', ...MATERIAL_TEXTURE_DEFINES, 'USE_ANISOTROPY_DIRECTIONS',\n 'USE_COAT_NORMAL_FRAMES', 'HAS_ALPHA_BLEND', 'HAS_ALPHA_MASK'\n];\nexport function wgpuPipelineConstantKeys(constants: Record<string, boolean | number>) {\n return Object.keys(constants).sort().filter(key => !WGPU_SHADER_DEFINE_KEYS.includes(key) && !constantsNotUsed.includes(key));\n}\nexport function wgpuShaderPreamble(constants: Record<string, boolean | number>, bake = false): string {\n const defineCode = WGPU_SHADER_DEFINE_KEYS.map(key => `const ${key} = ${constants[key]};`).join('\\n');\n const keys = wgpuPipelineConstantKeys(constants);\n const constantsCode = keys.map(key => {\n const value = constants[key];\n return `${bake ? 'const' : 'override'} ${key} = ${typeof value === 'boolean' ? (value ? 'true' : 'false') : value.toString()};`;\n }).join('\\n');\n return `\\n${defineCode}\\n${constantsCode}\\n`;\n}\nexport function wgpuShaderSource(constants: Record<string, boolean | number>, features: WGPUShaderFeatures, bake = false, sceneIntersections = '', pathDiagnostic = false, inputHooks?: RayTraceInputHooks): string {\n constants = researchTransport()?.constants(constants, features) ?? constants;\n return wgpuShaderPreamble(constants, bake) + rayTraceCompute({\n ...features,\n spectral: bake ? Boolean(constants.SPECTRAL) : undefined,\n sceneCapabilities: bake && constants.SCENE_CAPABILITIES !== undefined ? Number(constants.SCENE_CAPABILITIES) : undefined,\n materialCapabilities: bake && constants.MATERIAL_CAPABILITIES !== undefined ? Number(constants.MATERIAL_CAPABILITIES) : undefined,\n lightCapabilities: bake && constants.LIGHT_CAPABILITIES !== undefined ? Number(constants.LIGHT_CAPABILITIES) : undefined\n }, sceneIntersections, pathDiagnostic, inputHooks);\n}\n"]}
@@ -9,17 +9,17 @@ struct DisplayAovParams { sizeKind: vec4u, mapping: vec4f, flags: vec4u, }
9
9
  @group(0) @binding(2) var image: texture_storage_2d<${format}, write>;
10
10
  @group(0) @binding(3) var<uniform> params: DisplayAovParams;
11
11
 
12
- fn tF(input: u32) -> u32 {
12
+ fn tG(input: u32) -> u32 {
13
13
  var v = input;
14
14
  v = (v ^ (v >> 16u)) * 0x7feb352du;
15
15
  v = (v ^ (v >> 15u)) * 0x846ca68bu;
16
16
  return v ^ (v >> 16u);
17
17
  }
18
- fn tG(rgb: vec3f) -> vec3f {
18
+ fn tH(rgb: vec3f) -> vec3f {
19
19
  return select(1.055 * pow(max(rgb, vec3f(0.0)), vec3f(1.0 / 2.4)) - 0.055,
20
20
  12.92 * rgb, rgb <= vec3f(0.0031308));
21
21
  }
22
- fn sE(rgb: vec3f) -> vec3f {
22
+ fn sA(rgb: vec3f) -> vec3f {
23
23
  return select(pow((rgb + 0.055) / 1.055, vec3f(2.4)), rgb / 12.92,
24
24
  rgb <= vec3f(0.04045));
25
25
  }
@@ -45,24 +45,24 @@ color = clamp(pixel.geometry.yzw * inv, vec3f(0.0), vec3f(1.0));
45
45
  } else if (kind == 3u || kind == 4u) {
46
46
  let r = select(pixel.diffuse, pixel.specular, kind == 4u);
47
47
  alpha = r.w / f32(max(params.sizeKind.z, 1u));
48
- color = tG(clamp(r.xyz / max(r.w, 1.0), vec3f(0.0), vec3f(1.0)));
48
+ color = tH(clamp(r.xyz / max(r.w, 1.0), vec3f(0.0), vec3f(1.0)));
49
49
  } else if (kind == 5u) {
50
50
  let identity = center[index].identity;
51
51
  alpha = select(1.0, 0.0, identity.x == 0xffffffffu);
52
- let hash = tF(identity[params.flags.x]);
52
+ let hash = tG(identity[params.flags.x]);
53
53
  color = 0.2 + 0.8 * vec3f(f32(hash & 255u), f32((hash >> 8u) & 255u),
54
54
  f32((hash >> 16u) & 255u)) / 255.0;
55
55
  } else {
56
56
  let m = center[index].motion;
57
57
  alpha = select(0.0, 1.0, m.w == 3.0);
58
- let gR = length(m.xy);
58
+ let gz = length(m.xy);
59
59
  var hue = 0.0;
60
- if (gR > 0.0) { hue = fract(atan2(m.y, m.x) / 6.28318530718 + 1.0); }
60
+ if (gz > 0.0) { hue = fract(atan2(m.y, m.x) / 6.28318530718 + 1.0); }
61
61
  let p = abs(fract(vec3f(hue) + vec3f(0.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - 3.0);
62
- color = clamp(p - 1.0, vec3f(0.0), vec3f(1.0)) * clamp(gR / params.mapping.w, 0.0, 1.0);
62
+ color = clamp(p - 1.0, vec3f(0.0), vec3f(1.0)) * clamp(gz / params.mapping.w, 0.0, 1.0);
63
63
  }
64
64
  if (alpha <= 0.0) { color = vec3f(0.0); }
65
- if (params.flags.y != 0u) { color = sE(color); }
65
+ if (params.flags.y != 0u) { color = sA(color); }
66
66
  textureStore(image, vec2i(id.xy), vec4f(color, clamp(alpha, 0.0, 1.0)));
67
67
  }
68
68
  `;
@@ -4,95 +4,95 @@ import proposals from './aovReflectanceSamples.wgsl.js';
4
4
  // evaluate diffuse and reflection together without traversing the full layered
5
5
  // BSDF twice. Callers reject coat, fuzz, anisotropy, SSS and thin film first.
6
6
  export default proposals + /* wgsl */ `
7
- struct oa {
7
+ struct nQ {
8
8
  diffuse: vec3f,
9
9
  specular: vec3f,
10
10
  }
11
- fn hN(prepared: ag, qQ: bool, kY: bool) -> oa {
12
- var result = oa();
11
+ fn hu(prepared: OpenPbrPreparedBsdf, qH: bool, kE: bool) -> nQ {
12
+ var result = nQ();
13
13
  let view = prepared.viewLocal;
14
14
  if (view.z <= 1e-6) { return result; }
15
15
  let material = prepared.material;
16
16
  let alpha = vec2f(material.alphaX, material.alphaY);
17
- let mg = aF(view, alpha);
18
- let jl = prepared.weights[OPENPBR_LOBE_DIFFUSE];
19
- let gb = prepared.weights[OPENPBR_LOBE_METAL];
17
+ let lN = aD(view, alpha);
18
+ let iR = prepared.weights[OPENPBR_LOBE_DIFFUSE];
19
+ let fN = prepared.weights[OPENPBR_LOBE_METAL];
20
20
  let specularWeight = prepared.weights[OPENPBR_LOBE_SPECULAR];
21
- var ei = 1.0;
22
- if (kY && prepared.dielectricMsProbability > 0.0) {
23
- ei = max(bf(prepared.refractionEtaTOverEtaI, prepared.specularAlpha), 1e-12);
21
+ var dS = 1.0;
22
+ if (kE && prepared.dielectricMsProbability > 0.0) {
23
+ dS = max(aY(prepared.refractionEtaTOverEtaI, prepared.specularAlpha), 1e-12);
24
24
  }
25
- let cr = normalize(vec3f(alpha, 1.0) * view);
26
- let qP = min(cr.z, 1.0);
27
- let kh = 1.0 + qP;
25
+ let ce = normalize(vec3f(alpha, 1.0) * view);
26
+ let qG = min(ce.z, 1.0);
27
+ let jN = 1.0 + qG;
28
28
  let count = 256u * min(rtUniform.loopUnrollGuard, 4u) / 4u;
29
29
  for (var i = 0u; i < count; i++) {
30
- let vV = i / 2u;
31
- let u = vec2f((f32(vV) + 0.5) / f32(count / 2u),
32
- fract(f32(reverseBits(vV)) * 2.3283064365386963e-10 + 0.37));
33
- let kK = hp[vV];
30
+ let wn = i / 2u;
31
+ let u = vec2f((f32(wn) + 0.5) / f32(count / 2u),
32
+ fract(f32(reverseBits(wn)) * 2.3283064365386963e-10 + 0.37));
33
+ let proposal = gV[wn];
34
34
 
35
35
 
36
- let hO = prepared.specularAlpha < 0.001 || count != 256u;
37
- var light = vec3f(kK.xy, sqrt(max(0.0, 1.0 - dot(kK.xy, kK.xy))));
38
- if (hO) { light = aO(u); }
36
+ let hv = prepared.specularAlpha < 0.001 || count != 256u;
37
+ var light = vec3f(proposal.xy, sqrt(max(0.0, 1.0 - dot(proposal.xy, proposal.xy))));
38
+ if (hv) { light = aM(u); }
39
39
  if ((i & 1u) != 0u) {
40
- let kC = (1.0 - u.y) * kh;
41
- let ex = sqrt(max(0.0, (kC + (1.0 - qP)) * (u.y * kh)));
42
- var half = normalize(vec3f(alpha, 1.0) * vec3f(cr.xy + ex * kK.zw, kC));
43
- if (hO) { half = aD(view, alpha, u); }
40
+ let kf = (1.0 - u.y) * jN;
41
+ let dr = sqrt(max(0.0, (kf + (1.0 - qG)) * (u.y * jN)));
42
+ var half = normalize(vec3f(alpha, 1.0) * vec3f(ce.xy + dr * proposal.zw, kf));
43
+ if (hv) { half = aA(view, alpha, u); }
44
44
  light = reflect(-view, half);
45
45
  }
46
46
 
47
47
  if (light.z <= 0.0) { continue; }
48
48
  let half = normalize(view + light);
49
- let qH = mg * cW(half, alpha) / (4.0 * view.z);
50
- let pdf = 0.5 * (light.z * INVPI + qH);
49
+ let specularPdf = lN * sampling_ggx_ndf(half, alpha) / (4.0 * view.z);
50
+ let pdf = 0.5 * (light.z * INVPI + specularPdf);
51
51
 
52
52
 
53
- let aA = openpbr_to_local(prepared.basis, openpbr_to_world(prepared.basis, light));
54
- if (aA.z <= 0.0) { continue; }
55
- let scale = abs(aA.z) / (pdf * f32(count));
56
- if (qQ && jl > 0.0) {
57
- let compensation = prepared.diffuseSpecularEnergyCompensation * aw(
58
- prepared.specularEtaTOverEtaI, prepared.specularAlpha, abs(aA.z));
59
- var sZ = 0.0;
60
- result.diffuse += jl * bJ(material, prepared.diffuseColor,
61
- view, aA, compensation, &sZ) * scale;
53
+ let au = openpbr_to_local(prepared.basis, openpbr_to_world(prepared.basis, light));
54
+ if (au.z <= 0.0) { continue; }
55
+ let scale = abs(au.z) / (pdf * f32(count));
56
+ if (qH && iR > 0.0) {
57
+ let compensation = prepared.diffuseSpecularEnergyCompensation * ar(
58
+ prepared.specularEtaTOverEtaI, prepared.specularAlpha, abs(au.z));
59
+ var unusedPdf = 0.0;
60
+ result.diffuse += iR * bw(material, prepared.diffuseColor,
61
+ view, au, compensation, &unusedPdf) * scale;
62
62
  }
63
- if (kY) {
64
- let jn = normalize(view + aA);
65
- let bm = abs(dot(view, jn));
66
- var bG = vec3f(0.0);
63
+ if (kE) {
64
+ let evaluatedHalf = normalize(view + au);
65
+ let viewHalf = abs(dot(view, evaluatedHalf));
66
+ var fresnel = vec3f(0.0);
67
67
  if (specularWeight > 0.0) {
68
- var dielectric = vec3f(af(bm, material.relativeIor));
69
- if (material.thinWalled) { dielectric = av(dielectric, material.transmissionWeight); }
70
- bG += specularWeight * material.specularColor * dielectric;
68
+ var dielectric = vec3f(ae(viewHalf, material.relativeIor));
69
+ if (material.thinWalled) { dielectric = aq(dielectric, material.transmissionWeight); }
70
+ fresnel += specularWeight * material.specularColor * dielectric;
71
71
  }
72
- if (gb > 0.0) {
73
- bG += gb * material.specularWeight * cw(bm,
72
+ if (fN > 0.0) {
73
+ fresnel += fN * material.specularWeight * openpbr_metal_fresnel(viewHalf,
74
74
  material.baseWeight * material.baseColor, material.specularColor);
75
75
  }
76
- let rJ = cW(jn, alpha) * mg
77
- * aF(aA, alpha) / (4.0 * max(aA.z * view.z, 1e-8));
78
- var reflection = bG * rJ;
79
- if (gb > 0.0 && prepared.metalMsFactor > 0.0) {
80
- let cL = min(prepared.metalMsFactor * aV(prepared.specularAlpha, aA.z), 1.0 / max(aA.z, 1e-8));
81
- reflection += gb * prepared.metalMsScale * cL * INVPI;
76
+ let rE = sampling_ggx_ndf(evaluatedHalf, alpha) * lN
77
+ * aD(au, alpha) / (4.0 * max(au.z * view.z, 1e-8));
78
+ var reflection = fresnel * rE;
79
+ if (fN > 0.0 && prepared.metalMsFactor > 0.0) {
80
+ let cv = min(prepared.metalMsFactor * aR(prepared.specularAlpha, au.z), 1.0 / max(au.z, 1e-8));
81
+ reflection += fN * prepared.metalMsScale * cv * INVPI;
82
82
  }
83
83
  if (prepared.dielectricMsProbability > 0.0) {
84
- let oZ = prepared.dielectricMsViewLoss * bb(
85
- prepared.refractionEtaTOverEtaI, prepared.specularAlpha, abs(aA.z)) / ei;
86
- let cL = min(oZ * prepared.dielectricMsReflectionRatio, 1.0 / abs(aA.z));
87
- reflection += prepared.dielectricMsReflectionScale * cL * INVPI;
84
+ let oP = prepared.dielectricMsViewLoss * aV(
85
+ prepared.refractionEtaTOverEtaI, prepared.specularAlpha, abs(au.z)) / dS;
86
+ let cv = min(oP * prepared.dielectricMsReflectionRatio, 1.0 / abs(au.z));
87
+ reflection += prepared.dielectricMsReflectionScale * cv * INVPI;
88
88
  }
89
89
  result.specular += reflection * scale;
90
90
  }
91
91
  }
92
92
  return result;
93
93
  }
94
- fn vT(prepared: ag) -> oa {
95
- return hN(prepared, true, true);
94
+ fn wl(prepared: OpenPbrPreparedBsdf) -> nQ {
95
+ return hu(prepared, true, true);
96
96
  }
97
97
  `;
98
98
  //# sourceMappingURL=aovReflectance.wgsl.js.map
@@ -1,7 +1,7 @@
1
1
  // Generated by scripts/generate-aov-reflectance-samples.mjs; do not edit.
2
2
  // xy: concentric cosine disk; zw: spherical-cap azimuth cosine/sine.
3
3
  export default /* wgsl */ `
4
- const hp: array<vec4f, 128> = array<vec4f, 128>(
4
+ const gV: array<vec4f, 128> = array<vec4f, 128>(
5
5
  vec4f(-0.971247852, -0.202764958, 0.999698818, 0.0245412290),
6
6
  vec4f(-0.808660269, 0.547487736, 0.997290432, 0.0735645667),
7
7
  vec4f(-0.942509234, 0.187289074, 0.992479563, 0.122410677),
@@ -5,7 +5,7 @@ struct AovResolvedCenter { identity: vec4u, motion: vec4f, }
5
5
  @group(0) @binding(0) var<storage,read> accumulation: array<AovResolvedAccumulation>;
6
6
  @group(0) @binding(1) var<storage,read> center: array<AovResolvedCenter>;
7
7
  @group(0) @binding(3) var<uniform> parameters: vec4u;
8
- fn ox(source: u32, kind: u32) -> vec4u {
8
+ fn oj(source: u32, kind: u32) -> vec4u {
9
9
  if(kind==5u){return center[source].identity;}
10
10
  var value=vec4f(0.0);
11
11
  if(kind==6u){let motion=center[source].motion;value=vec4f(motion.xy,0.0,motion.w);}
@@ -32,7 +32,7 @@ let width=parameters.x;let height=parameters.y;
32
32
  if(id.x>=width||id.y>=height){return;}
33
33
  let index=id.x+id.y*width;
34
34
  let source=id.x+select(id.y,height-1u-id.y,(parameters.w&256u)!=0u)*width;
35
- output[index]=ox(source,parameters.w&255u);
35
+ output[index]=oj(source,parameters.w&255u);
36
36
  }
37
37
  `;
38
38
  //# sourceMappingURL=aovResolve.wgsl.js.map
@@ -14,7 +14,7 @@ let width=parameters.x;let height=parameters.y;
14
14
  if(id.x>=width||id.y>=height){return;}
15
15
  let index=id.x+id.y*width;
16
16
  let source=id.x+(height-1u-id.y)*width;
17
- ${kinds.map((kind, i) => `output${i}[index]=ox(source,${AOV_KINDS.indexOf(kind)}u);`).join('\n ')}
17
+ ${kinds.map((kind, i) => `output${i}[index]=oj(source,${AOV_KINDS.indexOf(kind)}u);`).join('\n ')}
18
18
  }
19
19
  `;
20
20
  }