@meimu/mutracer 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +222 -0
- package/LICENSES/FidelityFX-FSR.txt +19 -0
- package/LICENSES/pbrt-v4-Apache-2.0.txt +202 -0
- package/THIRD_PARTY_NOTICES.md +18 -4
- package/dist/assets/bvh.worker.js +1 -1
- package/dist/mutracer.umd.js +6260 -5580
- package/docs/api.md +45 -18
- package/docs/materials.md +4 -2
- package/docs/performance.md +7 -0
- package/docs/post-processing.md +62 -9
- package/docs/progressive-rendering.md +3 -0
- package/docs/sampling.md +4 -0
- package/docs/technical/acceleration.md +52 -0
- package/docs/technical/aovs.md +79 -0
- package/docs/technical/caustics.md +109 -0
- package/docs/technical/continuous-spectrum.md +29 -0
- package/docs/technical/display-upscale.md +82 -0
- package/docs/technical/openpbr.md +99 -0
- package/docs/technical/pbrt-import.md +168 -0
- package/docs/technical/tiling.md +74 -0
- package/docs/technical/webgl.md +53 -0
- package/docs/viewer.md +11 -1
- package/lib/Display.d.ts +19 -5
- package/lib/Display.js +10 -7
- package/lib/Display.js.map +1 -1
- package/lib/Engine.d.ts +2 -1
- package/lib/Engine.js +30 -2
- package/lib/Engine.js.map +1 -1
- package/lib/Render.d.ts +20 -11
- package/lib/Render.js +32 -7
- package/lib/Render.js.map +1 -1
- package/lib/main.d.ts +3 -1
- package/lib/main.js +1 -0
- package/lib/main.js.map +1 -1
- package/lib/oidn.d.ts +1 -1
- package/lib/renderer/AovPrimitiveCache.js +39 -8
- package/lib/renderer/AovPrimitiveCache.js.map +1 -1
- package/lib/renderer/DisplayPostProcess.d.ts +8 -3
- package/lib/renderer/DisplayPostProcess.js +51 -18
- package/lib/renderer/DisplayPostProcess.js.map +1 -1
- package/lib/renderer/MaterialData.js +7 -2
- package/lib/renderer/MaterialData.js.map +1 -1
- package/lib/renderer/RenderingPipeline.d.ts +7 -1
- package/lib/renderer/RenderingPipeline.js.map +1 -1
- package/lib/renderer/TileRender.d.ts +2 -2
- package/lib/renderer/TileRender.js +128 -7
- package/lib/renderer/TileRender.js.map +1 -1
- package/lib/renderer/WGPUAov.d.ts +6 -1
- package/lib/renderer/WGPUAov.js +108 -14
- package/lib/renderer/WGPUAov.js.map +1 -1
- package/lib/renderer/WGPUCanvasSurface.js +12 -1
- package/lib/renderer/WGPUCanvasSurface.js.map +1 -1
- package/lib/renderer/WGPUEnvMapDistribution.js +19 -19
- package/lib/renderer/WGPUFullQuadPass.js +3 -3
- package/lib/renderer/WGPURayTraceOutput.js +12 -12
- package/lib/renderer/WGPURayTracePass.d.ts +17 -4
- package/lib/renderer/WGPURayTracePass.js +194 -71
- package/lib/renderer/WGPURayTracePass.js.map +1 -1
- package/lib/renderer/WGPURenderingPipeline.js +46 -40
- package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLAov.js +3 -3
- package/lib/renderer/WebGLAov.js.map +1 -1
- package/lib/renderer/WebGLFullQuadPass.d.ts +1 -1
- package/lib/renderer/WebGLFullQuadPass.js +8 -2
- package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
- package/lib/renderer/WebGLJNLMDenoiser.js +66 -66
- package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
- package/lib/renderer/WebGLRayTraceOutputPass.js +4 -4
- package/lib/renderer/WebGLRayTracePass.d.ts +14 -10
- package/lib/renderer/WebGLRayTracePass.js +115 -111
- package/lib/renderer/WebGLRayTracePass.js.map +1 -1
- package/lib/renderer/WebGLRenderingPipeline.js +10 -3
- package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLSkyBaker.js +57 -57
- package/lib/renderer/aov.d.ts +13 -1
- package/lib/renderer/aov.js +21 -2
- package/lib/renderer/aov.js.map +1 -1
- package/lib/renderer/aovDisplay.js +2 -2
- package/lib/renderer/aovDisplay.js.map +1 -1
- package/lib/renderer/binnedSahBvh.d.ts +2 -1
- package/lib/renderer/binnedSahBvh.js +24 -17
- package/lib/renderer/binnedSahBvh.js.map +1 -1
- package/lib/renderer/bvhAccel.d.ts +3 -1
- package/lib/renderer/bvhAccel.js +36 -6
- package/lib/renderer/bvhAccel.js.map +1 -1
- package/lib/renderer/cameraUniform.d.ts +1 -1
- package/lib/renderer/cameraUniform.js +6 -2
- package/lib/renderer/cameraUniform.js.map +1 -1
- package/lib/renderer/compactVertices.d.ts +14 -0
- package/lib/renderer/compactVertices.js +124 -0
- package/lib/renderer/compactVertices.js.map +1 -0
- package/lib/renderer/easu.d.ts +11 -0
- package/lib/renderer/easu.js +156 -0
- package/lib/renderer/easu.js.map +1 -0
- package/lib/renderer/geometryUpdateState.d.ts +1 -0
- package/lib/renderer/geometryUpdateState.js.map +1 -1
- package/lib/renderer/geometryUpdates.js +3 -2
- package/lib/renderer/geometryUpdates.js.map +1 -1
- package/lib/renderer/glsl/aovDisplay.frag.js +5 -5
- package/lib/renderer/glsl/aovReflectance.glsl.js +35 -35
- package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +1 -1
- package/lib/renderer/glsl/aovTrace.frag.js +44 -36
- package/lib/renderer/glsl/aovTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/chunks/cameraProjection.glsl.d.ts +2 -0
- package/lib/renderer/glsl/chunks/cameraProjection.glsl.js +16 -0
- package/lib/renderer/glsl/chunks/cameraProjection.glsl.js.map +1 -0
- package/lib/renderer/glsl/chunks/cameraRay.glsl.d.ts +1 -1
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js +9 -16
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/emissiveLight.glsl.js +8 -8
- package/lib/renderer/glsl/chunks/envMap.glsl.js +67 -67
- package/lib/renderer/glsl/chunks/intersect.glsl.js +390 -375
- package/lib/renderer/glsl/chunks/intersect.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js +120 -120
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +562 -562
- package/lib/renderer/glsl/chunks/rand.glsl.js +3 -3
- package/lib/renderer/glsl/chunks/random.glsl.js +4 -4
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js +37 -35
- package/lib/renderer/glsl/chunks/rayTraceCore.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js +70 -74
- package/lib/renderer/glsl/chunks/sampleLight.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.d.ts +1 -1
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js +107 -88
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +11 -11
- package/lib/renderer/glsl/chunks/sampling.glsl.js +77 -77
- package/lib/renderer/glsl/chunks/sobol.glsl.js +70 -70
- package/lib/renderer/glsl/chunks/spectralPrefix.glsl.d.ts +6 -0
- package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js +47 -0
- package/lib/renderer/glsl/chunks/spectralPrefix.glsl.js.map +1 -0
- package/lib/renderer/glsl/chunks/textureLinear.glsl.js +5 -5
- package/lib/renderer/glsl/chunks/uvProjection.glsl.js +11 -11
- package/lib/renderer/glsl/depthQuery.frag.js +12 -6
- package/lib/renderer/glsl/depthQuery.frag.js.map +1 -1
- package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +10 -10
- package/lib/renderer/glsl/rayTrace.frag.js +233 -170
- package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/shadowCatcher.frag.js +10 -9
- package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
- package/lib/renderer/glsl/webglPipeline.frag.js +58 -58
- package/lib/renderer/gpuMemory.js +1 -1
- package/lib/renderer/gpuMemory.js.map +1 -1
- package/lib/renderer/openPbrEnergyLut.d.ts +2 -1
- package/lib/renderer/openPbrEnergyLut.js +18 -1
- package/lib/renderer/openPbrEnergyLut.js.map +1 -1
- package/lib/renderer/pathLimits.d.ts +5 -4
- package/lib/renderer/pathLimits.js +6 -5
- package/lib/renderer/pathLimits.js.map +1 -1
- package/lib/renderer/prepareMeshBVH.d.ts +2 -0
- package/lib/renderer/prepareMeshBVH.js +5 -3
- package/lib/renderer/prepareMeshBVH.js.map +1 -1
- package/lib/renderer/progressiveScheduler.js +1 -1
- package/lib/renderer/progressiveScheduler.js.map +1 -1
- package/lib/renderer/renderPipeline.d.ts +4 -0
- package/lib/renderer/renderPipeline.js +1 -0
- package/lib/renderer/renderPipeline.js.map +1 -1
- package/lib/renderer/researchTransport.d.ts +1 -1
- package/lib/renderer/researchTransport.js.map +1 -1
- package/lib/renderer/sceneAssembly.d.ts +1 -1
- package/lib/renderer/sceneAssembly.js +4 -3
- package/lib/renderer/sceneAssembly.js.map +1 -1
- package/lib/renderer/sceneChangeDetector.d.ts +1 -1
- package/lib/renderer/sceneChangeDetector.js +3 -1
- package/lib/renderer/sceneChangeDetector.js.map +1 -1
- package/lib/renderer/sceneLayoutSchema.d.ts +1 -0
- package/lib/renderer/sceneLayoutSchema.js +2 -2
- package/lib/renderer/sceneLayoutSchema.js.map +1 -1
- package/lib/renderer/scenePreparation.d.ts +3 -2
- package/lib/renderer/scenePreparation.js +18 -2
- package/lib/renderer/scenePreparation.js.map +1 -1
- package/lib/renderer/scratchSampling.js +6 -6
- package/lib/renderer/spectrum.js +2 -2
- package/lib/renderer/staticBvhPreparation.d.ts +9 -0
- package/lib/renderer/staticBvhPreparation.js +8 -2
- package/lib/renderer/staticBvhPreparation.js.map +1 -1
- package/lib/renderer/surfaceHit.d.ts +10 -0
- package/lib/renderer/surfaceHit.js +22 -0
- package/lib/renderer/surfaceHit.js.map +1 -0
- package/lib/renderer/tangentRequirements.d.ts +2 -0
- package/lib/renderer/tangentRequirements.js +7 -0
- package/lib/renderer/tangentRequirements.js.map +1 -0
- package/lib/renderer/textureAtlas.d.ts +4 -0
- package/lib/renderer/textureAtlas.js +48 -2
- package/lib/renderer/textureAtlas.js.map +1 -1
- package/lib/renderer/webgl.js +4 -0
- package/lib/renderer/webgl.js.map +1 -1
- package/lib/renderer/webglVertexAttributes.d.ts +17 -0
- package/lib/renderer/webglVertexAttributes.js +44 -0
- package/lib/renderer/webglVertexAttributes.js.map +1 -0
- package/lib/renderer/wgpuBindlessSource.js +2 -2
- package/lib/renderer/wgpuMaterialConstants.js +4 -3
- package/lib/renderer/wgpuMaterialConstants.js.map +1 -1
- package/lib/renderer/wgpuSceneUniform.js +2 -2
- package/lib/renderer/wgpuSceneUniform.js.map +1 -1
- package/lib/renderer/wgpuShaderSource.d.ts +15 -0
- package/lib/renderer/wgpuShaderSource.js +1 -1
- package/lib/renderer/wgpuShaderSource.js.map +1 -1
- package/lib/renderer/wgsl/aovCompact.wgsl.d.ts +4 -0
- package/lib/renderer/wgsl/aovCompact.wgsl.js +26 -0
- package/lib/renderer/wgsl/aovCompact.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/aovDisplay.wgsl.js +9 -9
- package/lib/renderer/wgsl/aovReflectance.wgsl.js +39 -39
- package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +1 -1
- package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +4 -0
- package/lib/renderer/wgsl/aovResolve.wgsl.js +21 -2
- package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/aovSnapshot.wgsl.js +2 -2
- package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/aovTrace.wgsl.js +31 -31
- package/lib/renderer/wgsl/aovTraceDisplay.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js +56 -0
- package/lib/renderer/wgsl/aovTraceDisplay.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/bloom.wgsl.js +18 -18
- package/lib/renderer/wgsl/causticLight.compute.d.ts +1 -1
- package/lib/renderer/wgsl/causticLight.compute.js +121 -119
- package/lib/renderer/wgsl/causticLight.compute.js.map +1 -1
- package/lib/renderer/wgsl/chunks/background.wgsl.js +46 -46
- package/lib/renderer/wgsl/chunks/causticAdjoint.wgsl.js +4 -4
- package/lib/renderer/wgsl/chunks/causticAtomic.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/caustics.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/common.wgsl.js +4 -4
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js +30 -30
- package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +6 -2
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js +550 -542
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +18 -16
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +2 -1
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +70 -69
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +593 -593
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +118 -118
- package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +4 -1
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +32 -26
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleLight.wgsl.js +114 -114
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js +159 -137
- package/lib/renderer/wgsl/chunks/sampleMaterial.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sampleShadowCatcher.wgsl.js +8 -8
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js +74 -74
- package/lib/renderer/wgsl/chunks/sobol.wgsl.js +74 -74
- package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.d.ts +6 -0
- package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js +66 -0
- package/lib/renderer/wgsl/chunks/spectralPrefix.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +11 -11
- package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js +61 -0
- package/lib/renderer/wgsl/chunks/vertexAttributes.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/compose.frag.wgsl.js +99 -97
- package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
- package/lib/renderer/wgsl/depthQuery.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/depthQuery.wgsl.js +11 -2
- package/lib/renderer/wgsl/depthQuery.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/easu.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/easu.wgsl.js +80 -0
- package/lib/renderer/wgsl/easu.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/easuDetail.wgsl.d.ts +21 -0
- package/lib/renderer/wgsl/easuDetail.wgsl.js +230 -0
- package/lib/renderer/wgsl/easuDetail.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/jnlm.compute.js +74 -74
- package/lib/renderer/wgsl/jnlm.compute.js.map +1 -1
- package/lib/renderer/wgsl/offlineAdaptive.compute.js +26 -26
- package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +3 -3
- package/lib/renderer/wgsl/photonMap.compute.js +89 -89
- package/lib/renderer/wgsl/proceduralSky.compute.js +61 -61
- package/lib/renderer/wgsl/rayTrace.compute.d.ts +14 -0
- package/lib/renderer/wgsl/rayTrace.compute.js +395 -333
- package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
- package/lib/scene/Scene.js.map +1 -1
- package/lib/settings/render.d.ts +11 -0
- package/lib/settings/render.js +1 -0
- package/lib/settings/render.js.map +1 -1
- package/lib/util/fromTHREE.js +21 -14
- package/lib/util/fromTHREE.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/package.json +5 -2
- package/lib/oidnBundledWeights.cdn.d.ts +0 -2
- package/lib/oidnBundledWeights.cdn.js +0 -5
- package/lib/oidnBundledWeights.cdn.js.map +0 -1
- package/lib/renderer/bvh.worker.d.ts +0 -1
- package/lib/renderer/bvh.worker.js +0 -16
- package/lib/renderer/bvh.worker.js.map +0 -1
- package/lib/renderer/bvhWorkerBuild.d.ts +0 -13
- package/lib/renderer/bvhWorkerBuild.js +0 -17
- package/lib/renderer/bvhWorkerBuild.js.map +0 -1
- package/lib/renderer/lightCore.d.ts +0 -12
- package/lib/renderer/lightCore.js +0 -13
- package/lib/renderer/lightCore.js.map +0 -1
- package/lib/renderer/materialCore.d.ts +0 -8
- package/lib/renderer/materialCore.js +0 -9
- package/lib/renderer/materialCore.js.map +0 -1
- package/lib/scene/PixelTexture.d.ts +0 -6
- package/lib/scene/PixelTexture.js +0 -10
- package/lib/scene/PixelTexture.js.map +0 -1
- package/lib/util/studioLighting.d.ts +0 -18
- package/lib/util/studioLighting.js +0 -36
- package/lib/util/studioLighting.js.map +0 -1
|
@@ -25,18 +25,20 @@ return clamp((value-EPS)/(1.0-2.0*EPS),0.0,0.99999994);
|
|
|
25
25
|
`;
|
|
26
26
|
// Preserve the dormant R1 uniform slot and shader spelling for source parity.
|
|
27
27
|
// Production packs zero; only an explicitly registered research adapter sets it.
|
|
28
|
-
export function causticLightCompute(constants, blasLayoutMask, hasDisplacement, mode = 'lightTracing', sceneIntersections = '', fullEmissionRandom = true) {
|
|
28
|
+
export function causticLightCompute(constants, blasLayoutMask, hasDisplacement, mode = 'lightTracing', sceneIntersections = '', fullEmissionRandom = true, perInstanceBounds = false, compactVertices = false, independentTextures = false) {
|
|
29
29
|
const frames = !!constants.USE_ANISOTROPY_DIRECTIONS || !!constants.USE_COAT_NORMAL_FRAMES, coat = !!constants.USE_COAT_NORMAL_FRAMES;
|
|
30
|
-
return (wgpuShaderPreamble(constants, true) +
|
|
30
|
+
return ((independentTextures ? 'enable wgpu_binding_array;\n' : '') + wgpuShaderPreamble(constants, true) +
|
|
31
31
|
/* wgsl */ `
|
|
32
32
|
${Object.keys(WGPU_SCENE_FEATURE_BITS)
|
|
33
33
|
.map((name) => `const ${name} = ${!!constants[name]};`)
|
|
34
34
|
.join('\n')}
|
|
35
35
|
${common}
|
|
36
|
-
${core({ hasAnisotropyDirections: frames, hasCoatNormals: coat, caustics: true })}
|
|
36
|
+
${core({ independentTextures, compactVertices, hasAnisotropyDirections: frames, hasCoatNormals: coat, caustics: true })}
|
|
37
37
|
${uv}
|
|
38
|
-
${createMaterialBufferWgsl({ hasAnisotropyDirections: frames, hasCoatNormals: coat, caustics: true })}
|
|
38
|
+
${createMaterialBufferWgsl({ independentTextures, hasAnisotropyDirections: frames, hasCoatNormals: coat, caustics: true })}
|
|
39
39
|
${intersect({
|
|
40
|
+
perInstanceBounds,
|
|
41
|
+
compactVertices,
|
|
40
42
|
hasDisplacement,
|
|
41
43
|
blasLayoutMask,
|
|
42
44
|
hasAnisotropyDirections: frames,
|
|
@@ -75,7 +77,7 @@ fn photonStore(i:u32,value:vec4u) {
|
|
|
75
77
|
let width=textureDimensions(photons).x;
|
|
76
78
|
textureStore(photons,vec2i(vec2u(i%width,i/width)),value);
|
|
77
79
|
}
|
|
78
|
-
fn
|
|
80
|
+
fn qX(i:u32,value:u32) {
|
|
79
81
|
let buckets=min(${PHOTON_GRID_SIZE}u,1u<<(32u-countLeadingZeros(max(256u,caustic.photonCount*2u)-1u)));
|
|
80
82
|
let index=8u+buckets*4u+caustic.photonCount*2u+i;
|
|
81
83
|
let width=textureDimensions(photonDirections).x;
|
|
@@ -86,29 +88,29 @@ textureStore(photonDirections,vec2i(vec2u(index%width,index/width)),vec4u(value)
|
|
|
86
88
|
|
|
87
89
|
|
|
88
90
|
${causticUnitRandomWgsl}
|
|
89
|
-
fn
|
|
91
|
+
fn fN(dim:u32)->f32{return ${fullEmissionRandom ? 'causticUnitRandom(sample1D(dim))' : 'sample1D(dim)'}
|
|
90
92
|
;
|
|
91
93
|
}
|
|
92
|
-
fn
|
|
94
|
+
fn ie(dim:u32)->vec2f {
|
|
93
95
|
let value=sample2D(dim);
|
|
94
96
|
return
|
|
95
97
|
${fullEmissionRandom ? 'vec2f(causticUnitRandom(value.x),causticUnitRandom(value.y))' : 'value'}
|
|
96
98
|
;
|
|
97
99
|
}
|
|
98
|
-
fn
|
|
100
|
+
fn oF(i:u32)->vec4f {
|
|
99
101
|
let width=textureDimensions(causticDistribution).x;
|
|
100
102
|
return textureLoad(causticDistribution,vec2i(vec2u(i%width,i/width)),0);
|
|
101
103
|
}
|
|
102
|
-
fn
|
|
103
|
-
return
|
|
104
|
+
fn dz(i:u32)->vec4f {
|
|
105
|
+
return oF(i);
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
|
|
107
109
|
|
|
108
|
-
var<private>
|
|
110
|
+
var<private> lu:vec3f;
|
|
109
111
|
var<private> causticEmissionAreaPdf:f32;
|
|
110
|
-
fn
|
|
111
|
-
|
|
112
|
+
fn lE(origin:vec3f,position:vec3f,normal:vec3f,emission:vec3f,area:f32,pmf:f32,result:ptr<function,LightSampleRec>) {
|
|
113
|
+
lu=position;
|
|
112
114
|
causticEmissionAreaPdf=pmf/max(area,1e-20);
|
|
113
115
|
let offset=position-origin;
|
|
114
116
|
(*result).dist=length(offset);
|
|
@@ -117,7 +119,7 @@ let offset=position-origin;
|
|
|
117
119
|
(*result).emission=emission;
|
|
118
120
|
(*result).pdf=pmf;
|
|
119
121
|
}
|
|
120
|
-
fn
|
|
122
|
+
fn sE(origin:vec3f,rnd:vec3f,result:ptr<function,LightSampleRec>) {
|
|
121
123
|
causticEmissionAreaPdf=0.0;
|
|
122
124
|
(*result).pdf=0.0;
|
|
123
125
|
(*result).sourceType=0u;
|
|
@@ -127,14 +129,14 @@ return;
|
|
|
127
129
|
}
|
|
128
130
|
let scaled=rnd.z*f32(caustic.choiceCount);
|
|
129
131
|
let column=min(u32(scaled),caustic.choiceCount-1u);
|
|
130
|
-
let entry=
|
|
132
|
+
let entry=oF(caustic.casterCount+column*2u);
|
|
131
133
|
let selected=select(bitcast<u32>(entry.y),column,fract(scaled)<entry.x);
|
|
132
|
-
let choice=bitcast<vec4u>(
|
|
133
|
-
let pmf=
|
|
134
|
+
let choice=bitcast<vec4u>(oF(caustic.casterCount+selected*2u));
|
|
135
|
+
let pmf=oF(caustic.casterCount+selected*2u+1u).x;
|
|
134
136
|
(*result).sourceIndex=choice.w;
|
|
135
137
|
if(choice.z==1u) {
|
|
136
138
|
(*result).sourceType=0u;
|
|
137
|
-
|
|
139
|
+
iM(rnd.xy,result);
|
|
138
140
|
(*result).pdf*=pmf;
|
|
139
141
|
}
|
|
140
142
|
else if(choice.z==2u) {
|
|
@@ -143,15 +145,15 @@ var light=analyticLights[choice.w];
|
|
|
143
145
|
let kind=i32(light.litType);
|
|
144
146
|
if(kind==RECT_LIGHT) {
|
|
145
147
|
let position=light.position+light.u*(rnd.x-.5)+light.v*(rnd.y-.5);
|
|
146
|
-
|
|
148
|
+
lE(origin,position,normalize(cross(light.u,light.v)),light.emission,light.area,pmf,result);
|
|
147
149
|
(*result).isDelta=light.radiusOrSpread<=.001;
|
|
148
|
-
|
|
150
|
+
bD(light,position,light.position-.5*(light.u+light.v),result);
|
|
149
151
|
}
|
|
150
152
|
else if((kind==SPHERE_LIGHT||kind==SPOT_LIGHT)&&light.radiusOrSpread>0.0) {
|
|
151
153
|
let z=1.0-2.0*rnd.x;
|
|
152
|
-
let
|
|
153
|
-
let normal=vec3f(
|
|
154
|
-
|
|
154
|
+
let dw=sqrt(max(0.0,1.0-z*z));
|
|
155
|
+
let normal=vec3f(dw*cos(TWOPI*rnd.y),dw*sin(TWOPI*rnd.y),z);
|
|
156
|
+
lE(origin,light.position+light.radiusOrSpread*normal,normal,light.emission,light.area,pmf,result);
|
|
155
157
|
}
|
|
156
158
|
else {
|
|
157
159
|
light.selectionPmf=1.0;
|
|
@@ -168,55 +170,55 @@ let position=bary.x*triangle.p0+bary.y*triangle.p1+bary.z*triangle.p2;
|
|
|
168
170
|
let normal=normalize(cross(triangle.p1-triangle.p0,triangle.p2-triangle.p0));
|
|
169
171
|
let uv=bary.x*triangle.uv0+bary.y*triangle.uv1+bary.z*triangle.uv2;
|
|
170
172
|
let uv1=bary.x*triangle.secondaryUv0+bary.y*triangle.secondaryUv1+bary.z*triangle.secondaryUv2;
|
|
171
|
-
|
|
173
|
+
lE(origin,position,normal,getMaterialEmission(i32(triangle.materialIndex),uv,uv1),triangle.area,pmf,result);
|
|
172
174
|
}
|
|
173
175
|
(*result).selectionPmf=pmf;
|
|
174
176
|
}
|
|
175
|
-
fn
|
|
177
|
+
fn mf(s:vec4f,origin:vec3f)->f32 {
|
|
176
178
|
let bG=dot(s.xyz-origin,s.xyz-origin);
|
|
177
179
|
return select(-1.0,sqrt(max(0.0,1.0-s.w*s.w/bG)),bG>s.w*s.w);
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
|
|
181
|
-
fn
|
|
183
|
+
fn dS(s:vec4f)->f32 {
|
|
182
184
|
if((caustic.experiments&2u)==0u) { return 1.0; }
|
|
183
|
-
let
|
|
185
|
+
let vb=caustic.viewProjection*vec4f(s.xyz,1.0);
|
|
184
186
|
let m=caustic.viewProjection;
|
|
185
187
|
let extent=s.w*max(length(vec3f(m[0].x,m[1].x,m[2].x)),length(vec3f(m[0].y,m[1].y,m[2].y)));
|
|
186
|
-
if(
|
|
187
|
-
return .05+min(1.0,extent*extent/max(
|
|
188
|
+
if(vb.w<=0.0||abs(vb.x)>vb.w+extent||abs(vb.y)>vb.w+extent) { return .05; }
|
|
189
|
+
return .05+min(1.0,extent*extent/max(vb.w*vb.w,1e-20));
|
|
188
190
|
}
|
|
189
|
-
fn
|
|
191
|
+
fn tV(origin:vec3f,rnd:vec3f,pdf:ptr<function,f32>,lh:ptr<function,f32>)->vec3f {
|
|
190
192
|
var total=0.0;
|
|
191
193
|
for(var i=0u;i<caustic.casterCount;i++) {
|
|
192
|
-
total+=(1.0-
|
|
194
|
+
total+=(1.0-mf(dz(i),origin))*dS(dz(i));
|
|
193
195
|
}
|
|
194
196
|
var choice=rnd.z*total;
|
|
195
|
-
var selected=
|
|
197
|
+
var selected=dz(0u);
|
|
196
198
|
for(var i=0u;i<caustic.casterCount;i++) {
|
|
197
|
-
let sphere=
|
|
199
|
+
let sphere=dz(i);
|
|
198
200
|
selected=sphere;
|
|
199
|
-
choice-=(1.0-
|
|
201
|
+
choice-=(1.0-mf(sphere,origin))*dS(sphere);
|
|
200
202
|
if(choice<=0.0) {
|
|
201
203
|
break;
|
|
202
204
|
}
|
|
203
205
|
}
|
|
204
|
-
*
|
|
206
|
+
*lh=selected.w;
|
|
205
207
|
let offset=selected.xyz-origin;
|
|
206
208
|
let axis=normalize(select(vec3f(0.0,0.0,1.0),offset,dot(offset,offset)>1e-20));
|
|
207
|
-
let cosine=mix(1.0,
|
|
208
|
-
let
|
|
209
|
+
let cosine=mix(1.0,mf(selected,origin),rnd.x);
|
|
210
|
+
let wk=sqrt(max(0.0,1.0-cosine*cosine));
|
|
209
211
|
var t:vec3f;
|
|
210
212
|
var b:vec3f;
|
|
211
213
|
sampling_orthonormal_basis(axis,&t,&b);
|
|
212
|
-
let direction=axis*cosine+(t*cos(TWOPI*rnd.y)+b*sin(TWOPI*rnd.y))*
|
|
214
|
+
let direction=axis*cosine+(t*cos(TWOPI*rnd.y)+b*sin(TWOPI*rnd.y))*wk;
|
|
213
215
|
|
|
214
216
|
var overlaps=0.0;
|
|
215
217
|
for(var i=0u;i<caustic.casterCount;i++) {
|
|
216
|
-
let sphere=
|
|
217
|
-
let c=
|
|
218
|
+
let sphere=dz(i);
|
|
219
|
+
let c=mf(sphere,origin);
|
|
218
220
|
if(c<0.0||dot(normalize(sphere.xyz-origin),direction)>=c) {
|
|
219
|
-
overlaps+=
|
|
221
|
+
overlaps+=dS(sphere);
|
|
220
222
|
}
|
|
221
223
|
}
|
|
222
224
|
*pdf=overlaps/max(TWOPI*total,1e-20);
|
|
@@ -224,26 +226,26 @@ return direction;
|
|
|
224
226
|
}
|
|
225
227
|
${mode === 'lightTracing'
|
|
226
228
|
? /* wgsl */ `${atomic}
|
|
227
|
-
fn causticCameraConnect(si: SurfaceInteraction, material: OpenPbrResolvedMaterial,
|
|
229
|
+
fn causticCameraConnect(si: SurfaceInteraction, material: OpenPbrResolvedMaterial, iA: Ray, flux: vec3f, extinction: vec3f) {
|
|
228
230
|
let eye=cameraUniform.viewInv[3].xyz;
|
|
229
231
|
let forward=-normalize(cameraUniform.viewInv[2].xyz);
|
|
230
|
-
let
|
|
231
|
-
let radius=cameraUniform.apertureRadius*sqrt(
|
|
232
|
-
var
|
|
232
|
+
let sQ=ie(causticDim(68u));
|
|
233
|
+
let radius=cameraUniform.apertureRadius*sqrt(sQ.x);
|
|
234
|
+
var wJ=eye+radius*(cos(TWOPI*sQ.y)*cameraUniform.viewInv[0].xyz+sin(TWOPI*sQ.y)*cameraUniform.viewInv[1].xyz);
|
|
233
235
|
if(caustic.orthographic!=0u) {
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
let
|
|
237
|
-
let bG=dot(
|
|
238
|
-
let direction=normalize(
|
|
239
|
-
let
|
|
240
|
-
let
|
|
241
|
-
let
|
|
242
|
-
if(
|
|
236
|
+
wJ=si.position-forward*dot(si.position-eye,forward);
|
|
237
|
+
}
|
|
238
|
+
let zJ=wJ-si.position;
|
|
239
|
+
let bG=dot(zJ,zJ);
|
|
240
|
+
let direction=normalize(zJ);
|
|
241
|
+
let ye=wJ+(si.position-wJ)*cameraUniform.focusDistance/max(dot(si.position-wJ,forward),1e-12);
|
|
242
|
+
let sc=select(ye,si.position,caustic.orthographic!=0u);
|
|
243
|
+
let vb=caustic.viewProjection*vec4f(sc,1.0);
|
|
244
|
+
if(vb.w<=0.0||dot(si.position-eye,forward)<=0.0) {
|
|
243
245
|
return;
|
|
244
246
|
}
|
|
245
|
-
let
|
|
246
|
-
if(any(
|
|
247
|
+
let yY=vb.xy/vb.w*0.5+0.5;
|
|
248
|
+
if(any(yY<vec2f(0.0))||any(yY>=vec2f(1.0))) {
|
|
247
249
|
return;
|
|
248
250
|
}
|
|
249
251
|
var shadow:Ray;
|
|
@@ -252,31 +254,31 @@ if(intersectSceneShadow(shadow,sqrt(bG)-2.0*EPS,si.triangleIndex,si.instanceInde
|
|
|
252
254
|
return;
|
|
253
255
|
}
|
|
254
256
|
var m=material;
|
|
255
|
-
let normal=select(-si.normal,si.normal,dot(si.normal,-
|
|
256
|
-
m.inside=dot(si.faceNormal,-
|
|
257
|
+
let normal=select(-si.normal,si.normal,dot(si.normal,-iA.d)>=0.0);
|
|
258
|
+
m.inside=dot(si.faceNormal,-iA.d)<0.0;
|
|
257
259
|
m.relativeIor=select(1.0/m.specularIor,m.specularIor,m.inside);
|
|
258
260
|
${frames
|
|
259
261
|
? /* wgsl */ ` m.directionTangent=si.directionTangent;m.directionBitangent=si.directionBitangent;m.geometryTangent=si.geometryTangent;m.geometryBitangent=si.geometryBitangent;`
|
|
260
262
|
: ''}
|
|
261
263
|
${coat
|
|
262
|
-
? /* wgsl */ ` m.coatNormal=normal;m.geometricNormal=select(-si.faceNormal,si.faceNormal,dot(si.faceNormal,-
|
|
264
|
+
? /* wgsl */ ` m.coatNormal=normal;m.geometricNormal=select(-si.faceNormal,si.faceNormal,dot(si.faceNormal,-iA.d)>=0.0);`
|
|
263
265
|
: ''}
|
|
264
|
-
let prepared=
|
|
266
|
+
let prepared=bX(si.tangent,m,-iA.d,normal);
|
|
265
267
|
var pdf:f32;
|
|
266
268
|
let response=openpbr_eval_prepared(prepared,direction,&pdf);
|
|
267
|
-
let
|
|
268
|
-
if(
|
|
269
|
+
let ne=dot(-direction,-normalize(cameraUniform.viewInv[2].xyz));
|
|
270
|
+
if(ne<=0.0) {
|
|
269
271
|
return;
|
|
270
272
|
}
|
|
271
|
-
let
|
|
272
|
-
select(max(bG*
|
|
273
|
+
let yf=caustic.cameraScale*rtUniform.outputSize.x*rtUniform.outputSize.y /
|
|
274
|
+
select(max(bG*ne*ne*ne,1e-12),1.0,caustic.orthographic!=0u);
|
|
273
275
|
let transmittance=exp(-extinction*sqrt(bG));
|
|
274
|
-
let value=flux*transmittance*response*causticAdjoint(-
|
|
275
|
-
let pixel=vec2u(
|
|
276
|
+
let value=flux*transmittance*response*causticAdjoint(-iA.d,direction,normal,si.faceNormal)*yf/f32(caustic.photonCount);
|
|
277
|
+
let pixel=vec2u(yY*rtUniform.outputSize);
|
|
276
278
|
let index=(pixel.x+pixel.y*u32(rtUniform.outputSize.x))*3u;
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
279
|
+
lF(index,value.x);
|
|
280
|
+
lF(index+1u,value.y);
|
|
281
|
+
lF(index+2u,value.z);
|
|
280
282
|
}
|
|
281
283
|
`
|
|
282
284
|
: ''}
|
|
@@ -287,18 +289,18 @@ if(photonIndex>=caustic.photonCount) {
|
|
|
287
289
|
return;
|
|
288
290
|
}
|
|
289
291
|
${mode === 'photonMapping' ? 'photonStore(photonIndex,vec4u(0u));' : ''}
|
|
290
|
-
|
|
292
|
+
aJ=vec2u(photonIndex%u32(rtUniform.outputSize.x),photonIndex/u32(rtUniform.outputSize.x));
|
|
291
293
|
aC=caustic.batch;
|
|
292
294
|
samplingBounce=0u;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
+
hashedCounter=0u;
|
|
296
|
+
fA(aJ,i32(ak(caustic.batch,rtUniform.sequenceSeed)));
|
|
295
297
|
var wavelength=0.0;
|
|
296
298
|
var wavelengthPdf=1.0;
|
|
297
299
|
if(SPECTRAL) {
|
|
298
|
-
wavelength=spectrumSample(${SPECTRUM_PHOTON_DISTRIBUTION}u,
|
|
300
|
+
wavelength=spectrumSample(${SPECTRUM_PHOTON_DISTRIBUTION}u,fN(causticDim(69u)),&wavelengthPdf);
|
|
299
301
|
}
|
|
300
302
|
var ls:LightSampleRec;
|
|
301
|
-
|
|
303
|
+
sE(caustic.casterSphere.xyz,vec3f(ie(causticDim(60u)),fN(causticDim(61u))),&ls);
|
|
302
304
|
if(ls.pdf<=0.0) {
|
|
303
305
|
return;
|
|
304
306
|
}
|
|
@@ -307,41 +309,41 @@ if(ls.sourceType==2u) {
|
|
|
307
309
|
infinite=i32(analyticLights[ls.sourceIndex].litType)==DIRECTIONAL_LIGHT;
|
|
308
310
|
}
|
|
309
311
|
var origin=caustic.casterSphere.xyz+ls.direction*ls.dist;
|
|
310
|
-
if(causticEmissionAreaPdf>0.0) { origin=
|
|
312
|
+
if(causticEmissionAreaPdf>0.0) { origin=lu; }
|
|
311
313
|
var direction:vec3f;
|
|
312
314
|
var flux:vec3f;
|
|
313
|
-
var
|
|
315
|
+
var lh=caustic.casterSphere.w;
|
|
314
316
|
if(infinite) {
|
|
315
317
|
direction=-ls.direction;
|
|
316
318
|
|
|
317
319
|
var area=0.0;
|
|
318
320
|
for(var i=0u;i<caustic.casterCount;i++) {
|
|
319
|
-
let s=
|
|
320
|
-
area+=s.w*s.w*
|
|
321
|
+
let s=dz(i);
|
|
322
|
+
area+=s.w*s.w*dS(s);
|
|
321
323
|
}
|
|
322
|
-
var choice=
|
|
323
|
-
var sphere=
|
|
324
|
+
var choice=fN(causticDim(63u))*area;
|
|
325
|
+
var sphere=dz(0u);
|
|
324
326
|
for(var i=0u;i<caustic.casterCount;i++) {
|
|
325
|
-
sphere=
|
|
326
|
-
choice-=sphere.w*sphere.w*
|
|
327
|
+
sphere=dz(i);
|
|
328
|
+
choice-=sphere.w*sphere.w*dS(sphere);
|
|
327
329
|
if(choice<=0.0) {
|
|
328
330
|
break;
|
|
329
331
|
}
|
|
330
332
|
}
|
|
331
|
-
|
|
333
|
+
lh=sphere.w;
|
|
332
334
|
var t:vec3f;
|
|
333
335
|
var b:vec3f;
|
|
334
336
|
sampling_orthonormal_basis(direction,&t,&b);
|
|
335
|
-
let rnd=
|
|
337
|
+
let rnd=ie(causticDim(62u));
|
|
336
338
|
origin=sphere.xyz-direction*dot(sphere.xyz-caustic.casterSphere.xyz,direction)-direction*caustic.casterSphere.w;
|
|
337
339
|
origin+=sphere.w*sqrt(rnd.x)*(t*cos(TWOPI*rnd.y)+b*sin(TWOPI*rnd.y));
|
|
338
340
|
var overlaps=0.0;
|
|
339
341
|
for(var i=0u;i<caustic.casterCount;i++) {
|
|
340
|
-
let s=
|
|
342
|
+
let s=dz(i);
|
|
341
343
|
let offset=origin-s.xyz;
|
|
342
|
-
let
|
|
343
|
-
if(dot(
|
|
344
|
-
overlaps+=
|
|
344
|
+
let sc=offset-direction*dot(offset,direction);
|
|
345
|
+
if(dot(sc,sc)<=s.w*s.w) {
|
|
346
|
+
overlaps+=dS(s);
|
|
345
347
|
}
|
|
346
348
|
}
|
|
347
349
|
let diskPdf=overlaps/max(PI*area,1e-20);
|
|
@@ -362,9 +364,9 @@ if(areaPdf<=0.0) {
|
|
|
362
364
|
return;
|
|
363
365
|
}
|
|
364
366
|
var directionPdf:f32;
|
|
365
|
-
direction=
|
|
367
|
+
direction=tV(origin,vec3f(ie(causticDim(62u)),fN(causticDim(63u))),&directionPdf,&lh);
|
|
366
368
|
var emission=ls.emission;
|
|
367
|
-
var
|
|
369
|
+
var nN=max(0.0,dot(ls.normal,direction));
|
|
368
370
|
if(ls.sourceType==2u) {
|
|
369
371
|
let source=analyticLights[ls.sourceIndex];
|
|
370
372
|
emission=source.emission;
|
|
@@ -372,27 +374,27 @@ if(i32(source.litType)==SPOT_LIGHT) {
|
|
|
372
374
|
emission*=evaluateSpotEmissionMultiplier(source,direction);
|
|
373
375
|
}
|
|
374
376
|
if(ls.isDelta) {
|
|
375
|
-
|
|
377
|
+
nN=1.0;
|
|
376
378
|
}
|
|
377
|
-
if(i32(source.litType)==RECT_LIGHT&&
|
|
379
|
+
if(i32(source.litType)==RECT_LIGHT&&nN<cos(0.5*PI*source.radiusOrSpread)) {
|
|
378
380
|
return;
|
|
379
381
|
}
|
|
380
382
|
if(i32(source.litType)==RECT_LIGHT) {
|
|
381
|
-
var
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
emission=
|
|
383
|
+
var vX:LightSampleRec;
|
|
384
|
+
vX.emission=emission;
|
|
385
|
+
bD(source,origin,source.position-0.5*(source.u+source.v),&vX);
|
|
386
|
+
emission=vX.emission;
|
|
385
387
|
}
|
|
386
388
|
}
|
|
387
|
-
flux=emission*
|
|
389
|
+
flux=emission*nN/(areaPdf*directionPdf);
|
|
388
390
|
}
|
|
389
391
|
if(SPECTRAL) {
|
|
390
392
|
flux*=spectrumRgb(wavelength)/(wavelengthPdf*${SPECTRUM_WAVELENGTH_RANGE}.0);
|
|
391
393
|
}
|
|
392
394
|
var ray:Ray;
|
|
393
395
|
initRay(&ray,origin+EPS*direction,direction);
|
|
394
|
-
var
|
|
395
|
-
var
|
|
396
|
+
var xk=0u;
|
|
397
|
+
var yN=false;
|
|
396
398
|
var extinction=vec3f(0.0);
|
|
397
399
|
var scattering=vec3f(0.0);
|
|
398
400
|
var si:SurfaceInteraction;
|
|
@@ -408,7 +410,7 @@ return;
|
|
|
408
410
|
}
|
|
409
411
|
var m:OpenPbrResolvedMaterial;
|
|
410
412
|
getMaterial(&m,si.materialIndex,si.uv,si.uv1,si.instanceIndex,si.vertexColor);
|
|
411
|
-
if(m.alpha<1.0&&
|
|
413
|
+
if(m.alpha<1.0&&fN(causticDim(79u))>=m.alpha) {
|
|
412
414
|
initRay(&ray,si.position+EPS*ray.d,ray.d);
|
|
413
415
|
continue;
|
|
414
416
|
}
|
|
@@ -416,9 +418,9 @@ if(m.materialType==SHADOW_CATCHER) {
|
|
|
416
418
|
return;
|
|
417
419
|
}
|
|
418
420
|
let segment=distance(ray.o,si.position);
|
|
419
|
-
let channel=min(u32(sample1D(
|
|
421
|
+
let channel=min(u32(sample1D(aX(MEDIUM_CHANNEL))*3.0),2u);
|
|
420
422
|
if(extinction[channel]>0.0&&any(scattering>vec3f(0.0))) {
|
|
421
|
-
let sampled=-log(max(sample1D(
|
|
423
|
+
let sampled=-log(max(sample1D(aX(MEDIUM_DISTANCE)),1e-7))/extinction[channel];
|
|
422
424
|
|
|
423
425
|
|
|
424
426
|
if(sampled<segment) {
|
|
@@ -426,31 +428,31 @@ return;
|
|
|
426
428
|
}
|
|
427
429
|
}
|
|
428
430
|
let transmittance=exp(-extinction*segment);
|
|
429
|
-
let
|
|
430
|
-
flux*=transmittance/select(1.0,max(
|
|
431
|
+
let oq=(transmittance.x+transmittance.y+transmittance.z)/3.0;
|
|
432
|
+
flux*=transmittance/select(1.0,max(oq,1e-20),any(scattering>vec3f(0.0)));
|
|
431
433
|
if(SPECTRAL) {
|
|
432
434
|
m.specularIor=sampling_cauchy_ior(spectrumIorWavelength(wavelength),m.specularIor,m.transmissionDispersionScale,m.dispersionAbbeNumber);
|
|
433
435
|
}
|
|
434
436
|
if(!isCausticSpecular(m)) {
|
|
435
|
-
let
|
|
436
|
-
let r1=
|
|
437
|
-
if(
|
|
437
|
+
let yi=ls.sourceType!=0u;
|
|
438
|
+
let r1=yi&&xk==1u&&yN&&m.baseWeight*(1.0-m.baseMetalness)*(1.0-m.transmissionWeight)>0.0&&m.subsurfaceWeight==0.0;
|
|
439
|
+
if(xk>0u
|
|
438
440
|
${mode === 'lightTracing' ? '&&!(caustic.mnee!=0u&&r1)' : ''}){${mode === 'lightTracing'
|
|
439
441
|
? 'causticCameraConnect(si,m,ray,flux,extinction);'
|
|
440
442
|
: /* wgsl */ `
|
|
441
443
|
let position=bitcast<vec3u>(si.position);
|
|
442
|
-
photonStore(photonIndex,vec4u(position,
|
|
444
|
+
photonStore(photonIndex,vec4u(position,us(flux)));
|
|
443
445
|
if((caustic.experiments&1u)!=0u) {
|
|
444
446
|
var maximum=1e-8;
|
|
445
|
-
for(var i=0u;i<caustic.casterCount;i++) { maximum=max(maximum,
|
|
447
|
+
for(var i=0u;i<caustic.casterCount;i++) { maximum=max(maximum,dz(i).w); }
|
|
446
448
|
let buckets=min(${PHOTON_GRID_SIZE}u,1u<<(32u-countLeadingZeros(max(256u,caustic.photonCount*2u)-1u)));
|
|
447
449
|
let index=8u+buckets*4u+caustic.photonCount*3u+photonIndex;
|
|
448
450
|
let width=textureDimensions(photonDirections).x;
|
|
449
|
-
textureStore(photonDirections,vec2i(vec2u(index%width,index/width)),vec4u(bitcast<u32>(
|
|
451
|
+
textureStore(photonDirections,vec2i(vec2u(index%width,index/width)),vec4u(bitcast<u32>(lh/maximum)));
|
|
450
452
|
}
|
|
451
|
-
let
|
|
452
|
-
let normal=vec2u(round(
|
|
453
|
-
|
|
453
|
+
let iA=vec2u(round(qR(-ray.d)*255.0));
|
|
454
|
+
let normal=vec2u(round(qR(si.faceNormal)*vec2f(255.0,127.0)));
|
|
455
|
+
qX(photonIndex,normal.x|(normal.y<<8u)|(select(0u,1u,r1)<<15u)|(iA.x<<16u)|(iA.y<<24u));
|
|
454
456
|
`}
|
|
455
457
|
}
|
|
456
458
|
return;
|
|
@@ -465,7 +467,7 @@ ${frames
|
|
|
465
467
|
${coat
|
|
466
468
|
? /* wgsl */ ` m.coatNormal=normal;m.geometricNormal=select(-si.faceNormal,si.faceNormal,dot(si.faceNormal,view)>=0.0);`
|
|
467
469
|
: ''}
|
|
468
|
-
let prepared=
|
|
470
|
+
let prepared=bX(si.tangent,m,view,normal);
|
|
469
471
|
var next:vec3f;
|
|
470
472
|
var pdf:f32;
|
|
471
473
|
var transmission:bool;
|
|
@@ -475,14 +477,14 @@ var diffuse:bool;
|
|
|
475
477
|
var lobe:u32;
|
|
476
478
|
var marginal:f32;
|
|
477
479
|
var full:vec3f;
|
|
478
|
-
let response=
|
|
480
|
+
let response=dr(prepared,vec3f(sample2D(aX(BSDF_DIRECTION)),sample1D(aX(BSDF_LOBE))),&next,&pdf,&transmission,&roughness,&specular,&diffuse,&lobe,&marginal,&full);
|
|
479
481
|
if(!(pdf>0.0)) {
|
|
480
482
|
return;
|
|
481
483
|
}
|
|
482
|
-
if(
|
|
484
|
+
if(xk==0u&&ls.sourceType==0u&&transmission) {
|
|
483
485
|
var reverse:Ray;
|
|
484
486
|
initRay(&reverse,si.position,-ray.d);
|
|
485
|
-
let bg=
|
|
487
|
+
let bg=fI(reverse);
|
|
486
488
|
flux*=bg.rgb*bg.a/max(ls.emission,vec3f(1e-20));
|
|
487
489
|
}
|
|
488
490
|
flux*=response/pdf*causticAdjoint(view,next,normal,si.faceNormal);
|
|
@@ -496,8 +498,8 @@ if(transmission&&!m.thinWalled&&m.mediumType>0) {
|
|
|
496
498
|
extinction=select(m.mediumExtinction,vec3f(0.0),m.inside);
|
|
497
499
|
scattering=select(m.mediumScattering,vec3f(0.0),m.inside);
|
|
498
500
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
+
xk++;
|
|
502
|
+
yN=xk==1u&&((isCausticT(m)&&transmission)||(isCausticR(m)&&!transmission));
|
|
501
503
|
initRay(&ray,si.position+EPS*next,next);
|
|
502
504
|
}
|
|
503
505
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"causticLight.compute.js","sourceRoot":"","sources":["../../../src/renderer/wgsl/causticLight.compute.ts"],"names":[],"mappings":"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","sourcesContent":["import {PHOTON_GRID_SIZE, photonPackingWgsl} from './photonMap.compute.js';\nimport {SPECTRUM_PHOTON_DISTRIBUTION, SPECTRUM_WAVELENGTH_RANGE, spectrumWgsl} from '../spectrum.js';\nimport background from './chunks/background.wgsl.js';\nimport atomic from './chunks/causticAtomic.wgsl.js';\nimport adjoint from './chunks/causticAdjoint.wgsl.js';\nimport common from './chunks/common.wgsl.js';\nimport core from './chunks/rayTraceCore.wgsl.js';\nimport {createMaterialBufferWgsl} from './chunks/materialBuffer.wgsl.js';\nimport intersect from './chunks/intersect.wgsl.js';\nimport rand from './chunks/rand.wgsl.js';\nimport random from './chunks/random.wgsl.js';\nimport env from './chunks/envMap.wgsl.js';\nimport sampling from './chunks/sampling.wgsl.js';\nimport {createOpenPbrBsdfWgsl} from './chunks/openPbrBsdf.wgsl.js';\nimport light from './chunks/sampleLight.wgsl.js';\nimport uv from './chunks/uvProjection.wgsl.js';\nimport {causticPartitionWGSL} from './chunks/caustics.wgsl.js';\nimport {wgpuShaderPreamble} from '../wgpuShaderSource.js';\nimport {WGPU_SCENE_FEATURE_BITS} from '../WGPUSceneFeatures.js';\n\nexport const CAUSTIC_UNIFORM_BYTES = 112;\nexport const causticUnitRandomWgsl = /* wgsl */ `\n fn causticUnitRandom(value:f32)->f32 {\n return clamp((value-EPS)/(1.0-2.0*EPS),0.0,0.99999994);\n }\n`;\n\n// Preserve the dormant R1 uniform slot and shader spelling for source parity.\n// Production packs zero; only an explicitly registered research adapter sets it.\nexport function causticLightCompute(\n constants: Record<string, boolean | number>,\n blasLayoutMask: number,\n hasDisplacement: boolean,\n mode: 'lightTracing' | 'photonMapping' = 'lightTracing',\n sceneIntersections = '',\n fullEmissionRandom = true\n) {\n const frames = !!constants.USE_ANISOTROPY_DIRECTIONS || !!constants.USE_COAT_NORMAL_FRAMES,\n coat = !!constants.USE_COAT_NORMAL_FRAMES;\n return (\n wgpuShaderPreamble(constants, true) +\n /* wgsl */ `\n${Object.keys(WGPU_SCENE_FEATURE_BITS)\n .map((name) => `const ${name} = ${!!constants[name]};`)\n .join('\\n')}\n${common}\n${core({hasAnisotropyDirections: frames, hasCoatNormals: coat, caustics: true})}\n${uv}\n${createMaterialBufferWgsl({hasAnisotropyDirections: frames, hasCoatNormals: coat, caustics: true})}\n${intersect({\n hasDisplacement,\n blasLayoutMask,\n hasAnisotropyDirections: frames,\n hasCoatNormals: coat,\n sceneIntersections\n})}\n${rand}\n${random}\n${env}\n${sampling}\n${spectrumWgsl}\n${createOpenPbrBsdfWgsl(frames, coat)}\n${light}\n${background.replace(\n /\\b(?:USE_BACKGROUND|USE_BACKGROUND_MAP|USE_GROUNDED_BACKGROUND)\\b/g,\n (name) =>\n `((rtUniform.backgroundFlags & ${\n {USE_BACKGROUND: 1, USE_BACKGROUND_MAP: 2, USE_GROUNDED_BACKGROUND: 4}[name]\n }) != 0)`\n)}\n${causticPartitionWGSL(false, false)}\n${adjoint}\n struct CausticLightUniform {\n casterSphere: vec4f,\n viewProjection: mat4x4f,\n photonCount: u32,\n batch: u32,\n mnee: u32,\n cameraScale: f32,\n casterCount: u32,\n choiceCount: u32,\n orthographic: u32,\n experiments: u32,\n }\n @group(3) @binding(0) var<uniform> caustic: CausticLightUniform;\n${\n mode === 'lightTracing'\n ? '@group(3) @binding(1) var<storage,read_write> splats: array<atomic<u32>>;'\n : `${photonPackingWgsl}\n @group(3)@binding(1)var photons:texture_storage_2d<rgba32uint,write>;\n @group(3)@binding(3)var photonDirections:texture_storage_2d<r32uint,write>;\n fn photonStore(i:u32,value:vec4u) {\n let width=textureDimensions(photons).x;\n textureStore(photons,vec2i(vec2u(i%width,i/width)),value);\n }\n fn photonStoreDirections(i:u32,value:u32) {\n let buckets=min(${PHOTON_GRID_SIZE}u,1u<<(32u-countLeadingZeros(max(256u,caustic.photonCount*2u)-1u)));\n let index=8u+buckets*4u+caustic.photonCount*2u+i;\n let width=textureDimensions(photonDirections).x;\n textureStore(photonDirections,vec2i(vec2u(index%width,index/width)),vec4u(value));\n }\n`\n}\n @group(3) @binding(2) var causticDistribution:texture_2d<f32>;\n // Geometric emission PDFs assume the full disk/cone/area support. The generic\n // BSDF sampler contracts randoms by EPS; undo that contraction for these warps.\n${causticUnitRandomWgsl}\nfn causticEmission1D(dim:u32)->f32{return ${\n fullEmissionRandom ? 'causticUnitRandom(sample1D(dim))' : 'sample1D(dim)'\n }\n ;\n}\nfn causticEmission2D(dim:u32)->vec2f {\n let value=sample2D(dim);\n return\n${fullEmissionRandom ? 'vec2f(causticUnitRandom(value.x),causticUnitRandom(value.y))' : 'value'}\n ;\n}\nfn causticLoad(i:u32)->vec4f {\n let width=textureDimensions(causticDistribution).x;\n return textureLoad(causticDistribution,vec2i(vec2u(i%width,i/width)),0);\n}\nfn causticSphere(i:u32)->vec4f {\n return causticLoad(i);\n}\n// Photon emission samples a source surface, independently of any receiver.\n// A NEE sample viewed from the aggregate caster center can reject an emitter\n// which still illuminates another caster, or sample only part of a sphere.\nvar<private> causticEmissionOrigin:vec3f;\nvar<private> causticEmissionAreaPdf:f32;\nfn causticAreaLight(origin:vec3f,position:vec3f,normal:vec3f,emission:vec3f,area:f32,pmf:f32,result:ptr<function,LightSampleRec>) {\n causticEmissionOrigin=position;\n causticEmissionAreaPdf=pmf/max(area,1e-20);\n let offset=position-origin;\n (*result).dist=length(offset);\n (*result).direction=offset/max((*result).dist,1e-20);\n (*result).normal=normal;\n (*result).emission=emission;\n (*result).pdf=pmf;\n}\nfn causticSampleLight(origin:vec3f,rnd:vec3f,result:ptr<function,LightSampleRec>) {\n causticEmissionAreaPdf=0.0;\n (*result).pdf=0.0;\n (*result).sourceType=0u;\n (*result).isDelta=false;\n if(caustic.choiceCount==0u) {\n return;\n }\n let scaled=rnd.z*f32(caustic.choiceCount);\n let column=min(u32(scaled),caustic.choiceCount-1u);\n let entry=causticLoad(caustic.casterCount+column*2u);\n let selected=select(bitcast<u32>(entry.y),column,fract(scaled)<entry.x);\n let choice=bitcast<vec4u>(causticLoad(caustic.casterCount+selected*2u));\n let pmf=causticLoad(caustic.casterCount+selected*2u+1u).x;\n (*result).sourceIndex=choice.w;\n if(choice.z==1u) {\n (*result).sourceType=0u;\n sampleEnvLight(rnd.xy,result);\n (*result).pdf*=pmf;\n }\n else if(choice.z==2u) {\n (*result).sourceType=2u;\n var light=analyticLights[choice.w];\n let kind=i32(light.litType);\n if(kind==RECT_LIGHT) {\n let position=light.position+light.u*(rnd.x-.5)+light.v*(rnd.y-.5);\n causticAreaLight(origin,position,normalize(cross(light.u,light.v)),light.emission,light.area,pmf,result);\n (*result).isDelta=light.radiusOrSpread<=.001;\n light_apply_rect_texture(light,position,light.position-.5*(light.u+light.v),result);\n }\n else if((kind==SPHERE_LIGHT||kind==SPOT_LIGHT)&&light.radiusOrSpread>0.0) {\n let z=1.0-2.0*rnd.x;\n let radial=sqrt(max(0.0,1.0-z*z));\n let normal=vec3f(radial*cos(TWOPI*rnd.y),radial*sin(TWOPI*rnd.y),z);\n causticAreaLight(origin,light.position+light.radiusOrSpread*normal,normal,light.emission,light.area,pmf,result);\n }\n else {\n light.selectionPmf=1.0;\n sampleAnalyticLight(light,origin,rnd.xy,result);\n (*result).pdf*=pmf;\n }\n }\n else {\n (*result).sourceType=1u;\n let triangle=getEmissiveTriangle(i32(choice.w));\n let u=sqrt(rnd.x);\n let bary=vec3f(1.0-u,u*(1.0-rnd.y),u*rnd.y);\n let position=bary.x*triangle.p0+bary.y*triangle.p1+bary.z*triangle.p2;\n let normal=normalize(cross(triangle.p1-triangle.p0,triangle.p2-triangle.p0));\n let uv=bary.x*triangle.uv0+bary.y*triangle.uv1+bary.z*triangle.uv2;\n let uv1=bary.x*triangle.secondaryUv0+bary.y*triangle.secondaryUv1+bary.z*triangle.secondaryUv2;\n causticAreaLight(origin,position,normal,getMaterialEmission(i32(triangle.materialIndex),uv,uv1),triangle.area,pmf,result);\n }\n (*result).selectionPmf=pmf;\n}\nfn causticCosMax(s:vec4f,origin:vec3f)->f32 {\n let distanceSquared=dot(s.xyz-origin,s.xyz-origin);\n return select(-1.0,sqrt(max(0.0,1.0-s.w*s.w/distanceSquared)),distanceSquared>s.w*s.w);\n}\n// A positive floor retains paths from offscreen or occluded casters. The\n// complete weighted mixture PDF below, including overlaps, compensates selection.\nfn causticTargetWeight(s:vec4f)->f32 {\n if((caustic.experiments&2u)==0u) { return 1.0; }\n let clip=caustic.viewProjection*vec4f(s.xyz,1.0);\n let m=caustic.viewProjection;\n let extent=s.w*max(length(vec3f(m[0].x,m[1].x,m[2].x)),length(vec3f(m[0].y,m[1].y,m[2].y)));\n if(clip.w<=0.0||abs(clip.x)>clip.w+extent||abs(clip.y)>clip.w+extent) { return .05; }\n return .05+min(1.0,extent*extent/max(clip.w*clip.w,1e-20));\n}\nfn causticDirection(origin:vec3f,rnd:vec3f,pdf:ptr<function,f32>,targetRadius:ptr<function,f32>)->vec3f {\n var total=0.0;\n for(var i=0u;i<caustic.casterCount;i++) {\n total+=(1.0-causticCosMax(causticSphere(i),origin))*causticTargetWeight(causticSphere(i));\n }\n var choice=rnd.z*total;\n var selected=causticSphere(0u);\n for(var i=0u;i<caustic.casterCount;i++) {\n let sphere=causticSphere(i);\n selected=sphere;\n choice-=(1.0-causticCosMax(sphere,origin))*causticTargetWeight(sphere);\n if(choice<=0.0) {\n break;\n }\n }\n *targetRadius=selected.w;\n let offset=selected.xyz-origin;\n let axis=normalize(select(vec3f(0.0,0.0,1.0),offset,dot(offset,offset)>1e-20));\n let cosine=mix(1.0,causticCosMax(selected,origin),rnd.x);\n let sine=sqrt(max(0.0,1.0-cosine*cosine));\n var t:vec3f;\n var b:vec3f;\n sampling_orthonormal_basis(axis,&t,&b);\n let direction=axis*cosine+(t*cos(TWOPI*rnd.y)+b*sin(TWOPI*rnd.y))*sine;\n // Evaluate the entire mixture, since intersecting bounding cones overlap.\n var overlaps=0.0;\n for(var i=0u;i<caustic.casterCount;i++) {\n let sphere=causticSphere(i);\n let c=causticCosMax(sphere,origin);\n if(c<0.0||dot(normalize(sphere.xyz-origin),direction)>=c) {\n overlaps+=causticTargetWeight(sphere);\n }\n }\n *pdf=overlaps/max(TWOPI*total,1e-20);\n return direction;\n}\n${\n mode === 'lightTracing'\n ? /* wgsl */ `${atomic}\n fn causticCameraConnect(si: SurfaceInteraction, material: OpenPbrResolvedMaterial, incoming: Ray, flux: vec3f, extinction: vec3f) {\n let eye=cameraUniform.viewInv[3].xyz;\n let forward=-normalize(cameraUniform.viewInv[2].xyz);\n let lensSample=causticEmission2D(causticDim(68u));\n let radius=cameraUniform.apertureRadius*sqrt(lensSample.x);\n var lens=eye+radius*(cos(TWOPI*lensSample.y)*cameraUniform.viewInv[0].xyz+sin(TWOPI*lensSample.y)*cameraUniform.viewInv[1].xyz);\n if(caustic.orthographic!=0u) {\n lens=si.position-forward*dot(si.position-eye,forward);\n }\n let toEye=lens-si.position;\n let distanceSquared=dot(toEye,toEye);\n let direction=normalize(toEye);\n let focalPoint=lens+(si.position-lens)*cameraUniform.focusDistance/max(dot(si.position-lens,forward),1e-12);\n let projected=select(focalPoint,si.position,caustic.orthographic!=0u);\n let clip=caustic.viewProjection*vec4f(projected,1.0);\n if(clip.w<=0.0||dot(si.position-eye,forward)<=0.0) {\n return;\n }\n let film=clip.xy/clip.w*0.5+0.5;\n if(any(film<vec2f(0.0))||any(film>=vec2f(1.0))) {\n return;\n }\n var shadow:Ray;\n initRay(&shadow,si.position+EPS*direction,direction);\n if(intersectSceneShadow(shadow,sqrt(distanceSquared)-2.0*EPS,si.triangleIndex,si.instanceIndex)) {\n return;\n }\n var m=material;\n let normal=select(-si.normal,si.normal,dot(si.normal,-incoming.d)>=0.0);\n m.inside=dot(si.faceNormal,-incoming.d)<0.0;\n m.relativeIor=select(1.0/m.specularIor,m.specularIor,m.inside);\n${\n frames\n ? /* wgsl */ ` m.directionTangent=si.directionTangent;m.directionBitangent=si.directionBitangent;m.geometryTangent=si.geometryTangent;m.geometryBitangent=si.geometryBitangent;`\n : ''\n}\n${\n coat\n ? /* wgsl */ ` m.coatNormal=normal;m.geometricNormal=select(-si.faceNormal,si.faceNormal,dot(si.faceNormal,-incoming.d)>=0.0);`\n : ''\n}\n let prepared=openpbr_prepare(si.tangent,m,-incoming.d,normal);\n var pdf:f32;\n let response=openpbr_eval_prepared(prepared,direction,&pdf);\n let cameraCosine=dot(-direction,-normalize(cameraUniform.viewInv[2].xyz));\n if(cameraCosine<=0.0) {\n return;\n }\n let importance=caustic.cameraScale*rtUniform.outputSize.x*rtUniform.outputSize.y /\n select(max(distanceSquared*cameraCosine*cameraCosine*cameraCosine,1e-12),1.0,caustic.orthographic!=0u);\n let transmittance=exp(-extinction*sqrt(distanceSquared));\n let value=flux*transmittance*response*causticAdjoint(-incoming.d,direction,normal,si.faceNormal)*importance/f32(caustic.photonCount);\n let pixel=vec2u(film*rtUniform.outputSize);\n let index=(pixel.x+pixel.y*u32(rtUniform.outputSize.x))*3u;\n causticAtomicAdd(index,value.x);\n causticAtomicAdd(index+1u,value.y);\n causticAtomicAdd(index+2u,value.z);\n}\n`\n : ''\n}\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) id:vec3u) {\n let photonIndex=id.x+id.y*4194240u;\n if(photonIndex>=caustic.photonCount) {\n return;\n }\n${mode === 'photonMapping' ? 'photonStore(photonIndex,vec4u(0u));' : ''}\n samplingPixel=vec2u(photonIndex%u32(rtUniform.outputSize.x),photonIndex/u32(rtUniform.outputSize.x));\n pixelSampleIndex=caustic.batch;\n samplingBounce=0u;\n hashedCounter=0u;\n rng_initialize(samplingPixel,i32(sampleSeed(caustic.batch,rtUniform.sequenceSeed)));\n var wavelength=0.0;\n var wavelengthPdf=1.0;\n if(SPECTRAL) {\n wavelength=spectrumSample(${SPECTRUM_PHOTON_DISTRIBUTION}u,causticEmission1D(causticDim(69u)),&wavelengthPdf);\n }\n var ls:LightSampleRec;\n causticSampleLight(caustic.casterSphere.xyz,vec3f(causticEmission2D(causticDim(60u)),causticEmission1D(causticDim(61u))),&ls);\n if(ls.pdf<=0.0) {\n return;\n }\n var infinite=ls.sourceType==0u;\n if(ls.sourceType==2u) {\n infinite=i32(analyticLights[ls.sourceIndex].litType)==DIRECTIONAL_LIGHT;\n }\n var origin=caustic.casterSphere.xyz+ls.direction*ls.dist;\n if(causticEmissionAreaPdf>0.0) { origin=causticEmissionOrigin; }\n var direction:vec3f;\n var flux:vec3f;\n var targetRadius=caustic.casterSphere.w;\n if(infinite) {\n direction=-ls.direction;\n // Mixture of projected caster disks. Every overlap contributes to the PDF.\n var area=0.0;\n for(var i=0u;i<caustic.casterCount;i++) {\n let s=causticSphere(i);\n area+=s.w*s.w*causticTargetWeight(s);\n }\n var choice=causticEmission1D(causticDim(63u))*area;\n var sphere=causticSphere(0u);\n for(var i=0u;i<caustic.casterCount;i++) {\n sphere=causticSphere(i);\n choice-=sphere.w*sphere.w*causticTargetWeight(sphere);\n if(choice<=0.0) {\n break;\n }\n }\n targetRadius=sphere.w;\n var t:vec3f;\n var b:vec3f;\n sampling_orthonormal_basis(direction,&t,&b);\n let rnd=causticEmission2D(causticDim(62u));\n origin=sphere.xyz-direction*dot(sphere.xyz-caustic.casterSphere.xyz,direction)-direction*caustic.casterSphere.w;\n origin+=sphere.w*sqrt(rnd.x)*(t*cos(TWOPI*rnd.y)+b*sin(TWOPI*rnd.y));\n var overlaps=0.0;\n for(var i=0u;i<caustic.casterCount;i++) {\n let s=causticSphere(i);\n let offset=origin-s.xyz;\n let projected=offset-direction*dot(offset,direction);\n if(dot(projected,projected)<=s.w*s.w) {\n overlaps+=causticTargetWeight(s);\n }\n }\n let diskPdf=overlaps/max(PI*area,1e-20);\n flux=ls.emission/(ls.pdf*diskPdf);\n }\n else if(ls.sourceType==2u&&i32(analyticLights[ls.sourceIndex].litType)==RECT_LIGHT&&ls.isDelta) {\n // A zero-spread rectangle is a collimated beam, matching PT's delta source.\n direction=ls.normal;\n flux=ls.emission/causticEmissionAreaPdf;\n }\n else {\n var areaPdf=ls.pdf*abs(dot(ls.normal,-ls.direction))/max(ls.dist*ls.dist,1e-20);\n if(causticEmissionAreaPdf>0.0) { areaPdf=causticEmissionAreaPdf; }\n if(ls.isDelta) {\n areaPdf=ls.selectionPmf;\n }\n if(areaPdf<=0.0) {\n return;\n }\n var directionPdf:f32;\n direction=causticDirection(origin,vec3f(causticEmission2D(causticDim(62u)),causticEmission1D(causticDim(63u))),&directionPdf,&targetRadius);\n var emission=ls.emission;\n var emissionCosine=max(0.0,dot(ls.normal,direction));\n if(ls.sourceType==2u) {\n let source=analyticLights[ls.sourceIndex];\n emission=source.emission;\n if(i32(source.litType)==SPOT_LIGHT) {\n emission*=evaluateSpotEmissionMultiplier(source,direction);\n }\n if(ls.isDelta) {\n emissionCosine=1.0;\n }\n if(i32(source.litType)==RECT_LIGHT&&emissionCosine<cos(0.5*PI*source.radiusOrSpread)) {\n return;\n }\n if(i32(source.litType)==RECT_LIGHT) {\n var textured:LightSampleRec;\n textured.emission=emission;\n light_apply_rect_texture(source,origin,source.position-0.5*(source.u+source.v),&textured);\n emission=textured.emission;\n }\n }\n flux=emission*emissionCosine/(areaPdf*directionPdf);\n }\n if(SPECTRAL) {\n flux*=spectrumRgb(wavelength)/(wavelengthPdf*${SPECTRUM_WAVELENGTH_RANGE}.0);\n }\n var ray:Ray;\n initRay(&ray,origin+EPS*direction,direction);\n var chain=0u;\n var singleT=false;\n var extinction=vec3f(0.0);\n var scattering=vec3f(0.0);\n var si:SurfaceInteraction;\n for(var depth=0;\n${mode === 'photonMapping' ? 'depth<=max(rtUniform.bounces,0)' : 'depth<20'}\n ;\n depth++) {\n samplingBounce=u32(depth);\n si.triangleIndex=-1;\n intersectScene(ray,&si,0,true,depth>0);\n if(!si.hit) {\n return;\n }\n var m:OpenPbrResolvedMaterial;\n getMaterial(&m,si.materialIndex,si.uv,si.uv1,si.instanceIndex,si.vertexColor);\n if(m.alpha<1.0&&causticEmission1D(causticDim(79u))>=m.alpha) {\n initRay(&ray,si.position+EPS*ray.d,ray.d);\n continue;\n }\n if(m.materialType==SHADOW_CATCHER) {\n return;\n }\n let segment=distance(ray.o,si.position);\n let channel=min(u32(sample1D(bounceDim(MEDIUM_CHANNEL))*3.0),2u);\n if(extinction[channel]>0.0&&any(scattering>vec3f(0.0))) {\n let sampled=-log(max(sample1D(bounceDim(MEDIUM_DISTANCE)),1e-7))/extinction[channel];\n // A volume event belongs to PT's R3. Surface-reaching photons use the\n // same RGB survival mixture as PT, retaining absorption and scattering.\n if(sampled<segment) {\n return;\n }\n }\n let transmittance=exp(-extinction*segment);\n let survival=(transmittance.x+transmittance.y+transmittance.z)/3.0;\n flux*=transmittance/select(1.0,max(survival,1e-20),any(scattering>vec3f(0.0)));\n if(SPECTRAL) {\n m.specularIor=sampling_cauchy_ior(spectrumIorWavelength(wavelength),m.specularIor,m.transmissionDispersionScale,m.dispersionAbbeNumber);\n }\n if(!isCausticSpecular(m)) {\n let mneeSource=ls.sourceType!=0u;\n let r1=mneeSource&&chain==1u&&singleT&&m.baseWeight*(1.0-m.baseMetalness)*(1.0-m.transmissionWeight)>0.0&&m.subsurfaceWeight==0.0;\n if(chain>0u\n${mode === 'lightTracing' ? '&&!(caustic.mnee!=0u&&r1)' : ''}){${\n mode === 'lightTracing'\n ? 'causticCameraConnect(si,m,ray,flux,extinction);'\n : /* wgsl */ `\n let position=bitcast<vec3u>(si.position);\n photonStore(photonIndex,vec4u(position,photonEncodeFlux(flux)));\n if((caustic.experiments&1u)!=0u) {\n var maximum=1e-8;\n for(var i=0u;i<caustic.casterCount;i++) { maximum=max(maximum,causticSphere(i).w); }\n let buckets=min(${PHOTON_GRID_SIZE}u,1u<<(32u-countLeadingZeros(max(256u,caustic.photonCount*2u)-1u)));\n let index=8u+buckets*4u+caustic.photonCount*3u+photonIndex;\n let width=textureDimensions(photonDirections).x;\n textureStore(photonDirections,vec2i(vec2u(index%width,index/width)),vec4u(bitcast<u32>(targetRadius/maximum)));\n }\n let incoming=vec2u(round(photonEncodeOct(-ray.d)*255.0));\n let normal=vec2u(round(photonEncodeOct(si.faceNormal)*vec2f(255.0,127.0)));\n photonStoreDirections(photonIndex,normal.x|(normal.y<<8u)|(select(0u,1u,r1)<<15u)|(incoming.x<<16u)|(incoming.y<<24u));\n`\n }\n}\nreturn;\n}\nlet view=-ray.d;\nlet normal=select(-si.normal,si.normal,dot(si.normal,view)>=0.0);\nm.inside=dot(si.faceNormal,view)<0.0;\nm.relativeIor=select(1.0/m.specularIor,m.specularIor,m.inside);\n${\n frames\n ? /* wgsl */ ` m.directionTangent=si.directionTangent;m.directionBitangent=si.directionBitangent;m.geometryTangent=si.geometryTangent;m.geometryBitangent=si.geometryBitangent;`\n : ''\n}\n${\n coat\n ? /* wgsl */ ` m.coatNormal=normal;m.geometricNormal=select(-si.faceNormal,si.faceNormal,dot(si.faceNormal,view)>=0.0);`\n : ''\n}\n let prepared=openpbr_prepare(si.tangent,m,view,normal);\n var next:vec3f;\n var pdf:f32;\n var transmission:bool;\n var roughness:f32;\n var specular:bool;\n var diffuse:bool;\n var lobe:u32;\n var marginal:f32;\n var full:vec3f;\n let response=openpbr_sample_prepared(prepared,vec3f(sample2D(bounceDim(BSDF_DIRECTION)),sample1D(bounceDim(BSDF_LOBE))),&next,&pdf,&transmission,&roughness,&specular,&diffuse,&lobe,&marginal,&full);\n if(!(pdf>0.0)) {\n return;\n }\n if(chain==0u&&ls.sourceType==0u&&transmission) {\n var reverse:Ray;\n initRay(&reverse,si.position,-ray.d);\n let bg=sampleSceneBackground(reverse);\n flux*=bg.rgb*bg.a/max(ls.emission,vec3f(1e-20));\n }\n flux*=response/pdf*causticAdjoint(view,next,normal,si.faceNormal);\n // The shared BSDF is a radiance sampler. Cancel its eta_i/eta_t squared\n // factor for adjoint (light-to-camera) propagation.\n if(transmission&&!m.thinWalled) {\n flux/=m.relativeIor*m.relativeIor;\n }\n // Match PT: a clear solid interface does not replace the active medium.\n if(transmission&&!m.thinWalled&&m.mediumType>0) {\n extinction=select(m.mediumExtinction,vec3f(0.0),m.inside);\n scattering=select(m.mediumScattering,vec3f(0.0),m.inside);\n }\n chain++;\n singleT=chain==1u&&((isCausticT(m)&&transmission)||(isCausticR(m)&&!transmission));\n initRay(&ray,si.position+EPS*next,next);\n}\n}\n`\n );\n}\n\nexport const causticAccumulateCompute = /* wgsl */ `\n @group(0) @binding(0) var<storage,read_write> splats:array<atomic<u32>>;\n @group(0) @binding(1) var<storage,read_write> accumulation:array<vec4f>;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) id:vec3u, @builtin(num_workgroups) groups:vec3u) {\n // Large images exceed one dispatch row; preserve the linear pixel order.\n let pixel=id.x+id.y*groups.x*64u;\n if(pixel>=arrayLength(&accumulation)) {\n return;\n }\n let i=pixel*3u;\n accumulation[pixel]+=vec4f(vec3f(bitcast<f32>(atomicLoad(&splats[i])),bitcast<f32>(atomicLoad(&splats[i+1u])),bitcast<f32>(atomicLoad(&splats[i+2u]))),1.0);\n }\n`;\n\n/** Native buffer resolve has the same independent batch normalization as web. */\nexport const causticBufferCompositeCompute = /* wgsl */ `\n @group(0)@binding(0)var<storage,read>pt:array<vec4f>;\n @group(0)@binding(1)var<storage,read>caustics:array<vec4f>;\n @group(0)@binding(2)var<storage,read>normal:array<vec4f>;\n @group(0)@binding(3)var<storage,read_write>output:array<vec4f>;\n @compute @workgroup_size(64)fn main(@builtin(global_invocation_id)id:vec3u,@builtin(num_workgroups)groups:vec3u) {\n // Large images exceed one dispatch row; preserve the linear pixel order.\n let pixel=id.x+id.y*groups.x*64u;\n if(pixel>=arrayLength(&output)) {\n return;\n }\n let c=caustics[pixel];\n let p=pt[pixel];\n output[pixel]=vec4f(p.rgb+c.rgb/max(c.a,1.0)*normal[pixel].a,p.a);\n }\n`;\n"]}
|
|
1
|
+
{"version":3,"file":"causticLight.compute.js","sourceRoot":"","sources":["../../../src/renderer/wgsl/causticLight.compute.ts"],"names":[],"mappings":"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","sourcesContent":["import {PHOTON_GRID_SIZE, photonPackingWgsl} from './photonMap.compute.js';\nimport {SPECTRUM_PHOTON_DISTRIBUTION, SPECTRUM_WAVELENGTH_RANGE, spectrumWgsl} from '../spectrum.js';\nimport background from './chunks/background.wgsl.js';\nimport atomic from './chunks/causticAtomic.wgsl.js';\nimport adjoint from './chunks/causticAdjoint.wgsl.js';\nimport common from './chunks/common.wgsl.js';\nimport core from './chunks/rayTraceCore.wgsl.js';\nimport {createMaterialBufferWgsl} from './chunks/materialBuffer.wgsl.js';\nimport intersect from './chunks/intersect.wgsl.js';\nimport rand from './chunks/rand.wgsl.js';\nimport random from './chunks/random.wgsl.js';\nimport env from './chunks/envMap.wgsl.js';\nimport sampling from './chunks/sampling.wgsl.js';\nimport {createOpenPbrBsdfWgsl} from './chunks/openPbrBsdf.wgsl.js';\nimport light from './chunks/sampleLight.wgsl.js';\nimport uv from './chunks/uvProjection.wgsl.js';\nimport {causticPartitionWGSL} from './chunks/caustics.wgsl.js';\nimport {wgpuShaderPreamble} from '../wgpuShaderSource.js';\nimport {WGPU_SCENE_FEATURE_BITS} from '../WGPUSceneFeatures.js';\n\nexport const CAUSTIC_UNIFORM_BYTES = 112;\nexport const causticUnitRandomWgsl = /* wgsl */ `\n fn causticUnitRandom(value:f32)->f32 {\n return clamp((value-EPS)/(1.0-2.0*EPS),0.0,0.99999994);\n }\n`;\n\n// Preserve the dormant R1 uniform slot and shader spelling for source parity.\n// Production packs zero; only an explicitly registered research adapter sets it.\nexport function causticLightCompute(\n constants: Record<string, boolean | number>,\n blasLayoutMask: number,\n hasDisplacement: boolean,\n mode: 'lightTracing' | 'photonMapping' = 'lightTracing',\n sceneIntersections = '',\n fullEmissionRandom = true,\n perInstanceBounds = false,\n compactVertices = false,\n independentTextures = false\n) {\n const frames = !!constants.USE_ANISOTROPY_DIRECTIONS || !!constants.USE_COAT_NORMAL_FRAMES,\n coat = !!constants.USE_COAT_NORMAL_FRAMES;\n return (\n (independentTextures ? 'enable wgpu_binding_array;\\n' : '') + wgpuShaderPreamble(constants, true) +\n /* wgsl */ `\n${Object.keys(WGPU_SCENE_FEATURE_BITS)\n .map((name) => `const ${name} = ${!!constants[name]};`)\n .join('\\n')}\n${common}\n${core({independentTextures, compactVertices, hasAnisotropyDirections: frames, hasCoatNormals: coat, caustics: true})}\n${uv}\n${createMaterialBufferWgsl({independentTextures, hasAnisotropyDirections: frames, hasCoatNormals: coat, caustics: true})}\n${intersect({\n perInstanceBounds,\n compactVertices,\n hasDisplacement,\n blasLayoutMask,\n hasAnisotropyDirections: frames,\n hasCoatNormals: coat,\n sceneIntersections\n})}\n${rand}\n${random}\n${env}\n${sampling}\n${spectrumWgsl}\n${createOpenPbrBsdfWgsl(frames, coat)}\n${light}\n${background.replace(\n /\\b(?:USE_BACKGROUND|USE_BACKGROUND_MAP|USE_GROUNDED_BACKGROUND)\\b/g,\n (name) =>\n `((rtUniform.backgroundFlags & ${\n {USE_BACKGROUND: 1, USE_BACKGROUND_MAP: 2, USE_GROUNDED_BACKGROUND: 4}[name]\n }) != 0)`\n)}\n${causticPartitionWGSL(false, false)}\n${adjoint}\n struct CausticLightUniform {\n casterSphere: vec4f,\n viewProjection: mat4x4f,\n photonCount: u32,\n batch: u32,\n mnee: u32,\n cameraScale: f32,\n casterCount: u32,\n choiceCount: u32,\n orthographic: u32,\n experiments: u32,\n }\n @group(3) @binding(0) var<uniform> caustic: CausticLightUniform;\n${\n mode === 'lightTracing'\n ? '@group(3) @binding(1) var<storage,read_write> splats: array<atomic<u32>>;'\n : `${photonPackingWgsl}\n @group(3)@binding(1)var photons:texture_storage_2d<rgba32uint,write>;\n @group(3)@binding(3)var photonDirections:texture_storage_2d<r32uint,write>;\n fn photonStore(i:u32,value:vec4u) {\n let width=textureDimensions(photons).x;\n textureStore(photons,vec2i(vec2u(i%width,i/width)),value);\n }\n fn photonStoreDirections(i:u32,value:u32) {\n let buckets=min(${PHOTON_GRID_SIZE}u,1u<<(32u-countLeadingZeros(max(256u,caustic.photonCount*2u)-1u)));\n let index=8u+buckets*4u+caustic.photonCount*2u+i;\n let width=textureDimensions(photonDirections).x;\n textureStore(photonDirections,vec2i(vec2u(index%width,index/width)),vec4u(value));\n }\n`\n}\n @group(3) @binding(2) var causticDistribution:texture_2d<f32>;\n // Geometric emission PDFs assume the full disk/cone/area support. The generic\n // BSDF sampler contracts randoms by EPS; undo that contraction for these warps.\n${causticUnitRandomWgsl}\nfn causticEmission1D(dim:u32)->f32{return ${\n fullEmissionRandom ? 'causticUnitRandom(sample1D(dim))' : 'sample1D(dim)'\n }\n ;\n}\nfn causticEmission2D(dim:u32)->vec2f {\n let value=sample2D(dim);\n return\n${fullEmissionRandom ? 'vec2f(causticUnitRandom(value.x),causticUnitRandom(value.y))' : 'value'}\n ;\n}\nfn causticLoad(i:u32)->vec4f {\n let width=textureDimensions(causticDistribution).x;\n return textureLoad(causticDistribution,vec2i(vec2u(i%width,i/width)),0);\n}\nfn causticSphere(i:u32)->vec4f {\n return causticLoad(i);\n}\n// Photon emission samples a source surface, independently of any receiver.\n// A NEE sample viewed from the aggregate caster center can reject an emitter\n// which still illuminates another caster, or sample only part of a sphere.\nvar<private> causticEmissionOrigin:vec3f;\nvar<private> causticEmissionAreaPdf:f32;\nfn causticAreaLight(origin:vec3f,position:vec3f,normal:vec3f,emission:vec3f,area:f32,pmf:f32,result:ptr<function,LightSampleRec>) {\n causticEmissionOrigin=position;\n causticEmissionAreaPdf=pmf/max(area,1e-20);\n let offset=position-origin;\n (*result).dist=length(offset);\n (*result).direction=offset/max((*result).dist,1e-20);\n (*result).normal=normal;\n (*result).emission=emission;\n (*result).pdf=pmf;\n}\nfn causticSampleLight(origin:vec3f,rnd:vec3f,result:ptr<function,LightSampleRec>) {\n causticEmissionAreaPdf=0.0;\n (*result).pdf=0.0;\n (*result).sourceType=0u;\n (*result).isDelta=false;\n if(caustic.choiceCount==0u) {\n return;\n }\n let scaled=rnd.z*f32(caustic.choiceCount);\n let column=min(u32(scaled),caustic.choiceCount-1u);\n let entry=causticLoad(caustic.casterCount+column*2u);\n let selected=select(bitcast<u32>(entry.y),column,fract(scaled)<entry.x);\n let choice=bitcast<vec4u>(causticLoad(caustic.casterCount+selected*2u));\n let pmf=causticLoad(caustic.casterCount+selected*2u+1u).x;\n (*result).sourceIndex=choice.w;\n if(choice.z==1u) {\n (*result).sourceType=0u;\n sampleEnvLight(rnd.xy,result);\n (*result).pdf*=pmf;\n }\n else if(choice.z==2u) {\n (*result).sourceType=2u;\n var light=analyticLights[choice.w];\n let kind=i32(light.litType);\n if(kind==RECT_LIGHT) {\n let position=light.position+light.u*(rnd.x-.5)+light.v*(rnd.y-.5);\n causticAreaLight(origin,position,normalize(cross(light.u,light.v)),light.emission,light.area,pmf,result);\n (*result).isDelta=light.radiusOrSpread<=.001;\n light_apply_rect_texture(light,position,light.position-.5*(light.u+light.v),result);\n }\n else if((kind==SPHERE_LIGHT||kind==SPOT_LIGHT)&&light.radiusOrSpread>0.0) {\n let z=1.0-2.0*rnd.x;\n let radial=sqrt(max(0.0,1.0-z*z));\n let normal=vec3f(radial*cos(TWOPI*rnd.y),radial*sin(TWOPI*rnd.y),z);\n causticAreaLight(origin,light.position+light.radiusOrSpread*normal,normal,light.emission,light.area,pmf,result);\n }\n else {\n light.selectionPmf=1.0;\n sampleAnalyticLight(light,origin,rnd.xy,result);\n (*result).pdf*=pmf;\n }\n }\n else {\n (*result).sourceType=1u;\n let triangle=getEmissiveTriangle(i32(choice.w));\n let u=sqrt(rnd.x);\n let bary=vec3f(1.0-u,u*(1.0-rnd.y),u*rnd.y);\n let position=bary.x*triangle.p0+bary.y*triangle.p1+bary.z*triangle.p2;\n let normal=normalize(cross(triangle.p1-triangle.p0,triangle.p2-triangle.p0));\n let uv=bary.x*triangle.uv0+bary.y*triangle.uv1+bary.z*triangle.uv2;\n let uv1=bary.x*triangle.secondaryUv0+bary.y*triangle.secondaryUv1+bary.z*triangle.secondaryUv2;\n causticAreaLight(origin,position,normal,getMaterialEmission(i32(triangle.materialIndex),uv,uv1),triangle.area,pmf,result);\n }\n (*result).selectionPmf=pmf;\n}\nfn causticCosMax(s:vec4f,origin:vec3f)->f32 {\n let distanceSquared=dot(s.xyz-origin,s.xyz-origin);\n return select(-1.0,sqrt(max(0.0,1.0-s.w*s.w/distanceSquared)),distanceSquared>s.w*s.w);\n}\n// A positive floor retains paths from offscreen or occluded casters. The\n// complete weighted mixture PDF below, including overlaps, compensates selection.\nfn causticTargetWeight(s:vec4f)->f32 {\n if((caustic.experiments&2u)==0u) { return 1.0; }\n let clip=caustic.viewProjection*vec4f(s.xyz,1.0);\n let m=caustic.viewProjection;\n let extent=s.w*max(length(vec3f(m[0].x,m[1].x,m[2].x)),length(vec3f(m[0].y,m[1].y,m[2].y)));\n if(clip.w<=0.0||abs(clip.x)>clip.w+extent||abs(clip.y)>clip.w+extent) { return .05; }\n return .05+min(1.0,extent*extent/max(clip.w*clip.w,1e-20));\n}\nfn causticDirection(origin:vec3f,rnd:vec3f,pdf:ptr<function,f32>,targetRadius:ptr<function,f32>)->vec3f {\n var total=0.0;\n for(var i=0u;i<caustic.casterCount;i++) {\n total+=(1.0-causticCosMax(causticSphere(i),origin))*causticTargetWeight(causticSphere(i));\n }\n var choice=rnd.z*total;\n var selected=causticSphere(0u);\n for(var i=0u;i<caustic.casterCount;i++) {\n let sphere=causticSphere(i);\n selected=sphere;\n choice-=(1.0-causticCosMax(sphere,origin))*causticTargetWeight(sphere);\n if(choice<=0.0) {\n break;\n }\n }\n *targetRadius=selected.w;\n let offset=selected.xyz-origin;\n let axis=normalize(select(vec3f(0.0,0.0,1.0),offset,dot(offset,offset)>1e-20));\n let cosine=mix(1.0,causticCosMax(selected,origin),rnd.x);\n let sine=sqrt(max(0.0,1.0-cosine*cosine));\n var t:vec3f;\n var b:vec3f;\n sampling_orthonormal_basis(axis,&t,&b);\n let direction=axis*cosine+(t*cos(TWOPI*rnd.y)+b*sin(TWOPI*rnd.y))*sine;\n // Evaluate the entire mixture, since intersecting bounding cones overlap.\n var overlaps=0.0;\n for(var i=0u;i<caustic.casterCount;i++) {\n let sphere=causticSphere(i);\n let c=causticCosMax(sphere,origin);\n if(c<0.0||dot(normalize(sphere.xyz-origin),direction)>=c) {\n overlaps+=causticTargetWeight(sphere);\n }\n }\n *pdf=overlaps/max(TWOPI*total,1e-20);\n return direction;\n}\n${\n mode === 'lightTracing'\n ? /* wgsl */ `${atomic}\n fn causticCameraConnect(si: SurfaceInteraction, material: OpenPbrResolvedMaterial, incoming: Ray, flux: vec3f, extinction: vec3f) {\n let eye=cameraUniform.viewInv[3].xyz;\n let forward=-normalize(cameraUniform.viewInv[2].xyz);\n let lensSample=causticEmission2D(causticDim(68u));\n let radius=cameraUniform.apertureRadius*sqrt(lensSample.x);\n var lens=eye+radius*(cos(TWOPI*lensSample.y)*cameraUniform.viewInv[0].xyz+sin(TWOPI*lensSample.y)*cameraUniform.viewInv[1].xyz);\n if(caustic.orthographic!=0u) {\n lens=si.position-forward*dot(si.position-eye,forward);\n }\n let toEye=lens-si.position;\n let distanceSquared=dot(toEye,toEye);\n let direction=normalize(toEye);\n let focalPoint=lens+(si.position-lens)*cameraUniform.focusDistance/max(dot(si.position-lens,forward),1e-12);\n let projected=select(focalPoint,si.position,caustic.orthographic!=0u);\n let clip=caustic.viewProjection*vec4f(projected,1.0);\n if(clip.w<=0.0||dot(si.position-eye,forward)<=0.0) {\n return;\n }\n let film=clip.xy/clip.w*0.5+0.5;\n if(any(film<vec2f(0.0))||any(film>=vec2f(1.0))) {\n return;\n }\n var shadow:Ray;\n initRay(&shadow,si.position+EPS*direction,direction);\n if(intersectSceneShadow(shadow,sqrt(distanceSquared)-2.0*EPS,si.triangleIndex,si.instanceIndex)) {\n return;\n }\n var m=material;\n let normal=select(-si.normal,si.normal,dot(si.normal,-incoming.d)>=0.0);\n m.inside=dot(si.faceNormal,-incoming.d)<0.0;\n m.relativeIor=select(1.0/m.specularIor,m.specularIor,m.inside);\n${\n frames\n ? /* wgsl */ ` m.directionTangent=si.directionTangent;m.directionBitangent=si.directionBitangent;m.geometryTangent=si.geometryTangent;m.geometryBitangent=si.geometryBitangent;`\n : ''\n}\n${\n coat\n ? /* wgsl */ ` m.coatNormal=normal;m.geometricNormal=select(-si.faceNormal,si.faceNormal,dot(si.faceNormal,-incoming.d)>=0.0);`\n : ''\n}\n let prepared=openpbr_prepare(si.tangent,m,-incoming.d,normal);\n var pdf:f32;\n let response=openpbr_eval_prepared(prepared,direction,&pdf);\n let cameraCosine=dot(-direction,-normalize(cameraUniform.viewInv[2].xyz));\n if(cameraCosine<=0.0) {\n return;\n }\n let importance=caustic.cameraScale*rtUniform.outputSize.x*rtUniform.outputSize.y /\n select(max(distanceSquared*cameraCosine*cameraCosine*cameraCosine,1e-12),1.0,caustic.orthographic!=0u);\n let transmittance=exp(-extinction*sqrt(distanceSquared));\n let value=flux*transmittance*response*causticAdjoint(-incoming.d,direction,normal,si.faceNormal)*importance/f32(caustic.photonCount);\n let pixel=vec2u(film*rtUniform.outputSize);\n let index=(pixel.x+pixel.y*u32(rtUniform.outputSize.x))*3u;\n causticAtomicAdd(index,value.x);\n causticAtomicAdd(index+1u,value.y);\n causticAtomicAdd(index+2u,value.z);\n}\n`\n : ''\n}\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) id:vec3u) {\n let photonIndex=id.x+id.y*4194240u;\n if(photonIndex>=caustic.photonCount) {\n return;\n }\n${mode === 'photonMapping' ? 'photonStore(photonIndex,vec4u(0u));' : ''}\n samplingPixel=vec2u(photonIndex%u32(rtUniform.outputSize.x),photonIndex/u32(rtUniform.outputSize.x));\n pixelSampleIndex=caustic.batch;\n samplingBounce=0u;\n hashedCounter=0u;\n rng_initialize(samplingPixel,i32(sampleSeed(caustic.batch,rtUniform.sequenceSeed)));\n var wavelength=0.0;\n var wavelengthPdf=1.0;\n if(SPECTRAL) {\n wavelength=spectrumSample(${SPECTRUM_PHOTON_DISTRIBUTION}u,causticEmission1D(causticDim(69u)),&wavelengthPdf);\n }\n var ls:LightSampleRec;\n causticSampleLight(caustic.casterSphere.xyz,vec3f(causticEmission2D(causticDim(60u)),causticEmission1D(causticDim(61u))),&ls);\n if(ls.pdf<=0.0) {\n return;\n }\n var infinite=ls.sourceType==0u;\n if(ls.sourceType==2u) {\n infinite=i32(analyticLights[ls.sourceIndex].litType)==DIRECTIONAL_LIGHT;\n }\n var origin=caustic.casterSphere.xyz+ls.direction*ls.dist;\n if(causticEmissionAreaPdf>0.0) { origin=causticEmissionOrigin; }\n var direction:vec3f;\n var flux:vec3f;\n var targetRadius=caustic.casterSphere.w;\n if(infinite) {\n direction=-ls.direction;\n // Mixture of projected caster disks. Every overlap contributes to the PDF.\n var area=0.0;\n for(var i=0u;i<caustic.casterCount;i++) {\n let s=causticSphere(i);\n area+=s.w*s.w*causticTargetWeight(s);\n }\n var choice=causticEmission1D(causticDim(63u))*area;\n var sphere=causticSphere(0u);\n for(var i=0u;i<caustic.casterCount;i++) {\n sphere=causticSphere(i);\n choice-=sphere.w*sphere.w*causticTargetWeight(sphere);\n if(choice<=0.0) {\n break;\n }\n }\n targetRadius=sphere.w;\n var t:vec3f;\n var b:vec3f;\n sampling_orthonormal_basis(direction,&t,&b);\n let rnd=causticEmission2D(causticDim(62u));\n origin=sphere.xyz-direction*dot(sphere.xyz-caustic.casterSphere.xyz,direction)-direction*caustic.casterSphere.w;\n origin+=sphere.w*sqrt(rnd.x)*(t*cos(TWOPI*rnd.y)+b*sin(TWOPI*rnd.y));\n var overlaps=0.0;\n for(var i=0u;i<caustic.casterCount;i++) {\n let s=causticSphere(i);\n let offset=origin-s.xyz;\n let projected=offset-direction*dot(offset,direction);\n if(dot(projected,projected)<=s.w*s.w) {\n overlaps+=causticTargetWeight(s);\n }\n }\n let diskPdf=overlaps/max(PI*area,1e-20);\n flux=ls.emission/(ls.pdf*diskPdf);\n }\n else if(ls.sourceType==2u&&i32(analyticLights[ls.sourceIndex].litType)==RECT_LIGHT&&ls.isDelta) {\n // A zero-spread rectangle is a collimated beam, matching PT's delta source.\n direction=ls.normal;\n flux=ls.emission/causticEmissionAreaPdf;\n }\n else {\n var areaPdf=ls.pdf*abs(dot(ls.normal,-ls.direction))/max(ls.dist*ls.dist,1e-20);\n if(causticEmissionAreaPdf>0.0) { areaPdf=causticEmissionAreaPdf; }\n if(ls.isDelta) {\n areaPdf=ls.selectionPmf;\n }\n if(areaPdf<=0.0) {\n return;\n }\n var directionPdf:f32;\n direction=causticDirection(origin,vec3f(causticEmission2D(causticDim(62u)),causticEmission1D(causticDim(63u))),&directionPdf,&targetRadius);\n var emission=ls.emission;\n var emissionCosine=max(0.0,dot(ls.normal,direction));\n if(ls.sourceType==2u) {\n let source=analyticLights[ls.sourceIndex];\n emission=source.emission;\n if(i32(source.litType)==SPOT_LIGHT) {\n emission*=evaluateSpotEmissionMultiplier(source,direction);\n }\n if(ls.isDelta) {\n emissionCosine=1.0;\n }\n if(i32(source.litType)==RECT_LIGHT&&emissionCosine<cos(0.5*PI*source.radiusOrSpread)) {\n return;\n }\n if(i32(source.litType)==RECT_LIGHT) {\n var textured:LightSampleRec;\n textured.emission=emission;\n light_apply_rect_texture(source,origin,source.position-0.5*(source.u+source.v),&textured);\n emission=textured.emission;\n }\n }\n flux=emission*emissionCosine/(areaPdf*directionPdf);\n }\n if(SPECTRAL) {\n flux*=spectrumRgb(wavelength)/(wavelengthPdf*${SPECTRUM_WAVELENGTH_RANGE}.0);\n }\n var ray:Ray;\n initRay(&ray,origin+EPS*direction,direction);\n var chain=0u;\n var singleT=false;\n var extinction=vec3f(0.0);\n var scattering=vec3f(0.0);\n var si:SurfaceInteraction;\n for(var depth=0;\n${mode === 'photonMapping' ? 'depth<=max(rtUniform.bounces,0)' : 'depth<20'}\n ;\n depth++) {\n samplingBounce=u32(depth);\n si.triangleIndex=-1;\n intersectScene(ray,&si,0,true,depth>0);\n if(!si.hit) {\n return;\n }\n var m:OpenPbrResolvedMaterial;\n getMaterial(&m,si.materialIndex,si.uv,si.uv1,si.instanceIndex,si.vertexColor);\n if(m.alpha<1.0&&causticEmission1D(causticDim(79u))>=m.alpha) {\n initRay(&ray,si.position+EPS*ray.d,ray.d);\n continue;\n }\n if(m.materialType==SHADOW_CATCHER) {\n return;\n }\n let segment=distance(ray.o,si.position);\n let channel=min(u32(sample1D(bounceDim(MEDIUM_CHANNEL))*3.0),2u);\n if(extinction[channel]>0.0&&any(scattering>vec3f(0.0))) {\n let sampled=-log(max(sample1D(bounceDim(MEDIUM_DISTANCE)),1e-7))/extinction[channel];\n // A volume event belongs to PT's R3. Surface-reaching photons use the\n // same RGB survival mixture as PT, retaining absorption and scattering.\n if(sampled<segment) {\n return;\n }\n }\n let transmittance=exp(-extinction*segment);\n let survival=(transmittance.x+transmittance.y+transmittance.z)/3.0;\n flux*=transmittance/select(1.0,max(survival,1e-20),any(scattering>vec3f(0.0)));\n if(SPECTRAL) {\n m.specularIor=sampling_cauchy_ior(spectrumIorWavelength(wavelength),m.specularIor,m.transmissionDispersionScale,m.dispersionAbbeNumber);\n }\n if(!isCausticSpecular(m)) {\n let mneeSource=ls.sourceType!=0u;\n let r1=mneeSource&&chain==1u&&singleT&&m.baseWeight*(1.0-m.baseMetalness)*(1.0-m.transmissionWeight)>0.0&&m.subsurfaceWeight==0.0;\n if(chain>0u\n${mode === 'lightTracing' ? '&&!(caustic.mnee!=0u&&r1)' : ''}){${\n mode === 'lightTracing'\n ? 'causticCameraConnect(si,m,ray,flux,extinction);'\n : /* wgsl */ `\n let position=bitcast<vec3u>(si.position);\n photonStore(photonIndex,vec4u(position,photonEncodeFlux(flux)));\n if((caustic.experiments&1u)!=0u) {\n var maximum=1e-8;\n for(var i=0u;i<caustic.casterCount;i++) { maximum=max(maximum,causticSphere(i).w); }\n let buckets=min(${PHOTON_GRID_SIZE}u,1u<<(32u-countLeadingZeros(max(256u,caustic.photonCount*2u)-1u)));\n let index=8u+buckets*4u+caustic.photonCount*3u+photonIndex;\n let width=textureDimensions(photonDirections).x;\n textureStore(photonDirections,vec2i(vec2u(index%width,index/width)),vec4u(bitcast<u32>(targetRadius/maximum)));\n }\n let incoming=vec2u(round(photonEncodeOct(-ray.d)*255.0));\n let normal=vec2u(round(photonEncodeOct(si.faceNormal)*vec2f(255.0,127.0)));\n photonStoreDirections(photonIndex,normal.x|(normal.y<<8u)|(select(0u,1u,r1)<<15u)|(incoming.x<<16u)|(incoming.y<<24u));\n`\n }\n}\nreturn;\n}\nlet view=-ray.d;\nlet normal=select(-si.normal,si.normal,dot(si.normal,view)>=0.0);\nm.inside=dot(si.faceNormal,view)<0.0;\nm.relativeIor=select(1.0/m.specularIor,m.specularIor,m.inside);\n${\n frames\n ? /* wgsl */ ` m.directionTangent=si.directionTangent;m.directionBitangent=si.directionBitangent;m.geometryTangent=si.geometryTangent;m.geometryBitangent=si.geometryBitangent;`\n : ''\n}\n${\n coat\n ? /* wgsl */ ` m.coatNormal=normal;m.geometricNormal=select(-si.faceNormal,si.faceNormal,dot(si.faceNormal,view)>=0.0);`\n : ''\n}\n let prepared=openpbr_prepare(si.tangent,m,view,normal);\n var next:vec3f;\n var pdf:f32;\n var transmission:bool;\n var roughness:f32;\n var specular:bool;\n var diffuse:bool;\n var lobe:u32;\n var marginal:f32;\n var full:vec3f;\n let response=openpbr_sample_prepared(prepared,vec3f(sample2D(bounceDim(BSDF_DIRECTION)),sample1D(bounceDim(BSDF_LOBE))),&next,&pdf,&transmission,&roughness,&specular,&diffuse,&lobe,&marginal,&full);\n if(!(pdf>0.0)) {\n return;\n }\n if(chain==0u&&ls.sourceType==0u&&transmission) {\n var reverse:Ray;\n initRay(&reverse,si.position,-ray.d);\n let bg=sampleSceneBackground(reverse);\n flux*=bg.rgb*bg.a/max(ls.emission,vec3f(1e-20));\n }\n flux*=response/pdf*causticAdjoint(view,next,normal,si.faceNormal);\n // The shared BSDF is a radiance sampler. Cancel its eta_i/eta_t squared\n // factor for adjoint (light-to-camera) propagation.\n if(transmission&&!m.thinWalled) {\n flux/=m.relativeIor*m.relativeIor;\n }\n // Match PT: a clear solid interface does not replace the active medium.\n if(transmission&&!m.thinWalled&&m.mediumType>0) {\n extinction=select(m.mediumExtinction,vec3f(0.0),m.inside);\n scattering=select(m.mediumScattering,vec3f(0.0),m.inside);\n }\n chain++;\n singleT=chain==1u&&((isCausticT(m)&&transmission)||(isCausticR(m)&&!transmission));\n initRay(&ray,si.position+EPS*next,next);\n}\n}\n`\n );\n}\n\nexport const causticAccumulateCompute = /* wgsl */ `\n @group(0) @binding(0) var<storage,read_write> splats:array<atomic<u32>>;\n @group(0) @binding(1) var<storage,read_write> accumulation:array<vec4f>;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) id:vec3u, @builtin(num_workgroups) groups:vec3u) {\n // Large images exceed one dispatch row; preserve the linear pixel order.\n let pixel=id.x+id.y*groups.x*64u;\n if(pixel>=arrayLength(&accumulation)) {\n return;\n }\n let i=pixel*3u;\n accumulation[pixel]+=vec4f(vec3f(bitcast<f32>(atomicLoad(&splats[i])),bitcast<f32>(atomicLoad(&splats[i+1u])),bitcast<f32>(atomicLoad(&splats[i+2u]))),1.0);\n }\n`;\n\n/** Native buffer resolve has the same independent batch normalization as web. */\nexport const causticBufferCompositeCompute = /* wgsl */ `\n @group(0)@binding(0)var<storage,read>pt:array<vec4f>;\n @group(0)@binding(1)var<storage,read>caustics:array<vec4f>;\n @group(0)@binding(2)var<storage,read>normal:array<vec4f>;\n @group(0)@binding(3)var<storage,read_write>output:array<vec4f>;\n @compute @workgroup_size(64)fn main(@builtin(global_invocation_id)id:vec3u,@builtin(num_workgroups)groups:vec3u) {\n // Large images exceed one dispatch row; preserve the linear pixel order.\n let pixel=id.x+id.y*groups.x*64u;\n if(pixel>=arrayLength(&output)) {\n return;\n }\n let c=caustics[pixel];\n let p=pt[pixel];\n output[pixel]=vec4f(p.rgb+c.rgb/max(c.a,1.0)*normal[pixel].a,p.a);\n }\n`;\n"]}
|