@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,537 @@
|
|
|
1
|
+
import { PHOTON_GRID_SIZE, photonPackingWgsl } from './photonMap.compute.js';
|
|
2
|
+
import { SPECTRUM_PHOTON_DISTRIBUTION, SPECTRUM_WAVELENGTH_RANGE, spectrumWgsl } from '../spectrum.js';
|
|
3
|
+
import background from './chunks/background.wgsl.js';
|
|
4
|
+
import atomic from './chunks/causticAtomic.wgsl.js';
|
|
5
|
+
import adjoint from './chunks/causticAdjoint.wgsl.js';
|
|
6
|
+
import common from './chunks/common.wgsl.js';
|
|
7
|
+
import core from './chunks/rayTraceCore.wgsl.js';
|
|
8
|
+
import { createMaterialBufferWgsl } from './chunks/materialBuffer.wgsl.js';
|
|
9
|
+
import intersect from './chunks/intersect.wgsl.js';
|
|
10
|
+
import rand from './chunks/rand.wgsl.js';
|
|
11
|
+
import random from './chunks/random.wgsl.js';
|
|
12
|
+
import env from './chunks/envMap.wgsl.js';
|
|
13
|
+
import sampling from './chunks/sampling.wgsl.js';
|
|
14
|
+
import { createOpenPbrBsdfWgsl } from './chunks/openPbrBsdf.wgsl.js';
|
|
15
|
+
import light from './chunks/sampleLight.wgsl.js';
|
|
16
|
+
import uv from './chunks/uvProjection.wgsl.js';
|
|
17
|
+
import { causticPartitionWGSL } from './chunks/caustics.wgsl.js';
|
|
18
|
+
import { wgpuShaderPreamble } from '../wgpuShaderSource.js';
|
|
19
|
+
import { WGPU_SCENE_FEATURE_BITS } from '../WGPUSceneFeatures.js';
|
|
20
|
+
export const CAUSTIC_UNIFORM_BYTES = 112;
|
|
21
|
+
export const causticUnitRandomWgsl = /* wgsl */ `
|
|
22
|
+
fn causticUnitRandom(value:f32)->f32 {
|
|
23
|
+
return clamp((value-EPS)/(1.0-2.0*EPS),0.0,0.99999994);
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
// Preserve the dormant R1 uniform slot and shader spelling for source parity.
|
|
27
|
+
// Production packs zero; only an explicitly registered research adapter sets it.
|
|
28
|
+
export function causticLightCompute(constants, blasLayoutMask, hasDisplacement, mode = 'lightTracing', sceneIntersections = '', fullEmissionRandom = true) {
|
|
29
|
+
const frames = !!constants.USE_ANISOTROPY_DIRECTIONS || !!constants.USE_COAT_NORMAL_FRAMES, coat = !!constants.USE_COAT_NORMAL_FRAMES;
|
|
30
|
+
return (wgpuShaderPreamble(constants, true) +
|
|
31
|
+
/* wgsl */ `
|
|
32
|
+
${Object.keys(WGPU_SCENE_FEATURE_BITS)
|
|
33
|
+
.map((name) => `const ${name} = ${!!constants[name]};`)
|
|
34
|
+
.join('\n')}
|
|
35
|
+
${common}
|
|
36
|
+
${core({ hasAnisotropyDirections: frames, hasCoatNormals: coat, caustics: true })}
|
|
37
|
+
${uv}
|
|
38
|
+
${createMaterialBufferWgsl({ hasAnisotropyDirections: frames, hasCoatNormals: coat, caustics: true })}
|
|
39
|
+
${intersect({
|
|
40
|
+
hasDisplacement,
|
|
41
|
+
blasLayoutMask,
|
|
42
|
+
hasAnisotropyDirections: frames,
|
|
43
|
+
hasCoatNormals: coat,
|
|
44
|
+
sceneIntersections
|
|
45
|
+
})}
|
|
46
|
+
${rand}
|
|
47
|
+
${random}
|
|
48
|
+
${env}
|
|
49
|
+
${sampling}
|
|
50
|
+
${spectrumWgsl}
|
|
51
|
+
${createOpenPbrBsdfWgsl(frames, coat)}
|
|
52
|
+
${light}
|
|
53
|
+
${background.replace(/\b(?:USE_BACKGROUND|USE_BACKGROUND_MAP|USE_GROUNDED_BACKGROUND)\b/g, (name) => `((rtUniform.backgroundFlags & ${{ USE_BACKGROUND: 1, USE_BACKGROUND_MAP: 2, USE_GROUNDED_BACKGROUND: 4 }[name]}) != 0)`)}
|
|
54
|
+
${causticPartitionWGSL(false, false)}
|
|
55
|
+
${adjoint}
|
|
56
|
+
struct CausticLightUniform {
|
|
57
|
+
casterSphere: vec4f,
|
|
58
|
+
viewProjection: mat4x4f,
|
|
59
|
+
photonCount: u32,
|
|
60
|
+
batch: u32,
|
|
61
|
+
mnee: u32,
|
|
62
|
+
cameraScale: f32,
|
|
63
|
+
casterCount: u32,
|
|
64
|
+
choiceCount: u32,
|
|
65
|
+
orthographic: u32,
|
|
66
|
+
experiments: u32,
|
|
67
|
+
}
|
|
68
|
+
@group(3) @binding(0) var<uniform> caustic: CausticLightUniform;
|
|
69
|
+
${mode === 'lightTracing'
|
|
70
|
+
? '@group(3) @binding(1) var<storage,read_write> splats: array<atomic<u32>>;'
|
|
71
|
+
: `${photonPackingWgsl}
|
|
72
|
+
@group(3)@binding(1)var photons:texture_storage_2d<rgba32uint,write>;
|
|
73
|
+
@group(3)@binding(3)var photonDirections:texture_storage_2d<r32uint,write>;
|
|
74
|
+
fn photonStore(i:u32,value:vec4u) {
|
|
75
|
+
let width=textureDimensions(photons).x;
|
|
76
|
+
textureStore(photons,vec2i(vec2u(i%width,i/width)),value);
|
|
77
|
+
}
|
|
78
|
+
fn pU(i:u32,value:u32) {
|
|
79
|
+
let buckets=min(${PHOTON_GRID_SIZE}u,1u<<(32u-countLeadingZeros(max(256u,caustic.photonCount*2u)-1u)));
|
|
80
|
+
let index=8u+buckets*4u+caustic.photonCount*2u+i;
|
|
81
|
+
let width=textureDimensions(photonDirections).x;
|
|
82
|
+
textureStore(photonDirections,vec2i(vec2u(index%width,index/width)),vec4u(value));
|
|
83
|
+
}
|
|
84
|
+
`}
|
|
85
|
+
@group(3) @binding(2) var causticDistribution:texture_2d<f32>;
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
${causticUnitRandomWgsl}
|
|
89
|
+
fn fB(dim:u32)->f32{return ${fullEmissionRandom ? 'causticUnitRandom(sample1D(dim))' : 'sample1D(dim)'}
|
|
90
|
+
;
|
|
91
|
+
}
|
|
92
|
+
fn hE(dim:u32)->vec2f {
|
|
93
|
+
let value=sample2D(dim);
|
|
94
|
+
return
|
|
95
|
+
${fullEmissionRandom ? 'vec2f(causticUnitRandom(value.x),causticUnitRandom(value.y))' : 'value'}
|
|
96
|
+
;
|
|
97
|
+
}
|
|
98
|
+
fn nH(i:u32)->vec4f {
|
|
99
|
+
let width=textureDimensions(causticDistribution).x;
|
|
100
|
+
return textureLoad(causticDistribution,vec2i(vec2u(i%width,i/width)),0);
|
|
101
|
+
}
|
|
102
|
+
fn ds(i:u32)->vec4f {
|
|
103
|
+
return nH(i);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
var<private> kH:vec3f;
|
|
109
|
+
var<private> causticEmissionAreaPdf:f32;
|
|
110
|
+
fn kL(origin:vec3f,position:vec3f,normal:vec3f,emission:vec3f,area:f32,pmf:f32,result:ptr<function,LightSampleRec>) {
|
|
111
|
+
kH=position;
|
|
112
|
+
causticEmissionAreaPdf=pmf/max(area,1e-20);
|
|
113
|
+
let offset=position-origin;
|
|
114
|
+
(*result).dist=length(offset);
|
|
115
|
+
(*result).direction=offset/max((*result).dist,1e-20);
|
|
116
|
+
(*result).normal=normal;
|
|
117
|
+
(*result).emission=emission;
|
|
118
|
+
(*result).pdf=pmf;
|
|
119
|
+
}
|
|
120
|
+
fn rn(origin:vec3f,rnd:vec3f,result:ptr<function,LightSampleRec>) {
|
|
121
|
+
causticEmissionAreaPdf=0.0;
|
|
122
|
+
(*result).pdf=0.0;
|
|
123
|
+
(*result).sourceType=0u;
|
|
124
|
+
(*result).isDelta=false;
|
|
125
|
+
if(caustic.choiceCount==0u) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
let scaled=rnd.z*f32(caustic.choiceCount);
|
|
129
|
+
let column=min(u32(scaled),caustic.choiceCount-1u);
|
|
130
|
+
let entry=nH(caustic.casterCount+column*2u);
|
|
131
|
+
let selected=select(bitcast<u32>(entry.y),column,fract(scaled)<entry.x);
|
|
132
|
+
let choice=bitcast<vec4u>(nH(caustic.casterCount+selected*2u));
|
|
133
|
+
let pmf=nH(caustic.casterCount+selected*2u+1u).x;
|
|
134
|
+
(*result).sourceIndex=choice.w;
|
|
135
|
+
if(choice.z==1u) {
|
|
136
|
+
(*result).sourceType=0u;
|
|
137
|
+
ig(rnd.xy,result);
|
|
138
|
+
(*result).pdf*=pmf;
|
|
139
|
+
}
|
|
140
|
+
else if(choice.z==2u) {
|
|
141
|
+
(*result).sourceType=2u;
|
|
142
|
+
var light=analyticLights[choice.w];
|
|
143
|
+
let kind=i32(light.litType);
|
|
144
|
+
if(kind==RECT_LIGHT) {
|
|
145
|
+
let position=light.position+light.u*(rnd.x-.5)+light.v*(rnd.y-.5);
|
|
146
|
+
kL(origin,position,normalize(cross(light.u,light.v)),light.emission,light.area,pmf,result);
|
|
147
|
+
(*result).isDelta=light.radiusOrSpread<=.001;
|
|
148
|
+
bC(light,position,light.position-.5*(light.u+light.v),result);
|
|
149
|
+
}
|
|
150
|
+
else if((kind==SPHERE_LIGHT||kind==SPOT_LIGHT)&&light.radiusOrSpread>0.0) {
|
|
151
|
+
let z=1.0-2.0*rnd.x;
|
|
152
|
+
let dr=sqrt(max(0.0,1.0-z*z));
|
|
153
|
+
let normal=vec3f(dr*cos(TWOPI*rnd.y),dr*sin(TWOPI*rnd.y),z);
|
|
154
|
+
kL(origin,light.position+light.radiusOrSpread*normal,normal,light.emission,light.area,pmf,result);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
light.selectionPmf=1.0;
|
|
158
|
+
sampleAnalyticLight(light,origin,rnd.xy,result);
|
|
159
|
+
(*result).pdf*=pmf;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
(*result).sourceType=1u;
|
|
164
|
+
let triangle=getEmissiveTriangle(i32(choice.w));
|
|
165
|
+
let u=sqrt(rnd.x);
|
|
166
|
+
let bary=vec3f(1.0-u,u*(1.0-rnd.y),u*rnd.y);
|
|
167
|
+
let position=bary.x*triangle.p0+bary.y*triangle.p1+bary.z*triangle.p2;
|
|
168
|
+
let normal=normalize(cross(triangle.p1-triangle.p0,triangle.p2-triangle.p0));
|
|
169
|
+
let uv=bary.x*triangle.uv0+bary.y*triangle.uv1+bary.z*triangle.uv2;
|
|
170
|
+
let uv1=bary.x*triangle.secondaryUv0+bary.y*triangle.secondaryUv1+bary.z*triangle.secondaryUv2;
|
|
171
|
+
kL(origin,position,normal,getMaterialEmission(i32(triangle.materialIndex),uv,uv1),triangle.area,pmf,result);
|
|
172
|
+
}
|
|
173
|
+
(*result).selectionPmf=pmf;
|
|
174
|
+
}
|
|
175
|
+
fn lj(s:vec4f,origin:vec3f)->f32 {
|
|
176
|
+
let bG=dot(s.xyz-origin,s.xyz-origin);
|
|
177
|
+
return select(-1.0,sqrt(max(0.0,1.0-s.w*s.w/bG)),bG>s.w*s.w);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
fn dN(s:vec4f)->f32 {
|
|
182
|
+
if((caustic.experiments&2u)==0u) { return 1.0; }
|
|
183
|
+
let tB=caustic.viewProjection*vec4f(s.xyz,1.0);
|
|
184
|
+
let m=caustic.viewProjection;
|
|
185
|
+
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(tB.w<=0.0||abs(tB.x)>tB.w+extent||abs(tB.y)>tB.w+extent) { return .05; }
|
|
187
|
+
return .05+min(1.0,extent*extent/max(tB.w*tB.w,1e-20));
|
|
188
|
+
}
|
|
189
|
+
fn sv(origin:vec3f,rnd:vec3f,pdf:ptr<function,f32>,ky:ptr<function,f32>)->vec3f {
|
|
190
|
+
var total=0.0;
|
|
191
|
+
for(var i=0u;i<caustic.casterCount;i++) {
|
|
192
|
+
total+=(1.0-lj(ds(i),origin))*dN(ds(i));
|
|
193
|
+
}
|
|
194
|
+
var choice=rnd.z*total;
|
|
195
|
+
var selected=ds(0u);
|
|
196
|
+
for(var i=0u;i<caustic.casterCount;i++) {
|
|
197
|
+
let sphere=ds(i);
|
|
198
|
+
selected=sphere;
|
|
199
|
+
choice-=(1.0-lj(sphere,origin))*dN(sphere);
|
|
200
|
+
if(choice<=0.0) {
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
*ky=selected.w;
|
|
205
|
+
let offset=selected.xyz-origin;
|
|
206
|
+
let axis=normalize(select(vec3f(0.0,0.0,1.0),offset,dot(offset,offset)>1e-20));
|
|
207
|
+
let cosine=mix(1.0,lj(selected,origin),rnd.x);
|
|
208
|
+
let uC=sqrt(max(0.0,1.0-cosine*cosine));
|
|
209
|
+
var t:vec3f;
|
|
210
|
+
var b:vec3f;
|
|
211
|
+
sampling_orthonormal_basis(axis,&t,&b);
|
|
212
|
+
let direction=axis*cosine+(t*cos(TWOPI*rnd.y)+b*sin(TWOPI*rnd.y))*uC;
|
|
213
|
+
|
|
214
|
+
var overlaps=0.0;
|
|
215
|
+
for(var i=0u;i<caustic.casterCount;i++) {
|
|
216
|
+
let sphere=ds(i);
|
|
217
|
+
let c=lj(sphere,origin);
|
|
218
|
+
if(c<0.0||dot(normalize(sphere.xyz-origin),direction)>=c) {
|
|
219
|
+
overlaps+=dN(sphere);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
*pdf=overlaps/max(TWOPI*total,1e-20);
|
|
223
|
+
return direction;
|
|
224
|
+
}
|
|
225
|
+
${mode === 'lightTracing'
|
|
226
|
+
? /* wgsl */ `${atomic}
|
|
227
|
+
fn causticCameraConnect(si: SurfaceInteraction, material: OpenPbrResolvedMaterial, hQ: Ray, flux: vec3f, extinction: vec3f) {
|
|
228
|
+
let eye=cameraUniform.viewInv[3].xyz;
|
|
229
|
+
let forward=-normalize(cameraUniform.viewInv[2].xyz);
|
|
230
|
+
let rB=hE(causticDim(68u));
|
|
231
|
+
let radius=cameraUniform.apertureRadius*sqrt(rB.x);
|
|
232
|
+
var uV=eye+radius*(cos(TWOPI*rB.y)*cameraUniform.viewInv[0].xyz+sin(TWOPI*rB.y)*cameraUniform.viewInv[1].xyz);
|
|
233
|
+
if(caustic.orthographic!=0u) {
|
|
234
|
+
uV=si.position-forward*dot(si.position-eye,forward);
|
|
235
|
+
}
|
|
236
|
+
let xM=uV-si.position;
|
|
237
|
+
let bG=dot(xM,xM);
|
|
238
|
+
let direction=normalize(xM);
|
|
239
|
+
let wf=uV+(si.position-uV)*cameraUniform.focusDistance/max(dot(si.position-uV,forward),1e-12);
|
|
240
|
+
let qO=select(wf,si.position,caustic.orthographic!=0u);
|
|
241
|
+
let tB=caustic.viewProjection*vec4f(qO,1.0);
|
|
242
|
+
if(tB.w<=0.0||dot(si.position-eye,forward)<=0.0) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
let wY=tB.xy/tB.w*0.5+0.5;
|
|
246
|
+
if(any(wY<vec2f(0.0))||any(wY>=vec2f(1.0))) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
var shadow:Ray;
|
|
250
|
+
initRay(&shadow,si.position+EPS*direction,direction);
|
|
251
|
+
if(intersectSceneShadow(shadow,sqrt(bG)-2.0*EPS,si.triangleIndex,si.instanceIndex)) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
var m=material;
|
|
255
|
+
let normal=select(-si.normal,si.normal,dot(si.normal,-hQ.d)>=0.0);
|
|
256
|
+
m.inside=dot(si.faceNormal,-hQ.d)<0.0;
|
|
257
|
+
m.relativeIor=select(1.0/m.specularIor,m.specularIor,m.inside);
|
|
258
|
+
${frames
|
|
259
|
+
? /* wgsl */ ` m.directionTangent=si.directionTangent;m.directionBitangent=si.directionBitangent;m.geometryTangent=si.geometryTangent;m.geometryBitangent=si.geometryBitangent;`
|
|
260
|
+
: ''}
|
|
261
|
+
${coat
|
|
262
|
+
? /* wgsl */ ` m.coatNormal=normal;m.geometricNormal=select(-si.faceNormal,si.faceNormal,dot(si.faceNormal,-hQ.d)>=0.0);`
|
|
263
|
+
: ''}
|
|
264
|
+
let prepared=bV(si.tangent,m,-hQ.d,normal);
|
|
265
|
+
var pdf:f32;
|
|
266
|
+
let response=openpbr_eval_prepared(prepared,direction,&pdf);
|
|
267
|
+
let mj=dot(-direction,-normalize(cameraUniform.viewInv[2].xyz));
|
|
268
|
+
if(mj<=0.0) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
let wg=caustic.cameraScale*rtUniform.outputSize.x*rtUniform.outputSize.y /
|
|
272
|
+
select(max(bG*mj*mj*mj,1e-12),1.0,caustic.orthographic!=0u);
|
|
273
|
+
let transmittance=exp(-extinction*sqrt(bG));
|
|
274
|
+
let value=flux*transmittance*response*causticAdjoint(-hQ.d,direction,normal,si.faceNormal)*wg/f32(caustic.photonCount);
|
|
275
|
+
let pixel=vec2u(wY*rtUniform.outputSize);
|
|
276
|
+
let index=(pixel.x+pixel.y*u32(rtUniform.outputSize.x))*3u;
|
|
277
|
+
kM(index,value.x);
|
|
278
|
+
kM(index+1u,value.y);
|
|
279
|
+
kM(index+2u,value.z);
|
|
280
|
+
}
|
|
281
|
+
`
|
|
282
|
+
: ''}
|
|
283
|
+
@compute @workgroup_size(64)
|
|
284
|
+
fn main(@builtin(global_invocation_id) id:vec3u) {
|
|
285
|
+
let photonIndex=id.x+id.y*4194240u;
|
|
286
|
+
if(photonIndex>=caustic.photonCount) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
${mode === 'photonMapping' ? 'photonStore(photonIndex,vec4u(0u));' : ''}
|
|
290
|
+
aI=vec2u(photonIndex%u32(rtUniform.outputSize.x),photonIndex/u32(rtUniform.outputSize.x));
|
|
291
|
+
aC=caustic.batch;
|
|
292
|
+
samplingBounce=0u;
|
|
293
|
+
ca=0u;
|
|
294
|
+
fi(aI,i32(ak(caustic.batch,rtUniform.sequenceSeed)));
|
|
295
|
+
var wavelength=0.0;
|
|
296
|
+
var wavelengthPdf=1.0;
|
|
297
|
+
if(SPECTRAL) {
|
|
298
|
+
wavelength=spectrumSample(${SPECTRUM_PHOTON_DISTRIBUTION}u,fB(causticDim(69u)),&wavelengthPdf);
|
|
299
|
+
}
|
|
300
|
+
var ls:LightSampleRec;
|
|
301
|
+
rn(caustic.casterSphere.xyz,vec3f(hE(causticDim(60u)),fB(causticDim(61u))),&ls);
|
|
302
|
+
if(ls.pdf<=0.0) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
var infinite=ls.sourceType==0u;
|
|
306
|
+
if(ls.sourceType==2u) {
|
|
307
|
+
infinite=i32(analyticLights[ls.sourceIndex].litType)==DIRECTIONAL_LIGHT;
|
|
308
|
+
}
|
|
309
|
+
var origin=caustic.casterSphere.xyz+ls.direction*ls.dist;
|
|
310
|
+
if(causticEmissionAreaPdf>0.0) { origin=kH; }
|
|
311
|
+
var direction:vec3f;
|
|
312
|
+
var flux:vec3f;
|
|
313
|
+
var ky=caustic.casterSphere.w;
|
|
314
|
+
if(infinite) {
|
|
315
|
+
direction=-ls.direction;
|
|
316
|
+
|
|
317
|
+
var area=0.0;
|
|
318
|
+
for(var i=0u;i<caustic.casterCount;i++) {
|
|
319
|
+
let s=ds(i);
|
|
320
|
+
area+=s.w*s.w*dN(s);
|
|
321
|
+
}
|
|
322
|
+
var choice=fB(causticDim(63u))*area;
|
|
323
|
+
var sphere=ds(0u);
|
|
324
|
+
for(var i=0u;i<caustic.casterCount;i++) {
|
|
325
|
+
sphere=ds(i);
|
|
326
|
+
choice-=sphere.w*sphere.w*dN(sphere);
|
|
327
|
+
if(choice<=0.0) {
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
ky=sphere.w;
|
|
332
|
+
var t:vec3f;
|
|
333
|
+
var b:vec3f;
|
|
334
|
+
sampling_orthonormal_basis(direction,&t,&b);
|
|
335
|
+
let rnd=hE(causticDim(62u));
|
|
336
|
+
origin=sphere.xyz-direction*dot(sphere.xyz-caustic.casterSphere.xyz,direction)-direction*caustic.casterSphere.w;
|
|
337
|
+
origin+=sphere.w*sqrt(rnd.x)*(t*cos(TWOPI*rnd.y)+b*sin(TWOPI*rnd.y));
|
|
338
|
+
var overlaps=0.0;
|
|
339
|
+
for(var i=0u;i<caustic.casterCount;i++) {
|
|
340
|
+
let s=ds(i);
|
|
341
|
+
let offset=origin-s.xyz;
|
|
342
|
+
let qO=offset-direction*dot(offset,direction);
|
|
343
|
+
if(dot(qO,qO)<=s.w*s.w) {
|
|
344
|
+
overlaps+=dN(s);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
let diskPdf=overlaps/max(PI*area,1e-20);
|
|
348
|
+
flux=ls.emission/(ls.pdf*diskPdf);
|
|
349
|
+
}
|
|
350
|
+
else if(ls.sourceType==2u&&i32(analyticLights[ls.sourceIndex].litType)==RECT_LIGHT&&ls.isDelta) {
|
|
351
|
+
|
|
352
|
+
direction=ls.normal;
|
|
353
|
+
flux=ls.emission/causticEmissionAreaPdf;
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
var areaPdf=ls.pdf*abs(dot(ls.normal,-ls.direction))/max(ls.dist*ls.dist,1e-20);
|
|
357
|
+
if(causticEmissionAreaPdf>0.0) { areaPdf=causticEmissionAreaPdf; }
|
|
358
|
+
if(ls.isDelta) {
|
|
359
|
+
areaPdf=ls.selectionPmf;
|
|
360
|
+
}
|
|
361
|
+
if(areaPdf<=0.0) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
var directionPdf:f32;
|
|
365
|
+
direction=sv(origin,vec3f(hE(causticDim(62u)),fB(causticDim(63u))),&directionPdf,&ky);
|
|
366
|
+
var emission=ls.emission;
|
|
367
|
+
var mL=max(0.0,dot(ls.normal,direction));
|
|
368
|
+
if(ls.sourceType==2u) {
|
|
369
|
+
let source=analyticLights[ls.sourceIndex];
|
|
370
|
+
emission=source.emission;
|
|
371
|
+
if(i32(source.litType)==SPOT_LIGHT) {
|
|
372
|
+
emission*=evaluateSpotEmissionMultiplier(source,direction);
|
|
373
|
+
}
|
|
374
|
+
if(ls.isDelta) {
|
|
375
|
+
mL=1.0;
|
|
376
|
+
}
|
|
377
|
+
if(i32(source.litType)==RECT_LIGHT&&mL<cos(0.5*PI*source.radiusOrSpread)) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if(i32(source.litType)==RECT_LIGHT) {
|
|
381
|
+
var un:LightSampleRec;
|
|
382
|
+
un.emission=emission;
|
|
383
|
+
bC(source,origin,source.position-0.5*(source.u+source.v),&un);
|
|
384
|
+
emission=un.emission;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
flux=emission*mL/(areaPdf*directionPdf);
|
|
388
|
+
}
|
|
389
|
+
if(SPECTRAL) {
|
|
390
|
+
flux*=spectrumRgb(wavelength)/(wavelengthPdf*${SPECTRUM_WAVELENGTH_RANGE}.0);
|
|
391
|
+
}
|
|
392
|
+
var ray:Ray;
|
|
393
|
+
initRay(&ray,origin+EPS*direction,direction);
|
|
394
|
+
var vt=0u;
|
|
395
|
+
var wN=false;
|
|
396
|
+
var extinction=vec3f(0.0);
|
|
397
|
+
var scattering=vec3f(0.0);
|
|
398
|
+
var si:SurfaceInteraction;
|
|
399
|
+
for(var depth=0;
|
|
400
|
+
${mode === 'photonMapping' ? 'depth<=max(rtUniform.bounces,0)' : 'depth<20'}
|
|
401
|
+
;
|
|
402
|
+
depth++) {
|
|
403
|
+
samplingBounce=u32(depth);
|
|
404
|
+
si.triangleIndex=-1;
|
|
405
|
+
intersectScene(ray,&si,0,true,depth>0);
|
|
406
|
+
if(!si.hit) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
var m:OpenPbrResolvedMaterial;
|
|
410
|
+
getMaterial(&m,si.materialIndex,si.uv,si.uv1,si.instanceIndex,si.vertexColor);
|
|
411
|
+
if(m.alpha<1.0&&fB(causticDim(79u))>=m.alpha) {
|
|
412
|
+
initRay(&ray,si.position+EPS*ray.d,ray.d);
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
if(m.materialType==SHADOW_CATCHER) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
let segment=distance(ray.o,si.position);
|
|
419
|
+
let channel=min(u32(sample1D(bE(MEDIUM_CHANNEL))*3.0),2u);
|
|
420
|
+
if(extinction[channel]>0.0&&any(scattering>vec3f(0.0))) {
|
|
421
|
+
let sampled=-log(max(sample1D(bE(MEDIUM_DISTANCE)),1e-7))/extinction[channel];
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
if(sampled<segment) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
let transmittance=exp(-extinction*segment);
|
|
429
|
+
let xK=(transmittance.x+transmittance.y+transmittance.z)/3.0;
|
|
430
|
+
flux*=transmittance/select(1.0,max(xK,1e-20),any(scattering>vec3f(0.0)));
|
|
431
|
+
if(SPECTRAL) {
|
|
432
|
+
m.specularIor=sampling_cauchy_ior(spectrumIorWavelength(wavelength),m.specularIor,m.transmissionDispersionScale,m.dispersionAbbeNumber);
|
|
433
|
+
}
|
|
434
|
+
if(!isCausticSpecular(m)) {
|
|
435
|
+
let wj=ls.sourceType!=0u;
|
|
436
|
+
let r1=wj&&vt==1u&&wN&&m.baseWeight*(1.0-m.baseMetalness)*(1.0-m.transmissionWeight)>0.0&&m.subsurfaceWeight==0.0;
|
|
437
|
+
if(vt>0u
|
|
438
|
+
${mode === 'lightTracing' ? '&&!(caustic.mnee!=0u&&r1)' : ''}){${mode === 'lightTracing'
|
|
439
|
+
? 'causticCameraConnect(si,m,ray,flux,extinction);'
|
|
440
|
+
: /* wgsl */ `
|
|
441
|
+
let position=bitcast<vec3u>(si.position);
|
|
442
|
+
photonStore(photonIndex,vec4u(position,sS(flux)));
|
|
443
|
+
if((caustic.experiments&1u)!=0u) {
|
|
444
|
+
var maximum=1e-8;
|
|
445
|
+
for(var i=0u;i<caustic.casterCount;i++) { maximum=max(maximum,ds(i).w); }
|
|
446
|
+
let buckets=min(${PHOTON_GRID_SIZE}u,1u<<(32u-countLeadingZeros(max(256u,caustic.photonCount*2u)-1u)));
|
|
447
|
+
let index=8u+buckets*4u+caustic.photonCount*3u+photonIndex;
|
|
448
|
+
let width=textureDimensions(photonDirections).x;
|
|
449
|
+
textureStore(photonDirections,vec2i(vec2u(index%width,index/width)),vec4u(bitcast<u32>(ky/maximum)));
|
|
450
|
+
}
|
|
451
|
+
let hQ=vec2u(round(pN(-ray.d)*255.0));
|
|
452
|
+
let normal=vec2u(round(pN(si.faceNormal)*vec2f(255.0,127.0)));
|
|
453
|
+
pU(photonIndex,normal.x|(normal.y<<8u)|(select(0u,1u,r1)<<15u)|(hQ.x<<16u)|(hQ.y<<24u));
|
|
454
|
+
`}
|
|
455
|
+
}
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
let view=-ray.d;
|
|
459
|
+
let normal=select(-si.normal,si.normal,dot(si.normal,view)>=0.0);
|
|
460
|
+
m.inside=dot(si.faceNormal,view)<0.0;
|
|
461
|
+
m.relativeIor=select(1.0/m.specularIor,m.specularIor,m.inside);
|
|
462
|
+
${frames
|
|
463
|
+
? /* wgsl */ ` m.directionTangent=si.directionTangent;m.directionBitangent=si.directionBitangent;m.geometryTangent=si.geometryTangent;m.geometryBitangent=si.geometryBitangent;`
|
|
464
|
+
: ''}
|
|
465
|
+
${coat
|
|
466
|
+
? /* wgsl */ ` m.coatNormal=normal;m.geometricNormal=select(-si.faceNormal,si.faceNormal,dot(si.faceNormal,view)>=0.0);`
|
|
467
|
+
: ''}
|
|
468
|
+
let prepared=bV(si.tangent,m,view,normal);
|
|
469
|
+
var next:vec3f;
|
|
470
|
+
var pdf:f32;
|
|
471
|
+
var transmission:bool;
|
|
472
|
+
var roughness:f32;
|
|
473
|
+
var specular:bool;
|
|
474
|
+
var diffuse:bool;
|
|
475
|
+
var lobe:u32;
|
|
476
|
+
var marginal:f32;
|
|
477
|
+
var full:vec3f;
|
|
478
|
+
let response=dl(prepared,vec3f(sample2D(bE(BSDF_DIRECTION)),sample1D(bE(BSDF_LOBE))),&next,&pdf,&transmission,&roughness,&specular,&diffuse,&lobe,&marginal,&full);
|
|
479
|
+
if(!(pdf>0.0)) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if(vt==0u&&ls.sourceType==0u&&transmission) {
|
|
483
|
+
var reverse:Ray;
|
|
484
|
+
initRay(&reverse,si.position,-ray.d);
|
|
485
|
+
let bg=ft(reverse);
|
|
486
|
+
flux*=bg.rgb*bg.a/max(ls.emission,vec3f(1e-20));
|
|
487
|
+
}
|
|
488
|
+
flux*=response/pdf*causticAdjoint(view,next,normal,si.faceNormal);
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
if(transmission&&!m.thinWalled) {
|
|
492
|
+
flux/=m.relativeIor*m.relativeIor;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if(transmission&&!m.thinWalled&&m.mediumType>0) {
|
|
496
|
+
extinction=select(m.mediumExtinction,vec3f(0.0),m.inside);
|
|
497
|
+
scattering=select(m.mediumScattering,vec3f(0.0),m.inside);
|
|
498
|
+
}
|
|
499
|
+
vt++;
|
|
500
|
+
wN=vt==1u&&((isCausticT(m)&&transmission)||(isCausticR(m)&&!transmission));
|
|
501
|
+
initRay(&ray,si.position+EPS*next,next);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
`);
|
|
505
|
+
}
|
|
506
|
+
export const causticAccumulateCompute = /* wgsl */ `
|
|
507
|
+
@group(0) @binding(0) var<storage,read_write> splats:array<atomic<u32>>;
|
|
508
|
+
@group(0) @binding(1) var<storage,read_write> accumulation:array<vec4f>;
|
|
509
|
+
@compute @workgroup_size(64)
|
|
510
|
+
fn main(@builtin(global_invocation_id) id:vec3u, @builtin(num_workgroups) groups:vec3u) {
|
|
511
|
+
|
|
512
|
+
let pixel=id.x+id.y*groups.x*64u;
|
|
513
|
+
if(pixel>=arrayLength(&accumulation)) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
let i=pixel*3u;
|
|
517
|
+
accumulation[pixel]+=vec4f(vec3f(bitcast<f32>(atomicLoad(&splats[i])),bitcast<f32>(atomicLoad(&splats[i+1u])),bitcast<f32>(atomicLoad(&splats[i+2u]))),1.0);
|
|
518
|
+
}
|
|
519
|
+
`;
|
|
520
|
+
/** Native buffer resolve has the same independent batch normalization as web. */
|
|
521
|
+
export const causticBufferCompositeCompute = /* wgsl */ `
|
|
522
|
+
@group(0)@binding(0)var<storage,read>pt:array<vec4f>;
|
|
523
|
+
@group(0)@binding(1)var<storage,read>caustics:array<vec4f>;
|
|
524
|
+
@group(0)@binding(2)var<storage,read>normal:array<vec4f>;
|
|
525
|
+
@group(0)@binding(3)var<storage,read_write>output:array<vec4f>;
|
|
526
|
+
@compute @workgroup_size(64)fn main(@builtin(global_invocation_id)id:vec3u,@builtin(num_workgroups)groups:vec3u) {
|
|
527
|
+
|
|
528
|
+
let pixel=id.x+id.y*groups.x*64u;
|
|
529
|
+
if(pixel>=arrayLength(&output)) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
let c=caustics[pixel];
|
|
533
|
+
let p=pt[pixel];
|
|
534
|
+
output[pixel]=vec4f(p.rgb+c.rgb/max(c.a,1.0)*normal[pixel].a,p.a);
|
|
535
|
+
}
|
|
536
|
+
`;
|
|
537
|
+
//# sourceMappingURL=causticLight.compute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"causticLight.compute.js","sourceRoot":"","sources":["../../../src/renderer/wgsl/causticLight.compute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,gBAAgB,EAAE,iBAAiB,EAAC,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAC,4BAA4B,EAAE,yBAAyB,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAC;AACrG,OAAO,UAAU,MAAM,6BAA6B,CAAC;AACrD,OAAO,MAAM,MAAM,gCAAgC,CAAC;AACpD,OAAO,OAAO,MAAM,iCAAiC,CAAC;AACtD,OAAO,MAAM,MAAM,yBAAyB,CAAC;AAC7C,OAAO,IAAI,MAAM,+BAA+B,CAAC;AACjD,OAAO,EAAC,wBAAwB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,SAAS,MAAM,4BAA4B,CAAC;AACnD,OAAO,IAAI,MAAM,uBAAuB,CAAC;AACzC,OAAO,MAAM,MAAM,yBAAyB,CAAC;AAC7C,OAAO,GAAG,MAAM,yBAAyB,CAAC;AAC1C,OAAO,QAAQ,MAAM,2BAA2B,CAAC;AACjD,OAAO,EAAC,qBAAqB,EAAC,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,MAAM,8BAA8B,CAAC;AACjD,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC/C,OAAO,EAAC,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAC,kBAAkB,EAAC,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAC,uBAAuB,EAAC,MAAM,yBAAyB,CAAC;AAEhE,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AACzC,MAAM,CAAC,MAAM,qBAAqB,GAAG,UAAU,CAAC;;;;CAI/C,CAAC;AAEF,8EAA8E;AAC9E,iFAAiF;AACjF,MAAM,UAAU,mBAAmB,CACjC,SAA2C,EAC3C,cAAsB,EACtB,eAAwB,EACxB,OAAyC,cAAc,EACvD,kBAAkB,GAAG,EAAE,EACvB,kBAAkB,GAAG,IAAI;IAEzB,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,yBAAyB,IAAI,CAAC,CAAC,SAAS,CAAC,sBAAsB,EACxF,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC;IAC5C,OAAO,CACL,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC;QACnC,UAAU,CAAC;EACb,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC;aACnC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;aACtD,IAAI,CAAC,IAAI,CAAC;EACX,MAAM;EACN,IAAI,CAAC,EAAC,uBAAuB,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC;EAC7E,EAAE;EACF,wBAAwB,CAAC,EAAC,uBAAuB,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC;EACjG,SAAS,CAAC;YACV,eAAe;YACf,cAAc;YACd,uBAAuB,EAAE,MAAM;YAC/B,cAAc,EAAE,IAAI;YACpB,kBAAkB;SACnB,CAAC;EACA,IAAI;EACJ,MAAM;EACN,GAAG;EACH,QAAQ;EACR,YAAY;EACZ,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC;EACnC,KAAK;EACL,UAAU,CAAC,OAAO,CAClB,oEAAoE,EACpE,CAAC,IAAI,EAAE,EAAE,CACP,iCACE,EAAC,cAAc,EAAE,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,uBAAuB,EAAE,CAAC,EAAC,CAAC,IAAI,CAC7E,SAAS,CACZ;EACC,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC;EAClC,OAAO;;;;;;;;;;;;;;EAeP,IAAI,KAAK,cAAc;YACrB,CAAC,CAAC,2EAA2E;YAC7E,CAAC,CAAC,GAAG,iBAAiB;;;;;;;;kBAQJ,gBAAgB;;;;;CAMtC;;;;EAIE,qBAAqB;6BAEjB,kBAAkB,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,eAC5D;;;;;;EAMF,kBAAkB,CAAC,CAAC,CAAC,8DAA8D,CAAC,CAAC,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmI7F,IAAI,KAAK,cAAc;YACrB,CAAC,CAAC,UAAU,CAAC,GAAG,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCxB,MAAM;gBACJ,CAAC,CAAC,UAAU,CAAC,oKAAoK;gBACjL,CAAC,CAAC,EACN;EAEE,IAAI;gBACF,CAAC,CAAC,UAAU,CAAC,6GAAmH;gBAChI,CAAC,CAAC,EACN;;;;;;;;;;;;;;;;;;CAkBC;YACG,CAAC,CAAC,EACN;;;;;;;EAOE,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC,CAAC,EAAE;;;;;;;;;4BASvC,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CA4FT,yBAAyB;;;;;;;;;;EAU1E,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsCzE,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,EAAE,KACtD,IAAI,KAAK,cAAc;YACrB,CAAC,CAAC,iDAAiD;YACnD,CAAC,CAAC,UAAU,CAAC;;;;;;kBAMC,gBAAgB;;;;;;;;CASlC;;;;;;;;EASF,MAAM;YACJ,CAAC,CAAC,UAAU,CAAC,sKAAsK;YACnL,CAAC,CAAC,EACN;EAEE,IAAI;YACF,CAAC,CAAC,UAAU,CAAC,8GAA8G;YAC3H,CAAC,CAAC,EACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCC,CACE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,UAAU,CAAC;;;;;;;;;;;;;CAalD,CAAC;AAEF,iFAAiF;AACjF,MAAM,CAAC,MAAM,6BAA6B,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;CAevD,CAAC","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"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: "fn groundedSurfaceDistance(point: vec3f, height: f32, radius: f32) -> f32 {\n let pointLength = length(point);\n if (pointLength < 0.00001) {\n return -radius;\n }\n\n let originalY = radius * point.y / pointLength;\n let transitionY = -1.5 * height;\n var surfaceRadius: f32;\n if (originalY >= 0.0) {\n surfaceRadius = radius;\n }\n else if (originalY < transitionY) {\n surfaceRadius = -height * pointLength / point.y;\n }\n else {\n let factor = 1.0\n - originalY * originalY / (3.0 * transitionY * transitionY);\n surfaceRadius = radius * factor;\n }\n return pointLength - surfaceRadius;\n}\n\nfn groundedBackgroundDirection(origin: vec3f, direction: vec3f) -> vec3f {\n if (!USE_GROUNDED_BACKGROUND) {\n return direction;\n }\n let params = rtUniform.groundedBackgroundParams;\n let rayDirection = normalize(direction);\n let center = vec3f(\n rtUniform.groundedBackgroundCenter.x,\n params.z + params.x,\n rtUniform.groundedBackgroundCenter.y\n );\n let localOrigin = origin - center;\n let projectedOrigin = dot(localOrigin, rayDirection);\n let discriminant = projectedOrigin * projectedOrigin\n - dot(localOrigin, localOrigin) + params.y * params.y;\n if (\n discriminant <= 0.0\n || groundedSurfaceDistance(localOrigin, params.x, params.y) > 0.0\n ) {\n return direction;\n }\n\n var farDistance = -projectedOrigin + sqrt(discriminant);\n if (farDistance <= 0.0) {\n return direction;\n }\n\n let sphereHit = localOrigin + rayDirection * farDistance;\n if (sphereHit.y >= 0.0) {\n return normalize(sphereHit);\n }\n\n if (rayDirection.y < -0.00001 && params.y > 1.5 * params.x) {\n let groundDistance = (-params.x - localOrigin.y) / rayDirection.y;\n let groundHit = localOrigin + rayDirection * groundDistance;\n let groundRadius = (2.0 / 3.0)\n * sqrt(params.y * params.y - 2.25 * params.x * params.x);\n if (groundDistance > 0.0 && length(groundHit.xz) <= groundRadius) {\n return normalize(groundHit);\n }\n }\n\n var nearDistance = 0.0;\n for (var i = 0; i < 14; i++) {\n let middleDistance = (nearDistance + farDistance) * 0.5;\n let middlePoint = localOrigin + rayDirection * middleDistance;\n if (groundedSurfaceDistance(middlePoint, params.x, params.y) > 0.0) {\n farDistance = middleDistance;\n }\n else {\n nearDistance = middleDistance;\n }\n }\n let hit = localOrigin + rayDirection * ((nearDistance + farDistance) * 0.5);\n return normalize(hit);\n}\n\nfn sampleSceneBackground(ray: Ray) -> vec4f {\n var bg = vec4f(0.0);\n if (USE_BACKGROUND_MAP) {\n let d = groundedBackgroundDirection(ray.o, ray.d);\n let t = rtUniform.backgroundMapTransform;\n let backgroundDirection = vec3f(t.x * d.x - t.y * d.z, d.y, t.y * d.x + t.x * d.z);\n let rotation = rtUniform.backgroundMapRotation;\n let mapDirection = transpose(mat3x3f(rotation[0].xyz, rotation[1].xyz, rotation[2].xyz)) * backgroundDirection;\n let backgroundSample = textureLinear(backgroundMap, cartesianToEquirect(mapDirection));\n bg = vec4f(backgroundSample.rgb * t.z, backgroundSample.a);\n }\n else if (USE_BACKGROUND) {\n bg = rtUniform.backgroundColor;\n }\n else if (USE_ENVMAP) {\n bg = textureLinear(envMap, environmentUv(ray.d));\n }\n return bg;\n}\n\n";
|
|
2
|
+
export default _default;
|