@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.
- package/README.md +3 -0
- package/dist/assets/bvh.worker.js +1 -1
- package/dist/mutracer.umd.js +8949 -7915
- package/docs/api.md +42 -21
- package/docs/caustics.md +80 -0
- package/docs/materials.md +5 -4
- package/docs/progressive-rendering.md +50 -9
- package/docs/technical/continuous-spectrum.md +93 -0
- package/docs/updates.md +3 -1
- package/docs/viewer-checklist.md +65 -0
- package/docs/viewer.md +7 -5
- package/lib/Display.js +1 -1
- package/lib/Display.js.map +1 -1
- package/lib/Engine.d.ts +2 -0
- package/lib/Engine.js +47 -12
- package/lib/Engine.js.map +1 -1
- package/lib/Render.d.ts +61 -13
- package/lib/Render.js +169 -19
- package/lib/Render.js.map +1 -1
- package/lib/RenderLoop.d.ts +7 -3
- package/lib/RenderLoop.js +68 -44
- package/lib/RenderLoop.js.map +1 -1
- package/lib/main.d.ts +1 -1
- package/lib/main.js.map +1 -1
- package/lib/oidn.d.ts +1 -1
- package/lib/oidnBundledWeights.cdn.d.ts +2 -2
- package/lib/renderer/GPUTraceQueue.d.ts +2 -1
- package/lib/renderer/GPUTraceQueue.js +3 -1
- package/lib/renderer/GPUTraceQueue.js.map +1 -1
- package/lib/renderer/RenderingPipeline.d.ts +4 -0
- package/lib/renderer/RenderingPipeline.js.map +1 -1
- package/lib/renderer/WGPUCausticLightPass.d.ts +48 -0
- package/lib/renderer/WGPUCausticLightPass.js +217 -0
- package/lib/renderer/WGPUCausticLightPass.js.map +1 -0
- package/lib/renderer/WGPUEnvMapDistribution.js +18 -18
- package/lib/renderer/WGPUFullQuadPass.js +3 -3
- package/lib/renderer/WGPUPhotonMap.d.ts +37 -0
- package/lib/renderer/WGPUPhotonMap.js +191 -0
- package/lib/renderer/WGPUPhotonMap.js.map +1 -0
- package/lib/renderer/WGPURayTraceOutput.d.ts +13 -1
- package/lib/renderer/WGPURayTraceOutput.js +122 -28
- package/lib/renderer/WGPURayTraceOutput.js.map +1 -1
- package/lib/renderer/WGPURayTracePass.d.ts +48 -17
- package/lib/renderer/WGPURayTracePass.js +538 -340
- package/lib/renderer/WGPURayTracePass.js.map +1 -1
- package/lib/renderer/WGPURenderingPipeline.js +83 -47
- package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLFullQuadPass.d.ts +2 -1
- package/lib/renderer/WebGLFullQuadPass.js +4 -0
- package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
- package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
- package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
- package/lib/renderer/WebGLRayTracePass.d.ts +6 -1
- package/lib/renderer/WebGLRayTracePass.js +43 -6
- package/lib/renderer/WebGLRayTracePass.js.map +1 -1
- package/lib/renderer/WebGLRenderingPipeline.js +10 -14
- package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLSkyBaker.js +60 -60
- package/lib/renderer/accumulationTarget.d.ts +2 -1
- package/lib/renderer/accumulationTarget.js.map +1 -1
- package/lib/renderer/causticDiagnostics.d.ts +31 -0
- package/lib/renderer/causticDiagnostics.js +5 -0
- package/lib/renderer/causticDiagnostics.js.map +1 -0
- package/lib/renderer/causticEmission.d.ts +10 -0
- package/lib/renderer/causticEmission.js +40 -0
- package/lib/renderer/causticEmission.js.map +1 -0
- package/lib/renderer/causticUniform.d.ts +12 -0
- package/lib/renderer/causticUniform.js +22 -0
- package/lib/renderer/causticUniform.js.map +1 -0
- package/lib/renderer/caustics.d.ts +16 -0
- package/lib/renderer/caustics.js +93 -0
- package/lib/renderer/caustics.js.map +1 -0
- package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
- package/lib/renderer/glsl/aovReflectance.glsl.js +53 -53
- package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
- package/lib/renderer/glsl/aovTrace.frag.js +33 -33
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -9
- package/lib/renderer/glsl/chunks/emissiveLight.glsl.d.ts +2 -0
- package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +35 -0
- package/lib/renderer/glsl/chunks/emissiveLight.glsl.js.map +1 -0
- package/lib/renderer/glsl/chunks/envMap.glsl.js +73 -73
- package/lib/renderer/glsl/chunks/intersect.glsl.js +426 -428
- package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.d.ts +1 -0
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +156 -153
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +771 -771
- package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
- package/lib/renderer/glsl/chunks/random.glsl.js +6 -6
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.d.ts +1 -0
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +68 -63
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js +94 -76
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -0
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +140 -117
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +14 -14
- package/lib/renderer/glsl/chunks/sampling.glsl.js +89 -92
- package/lib/renderer/glsl/chunks/sampling.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sobol.glsl.js +79 -79
- package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
- package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
- package/lib/renderer/glsl/depthQuery.frag.js +2 -2
- package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
- package/lib/renderer/glsl/rayTrace.frag.d.ts +1 -1
- package/lib/renderer/glsl/rayTrace.frag.js +260 -237
- package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/shadowCatcher.frag.js +26 -26
- package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
- package/lib/renderer/glsl/webglPipeline.frag.js +59 -59
- package/lib/renderer/lightSampling.d.ts +3 -2
- package/lib/renderer/lightSampling.js +14 -8
- package/lib/renderer/lightSampling.js.map +1 -1
- package/lib/renderer/progressiveScheduler.js +4 -3
- package/lib/renderer/progressiveScheduler.js.map +1 -1
- package/lib/renderer/renderPipeline.d.ts +1 -0
- package/lib/renderer/renderPipeline.js.map +1 -1
- package/lib/renderer/researchTransport.d.ts +31 -0
- package/lib/renderer/researchTransport.js +5 -0
- package/lib/renderer/researchTransport.js.map +1 -0
- package/lib/renderer/sceneLayoutSchema.d.ts +1 -0
- package/lib/renderer/sceneLayoutSchema.js +2 -2
- package/lib/renderer/sceneLayoutSchema.js.map +1 -1
- package/lib/renderer/scratchSampling.js +6 -6
- package/lib/renderer/spectrum.d.ts +21 -0
- package/lib/renderer/spectrum.js +154 -0
- package/lib/renderer/spectrum.js.map +1 -0
- package/lib/renderer/webglLightSampling.d.ts +39 -0
- package/lib/renderer/webglLightSampling.js +15 -0
- package/lib/renderer/webglLightSampling.js.map +1 -0
- package/lib/renderer/wgpuBindlessSource.js +3 -3
- package/lib/renderer/wgpuShaderSource.d.ts +7 -0
- package/lib/renderer/wgpuShaderSource.js +2 -0
- package/lib/renderer/wgpuShaderSource.js.map +1 -1
- package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
- package/lib/renderer/wgsl/aovReflectance.wgsl.js +55 -55
- package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
- package/lib/renderer/wgsl/aovResolve.wgsl.js +2 -2
- package/lib/renderer/wgsl/aovSnapshot.wgsl.js +1 -1
- package/lib/renderer/wgsl/aovTrace.wgsl.js +33 -33
- package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
- package/lib/renderer/wgsl/causticLight.compute.d.ts +6 -0
- package/lib/renderer/wgsl/causticLight.compute.js +537 -0
- package/lib/renderer/wgsl/causticLight.compute.js.map +1 -0
- package/lib/renderer/wgsl/chunks/background.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/chunks/background.wgsl.js +102 -0
- package/lib/renderer/wgsl/chunks/background.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +12 -0
- package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +16 -0
- package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/caustics.wgsl.d.ts +1 -0
- package/lib/renderer/wgsl/chunks/caustics.wgsl.js +62 -0
- package/lib/renderer/wgsl/chunks/caustics.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js +37 -37
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js +598 -592
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +14 -14
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +2 -1
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +75 -73
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +794 -794
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +139 -139
- package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +2 -1
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +35 -25
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +142 -138
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +204 -187
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +11 -11
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampling.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js +89 -93
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sobol.wgsl.js +83 -83
- package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
- package/lib/renderer/wgsl/compose.frag.wgsl.js +98 -98
- package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
- package/lib/renderer/wgsl/depthQuery.wgsl.js +2 -2
- package/lib/renderer/wgsl/jnlm.compute.js +74 -74
- package/lib/renderer/wgsl/offlineAdaptive.compute.js +25 -25
- package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
- package/lib/renderer/wgsl/photonMap.compute.d.ts +29 -0
- package/lib/renderer/wgsl/photonMap.compute.js +352 -0
- package/lib/renderer/wgsl/photonMap.compute.js.map +1 -0
- package/lib/renderer/wgsl/proceduralSky.compute.js +64 -64
- package/lib/renderer/wgsl/rayTrace.compute.d.ts +9 -1
- package/lib/renderer/wgsl/rayTrace.compute.js +448 -522
- package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
- package/lib/scene/MaterialState.d.ts +2 -2
- package/lib/scene/MaterialState.js +2 -2
- package/lib/scene/MaterialState.js.map +1 -1
- package/lib/settings/render.d.ts +11 -0
- package/lib/settings/render.js +1 -0
- package/lib/settings/render.js.map +1 -1
- package/lib/util/fromTHREE.js +2 -2
- package/lib/util/fromTHREE.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/llms.txt +2 -1
- 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
|
|
35
|
+
return mq(c);
|
|
36
36
|
}
|
|
37
|
-
fn
|
|
37
|
+
fn af(val: i32) -> vec2f {
|
|
38
38
|
return vec2f(f32(val >> 8), f32(val & 0xff) / 255.0);
|
|
39
39
|
}
|
|
40
|
-
fn
|
|
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;
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
58
|
+
let gz = length(m.xy);
|
|
59
59
|
var hue = 0.0;
|
|
60
|
-
if (
|
|
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(
|
|
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 =
|
|
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
|
|
7
|
+
struct nQ {
|
|
8
8
|
diffuse: vec3f,
|
|
9
9
|
specular: vec3f,
|
|
10
10
|
}
|
|
11
|
-
fn
|
|
12
|
-
var result =
|
|
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
|
|
18
|
-
let
|
|
19
|
-
let
|
|
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
|
|
22
|
-
if (
|
|
23
|
-
|
|
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
|
|
26
|
-
let
|
|
27
|
-
let
|
|
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
|
|
31
|
-
let u = vec2f((f32(
|
|
32
|
-
fract(f32(reverseBits(
|
|
33
|
-
let
|
|
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
|
|
37
|
-
var light = vec3f(
|
|
38
|
-
if (
|
|
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
|
|
41
|
-
let
|
|
42
|
-
var half = normalize(vec3f(alpha, 1.0) * vec3f(
|
|
43
|
-
if (
|
|
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
|
|
50
|
-
let pdf = 0.5 * (light.z * INVPI +
|
|
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
|
|
54
|
-
if (
|
|
55
|
-
let scale = abs(
|
|
56
|
-
if (
|
|
57
|
-
let compensation = prepared.diffuseSpecularEnergyCompensation *
|
|
58
|
-
prepared.specularEtaTOverEtaI, prepared.specularAlpha, abs(
|
|
59
|
-
var
|
|
60
|
-
result.diffuse +=
|
|
61
|
-
view,
|
|
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 (
|
|
64
|
-
let
|
|
65
|
-
let
|
|
66
|
-
var
|
|
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(
|
|
69
|
-
if (material.thinWalled) { dielectric =
|
|
70
|
-
|
|
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 (
|
|
73
|
-
|
|
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
|
|
77
|
-
*
|
|
78
|
-
var reflection =
|
|
79
|
-
if (
|
|
80
|
-
let
|
|
81
|
-
reflection +=
|
|
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
|
|
85
|
-
prepared.refractionEtaTOverEtaI, prepared.specularAlpha, abs(
|
|
86
|
-
let
|
|
87
|
-
reflection += prepared.dielectricMsReflectionScale *
|
|
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
|
|
95
|
-
return
|
|
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
|
|
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
|
|
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]=
|
|
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]=
|
|
17
|
+
${kinds.map((kind, i) => `output${i}[index]=oj(source,${AOV_KINDS.indexOf(kind)}u);`).join('\n ')}
|
|
18
18
|
}
|
|
19
19
|
`;
|
|
20
20
|
}
|