@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rayTrace.compute.js","sourceRoot":"","sources":["../../../src/renderer/wgsl/rayTrace.compute.ts"],"names":[],"mappings":"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","sourcesContent":["import { MAX_PATH_EVENTS } from '../pathLimits.js';\nimport shadowCatcherAccumulationWGSL from './chunks/shadowCatcherAccumulation.wgsl.js';\nimport reflectanceWGSL from './aovReflectance.wgsl.js';\nimport aovTraceWGSL from './aovTrace.wgsl.js';\nimport depthQueryWGSL from './depthQuery.wgsl.js';\n// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport commonWgsl from './chunks/common.wgsl.js';\nimport { WGPU_SCENE_FEATURE_BITS } from '../WGPUSceneFeatures.js';\nimport {ALL_MATERIAL_SHADER_CAPABILITIES, MATERIAL_SHADER_CAPABILITIES} from '../shaderCapabilities.js';\nimport rayTraceCoreWgsl from './chunks/rayTraceCore.wgsl.js';\nimport { createMaterialBufferWgsl } from './chunks/materialBuffer.wgsl.js';\nimport createIntersectionWgsl from './chunks/intersect.wgsl.js';\nimport randomWgsl from './chunks/random.wgsl.js';\nimport randWgsl from './chunks/rand.wgsl.js';\nimport { createSampleMaterialWgsl } from './chunks/sampleMaterial.wgsl.js';\nimport sampleLightWgsl from './chunks/sampleLight.wgsl.js';\nimport { createOpenPbrBsdfWgsl } from './chunks/openPbrBsdf.wgsl.js';\nimport samplingWgsl from './chunks/sampling.wgsl.js';\nimport sampleShadowCatcherWgsl from './chunks/sampleShadowCatcher.wgsl.js';\nimport envMapWgsl from './chunks/envMap.wgsl.js';\nimport uvProjectionWgsl from './chunks/uvProjection.wgsl.js';\nexport const WORKGROUP_SIZE = 8;\n\n/** Native input instrumentation; omitted hooks preserve the production source. */\nexport interface RayTraceInputHooks {\n declarations: string;\n begin: string;\n primarySurface: string;\n unclampedColor: string;\n end: string;\n uniformJitter: boolean;\n}\n\nexport interface RayTraceShaderOptions {\n hasDisplacement: boolean;\n /** Include direction frames only when used, preserving the legacy shader arithmetic otherwise. */\n hasAnisotropyDirections?: boolean;\n /** Include independent coat normals only when a material uses that slot. */\n hasCoatNormals?: boolean;\n /** Split glTF BLEND primary hits deterministically instead of stochastically. */\n hasAlpha?: boolean;\n /** Any scene material uses alpha mask or blend. */\n hasSceneAlpha?: boolean;\n /** Bit mask of the BLAS layouts present in the scene (bvh2, bvh4, bvh-gpu, cwbvh). */\n blasLayoutMask?: number;\n /** Defined only when pipeline constants are baked into the source. */\n spectral?: boolean;\n sceneCapabilities?: number;\n /** Retained environment support must still respect the current illumination state. */\n dynamicEnvironment?: boolean;\n materialCapabilities?: number;\n lightCapabilities?: number;\n}\n\n/** Generates the WebGPU megakernel path tracing compute shader. */\nexport default function rayTraceCompute({\n hasDisplacement,\n hasAnisotropyDirections = false,\n hasCoatNormals = false,\n hasAlpha = true,\n hasSceneAlpha = true,\n blasLayoutMask = 1,\n spectral,\n sceneCapabilities,\n dynamicEnvironment = false,\n materialCapabilities = ALL_MATERIAL_SHADER_CAPABILITIES,\n lightCapabilities\n}: RayTraceShaderOptions, sceneIntersections = '', pathDiagnostic = false, inputHooks?: RayTraceInputHooks) {\n hasAnisotropyDirections ||= hasCoatNormals;\n const deterministicPrimaryAlpha = hasAlpha;\n const hasShadowCatcher = sceneCapabilities === undefined || (sceneCapabilities & WGPU_SCENE_FEATURE_BITS.HAS_SHADOW_CATCHER) !== 0;\n const primaryVariables = /* wgsl */ `\n var si: SurfaceInteraction;\n var ma: OpenPbrResolvedMaterial;\n si.albedoSampled = false;\n si.mediumType = 0;\n si.mediumExtinction = vec3f(0.0);\n si.mediumScattering = vec3f(0.0);\n si.mediumAnisotropy = 0.0;\n si.barycentrics = vec2f(0.0);\n\n`;\n const primaryGuides = /* wgsl */ `${inputHooks?.primarySurface ?? ''} if (${hasShadowCatcher ? /* wgsl */ `si.hit && ma.materialType == SHADOW_CATCHER` : /* wgsl */ `false`}) {\n (*outAux).isShadowCatcher = true;\n // The composed denoising input includes this receiver's shadow. Supply\n // neutral reflectance and its shading normal, independent of visibility.\n (*outAux).albedo = vec3f(1.0);\n (*outAux).normal = select(si.normal, -si.normal, dot(si.normal, ray.d) > 0.0);\n var path: Path;\n path.ray = ray;\n path.fromEye = true;\n (*outAux).shadowCounts = sampleShadowCatcher(si, 0, &path);\n // RGB stores primary catcher coverage. The shadow travels in the\n // auxiliary counts, out of reach of color clamping.\n return vec4f(1.0);\n }\n else {\n if (HAS_BEVEL && rtUniform.bevel != 0) {\n if (ma.bevel > 0.0) {\n si.normal = getBevelNormal(si, ma.bevel);\n }\n }\n\n // Store a clean first-hit fallback. Glass retains this stable guide;\n // other delta paths may replace it at their first non-delta hit.\n if (si.hit && (ma.materialType == STANDARD || ma.materialType == UNLIT)) {\n (*outAux).albedo = select(ma.baseColor, vec3f(1.0), ma.transmissionWeight > 0.5);\n (*outAux).normal = select(\n si.normal,\n -si.normal,\n dot(si.normal, si.position - cameraUniform.viewInv[3].xyz) > 0.0\n );\n\n // Do not replace a glass guide with a randomly selected downstream\n // refraction/reflection hit; OIDN auxiliary features must stay clean.\n si.albedoSampled = ma.transmissionWeight > 0.5;\n }\n\n (*outAux).isShadowCatcher = false;\n\n\n }\n`;\n const source = /* wgsl */ `\n\n${commonWgsl}\n\n${rayTraceCoreWgsl({ hasSceneAlpha, hasAnisotropyDirections, hasCoatNormals, lightCapabilities })}\n\n${uvProjectionWgsl}\n\n${createMaterialBufferWgsl({ hasSceneAlpha, hasAnisotropyDirections, hasCoatNormals,\n hasTransmission: (materialCapabilities & MATERIAL_SHADER_CAPABILITIES.transmission) !== 0,\n hasMetalness: (materialCapabilities & MATERIAL_SHADER_CAPABILITIES.metal) !== 0 })}\n\n${createIntersectionWgsl({ hasDisplacement, blasLayoutMask, hasAnisotropyDirections, hasCoatNormals, sceneIntersections })}\n${randWgsl}\n${randomWgsl}\n${envMapWgsl}\n\n${samplingWgsl}\n${createOpenPbrBsdfWgsl(hasAnisotropyDirections, hasCoatNormals)}\n${sampleLightWgsl}${inputHooks?.declarations ?? ''}\n\n// Index of the path vertex currently being shaded.\nvar<private> bounceIndex: u32 = 0u;\n\n// Number of light candidates resampled for each NEE estimate. Interactive\n// preview caps the budget at two.\nfn neeCandidateCount() -> u32 {\n return u32(max(rtUniform.directLightCandidates, 1));\n}\n\n// Hash-based random number used to select among light candidates. It is\n// independent of the path sample stream so the candidate count does not\n// change the dimensions consumed by later bounces.\nfn lightCandidateRandom(salt: u32) -> f32 {\n // Reservoir replacement needs the full [0, 1) interval: sample1D's EPS\n // remapping makes small positive replacement probabilities unreachable.\n // Preserve the legacy sequential RNG draw in the random sampling mode so\n // subsequent path samples retain their stream; selection uses the hash.\n if (SAMPLING_PATTERN == 2) { _ = rand(); }\n let pixelID = samplingPixel.x + samplingPixel.y * u32(rtUniform.outputSize.x);\n let key = sampleSeed(sampleSeed(pixelID, pixelSampleIndex), rtUniform.sequenceSeed);\n let dimension = sampleSeed(samplingBounce, salt) ^ 0xa511e9b3u;\n return f32(sampleHash(key ^ dimension) >> 8u) * (1.0 / 16777216.0);\n}\n\n// MIS weight of a light-sampled contribution. With several resampled\n// candidates the NEE technique is drawn neeCandidateCount() times.\nfn neeMISWeight(lightPdf: f32, scatteringPdf: f32) -> f32 {\n let candidateCount = neeCandidateCount();\n if (candidateCount <= 1u) {\n return sampling_power_heuristic(lightPdf, scatteringPdf);\n }\n let weightedLightPdf = f32(candidateCount) * lightPdf;\n return weightedLightPdf /\n max(weightedLightPdf + scatteringPdf, 1e-20);\n}\n\n// MIS weight of a BSDF-sampled ray that hits a light.\nfn bsdfMISWeight(scatteringPdf: f32, lightPdf: f32) -> f32 {\n let candidateCount = neeCandidateCount();\n if (candidateCount <= 1u) {\n return sampling_power_heuristic(scatteringPdf, lightPdf);\n }\n let weightedLightPdf = f32(candidateCount) * lightPdf;\n return scatteringPdf /\n max(scatteringPdf + weightedLightPdf, 1e-20);\n}\n\n${createSampleMaterialWgsl(hasAnisotropyDirections, hasCoatNormals)}\n${sampleShadowCatcherWgsl}\n\n// Output\n@group(3) @binding(0) var<storage, read_write> outColor: array<vec4f>;\n@group(3) @binding(1) var<storage, read_write> outNormal: array<vec4f>;\n@group(3) @binding(2) var<storage, read_write> outAlbedo: array<vec4f>;\n\n${shadowCatcherAccumulationWGSL}\n@group(3) @binding(3) var<storage, read_write> outShadowCatcher: array<ShadowCatcherAccumulation>;\n\n\nfn accumulateNormalOrVariance(\n pixelIndex: u32,\n radiance: vec3f,\n normal: vec3f\n) {\n if (rtUniform.varianceMoments != 0) {\n let value = dot(radiance, luminance);\n outNormal[pixelIndex] += vec4f(value * value, 0.0, 0.0, 1.0);\n }\n else {\n outNormal[pixelIndex] += vec4f(normal, 1.0);\n }\n}\n\n// Moments of the per-sample value the built-in denoiser compares, used to\n// estimate the variance of each pixel's mean.\nfn accumulateDenoiseMoments(pixelIndex: u32, value: f32) {\n outShadowCatcher[pixelIndex].moments += vec4f(value, value * value, 0.0, 0.0);\n}\n\n\nstruct RayTraceAuxiliary {\n // Denoising guides and auxiliary pixel classification.\n albedo: vec3f,\n isShadowCatcher: bool,\n normal: vec3f,\n isBlank: bool,\n // Shadow-catcher hits: occluded and total light sample counts.\n shadowCounts: vec2f,\n}\n\nfn 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\nfn sampleSpectralSceneBackground(ray: Ray, wavelength: f32) -> vec4f {\n let bg = sampleSceneBackground(ray);\n if (SPECTRAL) {\n return vec4f(sampling_spectral_rgb_filter(bg.rgb, wavelength), bg.a);\n }\n return bg;\n}\n\n// Split shading from hit completion so primary and secondary rays can share\n// one traversal/material-load call site in the common RGB integrator.\nfn shadeVertex(uv: vec2f, path: ptr<function, Path>, depth: ptr<function, i32>, si: ptr<function, SurfaceInteraction>, ma: ptr<function, OpenPbrResolvedMaterial>, outAux: ptr<function, RayTraceAuxiliary>, sampleIgnoredOut: ptr<function, bool>) {\n *sampleIgnoredOut = false;\n samplingBounce = u32(max(*depth, 0));\n let matType = (*ma).materialType;\n let isShadowCatcher = ${hasShadowCatcher ? /* wgsl */ `matType == SHADOW_CATCHER` : /* wgsl */ `false`};\n if (isShadowCatcher) {\n if ((*path).fromEye) {\n (*si).hit = false;\n }\n else {\n // The catcher only contributes to the separate shadow mask. Secondary\n // rays must see the environment behind it instead of its default white\n // material, otherwise it appears in reflections.\n if (USE_ENVMAP) {\n if ((*path).isNextRefraction) {\n // A camera-visible transmitted ray can hit the invisible catcher\n // before the grounded skybox. Preserve the displayed projection.\n let bg = sampleSpectralSceneBackground(\n (*path).ray,\n (*si).wavelength\n );\n let backgroundMisWeight = select(bsdfMISWeight((*path).scatteringPdf,\n envMapPdf(environmentUv((*path).ray.d)) * rtUniform.envLightPmf),\n 1.0, (*path).scatteringIsDelta);\n (*path).li += backgroundMisWeight * (*path).beta * bg.rgb * bg.a;\n (*path).alpha = max((*path).alpha, bg.a);\n }\n else {\n let envUv = environmentUv((*path).ray.d);\n var emission = textureLinear(envMap, envUv).rgb;\n if (SPECTRAL) {\n emission = sampling_spectral_rgb_filter(emission, (*si).wavelength);\n }\n (*path).li += (*path).beta * emission *\n bsdfMISWeight((*path).scatteringPdf, envMapPdf(envUv));\n }\n }\n else {\n let bg = sampleSpectralSceneBackground(\n (*path).ray,\n (*si).wavelength\n );\n (*path).li += (*path).beta * bg.rgb * bg.a;\n }\n (*path).abort = true;\n return;\n }\n }\n if (!(*si).hit) {\n // The spectral integrator traces red, green, and blue paths separately.\n // Split background radiance by wavelength as well so the three paths\n // reconstruct the skybox instead of adding three full RGB copies.\n let bg = sampleSpectralSceneBackground(\n (*path).ray,\n (*si).wavelength\n );\n if ((*path).fromEye) {\n // Sample from background if first ray not hit anything.\n (*path).li = (*path).beta * bg.rgb * bg.a;\n (*path).alpha = bg.a;\n // Only treat as blank pixels when the background is transparent\n (*outAux).isBlank = bg.a < 0.001 ||\n (USE_BACKGROUND && !USE_BACKGROUND_MAP);\n }\n else {\n let cam = initCameraRay(uv);\n let towardsCam = dot(cam.d, (*path).ray.d) > 0.0;\n if (rtUniform.transmissiveOutputAlphaMin < 1.0) {\n if (towardsCam) {\n (*path).alpha = max((*path).alpha, bg.a);\n } else {\n (*path).alpha = 1.0;\n }\n }\n if (!USE_ENVMAP) {\n // A display background is not an illumination environment, but a\n // visible secondary ray (e.g. through glass) still resolves it.\n if (towardsCam) {\n // This is a terminal BSDF-hit contribution, not a replacement for\n // NEE accumulated at earlier vertices on the same path.\n (*path).li += (*path).beta *\n bsdfMISWeight((*path).scatteringPdf, 1.0) * bg.rgb;\n }\n }\n }\n (*path).abort = true;\n return;\n }\n\n\n if ((matType == STANDARD || matType == UNLIT)) {\n let alpha = clamp((*ma).alpha, 0.0, 1.0);\n \n if (alpha < 1.0) {\n // Alpha is stochastic coverage. Keep accepted surface radiance at full\n // weight instead of multiplying it by alpha a second time.\n if (alpha <= 0.0 || hashedSample(3u) > alpha) {\n (*sampleIgnoredOut) = true;\n }\n }\n if (!(*sampleIgnoredOut) && rtUniform.transmissiveOutputAlphaMin < 1.0) {\n // A selected stochastic coverage sample is fully covered. Ignored\n // samples leave output alpha untouched and continue to the back layer.\n let outputAlpha = max(1.0 - (*ma).transmissionWeight, 1.0 - pow(1.0 - rtUniform.transmissiveOutputAlphaMin, f32(*depth)));\n (*path).alpha = max(outputAlpha, (*path).alpha);\n }\n if (!(*sampleIgnoredOut)) {\n bounceIndex = u32(max(*depth, 0));\n SampleMaterial(si, ma, path);\n }\n else {\n // Alpha-null traversal does not consume the receiver event. The first\n // accepted surface behind it is still the receiver for NEE/AOVs.\n (*path).ray.o = (*si).position + (*path).ray.d * EPS;\n }\n }\n\n if (!(*si).albedoSampled && !(*sampleIgnoredOut)) {\n // Select denoising guides.\n if ((matType == STANDARD || matType == UNLIT)) {\n if (!((*ma).transmissionWeight > 0.5 ||\n ((*ma).specularRoughness <= 0.02 && (*ma).baseMetalness > 0.98))) {\n (*outAux).albedo = (*ma).baseColor;\n (*outAux).normal = select(\n (*si).normal,\n -(*si).normal,\n dot(\n (*si).normal,\n (*si).position - cameraUniform.viewInv[3].xyz\n ) > 0.0\n );\n (*si).albedoSampled = true;\n }\n // TODO pick albedo and normal on the surface if ray is too deep and can't hit any rough or opaque surfaces.\n if (*depth > 4) {\n (*si).albedoSampled = true;\n }\n }\n }\n\n // Russian roulette with throughput compensation.\n if (USE_RR) {\n if (*depth >= RR_MAX && !(*sampleIgnoredOut)) {\n let rouletteBeta = (*path).beta;\n // Max-component survival (pbrt-v4) avoids over-amplifying saturated paths.\n let maxComponent = max(max(rouletteBeta.x, rouletteBeta.y), rouletteBeta.z);\n let q: f32 = max(0.0, 1.0 - maxComponent);\n if (sample1D(bounceDim(RUSSIAN_ROULETTE)) < q) {\n // TODO we force use alpha 1.0 here.\n // To avoid the glass pixels is transparent even if it's on an opaque object.\n (*path).alpha = 1.0;\n (*path).abort = true;\n }\n else {\n (*path).beta /= 1.0 - q;\n }\n }\n }\n if ((*path).abort) {\n return;\n }\n\n\n}\n\nfn completeVertex(path: ptr<function, Path>, depth: ptr<function, i32>, si: ptr<function, SurfaceInteraction>, ma: ptr<function, OpenPbrResolvedMaterial>, sampleIgnored: bool) {\n if (SPECTRAL) {\n // Is glass\n if ((*ma).transmissionWeight > 0.001) {\n (*ma).specularIor = sampling_cauchy_ior(\n (*si).wavelength,\n (*ma).specularIor,\n (*ma).transmissionDispersionScale,\n (*ma).dispersionAbbeNumber\n );\n }\n }\n\n if (sampleIgnored) {\n return;\n }\n\n // Use light if hit the light\n var lightSample: LightSampleRec;\n let isAlphaOccluded = hashedSample(3u) < (*ma).alpha;\n for (var i = 0; i < rtUniform.lightsCount; i++) {\n // Fetch light Data\n let light: Light = analyticLights[i];\n let d = intersectLight((*path).ray, light, &lightSample);\n lightSample.pdf *= light.selectionPmf;\n let occluded = (*si).hit && d > distance((*si).position, (*path).ray.o);\n if (d != INF && !(occluded && isAlphaOccluded)) {\n let misWeight = bsdfMISWeight((*path).scatteringPdf, lightSample.pdf);\n if (SPECTRAL) {\n lightSample.emission = sampling_spectral_rgb_filter(lightSample.emission, (*si).wavelength);\n }\n (*path).li += misWeight * (*path).beta * lightSample.emission;\n (*path).alpha = 1.0;\n (*path).abort = true;\n }\n }\n\n if (USE_ENVMAP) {\n if (!((*si).hit && isAlphaOccluded)) {\n if ((*path).isNextRefraction) {\n // Camera-visible transmission follows the displayed background,\n // including grounded projection. Other rays use the raw environment\n // map as the physically sampled lighting source.\n let bg = sampleSpectralSceneBackground(\n (*path).ray,\n (*si).wavelength\n );\n let backgroundMisWeight = select(bsdfMISWeight((*path).scatteringPdf,\n envMapPdf(environmentUv((*path).ray.d)) * rtUniform.envLightPmf),\n 1.0, (*path).scatteringIsDelta);\n (*path).li += backgroundMisWeight * (*path).beta * bg.rgb * bg.a;\n (*path).alpha = max((*path).alpha, bg.a);\n (*path).abort = true;\n return;\n }\n let _uv = environmentUv((*path).ray.d);\n var emission = textureLinear(envMap, _uv).rgb;\n let misWeight = bsdfMISWeight(\n (*path).scatteringPdf,\n envMapPdf(_uv) * rtUniform.envLightPmf\n );\n if (SPECTRAL) {\n emission = sampling_spectral_rgb_filter(emission, (*si).wavelength);\n }\n // TODO force the min alpha to be 1 when showing envmap as background\n if (!USE_BACKGROUND) {\n (*path).alpha = 1.0;\n }\n (*path).li += (*path).beta * emission * misWeight;\n (*path).abort = true;\n }\n }\n\n *depth = *depth + 1;\n}\n\nfn initCameraRay(coord: vec2f) -> Ray {\n let origin = cameraUniform.viewProjectionInv * vec4f(coord * 2.0 - 1.0, -1.0, 1.0);\n let center = cameraUniform.viewProjectionInv * vec4f(coord * 2.0 - 1.0, 1.0, 1.0);\n var cam: Ray;\n initRay(&cam, origin.xyz / origin.w, normalize(center.xyz * origin.w - origin.xyz * center.w));\n\n return cam;\n}\n\n\nfn bevelCubicSample(radius: f32, xi: f32) -> vec2f {\n var x = 0.25;\n for (var iteration = 0; iteration < 10; iteration++) {\n let x2 = x * x;\n let x3 = x2 * x;\n let oneMinusX = 1.0 - x;\n let value = 10.0 * x2 - 20.0 * x3 + 15.0 * x2 * x2 -\n 4.0 * x2 * x3 - xi;\n let derivative = 20.0 * x * oneMinusX * oneMinusX * oneMinusX;\n if (abs(value) < 1e-6 || derivative == 0.0) { break; }\n x = clamp(x - value / derivative, 0.0, 1.0);\n }\n let diskRadius = x * radius;\n return vec2f(diskRadius, sqrt(max(radius * radius - diskRadius * diskRadius, 0.0)));\n}\n\nfn bevelCubicPdf(radius: f32, distanceToSource: f32) -> f32 {\n if (distanceToSource >= radius) { return 0.0; }\n let falloff = radius - distanceToSource;\n return falloff * falloff * falloff;\n}\n\n// Shader bevel. Usage: docs/materials.md (Renderer extensions)\nfn getBevelNormal(source: SurfaceInteraction, radius: f32) -> vec3f {\n var geometricNormal = source.faceNormal;\n if (dot(geometricNormal, source.vertexNormal) < 0.0) {\n geometricNormal = -geometricNormal;\n }\n var tangent: vec3f;\n var bitangent: vec3f;\n sampling_orthonormal_basis(geometricNormal, &tangent, &bitangent);\n var normalSum = vec3f(0.0);\n\n for (var sampleIndex = 0; sampleIndex < BEVEL_SAMPLES; sampleIndex++) {\n let bevelRnd = sample2D(CAMERA_BEVEL_BASE + u32(sampleIndex));\n var axisSample = bevelRnd.x;\n let radialSample = bevelRnd.y;\n var diskNormal = geometricNormal;\n var diskTangent = tangent;\n var diskBitangent = bitangent;\n var pickPdfNormal = 0.5;\n var pickPdfTangent = 0.25;\n var pickPdfBitangent = 0.25;\n if (axisSample < 0.5) {\n axisSample *= 2.0;\n }\n else if (axisSample < 0.75) {\n let swapAxis = diskNormal;\n diskNormal = diskTangent;\n diskTangent = swapAxis;\n pickPdfNormal = 0.25;\n pickPdfTangent = 0.5;\n axisSample = (axisSample - 0.5) * 4.0;\n }\n else {\n let swapAxis = diskNormal;\n diskNormal = diskBitangent;\n diskBitangent = swapAxis;\n pickPdfNormal = 0.25;\n pickPdfBitangent = 0.5;\n axisSample = (axisSample - 0.75) * 4.0;\n }\n\n let diskSample = bevelCubicSample(radius, radialSample);\n let phi = TWOPI * axisSample;\n let diskOffset = diskSample.x *\n (cos(phi) * diskTangent + sin(phi) * diskBitangent);\n var ray: Ray;\n initRay(\n &ray,\n source.position + diskNormal * diskSample.y + diskOffset,\n -diskNormal\n );\n var hit: SurfaceInteraction;\n intersectSceneBevel(\n ray,\n &hit,\n source.instanceIndex,\n max(2.0 * diskSample.y, EPS)\n );\n if (!hit.hit) { continue; }\n\n let pdfNormal = pickPdfNormal * abs(dot(diskNormal, hit.faceNormal));\n let pdfTangent = pickPdfTangent * abs(dot(diskTangent, hit.faceNormal));\n let pdfBitangent = pickPdfBitangent * abs(dot(diskBitangent, hit.faceNormal));\n var weight = pdfNormal / max(\n pdfNormal * pdfNormal + pdfTangent * pdfTangent +\n pdfBitangent * pdfBitangent,\n 1e-8\n );\n weight *= bevelCubicPdf(radius, distance(hit.position, source.position)) /\n max(bevelCubicPdf(radius, diskSample.x), 1e-8);\n normalSum += weight * hit.vertexNormal;\n }\n\n if (dot(normalSum, normalSum) <= 1e-12) { return source.normal; }\n let bevelVertexNormal = normalize(normalSum);\n let result = source.normal + bevelVertexNormal - source.vertexNormal;\n if (dot(result, result) > 1e-12) { return normalize(result); }\n return source.normal;\n}\n\nfn integrateOnePathSample(uv: vec2f, si: ptr<function, SurfaceInteraction>, ray: Ray, ma: ptr<function, OpenPbrResolvedMaterial>, outAux: ptr<function, RayTraceAuxiliary>, initialQuery: bool) -> vec4f {\n var path: Path;\n path.ray = ray;\n path.li = vec3f(0.0);\n path.alpha = select(1.0, 0.0, rtUniform.transmissiveOutputAlphaMin < 1.0);\n path.beta = vec3f(1.0);\n path.fromEye = true;\n path.scatteringPdf = 0.0;\n path.scatteringIsDelta = false;\n path.isNextRefraction = false;\n path.abort = false;\n\n let bounceLimit = max(rtUniform.bounces, 0);\n let eventLimit = min(bounceLimit * 3, ${MAX_PATH_EVENTS});\n var depth = 0;\n var sampleIgnored = false;\n var isNotTransparent = false;\n // Back branches and subsequent bounces share one query/material call site.\n // Keep wavelength/branch loops separate so GPU lanes reconverge between\n // paths. Include the final query, which the legacy bounce completed before\n // testing its event budget. Even a zero-bounce back branch resolves guides.\n for (var event = 0; event <= eventLimit; event++) {\n if ((event != 0) | initialQuery) {\n intersectScene(path.ray, si, depth, event != 0, path.isNextRefraction);\n getMaterial(ma, (*si).materialIndex, (*si).uv, (*si).uv1, (*si).instanceIndex, (*si).vertexColor);\n }\n if (event == 0) {\n if (initialQuery & (*si).hit & ((*ma).materialType == STANDARD)) {\n (*outAux).albedo = select((*ma).baseColor, vec3f(1.0), (*ma).transmissionWeight > 0.5);\n (*outAux).normal = select((*si).normal, -(*si).normal,\n dot((*si).normal, (*si).position - cameraUniform.viewInv[3].xyz) > 0.0);\n (*si).albedoSampled = (*ma).transmissionWeight > 0.5;\n }\n isNotTransparent = ((*ma).transmissionWeight < 0.001) & ((*ma).subsurfaceWeight < 0.001) & ((*ma).alpha > 0.99);\n } else {\n completeVertex(&path, &depth, si, ma, sampleIgnored);\n }\n if (isNotTransparent & (event > bounceLimit)) { path.abort = true; }\n if (path.abort | (event >= eventLimit)) { break; }\n shadeVertex(uv, &path, &depth, si, ma, outAux, &sampleIgnored);\n if (path.abort) { break; }\n }\n\n return vec4f(path.li, path.alpha);\n}\n\nfn integrateOnePath(uv: vec2f, si: ptr<function, SurfaceInteraction>, ray: Ray, ma: ptr<function, OpenPbrResolvedMaterial>, outAux: ptr<function, RayTraceAuxiliary>) -> vec4f {\n ${deterministicPrimaryAlpha ? /* wgsl */ `let coverage = clamp((*ma).alpha, 0.0, 1.0);\n // glTF BLEND is source-over opacity, not a physical glass boundary. Split\n // the primary ray so reflected radiance is attenuated by alpha every sample\n // while the complementary branch continues straight to the scene behind it.\n // Secondary alpha hits remain stochastic to avoid exponential path growth.\n let split = (*si).hit & (((*ma).materialType == STANDARD) | ((*ma).materialType == UNLIT)) & (coverage < 1.0);\n var branchRay = ray;\n var branchSi = *si;\n var branchMa = *ma;\n var branchAux = *outAux;\n if (split) {\n branchRay.o = (*si).position + ray.d * (EPS * 0.02);\n branchSi.albedoSampled = false;\n }\n var back: vec4f;\n var backAux: RayTraceAuxiliary;\n // One path graph for opaque, back and front branches. The guard is fixed at\n // four; a dynamic bound keeps DXC from cloning that graph for each branch.\n let branchCount = select(1u, rtUniform.loopUnrollGuard >> 1u, split & (coverage > 0.0));\n for (var branch = 0u; branch < branchCount; branch++) {\n let result = integrateOnePathSample(uv, &branchSi, branchRay, &branchMa, &branchAux, split & (branch == 0u));\n if (!split | (coverage <= 0.0)) { *outAux = branchAux; return result; }\n if (branch == 0u) {\n back = result; backAux = branchAux;\n branchSi = *si; branchMa = *ma; branchMa.alpha = 1.0;\n branchRay = ray; branchAux = *outAux;\n } else {\n (*outAux).albedo = mix(backAux.albedo, branchAux.albedo, coverage);\n let blendedNormal = mix(backAux.normal, branchAux.normal, coverage);\n (*outAux).normal = select(vec3f(0.0), normalize(blendedNormal), dot(blendedNormal, blendedNormal) > 1e-12);\n (*outAux).isBlank = backAux.isBlank & branchAux.isBlank;\n (*outAux).isShadowCatcher = backAux.isShadowCatcher & branchAux.isShadowCatcher;\n (*outAux).shadowCounts = mix(backAux.shadowCounts, branchAux.shadowCounts, coverage);\n return mix(back, result, coverage);\n }\n }\n return vec4f(0.0);` : /* wgsl */ `return integrateOnePathSample(uv, si, ray, ma, outAux, false);`}\n}\n\n// Path tracing integrator as described in\n// http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Path_Tracing.html#\n${spectral === false && deterministicPrimaryAlpha ? /* wgsl */ `fn integrator(uv: vec2f, ray: Ray, outAux: ptr<function, RayTraceAuxiliary>) -> vec4f {\n${primaryVariables}\n var primarySi: SurfaceInteraction;\n var primaryMa: OpenPbrResolvedMaterial;\n var primaryAux: RayTraceAuxiliary;\n var primaryReady = false;\n var query = true;\n var split = false;\n var coverage = 1.0;\n var front = false;\n var back: vec4f;\n var backAux: RayTraceAuxiliary;\n var path: Path; path.ray = ray;\n path.alpha = select(1.0, 0.0, rtUniform.transmissiveOutputAlphaMin < 1.0);\n path.beta = vec3f(1.0); path.fromEye = true;\n let bounceLimit = max(rtUniform.bounces, 0);\n let eventLimit = min(bounceLimit * 3, ${MAX_PATH_EVENTS});\n var event = 0; var depth = 0; var sampleIgnored = false;\n var isNotTransparent = false;\n // Share one query/material graph across the primary, back and front paths.\n // Complete the back path before the front path to preserve random streams.\n for (var step = 0; step < (${MAX_PATH_EVENTS} + 1) * 2 + 2; step++) {\n if (query) {\n intersectScene(path.ray, &si, depth, event != 0, path.isNextRefraction);\n getMaterial(&ma, si.materialIndex, si.uv, si.uv1, si.instanceIndex, si.vertexColor);\n }\n query = true;\n if (!primaryReady) {\n${primaryGuides}\n primarySi = si; primaryMa = ma; primaryAux = *outAux;\n primaryReady = true;\n coverage = clamp(ma.alpha, 0.0, 1.0);\n split = si.hit & ((ma.materialType == STANDARD) | (ma.materialType == UNLIT)) & (coverage < 1.0);\n if (split) {\n path.ray.o = si.position + ray.d * (EPS * 0.02);\n si.albedoSampled = false;\n continue;\n }\n } else if ((event == 0) & !front & split) {\n if (si.hit & (ma.materialType == STANDARD)) {\n (*outAux).albedo = select(ma.baseColor, vec3f(1.0), ma.transmissionWeight > 0.5);\n (*outAux).normal = select(si.normal, -si.normal,\n dot(si.normal, si.position - cameraUniform.viewInv[3].xyz) > 0.0);\n si.albedoSampled = ma.transmissionWeight > 0.5;\n }\n }\n if (event == 0) {\n isNotTransparent = (ma.transmissionWeight < 0.001) & (ma.subsurfaceWeight < 0.001) & (ma.alpha > 0.99);\n } else {\n completeVertex(&path, &depth, &si, &ma, sampleIgnored);\n }\n if (isNotTransparent & (event > bounceLimit)) { path.abort = true; }\n if (!path.abort & (event < eventLimit)) {\n shadeVertex(uv, &path, &depth, &si, &ma, outAux, &sampleIgnored);\n }\n if (path.abort | (event >= eventLimit)) {\n let result = vec4f(path.li, path.alpha);\n if (!split | (coverage <= 0.0)) { return result; }\n if (front) {\n (*outAux).albedo = mix(backAux.albedo, (*outAux).albedo, coverage);\n let blendedNormal = mix(backAux.normal, (*outAux).normal, coverage);\n (*outAux).normal = select(vec3f(0.0), normalize(blendedNormal), dot(blendedNormal, blendedNormal) > 1e-12);\n (*outAux).isBlank = backAux.isBlank & (*outAux).isBlank;\n (*outAux).isShadowCatcher = backAux.isShadowCatcher & (*outAux).isShadowCatcher;\n (*outAux).shadowCounts = mix(backAux.shadowCounts, (*outAux).shadowCounts, coverage);\n return mix(back, result, coverage);\n }\n back = result; backAux = *outAux;\n front = true; query = false; event = 0; depth = 0; sampleIgnored = false;\n si = primarySi; ma = primaryMa; ma.alpha = 1.0; *outAux = primaryAux;\n path = Path(); path.ray = ray; path.beta = vec3f(1.0); path.fromEye = true;\n path.alpha = select(1.0, 0.0, rtUniform.transmissiveOutputAlphaMin < 1.0);\n } else { event++; }\n }\n return vec4f(0.0);\n}\n` : spectral === false && !deterministicPrimaryAlpha ? /* wgsl */ `fn integrator(uv: vec2f, ray: Ray, outAux: ptr<function, RayTraceAuxiliary>) -> vec4f {\n${primaryVariables}\n var path: Path;\n path.ray = ray;\n path.alpha = select(1.0, 0.0, rtUniform.transmissiveOutputAlphaMin < 1.0);\n path.beta = vec3f(1.0);\n path.fromEye = true;\n let bounceLimit = max(rtUniform.bounces, 0);\n let eventLimit = min(bounceLimit * 3, ${MAX_PATH_EVENTS});\n var isNotTransparent = false;\n var depth = 0;\n var sampleIgnored = false;\n // The extra iteration completes the last ray query, as the legacy bounce\n // did before testing the event budget. Queries consume the preceding bounce's\n // random stream; shading advances samplingBounce only after hit completion.\n for (var event = 0; event <= eventLimit; event++) {\n intersectScene(path.ray, &si, depth, event != 0, path.isNextRefraction);\n getMaterial(&ma, si.materialIndex, si.uv, si.uv1, si.instanceIndex, si.vertexColor);\n if (event == 0) {\n${primaryGuides}\n isNotTransparent = ma.transmissionWeight < 0.001 && ma.subsurfaceWeight < 0.001 && ma.alpha > 0.99;\n } else {\n completeVertex(&path, &depth, &si, &ma, sampleIgnored);\n }\n if (isNotTransparent && event > bounceLimit) { path.abort = true; }\n if (path.abort || event >= eventLimit) { break; }\n shadeVertex(uv, &path, &depth, &si, &ma, outAux, &sampleIgnored);\n if (path.abort) { break; }\n }\n return vec4f(path.li, path.alpha);\n}\n` : spectral === true && !deterministicPrimaryAlpha ? /* wgsl */ `\nfn integrator(uv: vec2f, ray: Ray, outAux: ptr<function, RayTraceAuxiliary>) -> vec4f {\n${primaryVariables}\n var primarySi: SurfaceInteraction;\n var primaryMa: OpenPbrResolvedMaterial;\n var primaryReady = false;\n var path: Path; path.ray = ray;\n let bounceLimit = max(rtUniform.bounces, 0);\n let eventLimit = min(bounceLimit * 3, ${MAX_PATH_EVENTS});\n var depth = 0; var event = 0; var sampleIgnored = false;\n var isNotTransparent = false;\n var wavelengthIndex = 0;\n let singleWavelength = rtUniform.dispersionWavelengths == 1;\n var dispersivePrimary = false;\n var heroWavelengthIndex = 0;\n var wavelengthRngState: vec4u;\n var wavelengthHashedCounter = 0u;\n var wavelengthAux: RayTraceAuxiliary;\n let wavelengths = array<f32, 3>(650.0, 510.0, 475.0);\n var color = vec3f(0.0); var alpha = 0.0;\n var accumulatedGuideAlbedo = vec3f(0.0);\n var accumulatedGuideNormal = vec3f(0.0);\n var allBlank = true;\n // Flatten the wavelength/path loops to share primary and secondary queries.\n // Each of the three paths uses at most MAX_PATH_EVENTS + 1 iterations,\n // including completion of its final query. The primary hit is reused.\n let stepLimit = min((${MAX_PATH_EVENTS} + 1) * 3, i32(rtUniform.loopUnrollGuard * ${Math.ceil((MAX_PATH_EVENTS + 1) * 3 / 4)}u));\n for (var step = 0; step < stepLimit; step++) {\n if (!primaryReady | ((event != 0) & !path.abort)) {\n intersectScene(path.ray, &si, depth, event != 0, path.isNextRefraction);\n getMaterial(&ma, si.materialIndex, si.uv, si.uv1, si.instanceIndex, si.vertexColor);\n }\n if (event == 0) {\n if (!primaryReady) {\n${primaryGuides}\n primarySi = si; primaryMa = ma; primaryReady = true;\n wavelengthRngState = s0; wavelengthHashedCounter = hashedCounter;\n dispersivePrimary = si.hit & (ma.transmissionWeight > 0.001) & (ma.transmissionDispersionScale > 0.001);\n if (singleWavelength & dispersivePrimary) {\n heroWavelengthIndex = min(i32(floor(sample1D(CAMERA_WAVELENGTH) * 3.0)), 2);\n }\n }\n si = primarySi; ma = primaryMa; wavelengthAux = *outAux;\n if (!singleWavelength) {\n s0 = wavelengthRngState; hashedCounter = wavelengthHashedCounter; samplingBounce = 0u;\n wavelengthAux.isBlank = false; wavelengthAux.isShadowCatcher = false;\n }\n si.wavelength = 0.0;\n if (!singleWavelength | dispersivePrimary) {\n si.wavelength = wavelengths[select(wavelengthIndex, heroWavelengthIndex, singleWavelength)];\n ma.specularIor = sampling_cauchy_ior(si.wavelength, ma.specularIor,\n ma.transmissionDispersionScale, ma.dispersionAbbeNumber);\n }\n path = Path(); path.ray = ray; path.beta = vec3f(1.0); path.fromEye = true;\n path.alpha = select(1.0, 0.0, rtUniform.transmissiveOutputAlphaMin < 1.0);\n depth = 0; sampleIgnored = false;\n isNotTransparent = (ma.transmissionWeight < 0.001) & (ma.subsurfaceWeight < 0.001) & (ma.alpha > 0.99);\n } else if (!path.abort) {\n completeVertex(&path, &depth, &si, &ma, sampleIgnored);\n }\n if (isNotTransparent & (event > bounceLimit)) { path.abort = true; }\n if (!path.abort & (event < eventLimit)) {\n shadeVertex(uv, &path, &depth, &si, &ma, &wavelengthAux, &sampleIgnored);\n }\n if (path.abort | (event >= eventLimit)) {\n let result = vec4f(path.li, path.alpha);\n if (singleWavelength) {\n *outAux = wavelengthAux;\n let scale = select(1.0, 3.0, dispersivePrimary);\n return vec4f(result.rgb * scale, result.a);\n }\n color += result.rgb; alpha = max(alpha, result.a);\n accumulatedGuideAlbedo += wavelengthAux.albedo;\n accumulatedGuideNormal += wavelengthAux.normal;\n allBlank = allBlank & wavelengthAux.isBlank;\n wavelengthIndex++;\n if (wavelengthIndex >= 3) {\n (*outAux).albedo = accumulatedGuideAlbedo / 3.0;\n (*outAux).normal = accumulatedGuideNormal / 3.0;\n (*outAux).isBlank = allBlank;\n return vec4f(color, alpha);\n }\n event = 0;\n } else { event++; }\n }\n return vec4f(0.0);\n}\n` : /* wgsl */ `fn integrator(uv: vec2f, ray: Ray, outAux: ptr<function, RayTraceAuxiliary>) -> vec4f {\n${primaryVariables}\n intersectScene(ray, &si, 0, false, false);\n getMaterial(&ma, si.materialIndex, si.uv, si.uv1, si.instanceIndex, si.vertexColor);\n${primaryGuides}\n if (SPECTRAL) {\n // Keep one source-level integrateOnePath call for both interactive\n // preview and offline RGB splitting. A runtime loop bound prevents the\n // WGSL compiler from cloning the full path graph three times while the\n // uniform still selects the exact existing preview/offline behavior.\n let rgbWaveLengths = array<f32, 3>(650.0, 510.0, 475.0);\n var colAccum = vec3f(0.0);\n var alpha = 0.0;\n var accumulatedGuideAlbedo = vec3f(0.0);\n var accumulatedGuideNormal = vec3f(0.0);\n var allBlank = true;\n var si2 = si;\n var ma2 = ma;\n let wavelengthRngState = s0;\n let wavelengthHashedCounter = hashedCounter;\n let singleWavelength = rtUniform.dispersionWavelengths == 1;\n let dispersivePrimary = si.hit &&\n ma.transmissionWeight > 0.001 &&\n ma.transmissionDispersionScale > 0.001;\n let wavelengthCount = select(3, 1, singleWavelength);\n var heroWavelengthIndex = 0;\n if (singleWavelength && dispersivePrimary) {\n // Stochastic hero wavelength: one RGB channel per pixel keeps the\n // preview spectral at one-third of the full three-path cost.\n heroWavelengthIndex = min(i32(floor(sample1D(CAMERA_WAVELENGTH) * 3.0)), 2);\n }\n for (var i = 0; i < wavelengthCount; i++) {\n // Common random numbers keep Monte Carlo error correlated between\n // RGB wavelengths. This removes much of the colored variance that\n // otherwise turns into dirty chromatic patches after OIDN.\n if (!singleWavelength) {\n s0 = wavelengthRngState;\n hashedCounter = wavelengthHashedCounter;\n samplingBounce = 0u;\n }\n si.wavelength = 0.0;\n if (!singleWavelength || dispersivePrimary) {\n si.wavelength = rgbWaveLengths[select(i, heroWavelengthIndex, singleWavelength)];\n ma.specularIor = sampling_cauchy_ior(\n si.wavelength,\n ma.specularIor,\n ma.transmissionDispersionScale,\n ma.dispersionAbbeNumber\n );\n }\n var wavelengthAux: RayTraceAuxiliary;\n if (singleWavelength) {\n wavelengthAux = *outAux;\n } else {\n wavelengthAux.albedo = (*outAux).albedo;\n wavelengthAux.normal = (*outAux).normal;\n wavelengthAux.isBlank = false;\n wavelengthAux.isShadowCatcher = false;\n }\n let res = integrateOnePath(uv, &si, ray, &ma, &wavelengthAux);\n if (singleWavelength) {\n *outAux = wavelengthAux;\n let scale = select(1.0, 3.0, dispersivePrimary);\n return vec4f(res.rgb * scale, res.a);\n }\n colAccum += res.rgb;\n alpha = max(alpha, res.a);\n accumulatedGuideAlbedo += wavelengthAux.albedo;\n accumulatedGuideNormal += wavelengthAux.normal;\n allBlank = allBlank && wavelengthAux.isBlank;\n si = si2;\n ma = ma2;\n }\n (*outAux).albedo = accumulatedGuideAlbedo / 3.0;\n (*outAux).normal = accumulatedGuideNormal / 3.0;\n (*outAux).isBlank = allBlank;\n return vec4f(colAccum, alpha);\n }\n else {\n return integrateOnePath(uv, &si, ray, &ma, outAux);\n }\n}\n`}\n\nfn sampleCircle(ip: vec2f) -> vec2f {\n let p = 2.0 * ip - 1.0;\n let greater = abs(p.x) > abs(p.y);\n let r = select(p.y, p.x, greater);\n let theta = select(PI * (0.5 - 0.25 * p.x / p.y), 0.25 * PI * p.y / p.x, greater);\n return r * vec2f(cos(theta), sin(theta));\n}\n\n\n@compute @workgroup_size(${WORKGROUP_SIZE}, ${WORKGROUP_SIZE}, 1)\nfn main(@builtin(global_invocation_id) globalId: vec3u) {\n let outputSize = rtUniform.outputSize;\n let scaleOffset = rtUniform.scaleOffset;\n var jitter = rtUniform.jitter;\n // TODO if outputSize is not multiple of 8?\n${pathDiagnostic ? ` if (globalId.y >= mtPathParameters.y) { return; }\n let pathId = vec2u(globalId.x, globalId.y + mtPathParameters.x);\n` : ''} let coord = ${pathDiagnostic ? 'pathId' : 'globalId.xy'} * vec2u(scaleOffset.xy) + vec2u(scaleOffset.zw);\n if (coord.x >= u32(outputSize.x) || coord.y >= u32(outputSize.y)) {\n return;\n }\n let idx = coord.x + coord.y * u32(outputSize.x);\n\n if (\n rtUniform.adaptiveSampling != 0 &&\n outNormal[idx].a >= textureLoad(\n adaptiveSampleTargets,\n vec2i(coord),\n 0\n ).r\n ) {\n return;\n }\n\n // Match WebGL's rasterized v_Texcoord, whose sample is at the center of\n // each pixel. Keeping the same origin is material relevant: a half-pixel\n // shift moves a narrow metal highlight across several pixels.\n let uv = (vec2<f32>(f32(coord.x), f32(coord.y)) + vec2f(0.5)) / outputSize;\n\n samplingPixel = coord;\n pixelSampleIndex = u32(outNormal[idx].a) + rtUniform.sampleSequenceOffset;\n samplingBounce = 0u; hashedCounter = 0u;\n if (${inputHooks?.uniformJitter ? 'false' : 'SAMPLING_PATTERN == 0 || SAMPLING_PATTERN == 1'}) {\n jitter = (sample2D(CAMERA_PIXEL) - 0.5) / outputSize;\n }\n rng_initialize(coord, i32(pixelSampleIndex ^ sampleHash(rtUniform.sequenceSeed)));\n var cam = initCameraRay(uv + jitter);\n\n if (cameraUniform.apertureRadius > 0.0) {\n // Thin lens: the focus plane is perpendicular to the view direction.\n let eye = cameraUniform.viewInv[3].xyz;\n let forward = -normalize(cameraUniform.viewInv[2].xyz);\n let focusPoint = eye + cam.d * (cameraUniform.focusDistance / dot(cam.d, forward));\n let lensPoint = cameraUniform.apertureRadius * sampleCircle(sample2D(CAMERA_LENS));\n let origin = (cameraUniform.viewInv * vec4f(lensPoint, 0.0, 1.0)).xyz;\n let dir = normalize(focusPoint - origin);\n initRay(&cam, origin, dir);\n }\n\n var outAux: RayTraceAuxiliary;\n outAux.albedo = vec3f(0.0);\n outAux.normal = vec3f(0.0);\n outAux.isBlank = false;\n outAux.isShadowCatcher = false;\n${pathDiagnostic ? ' mtPathBegin(coord);\\n' : ''}${inputHooks?.begin ?? ''} var liAndAlpha = integrator(uv + jitter, cam, &outAux);\n${inputHooks?.unclampedColor ?? ''}\n // Clamp at Integration Level to reduce fireflies.\n // TODO Path level configuration?\n if (rtUniform.clamping > 0.0) {\n let clamping = rtUniform.clamping;\n liAndAlpha = vec4(clamp(liAndAlpha.rgb, -vec3f(clamping), vec3f(clamping)), liAndAlpha.a);\n }\n if (!(liAndAlpha.x < INF && liAndAlpha.x > -EPS)) {\n liAndAlpha = vec4f(0.0, 0.0, 0.0, 1.0);\n }\n\n if (outAux.isShadowCatcher) {\n // The catcher is invisible, so the main color at this pixel must still\n // contain whatever the camera ray would have seen behind it.\n let backgroundSample = sampleSceneBackground(cam);\n let backgroundHit = select(0.0, 1.0, backgroundSample.a >= 0.01);\n let backgroundPremultiplied = vec4f(backgroundSample.rgb * backgroundSample.a, backgroundSample.a);\n // Keep the background contribution separate from geometry at silhouettes.\n // mask: catcher coverage, background alpha, background hits. The resolve\n // pass derives shadow opacity from the occluded/total sample counts in moments.zw.\n outShadowCatcher[idx].mask += vec4f(1.0, backgroundSample.a, backgroundHit, 0.0);\n outShadowCatcher[idx].moments += vec4f(0.0, 0.0, outAux.shadowCounts);\n outShadowCatcher[idx].background += backgroundPremultiplied;\n outColor[idx] += backgroundPremultiplied;\n outAlbedo[idx] += vec4f(outAux.albedo, backgroundHit);\n accumulateNormalOrVariance(idx, backgroundSample.rgb, outAux.normal);\n // The composed catcher is the background darkened by the shadow. Over a\n // transparent background the shadow is only its opacity.\n let counts = outAux.shadowCounts;\n let visibility = 1.0 - select(0.0, counts.x / counts.y, counts.y > 0.0);\n accumulateDenoiseMoments(idx, visibility * select(\n 1.0, dot(backgroundSample.rgb, luminance), backgroundSample.a >= 0.01\n ));\n }\n else {\n if (HAS_SHADOW_CATCHER && outAux.isBlank && USE_BACKGROUND && !USE_BACKGROUND_MAP && rtUniform.backgroundColor.a >= 0.99999) {\n // A primary miss sees the same opaque display backdrop as a catcher\n // hit, but must never receive its shadow. Keep that radiance and alpha\n // out of the geometry layer before hit-count normalization/tone mapping.\n // Red/blue still count only actual catcher hits; green counts all\n // background alpha removed from the foreground during normalization.\n outShadowCatcher[idx].background += liAndAlpha;\n outShadowCatcher[idx].mask.g += liAndAlpha.a;\n }\n outColor[idx] += liAndAlpha;\n outAlbedo[idx] += vec4f(outAux.albedo, select(1.0, 0.0, outAux.isBlank));\n accumulateNormalOrVariance(idx, liAndAlpha.rgb, outAux.normal);\n accumulateDenoiseMoments(idx, dot(liAndAlpha.rgb, luminance));\n }\n${pathDiagnostic ? ' mtPathEnd(outAux.normal, outAux.albedo);\\n' : ''}${inputHooks?.end ?? ''}}\n`;\n // The query entry point shares the exact BVH and material implementation.\n // It has no effect on the beauty entry point or its bindings.\n // Gate optional material layers on scene-level feature flags so drivers can\n // skip the unused BSDF lobes.\n let specializedSource = source + depthQueryWGSL + reflectanceWGSL + aovTraceWGSL;\n const backgroundBits: Record<string, number> = {\n USE_BACKGROUND: 1,\n USE_BACKGROUND_MAP: 2,\n USE_GROUNDED_BACKGROUND: 4\n };\n specializedSource = specializedSource.replace(\n /\\b(?:USE_BACKGROUND|USE_BACKGROUND_MAP|USE_GROUNDED_BACKGROUND)\\b/g,\n (name) => `((rtUniform.backgroundFlags & ${backgroundBits[name]}) != 0)`\n );\n specializedSource = specializedSource.replace(\n new RegExp(`\\\\b(?:${Object.keys(WGPU_SCENE_FEATURE_BITS).join('|')})\\\\b`, 'g'),\n // Material weights and absent texture indices already disable retained\n // material capabilities. Environment presence also changes background/MIS\n // behavior, so its active flag must remain dynamic when support is retained.\n name => sceneCapabilities === undefined || dynamicEnvironment && name === 'USE_ENVMAP' && (sceneCapabilities & WGPU_SCENE_FEATURE_BITS[name]) !== 0\n ? /* wgsl */ `((rtUniform.sceneFeatureFlags & ${WGPU_SCENE_FEATURE_BITS[name]}u) != 0u)`\n : (sceneCapabilities & WGPU_SCENE_FEATURE_BITS[name]) !== 0 ? /* wgsl */ `true` : /* wgsl */ `false`\n );\n return specializedSource;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"rayTrace.compute.js","sourceRoot":"","sources":["../../../src/renderer/wgsl/rayTrace.compute.ts"],"names":[],"mappings":"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","sourcesContent":["import {photonQuerySource} from './photonMap.compute.js';\nimport backgroundWgsl from './chunks/background.wgsl.js';\nimport {researchTransport} from '../researchTransport.js';\nimport {causticPartitionWGSL} from './chunks/caustics.wgsl.js';\nimport { MAX_PATH_EVENTS } from '../pathLimits.js';\nimport shadowCatcherAccumulationWGSL from './chunks/shadowCatcherAccumulation.wgsl.js';\nimport reflectanceWGSL from './aovReflectance.wgsl.js';\nimport aovTraceWGSL from './aovTrace.wgsl.js';\nimport depthQueryWGSL from './depthQuery.wgsl.js';\n// Portions adapted from ray-tracing-renderer@0.10.15\n// (https://github.com/hoverinc/ray-tracing-renderer).\n// Copyright (c) 2019 HOVER, MIT license; see LICENSES/RayTracingRenderer-MIT.txt.\n\nimport commonWgsl from './chunks/common.wgsl.js';\nimport { WGPU_SCENE_FEATURE_BITS } from '../WGPUSceneFeatures.js';\nimport {ALL_MATERIAL_SHADER_CAPABILITIES, MATERIAL_SHADER_CAPABILITIES} from '../shaderCapabilities.js';\nimport rayTraceCoreWgsl from './chunks/rayTraceCore.wgsl.js';\nimport { createMaterialBufferWgsl } from './chunks/materialBuffer.wgsl.js';\nimport createIntersectionWgsl from './chunks/intersect.wgsl.js';\nimport randomWgsl from './chunks/random.wgsl.js';\nimport randWgsl from './chunks/rand.wgsl.js';\nimport { createSampleMaterialWgsl } from './chunks/sampleMaterial.wgsl.js';\nimport sampleLightWgsl from './chunks/sampleLight.wgsl.js';\nimport { createOpenPbrBsdfWgsl } from './chunks/openPbrBsdf.wgsl.js';\nimport samplingWgsl from './chunks/sampling.wgsl.js';\nimport {spectrumWgsl} from '../spectrum.js';\nimport sampleShadowCatcherWgsl from './chunks/sampleShadowCatcher.wgsl.js';\nimport envMapWgsl from './chunks/envMap.wgsl.js';\nimport uvProjectionWgsl from './chunks/uvProjection.wgsl.js';\nexport const WORKGROUP_SIZE = 8;\n\n/** Native input instrumentation; omitted hooks preserve the production source. */\nexport interface RayTraceInputHooks {\n declarations: string;\n begin: string;\n primarySurface: string;\n unclampedColor: string;\n end: string;\n uniformJitter: boolean;\n}\n\nexport interface RayTraceShaderOptions {\n hasDisplacement: boolean;\n debugGroup?: number;\n /** Invocation block edge; the native host matches dispatch to this value. Default 8. */\n workgroupSize?:number;\n lightTracing?: boolean;\n photonMapping?:boolean;\n photonMaxDepth?:number;\n /** Internal A/B override for directly visible PM receivers. Default true; ignored with light tracing. */\n photonDirectSubset?:boolean;\n /** Include direction frames only when used, preserving the legacy shader arithmetic otherwise. */\n hasAnisotropyDirections?: boolean;\n /** Include independent coat normals only when a material uses that slot. */\n hasCoatNormals?: boolean;\n /** Split glTF BLEND primary hits deterministically instead of stochastically. */\n hasAlpha?: boolean;\n /** Any scene material uses alpha mask or blend. */\n hasSceneAlpha?: boolean;\n /** Bit mask of the BLAS layouts present in the scene (bvh2, bvh4, bvh-gpu, cwbvh). */\n blasLayoutMask?: number;\n /** Defined only when pipeline constants are baked into the source. */\n spectral?: boolean;\n sceneCapabilities?: number;\n /** Retained environment support must still respect the current illumination state. */\n dynamicEnvironment?: boolean;\n materialCapabilities?: number;\n lightCapabilities?: number;\n}\n\n/** Generates the WebGPU megakernel path tracing compute shader. */\nexport default function rayTraceCompute(options: RayTraceShaderOptions, sceneIntersections = '', pathDiagnostic = false, inputHooks?: RayTraceInputHooks) {\n const research = researchTransport()?.compute(options);\n const enabled = research?.enabled ?? false;\n const partitionDepth = research?.maxDepth ?? 20;\n const debugGroup = research?.debugGroup ?? options.debugGroup ?? -1;\n let {\n hasDisplacement,workgroupSize=WORKGROUP_SIZE,\n lightTracing = false,photonMapping=false,photonMaxDepth=20,\n hasAnisotropyDirections = false,\n hasCoatNormals = false,\n hasAlpha = true,\n hasSceneAlpha = true,\n blasLayoutMask = 1,\n spectral,\n sceneCapabilities,\n dynamicEnvironment = false,\n materialCapabilities = ALL_MATERIAL_SHADER_CAPABILITIES,\n lightCapabilities\n} = options;\n hasAnisotropyDirections ||= hasCoatNormals;\n const deterministicPrimaryAlpha = hasAlpha;\n const hasShadowCatcher = sceneCapabilities === undefined || (sceneCapabilities & WGPU_SCENE_FEATURE_BITS.HAS_SHADOW_CATCHER) !== 0;\n const primaryVariables = /* wgsl */ `\n var si: SurfaceInteraction;\n var ma: OpenPbrResolvedMaterial;\n si.albedoSampled = false;\n si.mediumType = 0;\n si.mediumExtinction = vec3f(0.0);\n si.mediumScattering = vec3f(0.0);\n si.mediumAnisotropy = 0.0;\n si.barycentrics = vec2f(0.0);\n\n`;\n const primaryGuides = /* wgsl */ `${inputHooks?.primarySurface ?? ''} if (${hasShadowCatcher ? /* wgsl */ `si.hit && ma.materialType == SHADOW_CATCHER` : /* wgsl */ `false`}) {\n (*outAux).isShadowCatcher = true;\n // The composed denoising input includes this receiver's shadow. Supply\n // neutral reflectance and its shading normal, independent of visibility.\n (*outAux).albedo = vec3f(1.0);\n (*outAux).normal = select(si.normal, -si.normal, dot(si.normal, ray.d) > 0.0);\n var path: Path;\n path.ray = ray;\n path.fromEye = true;\n (*outAux).shadowCounts = sampleShadowCatcher(si, 0, &path);\n // RGB stores primary catcher coverage. The shadow travels in the\n // auxiliary counts, out of reach of color clamping.\n return vec4f(1.0);\n }\n else {\n if (HAS_BEVEL && rtUniform.bevel != 0) {\n if (ma.bevel > 0.0) {\n si.normal = getBevelNormal(si, ma.bevel);\n }\n }\n\n // Store a clean first-hit fallback. Glass retains this stable guide;\n // other delta paths may replace it at their first non-delta hit.\n if (si.hit && (ma.materialType == STANDARD || ma.materialType == UNLIT)) {\n (*outAux).albedo = select(ma.baseColor, vec3f(1.0), ma.transmissionWeight > 0.5);\n (*outAux).normal = select(\n si.normal,\n -si.normal,\n dot(si.normal, si.position - cameraUniform.viewInv[3].xyz) > 0.0\n );\n\n // Do not replace a glass guide with a randomly selected downstream\n // refraction/reflection hit; OIDN auxiliary features must stay clean.\n si.albedoSampled = ma.transmissionWeight > 0.5;\n }\n\n (*outAux).isShadowCatcher = false;\n\n\n }\n`;\n const source = /* wgsl */ `\n\n${commonWgsl}\n\n${rayTraceCoreWgsl({ hasSceneAlpha, hasAnisotropyDirections, hasCoatNormals, lightCapabilities, caustics: enabled || lightTracing || photonMapping })}\n\n${uvProjectionWgsl}\n\n${createMaterialBufferWgsl({ hasSceneAlpha, hasAnisotropyDirections, hasCoatNormals,\n caustics: enabled || lightTracing || photonMapping,\n hasTransmission: (materialCapabilities & MATERIAL_SHADER_CAPABILITIES.transmission) !== 0,\n hasMetalness: (materialCapabilities & MATERIAL_SHADER_CAPABILITIES.metal) !== 0 })}\n\n${createIntersectionWgsl({ hasDisplacement, blasLayoutMask, hasAnisotropyDirections, hasCoatNormals, sceneIntersections })}\n${randWgsl}\n${randomWgsl}\n${envMapWgsl}\n\n${samplingWgsl}\n${spectrumWgsl}\n${createOpenPbrBsdfWgsl(hasAnisotropyDirections, hasCoatNormals)}\n${sampleLightWgsl}${inputHooks?.declarations ?? ''}\n\n// Index of the path vertex currently being shaded.\nvar<private> bounceIndex: u32 = 0u;\n\n// Number of light candidates resampled for each NEE estimate. Interactive\n// preview caps the budget at two.\nfn neeCandidateCount() -> u32 {\n return u32(max(rtUniform.directLightCandidates, 1));\n}\n\n// Hash-based random number used to select among light candidates. It is\n// independent of the path sample stream so the candidate count does not\n// change the dimensions consumed by later bounces.\nfn lightCandidateRandom(salt: u32) -> f32 {\n // Reservoir replacement needs the full [0, 1) interval: sample1D's EPS\n // remapping makes small positive replacement probabilities unreachable.\n // Preserve the legacy sequential RNG draw in the random sampling mode so\n // subsequent path samples retain their stream; selection uses the hash.\n if (SAMPLING_PATTERN == 2) { _ = rand(); }\n let pixelID = samplingPixel.x + samplingPixel.y * u32(rtUniform.outputSize.x);\n let key = sampleSeed(sampleSeed(pixelID, pixelSampleIndex), rtUniform.sequenceSeed);\n let dimension = sampleSeed(samplingBounce, salt) ^ 0xa511e9b3u;\n return f32(sampleHash(key ^ dimension) >> 8u) * (1.0 / 16777216.0);\n}\n\n// MIS weight of a light-sampled contribution. With several resampled\n// candidates the NEE technique is drawn neeCandidateCount() times.\nfn neeMISWeight(lightPdf: f32, scatteringPdf: f32) -> f32 {\n let candidateCount = neeCandidateCount();\n if (candidateCount <= 1u) {\n return sampling_power_heuristic(lightPdf, scatteringPdf);\n }\n let weightedLightPdf = f32(candidateCount) * lightPdf;\n return weightedLightPdf /\n max(weightedLightPdf + scatteringPdf, 1e-20);\n}\n\n// MIS weight of a BSDF-sampled ray that hits a light.\nfn bsdfMISWeight(scatteringPdf: f32, lightPdf: f32) -> f32 {\n let candidateCount = neeCandidateCount();\n if (candidateCount <= 1u) {\n return sampling_power_heuristic(scatteringPdf, lightPdf);\n }\n let weightedLightPdf = f32(candidateCount) * lightPdf;\n return scatteringPdf /\n max(scatteringPdf + weightedLightPdf, 1e-20);\n}\n\n${enabled || lightTracing || photonMapping ? causticPartitionWGSL(enabled, lightTracing,photonMapping,partitionDepth,debugGroup>=30,photonMaxDepth) : ''}${research?.declarations ?? ''}${photonMapping?`const CAUSTIC_LIGHT_TRACING=${lightTracing};\nconst CAUSTIC_MNEE=${enabled};\nconst CAUSTIC_MNEE_MAX_DEPTH=${partitionDepth}u;\nconst CAUSTIC_PHOTON_MAX_DEPTH=${photonMaxDepth}u;\n`+photonQuerySource(debugGroup, !lightTracing && (options.photonDirectSubset ?? true)):''}${createSampleMaterialWgsl(hasAnisotropyDirections, hasCoatNormals, enabled || lightTracing || photonMapping, research?.contribution,photonMapping)}\n${sampleShadowCatcherWgsl}\n\n// Output\n@group(3) @binding(0) var<storage, read_write> outColor: array<vec4f>;\n@group(3) @binding(1) var<storage, read_write> outNormal: array<vec4f>;\n@group(3) @binding(2) var<storage, read_write> outAlbedo: array<vec4f>;\n\n${shadowCatcherAccumulationWGSL}\n@group(3) @binding(3) var<storage, read_write> outShadowCatcher: array<ShadowCatcherAccumulation>;\n\n\nfn accumulateNormalOrVariance(\n pixelIndex: u32,\n radiance: vec3f,\n normal: vec3f\n) {\n if (rtUniform.varianceMoments != 0) {\n let value = dot(radiance, luminance);\n outNormal[pixelIndex] += vec4f(value * value, 0.0, 0.0, 1.0);\n }\n else {\n outNormal[pixelIndex] += vec4f(normal, 1.0);\n }\n}\n\n// Moments of the per-sample value the built-in denoiser compares, used to\n// estimate the variance of each pixel's mean.\nfn accumulateDenoiseMoments(pixelIndex: u32, value: f32) {\n outShadowCatcher[pixelIndex].moments += vec4f(value, value * value, 0.0, 0.0);\n}\n\n\nstruct RayTraceAuxiliary {\n // Denoising guides and auxiliary pixel classification.\n albedo: vec3f,\n isShadowCatcher: bool,\n normal: vec3f,\n isBlank: bool,\n // Shadow-catcher hits: occluded and total light sample counts.\n shadowCounts: vec2f,\n}\n\n${backgroundWgsl}fn sampleSpectralSceneBackground(ray: Ray, channel: i32) -> vec4f {\n let bg = sampleSceneBackground(ray);\n if (SPECTRAL) {\n return vec4f(sampling_spectral_rgb_filter(bg.rgb, channel), bg.a);\n }\n return bg;\n}\n\n// Split shading from hit completion so primary and secondary rays can share\n// one traversal/material-load call site in the common RGB integrator.\nfn shadeVertex(uv: vec2f, path: ptr<function, Path>, depth: ptr<function, i32>, si: ptr<function, SurfaceInteraction>, ma: ptr<function, OpenPbrResolvedMaterial>, outAux: ptr<function, RayTraceAuxiliary>, sampleIgnoredOut: ptr<function, bool>) {\n *sampleIgnoredOut = false;\n samplingBounce = u32(max(*depth, 0));\n let matType = (*ma).materialType;\n let isShadowCatcher = ${hasShadowCatcher ? /* wgsl */ `matType == SHADOW_CATCHER` : /* wgsl */ `false`};\n if (isShadowCatcher) {\n if ((*path).fromEye) {\n (*si).hit = false;\n }\n else {\n // The catcher only contributes to the separate shadow mask. Secondary\n // rays must see the environment behind it instead of its default white\n // material, otherwise it appears in reflections.\n if (USE_ENVMAP) {\n if ((*path).isNextRefraction) {\n // A camera-visible transmitted ray can hit the invisible catcher\n // before the grounded skybox. Preserve the displayed projection.\n let bg = sampleSpectralSceneBackground(\n (*path).ray,\n (*si).spectralChannel\n );\n let backgroundMisWeight = select(bsdfMISWeight((*path).scatteringPdf,\n envMapPdf(environmentUv((*path).ray.d)) * rtUniform.envLightPmf),\n 1.0, (*path).scatteringIsDelta);\n (*path).li += backgroundMisWeight * (*path).beta * bg.rgb * bg.a;\n (*path).alpha = max((*path).alpha, bg.a);\n }\n else {\n let envUv = environmentUv((*path).ray.d);\n var emission = textureLinear(envMap, envUv).rgb;\n if (SPECTRAL) {\n emission = sampling_spectral_rgb_filter(emission, (*si).spectralChannel);\n }\n (*path).li += (*path).beta * emission *\n bsdfMISWeight((*path).scatteringPdf, envMapPdf(envUv));\n }\n }\n else {\n let bg = sampleSpectralSceneBackground(\n (*path).ray,\n (*si).spectralChannel\n );\n (*path).li += (*path).beta * bg.rgb * bg.a;\n }\n (*path).abort = true;\n return;\n }\n }\n if (!(*si).hit) {\n // The spectral integrator traces red, green, and blue paths separately.\n // Split background radiance by wavelength as well so the three paths\n // reconstruct the skybox instead of adding three full RGB copies.\n let bg = sampleSpectralSceneBackground(\n (*path).ray,\n (*si).spectralChannel\n );\n if ((*path).fromEye) {\n // Sample from background if first ray not hit anything.\n (*path).li = (*path).beta * bg.rgb * bg.a;\n (*path).alpha = bg.a;\n // Only treat as blank pixels when the background is transparent\n (*outAux).isBlank = bg.a < 0.001 ||\n (USE_BACKGROUND && !USE_BACKGROUND_MAP);\n }\n else {\n let cam = initCameraRay(uv);\n let towardsCam = dot(cam.d, (*path).ray.d) > 0.0;\n if (rtUniform.transmissiveOutputAlphaMin < 1.0) {\n if (towardsCam) {\n (*path).alpha = max((*path).alpha, bg.a);\n } else {\n (*path).alpha = 1.0;\n }\n }\n if (!USE_ENVMAP) {\n // A display background is not an illumination environment, but a\n // visible secondary ray (e.g. through glass) still resolves it.\n if (towardsCam) {\n // This is a terminal BSDF-hit contribution, not a replacement for\n // NEE accumulated at earlier vertices on the same path.\n (*path).li += (*path).beta *\n bsdfMISWeight((*path).scatteringPdf, 1.0) * bg.rgb;\n }\n }\n }\n (*path).abort = true;\n return;\n }\n\n\n if ((matType == STANDARD || matType == UNLIT)) {\n let alpha = clamp((*ma).alpha, 0.0, 1.0);\n \n if (alpha < 1.0) {\n // Alpha is stochastic coverage. Keep accepted surface radiance at full\n // weight instead of multiplying it by alpha a second time.\n if (alpha <= 0.0 || hashedSample(3u) > alpha) {\n (*sampleIgnoredOut) = true;\n }\n }\n if (!(*sampleIgnoredOut) && rtUniform.transmissiveOutputAlphaMin < 1.0) {\n // A selected stochastic coverage sample is fully covered. Ignored\n // samples leave output alpha untouched and continue to the back layer.\n let outputAlpha = max(1.0 - (*ma).transmissionWeight, 1.0 - pow(1.0 - rtUniform.transmissiveOutputAlphaMin, f32(*depth)));\n (*path).alpha = max(outputAlpha, (*path).alpha);\n }\n if (!(*sampleIgnoredOut)) {\n bounceIndex = u32(max(*depth, 0));\n SampleMaterial(si, ma, path);\n }\n else {\n // Alpha-null traversal does not consume the receiver event. The first\n // accepted surface behind it is still the receiver for NEE/AOVs.\n (*path).ray.o = (*si).position + (*path).ray.d * EPS;\n }\n }\n\n if (!(*si).albedoSampled && !(*sampleIgnoredOut)) {\n // Select denoising guides.\n if ((matType == STANDARD || matType == UNLIT)) {\n if (!((*ma).transmissionWeight > 0.5 ||\n ((*ma).specularRoughness <= 0.02 && (*ma).baseMetalness > 0.98))) {\n (*outAux).albedo = (*ma).baseColor;\n (*outAux).normal = select(\n (*si).normal,\n -(*si).normal,\n dot(\n (*si).normal,\n (*si).position - cameraUniform.viewInv[3].xyz\n ) > 0.0\n );\n (*si).albedoSampled = true;\n }\n // TODO pick albedo and normal on the surface if ray is too deep and can't hit any rough or opaque surfaces.\n if (*depth > 4) {\n (*si).albedoSampled = true;\n }\n }\n }\n\n // Russian roulette with throughput compensation.\n if (USE_RR) {\n if (*depth >= RR_MAX && !(*sampleIgnoredOut)) {\n let rouletteBeta = (*path).beta;\n // Max-component survival (pbrt-v4) avoids over-amplifying saturated paths.\n let maxComponent = max(max(rouletteBeta.x, rouletteBeta.y), rouletteBeta.z);\n let q: f32 = max(0.0, 1.0 - maxComponent);\n if (sample1D(bounceDim(RUSSIAN_ROULETTE)) < q) {\n // TODO we force use alpha 1.0 here.\n // To avoid the glass pixels is transparent even if it's on an opaque object.\n (*path).alpha = 1.0;\n (*path).abort = true;\n }\n else {\n (*path).beta /= 1.0 - q;\n }\n }\n }\n if ((*path).abort) {\n return;\n }\n\n\n}\n\nfn completeVertex(path: ptr<function, Path>, depth: ptr<function, i32>, si: ptr<function, SurfaceInteraction>, ma: ptr<function, OpenPbrResolvedMaterial>, sampleIgnored: bool) {\n if (SPECTRAL) {\n // Is glass\n if ((*ma).transmissionWeight > 0.001) {\n (*ma).specularIor = sampling_cauchy_ior(\n (*si).wavelength,\n (*ma).specularIor,\n (*ma).transmissionDispersionScale,\n (*ma).dispersionAbbeNumber\n );\n }\n }\n\n if (sampleIgnored) {\n return;\n }\n\n // Use light if hit the light\n var lightSample: LightSampleRec;\n let isAlphaOccluded = hashedSample(3u) < (*ma).alpha;\n for (var i = 0; i < rtUniform.lightsCount; i++) {\n // Fetch light Data\n let light: Light = analyticLights[i];\n let d = intersectLight((*path).ray, light, &lightSample);\n lightSample.pdf *= light.selectionPmf;\n let occluded = (*si).hit && d > distance((*si).position, (*path).ray.o);\n if (d != INF && !(occluded && isAlphaOccluded)) {\n let misWeight = bsdfMISWeight((*path).scatteringPdf, lightSample.pdf);\n if (SPECTRAL) {\n lightSample.emission = sampling_spectral_rgb_filter(lightSample.emission, (*si).spectralChannel);\n }\n ${enabled || lightTracing || photonMapping ? 'if (!causticHitOwned(*path, true, true)) { causticHotRecord(*path,misWeight*lightSample.emission,2u); ' : ''}(*path).li += misWeight * (*path).beta * lightSample.emission;${enabled || lightTracing || photonMapping ? ' }' : ''}\n (*path).alpha = 1.0;\n (*path).abort = true;\n }\n }\n\n if (USE_ENVMAP) {\n if (!((*si).hit && isAlphaOccluded)) {${lightTracing||photonMapping ? /* wgsl */ `\n if(causticHitOwned(*path,false,true)){(*path).abort=true;return;}` : ''}\n if ((*path).isNextRefraction) {\n // Camera-visible transmission follows the displayed background,\n // including grounded projection. Other rays use the raw environment\n // map as the physically sampled lighting source.\n let bg = sampleSpectralSceneBackground(\n (*path).ray,\n (*si).spectralChannel\n );\n let backgroundMisWeight = select(bsdfMISWeight((*path).scatteringPdf,\n envMapPdf(environmentUv((*path).ray.d)) * rtUniform.envLightPmf),\n 1.0, (*path).scatteringIsDelta);\n (*path).li += backgroundMisWeight * (*path).beta * bg.rgb * bg.a;\n (*path).alpha = max((*path).alpha, bg.a);\n (*path).abort = true;\n return;\n }\n let _uv = environmentUv((*path).ray.d);\n var emission = textureLinear(envMap, _uv).rgb;\n let misWeight = bsdfMISWeight(\n (*path).scatteringPdf,\n envMapPdf(_uv) * rtUniform.envLightPmf\n );\n if (SPECTRAL) {\n emission = sampling_spectral_rgb_filter(emission, (*si).spectralChannel);\n }\n // TODO force the min alpha to be 1 when showing envmap as background\n if (!USE_BACKGROUND) {\n (*path).alpha = 1.0;\n }\n (*path).li += (*path).beta * emission * misWeight;\n (*path).abort = true;\n }\n }\n\n *depth = *depth + 1;\n}\n\nfn initCameraRay(coord: vec2f) -> Ray {\n let origin = cameraUniform.viewProjectionInv * vec4f(coord * 2.0 - 1.0, -1.0, 1.0);\n let center = cameraUniform.viewProjectionInv * vec4f(coord * 2.0 - 1.0, 1.0, 1.0);\n var cam: Ray;\n initRay(&cam, origin.xyz / origin.w, normalize(center.xyz * origin.w - origin.xyz * center.w));\n\n return cam;\n}\n\n\nfn bevelCubicSample(radius: f32, xi: f32) -> vec2f {\n var x = 0.25;\n for (var iteration = 0; iteration < 10; iteration++) {\n let x2 = x * x;\n let x3 = x2 * x;\n let oneMinusX = 1.0 - x;\n let value = 10.0 * x2 - 20.0 * x3 + 15.0 * x2 * x2 -\n 4.0 * x2 * x3 - xi;\n let derivative = 20.0 * x * oneMinusX * oneMinusX * oneMinusX;\n if (abs(value) < 1e-6 || derivative == 0.0) { break; }\n x = clamp(x - value / derivative, 0.0, 1.0);\n }\n let diskRadius = x * radius;\n return vec2f(diskRadius, sqrt(max(radius * radius - diskRadius * diskRadius, 0.0)));\n}\n\nfn bevelCubicPdf(radius: f32, distanceToSource: f32) -> f32 {\n if (distanceToSource >= radius) { return 0.0; }\n let falloff = radius - distanceToSource;\n return falloff * falloff * falloff;\n}\n\n// Shader bevel. Usage: docs/materials.md (Renderer extensions)\nfn getBevelNormal(source: SurfaceInteraction, radius: f32) -> vec3f {\n var geometricNormal = source.faceNormal;\n if (dot(geometricNormal, source.vertexNormal) < 0.0) {\n geometricNormal = -geometricNormal;\n }\n var tangent: vec3f;\n var bitangent: vec3f;\n sampling_orthonormal_basis(geometricNormal, &tangent, &bitangent);\n var normalSum = vec3f(0.0);\n\n for (var sampleIndex = 0; sampleIndex < BEVEL_SAMPLES; sampleIndex++) {\n let bevelRnd = sample2D(CAMERA_BEVEL_BASE + u32(sampleIndex));\n var axisSample = bevelRnd.x;\n let radialSample = bevelRnd.y;\n var diskNormal = geometricNormal;\n var diskTangent = tangent;\n var diskBitangent = bitangent;\n var pickPdfNormal = 0.5;\n var pickPdfTangent = 0.25;\n var pickPdfBitangent = 0.25;\n if (axisSample < 0.5) {\n axisSample *= 2.0;\n }\n else if (axisSample < 0.75) {\n let swapAxis = diskNormal;\n diskNormal = diskTangent;\n diskTangent = swapAxis;\n pickPdfNormal = 0.25;\n pickPdfTangent = 0.5;\n axisSample = (axisSample - 0.5) * 4.0;\n }\n else {\n let swapAxis = diskNormal;\n diskNormal = diskBitangent;\n diskBitangent = swapAxis;\n pickPdfNormal = 0.25;\n pickPdfBitangent = 0.5;\n axisSample = (axisSample - 0.75) * 4.0;\n }\n\n let diskSample = bevelCubicSample(radius, radialSample);\n let phi = TWOPI * axisSample;\n let diskOffset = diskSample.x *\n (cos(phi) * diskTangent + sin(phi) * diskBitangent);\n var ray: Ray;\n initRay(\n &ray,\n source.position + diskNormal * diskSample.y + diskOffset,\n -diskNormal\n );\n var hit: SurfaceInteraction;\n intersectSceneBevel(\n ray,\n &hit,\n source.instanceIndex,\n max(2.0 * diskSample.y, EPS)\n );\n if (!hit.hit) { continue; }\n\n let pdfNormal = pickPdfNormal * abs(dot(diskNormal, hit.faceNormal));\n let pdfTangent = pickPdfTangent * abs(dot(diskTangent, hit.faceNormal));\n let pdfBitangent = pickPdfBitangent * abs(dot(diskBitangent, hit.faceNormal));\n var weight = pdfNormal / max(\n pdfNormal * pdfNormal + pdfTangent * pdfTangent +\n pdfBitangent * pdfBitangent,\n 1e-8\n );\n weight *= bevelCubicPdf(radius, distance(hit.position, source.position)) /\n max(bevelCubicPdf(radius, diskSample.x), 1e-8);\n normalSum += weight * hit.vertexNormal;\n }\n\n if (dot(normalSum, normalSum) <= 1e-12) { return source.normal; }\n let bevelVertexNormal = normalize(normalSum);\n let result = source.normal + bevelVertexNormal - source.vertexNormal;\n if (dot(result, result) > 1e-12) { return normalize(result); }\n return source.normal;\n}\n\nfn integrateOnePathSample(uv: vec2f, si: ptr<function, SurfaceInteraction>, ray: Ray, ma: ptr<function, OpenPbrResolvedMaterial>, outAux: ptr<function, RayTraceAuxiliary>, initialQuery: bool) -> vec4f {\n var path: Path;\n path.ray = ray;\n path.li = vec3f(0.0);\n path.alpha = select(1.0, 0.0, rtUniform.transmissiveOutputAlphaMin < 1.0);\n path.beta = vec3f(1.0);\n path.fromEye = true;\n path.scatteringPdf = 0.0;\n path.scatteringIsDelta = false;\n path.isNextRefraction = false;\n path.abort = false;\n\n let bounceLimit = max(rtUniform.bounces, 0);\n let eventLimit = min(bounceLimit * 3, ${MAX_PATH_EVENTS});\n var depth = 0;\n var sampleIgnored = false;\n var isNotTransparent = false;\n // Back branches and subsequent bounces share one query/material call site.\n // Keep wavelength/branch loops separate so GPU lanes reconverge between\n // paths. Include the final query, which the legacy bounce completed before\n // testing its event budget. Even a zero-bounce back branch resolves guides.\n for (var event = 0; event <= eventLimit; event++) {\n if ((event != 0) | initialQuery) {\n intersectScene(path.ray, si, depth, event != 0, path.isNextRefraction);\n getMaterial(ma, (*si).materialIndex, (*si).uv, (*si).uv1, (*si).instanceIndex, (*si).vertexColor);\n }\n if (event == 0) {\n if (initialQuery & (*si).hit & ((*ma).materialType == STANDARD)) {\n (*outAux).albedo = select((*ma).baseColor, vec3f(1.0), (*ma).transmissionWeight > 0.5);\n (*outAux).normal = select((*si).normal, -(*si).normal,\n dot((*si).normal, (*si).position - cameraUniform.viewInv[3].xyz) > 0.0);\n (*si).albedoSampled = (*ma).transmissionWeight > 0.5;\n }\n isNotTransparent = ((*ma).transmissionWeight < 0.001) & ((*ma).subsurfaceWeight < 0.001) & ((*ma).alpha > 0.99);\n } else {\n completeVertex(&path, &depth, si, ma, sampleIgnored);\n }\n if (isNotTransparent & (event > bounceLimit)) { path.abort = true; }\n if (path.abort | (event >= eventLimit)) { break; }\n shadeVertex(uv, &path, &depth, si, ma, outAux, &sampleIgnored);\n if (path.abort) { break; }\n }\n\n return vec4f(path.li, path.alpha);\n}\n\nfn integrateOnePath(uv: vec2f, si: ptr<function, SurfaceInteraction>, ray: Ray, ma: ptr<function, OpenPbrResolvedMaterial>, outAux: ptr<function, RayTraceAuxiliary>) -> vec4f {\n ${deterministicPrimaryAlpha ? /* wgsl */ `let coverage = clamp((*ma).alpha, 0.0, 1.0);\n // glTF BLEND is source-over opacity, not a physical glass boundary. Split\n // the primary ray so reflected radiance is attenuated by alpha every sample\n // while the complementary branch continues straight to the scene behind it.\n // Secondary alpha hits remain stochastic to avoid exponential path growth.\n let split = (*si).hit & (((*ma).materialType == STANDARD) | ((*ma).materialType == UNLIT)) & (coverage < 1.0);\n var branchRay = ray;\n var branchSi = *si;\n var branchMa = *ma;\n var branchAux = *outAux;\n if (split) {\n branchRay.o = (*si).position + ray.d * (EPS * 0.02);\n branchSi.albedoSampled = false;\n }\n var back: vec4f;\n var backAux: RayTraceAuxiliary;\n // One path graph for opaque, back and front branches. The guard is fixed at\n // four; a dynamic bound keeps DXC from cloning that graph for each branch.\n let branchCount = select(1u, rtUniform.loopUnrollGuard >> 1u, split & (coverage > 0.0));\n for (var branch = 0u; branch < branchCount; branch++) {\n let result = integrateOnePathSample(uv, &branchSi, branchRay, &branchMa, &branchAux, split & (branch == 0u));\n if (!split | (coverage <= 0.0)) { *outAux = branchAux; return result; }\n if (branch == 0u) {\n back = result; backAux = branchAux;\n branchSi = *si; branchMa = *ma; branchMa.alpha = 1.0;\n branchRay = ray; branchAux = *outAux;\n } else {\n (*outAux).albedo = mix(backAux.albedo, branchAux.albedo, coverage);\n let blendedNormal = mix(backAux.normal, branchAux.normal, coverage);\n (*outAux).normal = select(vec3f(0.0), normalize(blendedNormal), dot(blendedNormal, blendedNormal) > 1e-12);\n (*outAux).isBlank = backAux.isBlank & branchAux.isBlank;\n (*outAux).isShadowCatcher = backAux.isShadowCatcher & branchAux.isShadowCatcher;\n (*outAux).shadowCounts = mix(backAux.shadowCounts, branchAux.shadowCounts, coverage);\n return mix(back, result, coverage);\n }\n }\n return vec4f(0.0);` : /* wgsl */ `return integrateOnePathSample(uv, si, ray, ma, outAux, false);`}\n}\n\n// Path tracing integrator as described in\n// http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Path_Tracing.html#\n${spectral === false && deterministicPrimaryAlpha ? /* wgsl */ `fn integrator(uv: vec2f, ray: Ray, outAux: ptr<function, RayTraceAuxiliary>) -> vec4f {\n${primaryVariables}\n var primarySi: SurfaceInteraction;\n var primaryMa: OpenPbrResolvedMaterial;\n var primaryAux: RayTraceAuxiliary;\n var primaryReady = false;\n var query = true;\n var split = false;\n var coverage = 1.0;\n var front = false;\n var back: vec4f;\n var backAux: RayTraceAuxiliary;\n var path: Path; path.ray = ray;\n path.alpha = select(1.0, 0.0, rtUniform.transmissiveOutputAlphaMin < 1.0);\n path.beta = vec3f(1.0); path.fromEye = true;\n let bounceLimit = max(rtUniform.bounces, 0);\n let eventLimit = min(bounceLimit * 3, ${MAX_PATH_EVENTS});\n var event = 0; var depth = 0; var sampleIgnored = false;\n var isNotTransparent = false;\n // Share one query/material graph across the primary, back and front paths.\n // Complete the back path before the front path to preserve random streams.\n for (var step = 0; step < (${MAX_PATH_EVENTS} + 1) * 2 + 2; step++) {\n if (query) {\n intersectScene(path.ray, &si, depth, event != 0, path.isNextRefraction);\n getMaterial(&ma, si.materialIndex, si.uv, si.uv1, si.instanceIndex, si.vertexColor);\n }\n query = true;\n if (!primaryReady) {\n${primaryGuides}\n primarySi = si; primaryMa = ma; primaryAux = *outAux;\n primaryReady = true;\n coverage = clamp(ma.alpha, 0.0, 1.0);\n split = si.hit & ((ma.materialType == STANDARD) | (ma.materialType == UNLIT)) & (coverage < 1.0);\n if (split) {\n path.ray.o = si.position + ray.d * (EPS * 0.02);\n si.albedoSampled = false;\n continue;\n }\n } else if ((event == 0) & !front & split) {\n if (si.hit & (ma.materialType == STANDARD)) {\n (*outAux).albedo = select(ma.baseColor, vec3f(1.0), ma.transmissionWeight > 0.5);\n (*outAux).normal = select(si.normal, -si.normal,\n dot(si.normal, si.position - cameraUniform.viewInv[3].xyz) > 0.0);\n si.albedoSampled = ma.transmissionWeight > 0.5;\n }\n }\n if (event == 0) {\n isNotTransparent = (ma.transmissionWeight < 0.001) & (ma.subsurfaceWeight < 0.001) & (ma.alpha > 0.99);\n } else {\n completeVertex(&path, &depth, &si, &ma, sampleIgnored);\n }\n if (isNotTransparent & (event > bounceLimit)) { path.abort = true; }\n if (!path.abort & (event < eventLimit)) {\n shadeVertex(uv, &path, &depth, &si, &ma, outAux, &sampleIgnored);\n }\n if (path.abort | (event >= eventLimit)) {\n let result = vec4f(path.li, path.alpha);\n if (!split | (coverage <= 0.0)) { return result; }\n if (front) {\n (*outAux).albedo = mix(backAux.albedo, (*outAux).albedo, coverage);\n let blendedNormal = mix(backAux.normal, (*outAux).normal, coverage);\n (*outAux).normal = select(vec3f(0.0), normalize(blendedNormal), dot(blendedNormal, blendedNormal) > 1e-12);\n (*outAux).isBlank = backAux.isBlank & (*outAux).isBlank;\n (*outAux).isShadowCatcher = backAux.isShadowCatcher & (*outAux).isShadowCatcher;\n (*outAux).shadowCounts = mix(backAux.shadowCounts, (*outAux).shadowCounts, coverage);\n return mix(back, result, coverage);\n }\n back = result; backAux = *outAux;\n front = true; query = false; event = 0; depth = 0; sampleIgnored = false;\n si = primarySi; ma = primaryMa; ma.alpha = 1.0; *outAux = primaryAux;\n path = Path(); path.ray = ray; path.beta = vec3f(1.0); path.fromEye = true;\n path.alpha = select(1.0, 0.0, rtUniform.transmissiveOutputAlphaMin < 1.0);\n } else { event++; }\n }\n return vec4f(0.0);\n}\n` : spectral === false && !deterministicPrimaryAlpha ? /* wgsl */ `fn integrator(uv: vec2f, ray: Ray, outAux: ptr<function, RayTraceAuxiliary>) -> vec4f {\n${primaryVariables}\n var path: Path;\n path.ray = ray;\n path.alpha = select(1.0, 0.0, rtUniform.transmissiveOutputAlphaMin < 1.0);\n path.beta = vec3f(1.0);\n path.fromEye = true;\n let bounceLimit = max(rtUniform.bounces, 0);\n let eventLimit = min(bounceLimit * 3, ${MAX_PATH_EVENTS});\n var isNotTransparent = false;\n var depth = 0;\n var sampleIgnored = false;\n // The extra iteration completes the last ray query, as the legacy bounce\n // did before testing the event budget. Queries consume the preceding bounce's\n // random stream; shading advances samplingBounce only after hit completion.\n for (var event = 0; event <= eventLimit; event++) {\n intersectScene(path.ray, &si, depth, event != 0, path.isNextRefraction);\n getMaterial(&ma, si.materialIndex, si.uv, si.uv1, si.instanceIndex, si.vertexColor);\n if (event == 0) {\n${primaryGuides}\n isNotTransparent = ma.transmissionWeight < 0.001 && ma.subsurfaceWeight < 0.001 && ma.alpha > 0.99;\n } else {\n completeVertex(&path, &depth, &si, &ma, sampleIgnored);\n }\n if (isNotTransparent && event > bounceLimit) { path.abort = true; }\n if (path.abort || event >= eventLimit) { break; }\n shadeVertex(uv, &path, &depth, &si, &ma, outAux, &sampleIgnored);\n if (path.abort) { break; }\n }\n return vec4f(path.li, path.alpha);\n}\n` : spectral === true && !deterministicPrimaryAlpha ? /* wgsl */ `\nfn integrator(uv: vec2f, ray: Ray, outAux: ptr<function, RayTraceAuxiliary>) -> vec4f {\n${primaryVariables}\n var primarySi: SurfaceInteraction;\n var primaryMa: OpenPbrResolvedMaterial;\n var primaryReady = false;\n var path: Path; path.ray = ray;\n let bounceLimit = max(rtUniform.bounces, 0);\n let eventLimit = min(bounceLimit * 3, ${MAX_PATH_EVENTS});\n var depth = 0; var event = 0; var sampleIgnored = false;\n var isNotTransparent = false;\n var wavelengthIndex = 0;\n let singleWavelength = rtUniform.dispersionWavelengths == 1;\n var dispersivePrimary = false;\n var heroWavelengthIndex = 0;\n var wavelengthSample = 0.0;\n var wavelengthRngState: vec4u;\n var wavelengthHashedCounter = 0u;\n var wavelengthAux: RayTraceAuxiliary;\n var color = vec3f(0.0); var alpha = 0.0;\n var accumulatedGuideAlbedo = vec3f(0.0);\n var accumulatedGuideNormal = vec3f(0.0);\n var allBlank = true;\n // Flatten the wavelength/path loops to share primary and secondary queries.\n // Each of the three paths uses at most MAX_PATH_EVENTS + 1 iterations,\n // including completion of its final query. The primary hit is reused.\n let stepLimit = min((${MAX_PATH_EVENTS} + 1) * 3, i32(rtUniform.loopUnrollGuard * ${Math.ceil((MAX_PATH_EVENTS + 1) * 3 / 4)}u));\n for (var step = 0; step < stepLimit; step++) {\n if (!primaryReady | ((event != 0) & !path.abort)) {\n intersectScene(path.ray, &si, depth, event != 0, path.isNextRefraction);\n getMaterial(&ma, si.materialIndex, si.uv, si.uv1, si.instanceIndex, si.vertexColor);\n }\n if (event == 0) {\n if (!primaryReady) {\n${primaryGuides}\n primarySi = si; primaryMa = ma; primaryReady = true;\n dispersivePrimary = si.hit & (ma.transmissionWeight > 0.001) & (ma.transmissionDispersionScale > 0.001);\n wavelengthSample = sample1D(CAMERA_WAVELENGTH);\n if (singleWavelength) {\n heroWavelengthIndex = min(i32(floor(wavelengthSample * 3.0)), 2);\n wavelengthSample = fract(wavelengthSample * 3.0);\n }\n wavelengthRngState = s0; wavelengthHashedCounter = hashedCounter;\n }\n si = primarySi; ma = primaryMa; wavelengthAux = *outAux;\n if (!singleWavelength) {\n s0 = wavelengthRngState; hashedCounter = wavelengthHashedCounter; samplingBounce = 0u;\n wavelengthAux.isBlank = false; wavelengthAux.isShadowCatcher = false;\n }\n si.wavelength = 0.0; si.spectralChannel = 0;\n if (!singleWavelength | dispersivePrimary) {\n let channel = select(wavelengthIndex, heroWavelengthIndex, singleWavelength);\n si.spectralChannel = channel + 1;\n si.wavelength = spectrumCameraWavelength(u32(channel), wavelengthSample);\n ma.specularIor = sampling_cauchy_ior(si.wavelength, ma.specularIor,\n ma.transmissionDispersionScale, ma.dispersionAbbeNumber);\n }\n path = Path(); path.ray = ray; path.beta = vec3f(1.0); path.fromEye = true;\n path.alpha = select(1.0, 0.0, rtUniform.transmissiveOutputAlphaMin < 1.0);\n depth = 0; sampleIgnored = false;\n isNotTransparent = (ma.transmissionWeight < 0.001) & (ma.subsurfaceWeight < 0.001) & (ma.alpha > 0.99);\n } else if (!path.abort) {\n completeVertex(&path, &depth, &si, &ma, sampleIgnored);\n }\n if (isNotTransparent & (event > bounceLimit)) { path.abort = true; }\n if (!path.abort & (event < eventLimit)) {\n shadeVertex(uv, &path, &depth, &si, &ma, &wavelengthAux, &sampleIgnored);\n }\n if (path.abort | (event >= eventLimit)) {\n let result = vec4f(path.li, path.alpha);\n if (singleWavelength) {\n *outAux = wavelengthAux;\n let scale = select(1.0, 3.0, dispersivePrimary);\n return vec4f(result.rgb * scale, result.a);\n }\n color += result.rgb; alpha = max(alpha, result.a);\n accumulatedGuideAlbedo += wavelengthAux.albedo;\n accumulatedGuideNormal += wavelengthAux.normal;\n allBlank = allBlank & wavelengthAux.isBlank;\n wavelengthIndex++;\n if (wavelengthIndex >= 3) {\n (*outAux).albedo = accumulatedGuideAlbedo / 3.0;\n (*outAux).normal = accumulatedGuideNormal / 3.0;\n (*outAux).isBlank = allBlank;\n return vec4f(color, alpha);\n }\n event = 0;\n } else { event++; }\n }\n return vec4f(0.0);\n}\n` : /* wgsl */ `fn integrator(uv: vec2f, ray: Ray, outAux: ptr<function, RayTraceAuxiliary>) -> vec4f {\n${primaryVariables}\n intersectScene(ray, &si, 0, false, false);\n getMaterial(&ma, si.materialIndex, si.uv, si.uv1, si.instanceIndex, si.vertexColor);\n${primaryGuides}\n if (SPECTRAL) {\n // Keep one source-level integrateOnePath call for both interactive\n // preview and offline RGB splitting. A runtime loop bound prevents the\n // WGSL compiler from cloning the full path graph three times while the\n // uniform still selects the exact existing preview/offline behavior.\n var colAccum = vec3f(0.0);\n var alpha = 0.0;\n var accumulatedGuideAlbedo = vec3f(0.0);\n var accumulatedGuideNormal = vec3f(0.0);\n var allBlank = true;\n var si2 = si;\n var ma2 = ma;\n var wavelengthSample = sample1D(CAMERA_WAVELENGTH);\n let wavelengthRngState = s0;\n let wavelengthHashedCounter = hashedCounter;\n let singleWavelength = rtUniform.dispersionWavelengths == 1;\n let dispersivePrimary = si.hit &&\n ma.transmissionWeight > 0.001 &&\n ma.transmissionDispersionScale > 0.001;\n let wavelengthCount = select(3, 1, singleWavelength);\n var heroWavelengthIndex = 0;\n if (singleWavelength) {\n // Stochastic hero wavelength: one RGB channel per pixel keeps the\n // preview spectral at one-third of the full three-path cost.\n heroWavelengthIndex = min(i32(floor(wavelengthSample * 3.0)), 2);\n wavelengthSample = fract(wavelengthSample * 3.0);\n }\n for (var i = 0; i < wavelengthCount; i++) {\n // Common random numbers keep Monte Carlo error correlated between\n // RGB wavelengths. This removes much of the colored variance that\n // otherwise turns into dirty chromatic patches after OIDN.\n if (!singleWavelength) {\n s0 = wavelengthRngState;\n hashedCounter = wavelengthHashedCounter;\n samplingBounce = 0u;\n }\n si.wavelength = 0.0;\n si.spectralChannel = 0;\n if (!singleWavelength || dispersivePrimary) {\n let channel = select(i, heroWavelengthIndex, singleWavelength);\n si.spectralChannel = channel + 1;\n si.wavelength = spectrumCameraWavelength(u32(channel), wavelengthSample);\n ma.specularIor = sampling_cauchy_ior(\n si.wavelength,\n ma.specularIor,\n ma.transmissionDispersionScale,\n ma.dispersionAbbeNumber\n );\n }\n var wavelengthAux: RayTraceAuxiliary;\n if (singleWavelength) {\n wavelengthAux = *outAux;\n } else {\n wavelengthAux.albedo = (*outAux).albedo;\n wavelengthAux.normal = (*outAux).normal;\n wavelengthAux.isBlank = false;\n wavelengthAux.isShadowCatcher = false;\n }\n let res = integrateOnePath(uv, &si, ray, &ma, &wavelengthAux);\n if (singleWavelength) {\n *outAux = wavelengthAux;\n let scale = select(1.0, 3.0, dispersivePrimary);\n return vec4f(res.rgb * scale, res.a);\n }\n colAccum += res.rgb;\n alpha = max(alpha, res.a);\n accumulatedGuideAlbedo += wavelengthAux.albedo;\n accumulatedGuideNormal += wavelengthAux.normal;\n allBlank = allBlank && wavelengthAux.isBlank;\n si = si2;\n ma = ma2;\n }\n (*outAux).albedo = accumulatedGuideAlbedo / 3.0;\n (*outAux).normal = accumulatedGuideNormal / 3.0;\n (*outAux).isBlank = allBlank;\n return vec4f(colAccum, alpha);\n }\n else {\n return integrateOnePath(uv, &si, ray, &ma, outAux);\n }\n}\n`}\n\nfn sampleCircle(ip: vec2f) -> vec2f {\n let p = 2.0 * ip - 1.0;\n let greater = abs(p.x) > abs(p.y);\n let r = select(p.y, p.x, greater);\n let theta = select(PI * (0.5 - 0.25 * p.x / p.y), 0.25 * PI * p.y / p.x, greater);\n return r * vec2f(cos(theta), sin(theta));\n}\n\n\n@compute @workgroup_size(${workgroupSize}, ${workgroupSize}, 1)\nfn main(@builtin(global_invocation_id) globalId: vec3u) {\n let outputSize = rtUniform.outputSize;\n let scaleOffset = rtUniform.scaleOffset;\n var jitter = rtUniform.jitter;\n // TODO if outputSize is not multiple of 8?\n${pathDiagnostic ? ` if (globalId.y >= mtPathParameters.y) { return; }\n let pathId = vec2u(globalId.x, globalId.y + mtPathParameters.x);\n` : ''} let coord = ${pathDiagnostic ? 'pathId' : 'globalId.xy'} * vec2u(scaleOffset.xy) + vec2u(scaleOffset.zw);\n if (coord.x >= u32(outputSize.x) || coord.y >= u32(outputSize.y)) {\n return;\n }\n let idx = coord.x + coord.y * u32(outputSize.x);\n\n if (\n rtUniform.adaptiveSampling != 0 &&\n outNormal[idx].a >= textureLoad(\n adaptiveSampleTargets,\n vec2i(coord),\n 0\n ).r\n ) {\n return;\n }\n\n // Match WebGL's rasterized v_Texcoord, whose sample is at the center of\n // each pixel. Keeping the same origin is material relevant: a half-pixel\n // shift moves a narrow metal highlight across several pixels.\n let uv = (vec2<f32>(f32(coord.x), f32(coord.y)) + vec2f(0.5)) / outputSize;\n\n samplingPixel = coord;\n pixelSampleIndex = u32(outNormal[idx].a) + rtUniform.sampleSequenceOffset;\n samplingBounce = 0u; hashedCounter = 0u;\n if (${inputHooks?.uniformJitter ? 'false' : 'SAMPLING_PATTERN == 0 || SAMPLING_PATTERN == 1'}) {\n jitter = (sample2D(CAMERA_PIXEL) - 0.5) / outputSize;\n }\n rng_initialize(coord, i32(pixelSampleIndex ^ sampleHash(rtUniform.sequenceSeed)));\n var cam = initCameraRay(uv + jitter);\n\n if (cameraUniform.apertureRadius > 0.0) {\n // Thin lens: the focus plane is perpendicular to the view direction.\n let eye = cameraUniform.viewInv[3].xyz;\n let forward = -normalize(cameraUniform.viewInv[2].xyz);\n let focusPoint = eye + cam.d * (cameraUniform.focusDistance / dot(cam.d, forward));\n let lensPoint = cameraUniform.apertureRadius * sampleCircle(sample2D(CAMERA_LENS));\n let origin = (cameraUniform.viewInv * vec4f(lensPoint, 0.0, 1.0)).xyz;\n let dir = normalize(focusPoint - origin);\n initRay(&cam, origin, dir);\n }\n\n var outAux: RayTraceAuxiliary;\n outAux.albedo = vec3f(0.0);\n outAux.normal = vec3f(0.0);\n outAux.isBlank = false;\n outAux.isShadowCatcher = false;\n${pathDiagnostic ? ' mtPathBegin(coord);\\n' : ''}${inputHooks?.begin ?? ''} var liAndAlpha = integrator(uv + jitter, cam, &outAux);\n${inputHooks?.unclampedColor ?? ''}\n${(enabled||lightTracing||photonMapping) && debugGroup >= 0 ? /* wgsl */ ` liAndAlpha=${debugGroup>=50?`vec4f(photonQueryStats,1.0)`:debugGroup>=30?`vec4f(causticHot[${debugGroup-30}],1.0)`:research?.debugOutput ?? 'liAndAlpha'};\n` : ''} // Clamp at Integration Level to reduce fireflies.\n // TODO Path level configuration?\n if (rtUniform.clamping > 0.0) {\n let clamping = rtUniform.clamping;\n liAndAlpha = vec4(clamp(liAndAlpha.rgb, -vec3f(clamping), vec3f(clamping)), liAndAlpha.a);\n }\n if (!(liAndAlpha.x < INF && liAndAlpha.x > -EPS)) {\n liAndAlpha = vec4f(0.0, 0.0, 0.0, 1.0);\n }\n\n if (outAux.isShadowCatcher) {\n // The catcher is invisible, so the main color at this pixel must still\n // contain whatever the camera ray would have seen behind it.\n let backgroundSample = sampleSceneBackground(cam);\n let backgroundHit = select(0.0, 1.0, backgroundSample.a >= 0.01);\n let backgroundPremultiplied = vec4f(backgroundSample.rgb * backgroundSample.a, backgroundSample.a);\n // Keep the background contribution separate from geometry at silhouettes.\n // mask: catcher coverage, background alpha, background hits. The resolve\n // pass derives shadow opacity from the occluded/total sample counts in moments.zw.\n outShadowCatcher[idx].mask += vec4f(1.0, backgroundSample.a, backgroundHit, 0.0);\n outShadowCatcher[idx].moments += vec4f(0.0, 0.0, outAux.shadowCounts);\n outShadowCatcher[idx].background += backgroundPremultiplied;\n outColor[idx] += backgroundPremultiplied;\n outAlbedo[idx] += vec4f(outAux.albedo, backgroundHit);\n accumulateNormalOrVariance(idx, backgroundSample.rgb, outAux.normal);\n // The composed catcher is the background darkened by the shadow. Over a\n // transparent background the shadow is only its opacity.\n let counts = outAux.shadowCounts;\n let visibility = 1.0 - select(0.0, counts.x / counts.y, counts.y > 0.0);\n accumulateDenoiseMoments(idx, visibility * select(\n 1.0, dot(backgroundSample.rgb, luminance), backgroundSample.a >= 0.01\n ));\n }\n else {\n if (HAS_SHADOW_CATCHER && outAux.isBlank && USE_BACKGROUND && !USE_BACKGROUND_MAP && rtUniform.backgroundColor.a >= 0.99999) {\n // A primary miss sees the same opaque display backdrop as a catcher\n // hit, but must never receive its shadow. Keep that radiance and alpha\n // out of the geometry layer before hit-count normalization/tone mapping.\n // Red/blue still count only actual catcher hits; green counts all\n // background alpha removed from the foreground during normalization.\n outShadowCatcher[idx].background += liAndAlpha;\n outShadowCatcher[idx].mask.g += liAndAlpha.a;\n }\n outColor[idx] += liAndAlpha;\n outAlbedo[idx] += vec4f(outAux.albedo, select(1.0, 0.0, outAux.isBlank));\n accumulateNormalOrVariance(idx, liAndAlpha.rgb, outAux.normal);\n accumulateDenoiseMoments(idx, dot(liAndAlpha.rgb, luminance));\n }\n${pathDiagnostic ? ' mtPathEnd(outAux.normal, outAux.albedo);\\n' : ''}${inputHooks?.end ?? ''}}\n`;\n // The query entry point shares the exact BVH and material implementation.\n // It has no effect on the beauty entry point or its bindings.\n // Gate optional material layers on scene-level feature flags so drivers can\n // skip the unused BSDF lobes.\n let specializedSource = source + depthQueryWGSL + reflectanceWGSL + aovTraceWGSL;\n const backgroundBits: Record<string, number> = {\n USE_BACKGROUND: 1,\n USE_BACKGROUND_MAP: 2,\n USE_GROUNDED_BACKGROUND: 4\n };\n specializedSource = specializedSource.replace(\n /\\b(?:USE_BACKGROUND|USE_BACKGROUND_MAP|USE_GROUNDED_BACKGROUND)\\b/g,\n (name) => `((rtUniform.backgroundFlags & ${backgroundBits[name]}) != 0)`\n );\n specializedSource = specializedSource.replace(\n new RegExp(`\\\\b(?:${Object.keys(WGPU_SCENE_FEATURE_BITS).join('|')})\\\\b`, 'g'),\n // Material weights and absent texture indices already disable retained\n // material capabilities. Environment presence also changes background/MIS\n // behavior, so its active flag must remain dynamic when support is retained.\n name => sceneCapabilities === undefined || dynamicEnvironment && name === 'USE_ENVMAP' && (sceneCapabilities & WGPU_SCENE_FEATURE_BITS[name]) !== 0\n ? /* wgsl */ `((rtUniform.sceneFeatureFlags & ${WGPU_SCENE_FEATURE_BITS[name]}u) != 0u)`\n : (sceneCapabilities & WGPU_SCENE_FEATURE_BITS[name]) !== 0 ? /* wgsl */ `true` : /* wgsl */ `false`\n );\n return specializedSource;\n}\n"]}
|
|
@@ -42,7 +42,7 @@ export default class MaterialState {
|
|
|
42
42
|
transmissionScatter: ColorRGB;
|
|
43
43
|
/** Henyey-Greenstein anisotropy of the medium, -1 to 1. Default `0`. */
|
|
44
44
|
transmissionScatterAnisotropy: number;
|
|
45
|
-
/**
|
|
45
|
+
/** Nonnegative dispersion strength; values above `1` are supported. `0` disables wavelength-dependent refraction. Default `0`. */
|
|
46
46
|
transmissionDispersionScale: number;
|
|
47
47
|
/** Abbe number controlling dispersion. Default `20`. */
|
|
48
48
|
transmissionDispersionAbbeNumber: number;
|
|
@@ -104,7 +104,7 @@ export default class MaterialState {
|
|
|
104
104
|
* @deprecated Use geometryThinWalled.
|
|
105
105
|
*/
|
|
106
106
|
thinWalled: boolean;
|
|
107
|
-
/** Disables geometric back-face culling
|
|
107
|
+
/** Disables geometric back-face culling and permits back-face emission. Default `false`. */
|
|
108
108
|
doubleSided: boolean;
|
|
109
109
|
/** Base color texture, multiplied with `baseColor` using Texture color encoding. Default `undefined`. */
|
|
110
110
|
map?: TextureState;
|
|
@@ -47,7 +47,7 @@ export default class MaterialState {
|
|
|
47
47
|
transmissionScatter = materialDefault('transmissionScatter');
|
|
48
48
|
/** Henyey-Greenstein anisotropy of the medium, -1 to 1. Default `0`. */
|
|
49
49
|
transmissionScatterAnisotropy = materialDefault('transmissionScatterAnisotropy');
|
|
50
|
-
/**
|
|
50
|
+
/** Nonnegative dispersion strength; values above `1` are supported. `0` disables wavelength-dependent refraction. Default `0`. */
|
|
51
51
|
transmissionDispersionScale = materialDefault('transmissionDispersionScale');
|
|
52
52
|
/** Abbe number controlling dispersion. Default `20`. */
|
|
53
53
|
transmissionDispersionAbbeNumber = materialDefault('transmissionDispersionAbbeNumber');
|
|
@@ -114,7 +114,7 @@ export default class MaterialState {
|
|
|
114
114
|
* @deprecated Use geometryThinWalled.
|
|
115
115
|
*/
|
|
116
116
|
thinWalled = materialDefault('thinWalled');
|
|
117
|
-
/** Disables geometric back-face culling
|
|
117
|
+
/** Disables geometric back-face culling and permits back-face emission. Default `false`. */
|
|
118
118
|
doubleSided = materialDefault('doubleSided');
|
|
119
119
|
// Textures
|
|
120
120
|
/** Base color texture, multiplied with `baseColor` using Texture color encoding. Default `undefined`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MaterialState.js","sourceRoot":"","sources":["../../src/scene/MaterialState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKlE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAO1E,iFAAiF;AACjF,MAAM,MAAM,GAAG,IAAI,OAAO,EAAyB,CAAC;AACpD,MAAM,CAAC,OAAO,OAAO,aAAa;IAChC,kIAAkI;IAClI,IAAI,KAAK,KAAa,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC;IACjD,IAAI,KAAK,CAAC,KAAa,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,4EAA4E;IAC5E,YAAY,SAA8B,EAAE;QAC1C,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QAClC,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO;IACP,yJAAyJ;IACzJ,UAAU,GAAW,eAAe,CAAC,YAAY,CAAC,CAAC;IACnD,sDAAsD;IACtD,SAAS,GAAa,eAAe,CAAC,WAAW,CAAC,CAAC;IACnD,mEAAmE;IACnE,oBAAoB,GAAW,eAAe,CAAC,sBAAsB,CAAC,CAAC;IACvE,8CAA8C;IAC9C,aAAa,GAAW,eAAe,CAAC,eAAe,CAAC,CAAC;IAEzD,WAAW;IACX,mGAAmG;IACnG,cAAc,GAAW,eAAe,CAAC,gBAAgB,CAAC,CAAC;IAC3D,wFAAwF;IACxF,aAAa,GAAa,eAAe,CAAC,eAAe,CAAC,CAAC;IAC3D,mIAAmI;IACnI,iBAAiB,GAAW,eAAe,CAAC,mBAAmB,CAAC,CAAC;IACjE,0CAA0C;IAC1C,WAAW,GAAW,eAAe,CAAC,aAAa,CAAC,CAAC;IACrD;;;OAGG;IACH,kBAAkB,GAAW,eAAe,CAAC,oBAAoB,CAAC,CAAC;IAEnE,eAAe;IACf,6EAA6E;IAC7E,kBAAkB,GAAW,eAAe,CAAC,oBAAoB,CAAC,CAAC;IACnE,0FAA0F;IAC1F,iBAAiB,GAAa,eAAe,CAAC,mBAAmB,CAAC,CAAC;IACnE,4FAA4F;IAC5F,iBAAiB,GAAW,eAAe,CAAC,mBAAmB,CAAC,CAAC;IACjE,+EAA+E;IAC/E,mBAAmB,GAAa,eAAe,CAAC,qBAAqB,CAAC,CAAC;IACvE,wEAAwE;IACxE,6BAA6B,GAAW,eAAe,CAAC,+BAA+B,CAAC,CAAC;IACzF,0EAA0E;IAC1E,2BAA2B,GAAW,eAAe,CAAC,6BAA6B,CAAC,CAAC;IACrF,wDAAwD;IACxD,gCAAgC,GAAW,eAAe,CAAC,kCAAkC,CAAC,CAAC;IAE/F,aAAa;IACb,iDAAiD;IACjD,gBAAgB,GAAW,eAAe,CAAC,kBAAkB,CAAC,CAAC;IAC/D,mDAAmD;IACnD,eAAe,GAAa,eAAe,CAAC,iBAAiB,CAAC,CAAC;IAC/D,kDAAkD;IAClD,gBAAgB,GAAW,eAAe,CAAC,kBAAkB,CAAC,CAAC;IAC/D,8EAA8E;IAC9E,qBAAqB,GAAa,eAAe,CAAC,uBAAuB,CAAC,CAAC;IAC3E;;;OAGG;IACH,oBAAoB,GAAW,eAAe,CAAC,sBAAsB,CAAC,CAAC;IAEvE,eAAe;IACf,8CAA8C;IAC9C,UAAU,GAAW,eAAe,CAAC,YAAY,CAAC,CAAC;IACnD,gGAAgG;IAChG,SAAS,GAAa,eAAe,CAAC,WAAW,CAAC,CAAC;IACnD,8EAA8E;IAC9E,aAAa,GAAW,eAAe,CAAC,eAAe,CAAC,CAAC;IAEzD,OAAO;IACP,sCAAsC;IACtC,UAAU,GAAW,eAAe,CAAC,YAAY,CAAC,CAAC;IACnD,8HAA8H;IAC9H,SAAS,GAAa,eAAe,CAAC,WAAW,CAAC,CAAC;IACnD,mCAAmC;IACnC,aAAa,GAAW,eAAe,CAAC,eAAe,CAAC,CAAC;IACzD;;;OAGG;IACH,cAAc,GAAW,eAAe,CAAC,gBAAgB,CAAC,CAAC;IAC3D,+CAA+C;IAC/C,OAAO,GAAW,eAAe,CAAC,SAAS,CAAC,CAAC;IAC7C,mHAAmH;IACnH,aAAa,GAAW,eAAe,CAAC,eAAe,CAAC,CAAC;IAEzD,YAAY;IACZ,gEAAgE;IAChE,cAAc,GAAW,eAAe,CAAC,gBAAgB,CAAC,CAAC;IAC3D,kGAAkG;IAClG,iBAAiB,GAAW,eAAe,CAAC,mBAAmB,CAAC,CAAC;IACjE,+CAA+C;IAC/C,WAAW,GAAW,eAAe,CAAC,aAAa,CAAC,CAAC;IAErD,mIAAmI;IACnI,wBAAwB,GAAW,eAAe,CAAC,0BAA0B,CAAC,CAAC;IAC/E,0DAA0D;IAC1D,kBAAkB,GAAuB,eAAe,CAAC,oBAAoB,CAAC,CAAC;IAE/E,wBAAwB;IACxB,2CAA2C;IAC3C,aAAa,GAAa,eAAe,CAAC,eAAe,CAAC,CAAC;IAC3D,kEAAkE;IAClE,iBAAiB,GAAW,eAAe,CAAC,mBAAmB,CAAC,CAAC;IACjE;;;OAGG;IACH,OAAO,GAAW,eAAe,CAAC,SAAS,CAAC,CAAC;IAC7C;;;OAGG;IACH,UAAU,GAAY,eAAe,CAAC,YAAY,CAAC,CAAC;IACpD,+EAA+E;IAC/E,WAAW,GAAY,eAAe,CAAC,aAAa,CAAC,CAAC;IAEtD,WAAW;IACX,yGAAyG;IACzG,GAAG,CAAgB;IACnB,yGAAyG;IACzG,QAAQ,CAAgB;IACxB;;;;OAIG;IACH,SAAS,GAAsB,eAAe,CAAC,WAAW,CAAC,CAAC;IAC5D,oFAAoF;IACpF,WAAW,GAAW,eAAe,CAAC,aAAa,CAAC,CAAC;IACrD,6EAA6E;IAC7E,WAAW,CAAgB;IAC3B,qDAAqD;IACrD,SAAS,CAAgB;IACzB,sDAAsD;IACtD,WAAW,GAAS,eAAe,CAAC,aAAa,CAAC,CAAC;IACnD,sFAAsF;IACtF,oBAAoB,CAAgB;IACpC,wFAAwF;IACxF,YAAY,CAAgB;IAC5B,uFAAuF;IACvF,YAAY,CAAgB;IAC5B,2FAA2F;IAC3F,eAAe,CAAgB;IAC/B,iHAAiH;IACjH,WAAW,CAAgB;IAE3B,4EAA4E;IAC5E,aAAa,CAAgB;IAC7B,iFAAiF;IACjF,gBAAgB,CAAgB;IAChC,8FAA8F;IAC9F,YAAY,CAAgB;IAC5B,iFAAiF;IACjF,gBAAgB,CAAgB;IAChC,kFAAkF;IAClF,iBAAiB,CAAgB;IACjC,kGAAkG;IAClG,gBAAgB,CAAgB;IAGhC,mLAAmL;IACnL,uBAAuB,GAAuB,SAAS,CAAC;IACxD,8GAA8G;IAC9G,aAAa,CAAgB;IAC7B,wJAAwJ;IACxJ,aAAa,CAAgB;IAC7B,kFAAkF;IAClF,eAAe,GAAS,eAAe,CAAC,iBAAiB,CAAC,CAAC;IAC3D,uHAAuH;IACvH,0BAA0B,GAAW,CAAC,CAAC;IACvC,wFAAwF;IACxF,sBAAsB,GAAW,CAAC,CAAC;IAEnC,uJAAuJ;IACvJ,IAAI,2BAA2B,KAAa,OAAO,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC7E,IAAI,2BAA2B,CAAC,KAAa,IAAI,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC,CAAC,CAAC;IAEnF,oEAAoE;IACpE,IAAI,uBAAuB,KAAa,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;IACrE,IAAI,uBAAuB,CAAC,KAAa,IAAI,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,CAAC,CAAC;IAE3E,gEAAgE;IAChE,IAAI,2BAA2B,KAAa,OAAO,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC/E,IAAI,2BAA2B,CAAC,KAAa,IAAI,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC,CAAC,CAAC;IAErF,8EAA8E;IAC9E,IAAI,eAAe,KAAa,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,IAAI,eAAe,CAAC,KAAa,IAAI,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC;IAE5D,sEAAsE;IACtE,IAAI,kBAAkB,KAAc,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAC7D,IAAI,kBAAkB,CAAC,KAAc,IAAI,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC;IAEnE,sBAAsB;IACtB;;;OAGG;IACH,aAAa,GAAY,eAAe,CAAC,eAAe,CAAC,CAAC;IAC1D,4FAA4F;IAC5F,KAAK,GAAY,eAAe,CAAC,OAAO,CAAC,CAAC;IAC1C,kGAAkG;IAClG,SAAS,GAAW,eAAe,CAAC,WAAW,CAAC,CAAC;IACjD,iFAAiF;IACjF,aAAa,GAAwC,eAAe,CAAC,eAAe,CAAC,CAAC;IACtF,gFAAgF;IAChF,YAAY,GAAiB,eAAe,CAAC,cAAc,CAAC,CAAC;IAC7D,2DAA2D;IAC3D,iBAAiB,GAAS,eAAe,CAAC,mBAAmB,CAAC,CAAC;IAE/D,eAAe;IACf,0EAA0E;IAC1E,eAAe,CAAgB;IAC/B,yEAAyE;IACzE,wBAAwB,GAAiB,eAAe,CAAC,0BAA0B,CAAC,CAAC;IACrF,kEAAkE;IAClE,6BAA6B,GAAW,eAAe,CAAC,+BAA+B,CAAC,CAAC;IACzF,uEAAuE;IACvE,iBAAiB,GAAW,eAAe,CAAC,mBAAmB,CAAC,CAAC;IACjE,iIAAiI;IACjI,gBAAgB,GAAW,eAAe,CAAC,kBAAkB,CAAC,CAAC;IAC/D,4GAA4G;IAC5G,oBAAoB,GAAW,eAAe,CAAC,sBAAsB,CAAC,CAAC;CAExE;AACD,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAI9E,MAAM,UAAU,wBAAwB,CACtC,QAAuB;IAEvB,IAAI,QAAQ,CAAC,SAAS,KAAK,MAAM;QAAE,OAAO,QAAQ,CAAC,SAAS,CAAC;IAC7D,IAAI,QAAQ,CAAC,WAAW,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAC5C,IACE,QAAQ,CAAC,OAAO,GAAG,CAAC;QACpB,QAAQ,CAAC,aAAa,KAAK,OAAO;QAClC,QAAQ,CAAC,QAAQ,EACjB,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["import { allocateAovId, validateAovId } from '../renderer/aov.js';\nimport type { Vec2 } from '../types.js';\nimport type { ColorRGB } from '../types.js';\nimport type TextureState from './TextureState.js';\nimport type { ThinFilmMapChannel } from './thinFilmParameters.js';\nimport { materialDefault, applyMaterialParams } from './openPbrSchema.js';\n\nexport type UVProjection = 'none' | 'triplanar' | 'spherical' | 'cylindrical';\nexport type MaterialAlphaMode = 'auto' | 'opaque' | 'mask' | 'blend';\n\nexport type { MaterialTextureName } from './materialTextureNames.js';\n\n// Shared material state; browser texture types are refined by the public facade.\nconst aovIds = new WeakMap<MaterialState, number>();\nexport default class MaterialState {\n /** Stable unsigned material ID, excluding 0xFFFFFFFF. Default process-unique monotonic ID; assign before committing the scene. */\n get aovId(): number { return aovIds.get(this)!; }\n set aovId(value: number) { aovIds.set(this, validateAovId(value)); }\n /** Creates a material. Omitted properties use their documented defaults. */\n constructor(params: MaterialStateParams = {}) {\n aovIds.set(this, allocateAovId());\n applyMaterialParams(this, params);\n if (params.aovId !== undefined) this.aovId = validateAovId(params.aovId);\n }\n\n // Base\n /** Multiplier of diffuse reflection and metallic normal-incidence color; does not scale dielectric specular, transmission or subsurface. Default `1`. */\n baseWeight: number = materialDefault('baseWeight');\n /** Diffuse/metal color. Default `[0.8, 0.8, 0.8]`. */\n baseColor: ColorRGB = materialDefault('baseColor');\n /** Oren-Nayar style roughness of the diffuse lobe. Default `0`. */\n baseDiffuseRoughness: number = materialDefault('baseDiffuseRoughness');\n /** `0` dielectric, `1` metal. Default `0`. */\n baseMetalness: number = materialDefault('baseMetalness');\n\n // Specular\n /** Dielectric specular strength (via IOR) and overall metallic Fresnel multiplier. Default `1`. */\n specularWeight: number = materialDefault('specularWeight');\n /** Dielectric specular tint and metallic F82 near-grazing tint. Default `[1, 1, 1]`. */\n specularColor: ColorRGB = materialDefault('specularColor');\n /** Microfacet roughness, `0` mirror to `1` rough; retains source perceptual roughness in `gltf` anisotropy mode. Default `0.3`. */\n specularRoughness: number = materialDefault('specularRoughness');\n /** Index of refraction. Default `1.5`. */\n specularIor: number = materialDefault('specularIor');\n /**\n * GGX anisotropy strength, 0 to 1 (requires tangents); native OpenPBR or source glTF strength according to specularAnisotropyModel. Default `0`.\n * @deprecated Use specularRoughnessAnisotropy.\n */\n specularAnisotropy: number = materialDefault('specularAnisotropy');\n\n // Transmission\n /** `1` makes the dielectric base fully transmissive (glass). Default `0`. */\n transmissionWeight: number = materialDefault('transmissionWeight');\n /** Transmission tint, or the medium color at `transmissionDepth`. Default `[1, 1, 1]`. */\n transmissionColor: ColorRGB = materialDefault('transmissionColor');\n /** Depth at which `transmissionColor` is reached; `0` tints at the surface. Default `0`. */\n transmissionDepth: number = materialDefault('transmissionDepth');\n /** Volume scattering color of the transmissive medium. Default `[0, 0, 0]`. */\n transmissionScatter: ColorRGB = materialDefault('transmissionScatter');\n /** Henyey-Greenstein anisotropy of the medium, -1 to 1. Default `0`. */\n transmissionScatterAnisotropy: number = materialDefault('transmissionScatterAnisotropy');\n /** Dispersion strength; `> 0` enables spectral rendering. Default `0`. */\n transmissionDispersionScale: number = materialDefault('transmissionDispersionScale');\n /** Abbe number controlling dispersion. Default `20`. */\n transmissionDispersionAbbeNumber: number = materialDefault('transmissionDispersionAbbeNumber');\n\n // Subsurface\n /** Subsurface scattering weight. Default `0`. */\n subsurfaceWeight: number = materialDefault('subsurfaceWeight');\n /** Subsurface color. Default `[0.8, 0.8, 0.8]`. */\n subsurfaceColor: ColorRGB = materialDefault('subsurfaceColor');\n /** Mean free path in scene units. Default `1`. */\n subsurfaceRadius: number = materialDefault('subsurfaceRadius');\n /** Per-channel multiplier of `subsurfaceRadius`. Default `[1, 0.5, 0.25]`. */\n subsurfaceRadiusScale: ColorRGB = materialDefault('subsurfaceRadiusScale');\n /**\n * Scattering anisotropy, -1 to 1. Default `0`.\n * @deprecated Use subsurfaceScatterAnisotropy.\n */\n subsurfaceAnisotropy: number = materialDefault('subsurfaceAnisotropy');\n\n // Fuzz (sheen)\n /** Fuzz (sheen) layer weight. Default `0`. */\n fuzzWeight: number = materialDefault('fuzzWeight');\n /** Fuzz reflection color; substrate attenuation uses white fuzz albedo. Default `[1, 1, 1]`. */\n fuzzColor: ColorRGB = materialDefault('fuzzColor');\n /** Fuzz roughness; imported from Three.js `sheenRoughness`. Default `0.5`. */\n fuzzRoughness: number = materialDefault('fuzzRoughness');\n\n // Coat\n /** Clear coat weight. Default `0`. */\n coatWeight: number = materialDefault('coatWeight');\n /** Squared coat transmittance at normal incidence; colors the base and emission, not coat reflection. Default `[1, 1, 1]`. */\n coatColor: ColorRGB = materialDefault('coatColor');\n /** Coat roughness. Default `0`. */\n coatRoughness: number = materialDefault('coatRoughness');\n /**\n * OpenPBR GGX coat anisotropy, 0 to 1; uses the base geometry tangent. Default `0`.\n * @deprecated Use coatRoughnessAnisotropy.\n */\n coatAnisotropy: number = materialDefault('coatAnisotropy');\n /** Coat index of refraction. Default `1.6`. */\n coatIor: number = materialDefault('coatIor');\n /** Internal-reflection darkening: 0 compensates darkening, 1 is physical; absorption remains at 0. Default `1`. */\n coatDarkening: number = materialDefault('coatDarkening');\n\n // Thin film\n /** Thin-film interference weight (iridescence). Default `0`. */\n thinFilmWeight: number = materialDefault('thinFilmWeight');\n /** Film thickness in micrometres; maximum endpoint when thinFilmMap is present. Default `0.5`. */\n thinFilmThickness: number = materialDefault('thinFilmThickness');\n /** Film index of refraction. Default `1.4`. */\n thinFilmIor: number = materialDefault('thinFilmIor');\n\n /** Thickness at a thinFilmMap sample of zero, in micrometres; ignored without a map. May exceed thinFilmThickness. Default `0`. */\n thinFilmThicknessMinimum: number = materialDefault('thinFilmThicknessMinimum');\n /** Linear channel sampled by thinFilmMap. Default `r`. */\n thinFilmMapChannel: ThinFilmMapChannel = materialDefault('thinFilmMapChannel');\n\n // Emission and geometry\n /** Emission color. Default `[1, 1, 1]`. */\n emissionColor: ColorRGB = materialDefault('emissionColor');\n /** Emitted radiance scale; `0` disables emission. Default `0`. */\n emissionLuminance: number = materialDefault('emissionLuminance');\n /**\n * Coverage, `1` opaque. See `alphaMode`. Default `1`.\n * @deprecated Use geometryOpacity.\n */\n opacity: number = materialDefault('opacity');\n /**\n * Treat the surface as an infinitely thin shell (leaves, paper). Default `false`.\n * @deprecated Use geometryThinWalled.\n */\n thinWalled: boolean = materialDefault('thinWalled');\n /** Disables geometric back-face culling for this material. Default `false`. */\n doubleSided: boolean = materialDefault('doubleSided');\n\n // Textures\n /** Base color texture, multiplied with `baseColor` using Texture color encoding. Default `undefined`. */\n map?: TextureState;\n /** Standalone opacity texture sampled from its green channel, matching Three.js. Default `undefined`. */\n alphaMap?: TextureState;\n /**\n * Coverage for surface and shadow rays: `opaque` ignores alpha inputs,\n * `mask` uses binary alpha testing, `blend` retains fractional coverage.\n * `auto` derives the mode from the alpha inputs. Default `auto`.\n */\n alphaMode: MaterialAlphaMode = materialDefault('alphaMode');\n /** Alpha-test threshold for `mask` mode; equality is fully covered. Default `0`. */\n alphaCutoff: number = materialDefault('alphaCutoff');\n /** Emission texture multiplied with `emissionColor`. Default `undefined`. */\n emissiveMap?: TextureState;\n /** Tangent-space normal map. Default `undefined`. */\n normalMap?: TextureState;\n /** Normal map strength per axis. Default `[1, 1]`. */\n normalScale: Vec2 = materialDefault('normalScale');\n /** glTF-compatible B=metalness, G=specular roughness texture. Default `undefined`. */\n metallicRoughnessMap?: TextureState;\n /** Standalone roughness texture sampled from its green channel. Default `undefined`. */\n roughnessMap?: TextureState;\n /** Standalone metalness texture sampled from its blue channel. Default `undefined`. */\n metalnessMap?: TextureState;\n /** Multiplies transmission weight using the texture's red channel. Default `undefined`. */\n transmissionMap?: TextureState;\n /** Linear thickness-range texture; R and zero minimum preserve multiplication semantics. Default `undefined`. */\n thinFilmMap?: TextureState;\n\n /** Multiplies coatWeight by the linear red channel. Default `undefined`. */\n coatWeightMap?: TextureState;\n /** Multiplies coatRoughness by the linear green channel. Default `undefined`. */\n coatRoughnessMap?: TextureState;\n /** Multiplies fuzzColor by RGB channels using Texture color encoding. Default `undefined`. */\n fuzzColorMap?: TextureState;\n /** Multiplies fuzzRoughness by the linear alpha channel. Default `undefined`. */\n fuzzRoughnessMap?: TextureState;\n /** Multiplies specularWeight by the linear alpha channel. Default `undefined`. */\n specularWeightMap?: TextureState;\n /** Multiplies specularColor by RGB channels using Texture color encoding. Default `undefined`. */\n specularColorMap?: TextureState;\n\n\n /** Source convention for base specular anisotropy. `gltf` converts textured roughness and strength before coat roughening; `openpbr` uses native parameters. Default `openpbr`. */\n specularAnisotropyModel: 'openpbr' | 'gltf' = 'openpbr';\n /** Linear RG tangent direction and B strength multiplier for the base specular layer. Default `undefined`. */\n anisotropyMap?: TextureState;\n /** Independent tangent-space coat normal in linear RGB. Without a map, coat uses the surface normal before base normal mapping. Default `undefined`. */\n coatNormalMap?: TextureState;\n /** XY strength of coatNormalMap; independent of normalScale. Default `[1, 1]`. */\n coatNormalScale: Vec2 = materialDefault('coatNormalScale');\n /** Base direction rotation in radians, counter-clockwise in tangent/bitangent space after RG decoding. Default `0`. */\n specularAnisotropyRotation: number = 0;\n /** Coat direction rotation in radians relative to the geometry tangent. Default `0`. */\n coatAnisotropyRotation: number = 0;\n\n /** GGX roughness anisotropy in [0, 1]; native OpenPBR or source glTF strength according to specularAnisotropyModel. Requires tangents. Default `0`. */\n get specularRoughnessAnisotropy(): number { return this.specularAnisotropy; }\n set specularRoughnessAnisotropy(value: number) { this.specularAnisotropy = value; }\n\n /** OpenPBR coat GGX roughness anisotropy in [0, 1]. Default `0`. */\n get coatRoughnessAnisotropy(): number { return this.coatAnisotropy; }\n set coatRoughnessAnisotropy(value: number) { this.coatAnisotropy = value; }\n\n /** Subsurface scattering anisotropy in [-1, 1]. Default `0`. */\n get subsurfaceScatterAnisotropy(): number { return this.subsurfaceAnisotropy; }\n set subsurfaceScatterAnisotropy(value: number) { this.subsurfaceAnisotropy = value; }\n\n /** Surface coverage; alphaMode controls testing and blending. Default `1`. */\n get geometryOpacity(): number { return this.opacity; }\n set geometryOpacity(value: number) { this.opacity = value; }\n\n /** Treat the surface as an infinitely thin shell. Default `false`. */\n get geometryThinWalled(): boolean { return this.thinWalled; }\n set geometryThinWalled(value: boolean) { this.thinWalled = value; }\n\n // Renderer extensions\n /**\n * Invisible ground that only receives shadows, composited over the\n * background. Default `false`.\n */\n shadowCatcher: boolean = materialDefault('shadowCatcher');\n /** Displays emission without scattering or illuminating other surfaces. Default `false`. */\n unlit: boolean = materialDefault('unlit');\n /** Radius of rounded-edge shading (shader bevel) in scene units. `0` disables it. Default `0`. */\n bevelSize: number = materialDefault('bevelSize');\n /** How map alpha is used: ignored, opacity or transmission. Default `opaque`. */\n mapAlphaUsage: 'opaque' | 'alpha' | 'transmission' = materialDefault('mapAlphaUsage');\n /** Procedural UV projection for textures without (good) UVs. Default `none`. */\n uvProjection: UVProjection = materialDefault('uvProjection');\n /** UV scale used with `uvProjection`. Default `[1, 1]`. */\n uvProjectionScale: Vec2 = materialDefault('uvProjectionScale');\n\n // Displacement\n /** Height texture (red channel) for displacement. Default `undefined`. */\n displacementMap?: TextureState;\n /** Procedural UV projection for the displacement map. Default `none`. */\n displacementUvProjection: UVProjection = materialDefault('displacementUvProjection');\n /** UV scale used with `displacementUvProjection`. Default `1`. */\n displacementUvProjectionScale: number = materialDefault('displacementUvProjectionScale');\n /** Height multiplier in mesh-local coordinate units. Default `0.1`. */\n displacementScale: number = materialDefault('displacementScale');\n /** Offset added after scaling the height map, along the normal in mesh-local units; requires a displacement map. Default `0`. */\n displacementBias: number = materialDefault('displacementBias');\n /** Ray-marching step in mesh-local units; 0 automatically uses abs(displacementScale) / 32. Default `0`. */\n displacementStepSize: number = materialDefault('displacementStepSize');\n\n}\nexport { materialAlphaModeConstants } from '../renderer/materialConstants.js';\n\nexport type ResolvedMaterialAlphaMode = Exclude<MaterialAlphaMode, 'auto'>;\n\nexport function resolveMaterialAlphaMode(\n material: MaterialState\n): ResolvedMaterialAlphaMode {\n if (material.alphaMode !== 'auto') return material.alphaMode;\n if (material.alphaCutoff > 0) return 'mask';\n if (\n material.opacity < 1 ||\n material.mapAlphaUsage === \"alpha\" ||\n material.alphaMap\n ) {\n return 'blend';\n }\n return 'opaque';\n}\n\nexport { materialKeys, materialTextureKeys, materialGeometryKeys, materialParamKeys, getMaterialHashKey, resetMaterialTextureId } from './openPbrSchema.js';\n\n/** Optional material properties; defaults are documented on MaterialState. */\nexport type MaterialStateParams = Partial<MaterialState>;\n"]}
|
|
1
|
+
{"version":3,"file":"MaterialState.js","sourceRoot":"","sources":["../../src/scene/MaterialState.ts"],"names":[],"mappings":"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","sourcesContent":["import { allocateAovId, validateAovId } from '../renderer/aov.js';\nimport type { Vec2 } from '../types.js';\nimport type { ColorRGB } from '../types.js';\nimport type TextureState from './TextureState.js';\nimport type { ThinFilmMapChannel } from './thinFilmParameters.js';\nimport { materialDefault, applyMaterialParams } from './openPbrSchema.js';\n\nexport type UVProjection = 'none' | 'triplanar' | 'spherical' | 'cylindrical';\nexport type MaterialAlphaMode = 'auto' | 'opaque' | 'mask' | 'blend';\n\nexport type { MaterialTextureName } from './materialTextureNames.js';\n\n// Shared material state; browser texture types are refined by the public facade.\nconst aovIds = new WeakMap<MaterialState, number>();\nexport default class MaterialState {\n /** Stable unsigned material ID, excluding 0xFFFFFFFF. Default process-unique monotonic ID; assign before committing the scene. */\n get aovId(): number { return aovIds.get(this)!; }\n set aovId(value: number) { aovIds.set(this, validateAovId(value)); }\n /** Creates a material. Omitted properties use their documented defaults. */\n constructor(params: MaterialStateParams = {}) {\n aovIds.set(this, allocateAovId());\n applyMaterialParams(this, params);\n if (params.aovId !== undefined) this.aovId = validateAovId(params.aovId);\n }\n\n // Base\n /** Multiplier of diffuse reflection and metallic normal-incidence color; does not scale dielectric specular, transmission or subsurface. Default `1`. */\n baseWeight: number = materialDefault('baseWeight');\n /** Diffuse/metal color. Default `[0.8, 0.8, 0.8]`. */\n baseColor: ColorRGB = materialDefault('baseColor');\n /** Oren-Nayar style roughness of the diffuse lobe. Default `0`. */\n baseDiffuseRoughness: number = materialDefault('baseDiffuseRoughness');\n /** `0` dielectric, `1` metal. Default `0`. */\n baseMetalness: number = materialDefault('baseMetalness');\n\n // Specular\n /** Dielectric specular strength (via IOR) and overall metallic Fresnel multiplier. Default `1`. */\n specularWeight: number = materialDefault('specularWeight');\n /** Dielectric specular tint and metallic F82 near-grazing tint. Default `[1, 1, 1]`. */\n specularColor: ColorRGB = materialDefault('specularColor');\n /** Microfacet roughness, `0` mirror to `1` rough; retains source perceptual roughness in `gltf` anisotropy mode. Default `0.3`. */\n specularRoughness: number = materialDefault('specularRoughness');\n /** Index of refraction. Default `1.5`. */\n specularIor: number = materialDefault('specularIor');\n /**\n * GGX anisotropy strength, 0 to 1 (requires tangents); native OpenPBR or source glTF strength according to specularAnisotropyModel. Default `0`.\n * @deprecated Use specularRoughnessAnisotropy.\n */\n specularAnisotropy: number = materialDefault('specularAnisotropy');\n\n // Transmission\n /** `1` makes the dielectric base fully transmissive (glass). Default `0`. */\n transmissionWeight: number = materialDefault('transmissionWeight');\n /** Transmission tint, or the medium color at `transmissionDepth`. Default `[1, 1, 1]`. */\n transmissionColor: ColorRGB = materialDefault('transmissionColor');\n /** Depth at which `transmissionColor` is reached; `0` tints at the surface. Default `0`. */\n transmissionDepth: number = materialDefault('transmissionDepth');\n /** Volume scattering color of the transmissive medium. Default `[0, 0, 0]`. */\n transmissionScatter: ColorRGB = materialDefault('transmissionScatter');\n /** Henyey-Greenstein anisotropy of the medium, -1 to 1. Default `0`. */\n transmissionScatterAnisotropy: number = materialDefault('transmissionScatterAnisotropy');\n /** Nonnegative dispersion strength; values above `1` are supported. `0` disables wavelength-dependent refraction. Default `0`. */\n transmissionDispersionScale: number = materialDefault('transmissionDispersionScale');\n /** Abbe number controlling dispersion. Default `20`. */\n transmissionDispersionAbbeNumber: number = materialDefault('transmissionDispersionAbbeNumber');\n\n // Subsurface\n /** Subsurface scattering weight. Default `0`. */\n subsurfaceWeight: number = materialDefault('subsurfaceWeight');\n /** Subsurface color. Default `[0.8, 0.8, 0.8]`. */\n subsurfaceColor: ColorRGB = materialDefault('subsurfaceColor');\n /** Mean free path in scene units. Default `1`. */\n subsurfaceRadius: number = materialDefault('subsurfaceRadius');\n /** Per-channel multiplier of `subsurfaceRadius`. Default `[1, 0.5, 0.25]`. */\n subsurfaceRadiusScale: ColorRGB = materialDefault('subsurfaceRadiusScale');\n /**\n * Scattering anisotropy, -1 to 1. Default `0`.\n * @deprecated Use subsurfaceScatterAnisotropy.\n */\n subsurfaceAnisotropy: number = materialDefault('subsurfaceAnisotropy');\n\n // Fuzz (sheen)\n /** Fuzz (sheen) layer weight. Default `0`. */\n fuzzWeight: number = materialDefault('fuzzWeight');\n /** Fuzz reflection color; substrate attenuation uses white fuzz albedo. Default `[1, 1, 1]`. */\n fuzzColor: ColorRGB = materialDefault('fuzzColor');\n /** Fuzz roughness; imported from Three.js `sheenRoughness`. Default `0.5`. */\n fuzzRoughness: number = materialDefault('fuzzRoughness');\n\n // Coat\n /** Clear coat weight. Default `0`. */\n coatWeight: number = materialDefault('coatWeight');\n /** Squared coat transmittance at normal incidence; colors the base and emission, not coat reflection. Default `[1, 1, 1]`. */\n coatColor: ColorRGB = materialDefault('coatColor');\n /** Coat roughness. Default `0`. */\n coatRoughness: number = materialDefault('coatRoughness');\n /**\n * OpenPBR GGX coat anisotropy, 0 to 1; uses the base geometry tangent. Default `0`.\n * @deprecated Use coatRoughnessAnisotropy.\n */\n coatAnisotropy: number = materialDefault('coatAnisotropy');\n /** Coat index of refraction. Default `1.6`. */\n coatIor: number = materialDefault('coatIor');\n /** Internal-reflection darkening: 0 compensates darkening, 1 is physical; absorption remains at 0. Default `1`. */\n coatDarkening: number = materialDefault('coatDarkening');\n\n // Thin film\n /** Thin-film interference weight (iridescence). Default `0`. */\n thinFilmWeight: number = materialDefault('thinFilmWeight');\n /** Film thickness in micrometres; maximum endpoint when thinFilmMap is present. Default `0.5`. */\n thinFilmThickness: number = materialDefault('thinFilmThickness');\n /** Film index of refraction. Default `1.4`. */\n thinFilmIor: number = materialDefault('thinFilmIor');\n\n /** Thickness at a thinFilmMap sample of zero, in micrometres; ignored without a map. May exceed thinFilmThickness. Default `0`. */\n thinFilmThicknessMinimum: number = materialDefault('thinFilmThicknessMinimum');\n /** Linear channel sampled by thinFilmMap. Default `r`. */\n thinFilmMapChannel: ThinFilmMapChannel = materialDefault('thinFilmMapChannel');\n\n // Emission and geometry\n /** Emission color. Default `[1, 1, 1]`. */\n emissionColor: ColorRGB = materialDefault('emissionColor');\n /** Emitted radiance scale; `0` disables emission. Default `0`. */\n emissionLuminance: number = materialDefault('emissionLuminance');\n /**\n * Coverage, `1` opaque. See `alphaMode`. Default `1`.\n * @deprecated Use geometryOpacity.\n */\n opacity: number = materialDefault('opacity');\n /**\n * Treat the surface as an infinitely thin shell (leaves, paper). Default `false`.\n * @deprecated Use geometryThinWalled.\n */\n thinWalled: boolean = materialDefault('thinWalled');\n /** Disables geometric back-face culling and permits back-face emission. Default `false`. */\n doubleSided: boolean = materialDefault('doubleSided');\n\n // Textures\n /** Base color texture, multiplied with `baseColor` using Texture color encoding. Default `undefined`. */\n map?: TextureState;\n /** Standalone opacity texture sampled from its green channel, matching Three.js. Default `undefined`. */\n alphaMap?: TextureState;\n /**\n * Coverage for surface and shadow rays: `opaque` ignores alpha inputs,\n * `mask` uses binary alpha testing, `blend` retains fractional coverage.\n * `auto` derives the mode from the alpha inputs. Default `auto`.\n */\n alphaMode: MaterialAlphaMode = materialDefault('alphaMode');\n /** Alpha-test threshold for `mask` mode; equality is fully covered. Default `0`. */\n alphaCutoff: number = materialDefault('alphaCutoff');\n /** Emission texture multiplied with `emissionColor`. Default `undefined`. */\n emissiveMap?: TextureState;\n /** Tangent-space normal map. Default `undefined`. */\n normalMap?: TextureState;\n /** Normal map strength per axis. Default `[1, 1]`. */\n normalScale: Vec2 = materialDefault('normalScale');\n /** glTF-compatible B=metalness, G=specular roughness texture. Default `undefined`. */\n metallicRoughnessMap?: TextureState;\n /** Standalone roughness texture sampled from its green channel. Default `undefined`. */\n roughnessMap?: TextureState;\n /** Standalone metalness texture sampled from its blue channel. Default `undefined`. */\n metalnessMap?: TextureState;\n /** Multiplies transmission weight using the texture's red channel. Default `undefined`. */\n transmissionMap?: TextureState;\n /** Linear thickness-range texture; R and zero minimum preserve multiplication semantics. Default `undefined`. */\n thinFilmMap?: TextureState;\n\n /** Multiplies coatWeight by the linear red channel. Default `undefined`. */\n coatWeightMap?: TextureState;\n /** Multiplies coatRoughness by the linear green channel. Default `undefined`. */\n coatRoughnessMap?: TextureState;\n /** Multiplies fuzzColor by RGB channels using Texture color encoding. Default `undefined`. */\n fuzzColorMap?: TextureState;\n /** Multiplies fuzzRoughness by the linear alpha channel. Default `undefined`. */\n fuzzRoughnessMap?: TextureState;\n /** Multiplies specularWeight by the linear alpha channel. Default `undefined`. */\n specularWeightMap?: TextureState;\n /** Multiplies specularColor by RGB channels using Texture color encoding. Default `undefined`. */\n specularColorMap?: TextureState;\n\n\n /** Source convention for base specular anisotropy. `gltf` converts textured roughness and strength before coat roughening; `openpbr` uses native parameters. Default `openpbr`. */\n specularAnisotropyModel: 'openpbr' | 'gltf' = 'openpbr';\n /** Linear RG tangent direction and B strength multiplier for the base specular layer. Default `undefined`. */\n anisotropyMap?: TextureState;\n /** Independent tangent-space coat normal in linear RGB. Without a map, coat uses the surface normal before base normal mapping. Default `undefined`. */\n coatNormalMap?: TextureState;\n /** XY strength of coatNormalMap; independent of normalScale. Default `[1, 1]`. */\n coatNormalScale: Vec2 = materialDefault('coatNormalScale');\n /** Base direction rotation in radians, counter-clockwise in tangent/bitangent space after RG decoding. Default `0`. */\n specularAnisotropyRotation: number = 0;\n /** Coat direction rotation in radians relative to the geometry tangent. Default `0`. */\n coatAnisotropyRotation: number = 0;\n\n /** GGX roughness anisotropy in [0, 1]; native OpenPBR or source glTF strength according to specularAnisotropyModel. Requires tangents. Default `0`. */\n get specularRoughnessAnisotropy(): number { return this.specularAnisotropy; }\n set specularRoughnessAnisotropy(value: number) { this.specularAnisotropy = value; }\n\n /** OpenPBR coat GGX roughness anisotropy in [0, 1]. Default `0`. */\n get coatRoughnessAnisotropy(): number { return this.coatAnisotropy; }\n set coatRoughnessAnisotropy(value: number) { this.coatAnisotropy = value; }\n\n /** Subsurface scattering anisotropy in [-1, 1]. Default `0`. */\n get subsurfaceScatterAnisotropy(): number { return this.subsurfaceAnisotropy; }\n set subsurfaceScatterAnisotropy(value: number) { this.subsurfaceAnisotropy = value; }\n\n /** Surface coverage; alphaMode controls testing and blending. Default `1`. */\n get geometryOpacity(): number { return this.opacity; }\n set geometryOpacity(value: number) { this.opacity = value; }\n\n /** Treat the surface as an infinitely thin shell. Default `false`. */\n get geometryThinWalled(): boolean { return this.thinWalled; }\n set geometryThinWalled(value: boolean) { this.thinWalled = value; }\n\n // Renderer extensions\n /**\n * Invisible ground that only receives shadows, composited over the\n * background. Default `false`.\n */\n shadowCatcher: boolean = materialDefault('shadowCatcher');\n /** Displays emission without scattering or illuminating other surfaces. Default `false`. */\n unlit: boolean = materialDefault('unlit');\n /** Radius of rounded-edge shading (shader bevel) in scene units. `0` disables it. Default `0`. */\n bevelSize: number = materialDefault('bevelSize');\n /** How map alpha is used: ignored, opacity or transmission. Default `opaque`. */\n mapAlphaUsage: 'opaque' | 'alpha' | 'transmission' = materialDefault('mapAlphaUsage');\n /** Procedural UV projection for textures without (good) UVs. Default `none`. */\n uvProjection: UVProjection = materialDefault('uvProjection');\n /** UV scale used with `uvProjection`. Default `[1, 1]`. */\n uvProjectionScale: Vec2 = materialDefault('uvProjectionScale');\n\n // Displacement\n /** Height texture (red channel) for displacement. Default `undefined`. */\n displacementMap?: TextureState;\n /** Procedural UV projection for the displacement map. Default `none`. */\n displacementUvProjection: UVProjection = materialDefault('displacementUvProjection');\n /** UV scale used with `displacementUvProjection`. Default `1`. */\n displacementUvProjectionScale: number = materialDefault('displacementUvProjectionScale');\n /** Height multiplier in mesh-local coordinate units. Default `0.1`. */\n displacementScale: number = materialDefault('displacementScale');\n /** Offset added after scaling the height map, along the normal in mesh-local units; requires a displacement map. Default `0`. */\n displacementBias: number = materialDefault('displacementBias');\n /** Ray-marching step in mesh-local units; 0 automatically uses abs(displacementScale) / 32. Default `0`. */\n displacementStepSize: number = materialDefault('displacementStepSize');\n\n}\nexport { materialAlphaModeConstants } from '../renderer/materialConstants.js';\n\nexport type ResolvedMaterialAlphaMode = Exclude<MaterialAlphaMode, 'auto'>;\n\nexport function resolveMaterialAlphaMode(\n material: MaterialState\n): ResolvedMaterialAlphaMode {\n if (material.alphaMode !== 'auto') return material.alphaMode;\n if (material.alphaCutoff > 0) return 'mask';\n if (\n material.opacity < 1 ||\n material.mapAlphaUsage === \"alpha\" ||\n material.alphaMap\n ) {\n return 'blend';\n }\n return 'opaque';\n}\n\nexport { materialKeys, materialTextureKeys, materialGeometryKeys, materialParamKeys, getMaterialHashKey, resetMaterialTextureId } from './openPbrSchema.js';\n\n/** Optional material properties; defaults are documented on MaterialState. */\nexport type MaterialStateParams = Partial<MaterialState>;\n"]}
|
package/lib/settings/render.d.ts
CHANGED
|
@@ -45,6 +45,12 @@ export declare const traceSchema: {
|
|
|
45
45
|
kind: "image";
|
|
46
46
|
backends: readonly ["webgpu", "webgl"];
|
|
47
47
|
};
|
|
48
|
+
caustics: {
|
|
49
|
+
key: "caustics";
|
|
50
|
+
default: undefined;
|
|
51
|
+
kind: "image";
|
|
52
|
+
backends: readonly ["webgpu", "webgl"];
|
|
53
|
+
};
|
|
48
54
|
directLightCandidates: {
|
|
49
55
|
key: "directLightCandidates";
|
|
50
56
|
default: number;
|
|
@@ -131,6 +137,11 @@ export declare const renderSchema: readonly [...({
|
|
|
131
137
|
default: 3;
|
|
132
138
|
kind: "image";
|
|
133
139
|
backends: readonly ["webgpu", "webgl"];
|
|
140
|
+
} | {
|
|
141
|
+
key: "caustics";
|
|
142
|
+
default: undefined;
|
|
143
|
+
kind: "image";
|
|
144
|
+
backends: readonly ["webgpu", "webgl"];
|
|
134
145
|
} | {
|
|
135
146
|
key: "directLightCandidates";
|
|
136
147
|
default: number;
|
package/lib/settings/render.js
CHANGED
|
@@ -9,6 +9,7 @@ export const traceSchema = {
|
|
|
9
9
|
bevel: { key: 'bevel', default: true, kind: 'image', backends: both },
|
|
10
10
|
bevelSamples: { key: 'bevelSamples', default: 8, kind: 'image', backends: both, normalize: normalizeBevelSamples },
|
|
11
11
|
dispersionWavelengths: { key: 'dispersionWavelengths', default: 3, kind: 'image', backends: both },
|
|
12
|
+
caustics: { key: 'caustics', default: undefined, kind: 'image', backends: both },
|
|
12
13
|
directLightCandidates: { key: 'directLightCandidates', default: 4, kind: 'image', backends: ['webgpu'], normalize: normalizeDirectLightCandidates },
|
|
13
14
|
alphaBlendStrategy: { key: 'alphaBlendStrategy', default: 'stochastic', kind: 'image', backends: ['webgpu'] },
|
|
14
15
|
transmissiveOutputAlphaMin: { key: 'transmissiveOutputAlphaMin', default: 1, kind: 'image', backends: both },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/settings/render.ts"],"names":[],"mappings":"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","sourcesContent":["import type Render from '../Render.js';\nimport type { TraceSettings } from '../renderer/renderPipeline.js';\nimport { normalizeBevelSamples, normalizeDirectLightCandidates } from '../renderer/RenderingPipeline.js';\n\ntype Descriptor<K extends keyof Render> = {\n key: K; default: Render[K]; kind: 'image' | 'execution' | 'sample-cap' | 'resolution' | 'denoiser' | 'fallback';\n backends: readonly ('webgpu' | 'webgl')[]; normalize?: (value: Render[K]) => Render[K];\n};\nconst both = ['webgpu', 'webgl'] as const;\n// Trace fields are exhaustive: adding a pipeline setting requires a descriptor.\nexport const traceSchema = {\n aovs: {key: 'aovs', default: [], kind: 'execution', backends: both},\n aovSamples: {key: 'aovSamples', default: 16, kind: 'execution', backends: both},\n bounces: {key: 'bounces', default: 3, kind: 'image', backends: both},\n clamping: {key: 'clamping', default: 0, kind: 'image', backends: both},\n bevel: {key: 'bevel', default: true, kind: 'image', backends: both},\n bevelSamples: {key: 'bevelSamples', default: 8, kind: 'image', backends: both, normalize: normalizeBevelSamples},\n dispersionWavelengths: {key: 'dispersionWavelengths', default: 3, kind: 'image', backends: both},\n directLightCandidates: {key: 'directLightCandidates', default: 4, kind: 'image', backends: ['webgpu'], normalize: normalizeDirectLightCandidates},\n alphaBlendStrategy: {key: 'alphaBlendStrategy', default: 'stochastic', kind: 'image', backends: ['webgpu']},\n transmissiveOutputAlphaMin: {key: 'transmissiveOutputAlphaMin', default: 1, kind: 'image', backends: both},\n samplingPattern: {key: 'samplingPattern', default: 'sobol', kind: 'image', backends: both},\n maxSamples: {key: 'maxSamples', default: 2048, kind: 'sample-cap', backends: both},\n timeBudgetMs: {key: 'timeBudgetMs', default: 21, kind: 'execution', backends: both},\n maxTiles: {key: 'maxTiles', default: Infinity, kind: 'execution', backends: both},\n batchSamples: {key: 'batchSamples', default: false, kind: 'execution', backends: both},\n} satisfies {[K in keyof TraceSettings]: Descriptor<K>};\nexport const renderSchema = [\n ...Object.values(traceSchema),\n {key: 'resolutionScale', default: 1, kind: 'resolution', backends: both},\n {key: 'adaptiveResolution', default: false, kind: 'resolution', backends: both},\n {key: 'fallback', default: null, kind: 'fallback', backends: both},\n {key: 'denoiser', default: null, kind: 'denoiser', backends: both},\n] as const;\n\nexport function collectTraceSettings(render: Render, sampleCap: number): TraceSettings {\n return Object.fromEntries(Object.values(traceSchema).map(field => [field.key,\n field.key === 'maxSamples' ? sampleCap : 'normalize' in field ? field.normalize(render[field.key]) : render[field.key]\n ])) as unknown as TraceSettings;\n}\nexport function imageSettingsKey(settings: TraceSettings, size: {width: number; height: number}) {\n const fields = Object.values(traceSchema).filter(field => field.kind === 'image');\n return JSON.stringify({...Object.fromEntries(fields.map(field => [field.key, settings[field.key]])),\n samples: settings.samplingPattern === 'zsobol' ? settings.maxSamples : undefined, ...size});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/settings/render.ts"],"names":[],"mappings":"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","sourcesContent":["import type Render from '../Render.js';\nimport type { TraceSettings } from '../renderer/renderPipeline.js';\nimport { normalizeBevelSamples, normalizeDirectLightCandidates } from '../renderer/RenderingPipeline.js';\n\ntype Descriptor<K extends keyof Render> = {\n key: K; default: Render[K]; kind: 'image' | 'execution' | 'sample-cap' | 'resolution' | 'denoiser' | 'fallback';\n backends: readonly ('webgpu' | 'webgl')[]; normalize?: (value: Render[K]) => Render[K];\n};\nconst both = ['webgpu', 'webgl'] as const;\n// Trace fields are exhaustive: adding a pipeline setting requires a descriptor.\nexport const traceSchema = {\n aovs: {key: 'aovs', default: [], kind: 'execution', backends: both},\n aovSamples: {key: 'aovSamples', default: 16, kind: 'execution', backends: both},\n bounces: {key: 'bounces', default: 3, kind: 'image', backends: both},\n clamping: {key: 'clamping', default: 0, kind: 'image', backends: both},\n bevel: {key: 'bevel', default: true, kind: 'image', backends: both},\n bevelSamples: {key: 'bevelSamples', default: 8, kind: 'image', backends: both, normalize: normalizeBevelSamples},\n dispersionWavelengths: {key: 'dispersionWavelengths', default: 3, kind: 'image', backends: both},\n caustics: {key: 'caustics', default: undefined, kind: 'image', backends: both},\n directLightCandidates: {key: 'directLightCandidates', default: 4, kind: 'image', backends: ['webgpu'], normalize: normalizeDirectLightCandidates},\n alphaBlendStrategy: {key: 'alphaBlendStrategy', default: 'stochastic', kind: 'image', backends: ['webgpu']},\n transmissiveOutputAlphaMin: {key: 'transmissiveOutputAlphaMin', default: 1, kind: 'image', backends: both},\n samplingPattern: {key: 'samplingPattern', default: 'sobol', kind: 'image', backends: both},\n maxSamples: {key: 'maxSamples', default: 2048, kind: 'sample-cap', backends: both},\n timeBudgetMs: {key: 'timeBudgetMs', default: 21, kind: 'execution', backends: both},\n maxTiles: {key: 'maxTiles', default: Infinity, kind: 'execution', backends: both},\n batchSamples: {key: 'batchSamples', default: false, kind: 'execution', backends: both},\n} satisfies {[K in keyof TraceSettings]: Descriptor<K>};\nexport const renderSchema = [\n ...Object.values(traceSchema),\n {key: 'resolutionScale', default: 1, kind: 'resolution', backends: both},\n {key: 'adaptiveResolution', default: false, kind: 'resolution', backends: both},\n {key: 'fallback', default: null, kind: 'fallback', backends: both},\n {key: 'denoiser', default: null, kind: 'denoiser', backends: both},\n] as const;\n\nexport function collectTraceSettings(render: Render, sampleCap: number): TraceSettings {\n return Object.fromEntries(Object.values(traceSchema).map(field => [field.key,\n field.key === 'maxSamples' ? sampleCap : 'normalize' in field ? field.normalize(render[field.key]) : render[field.key]\n ])) as unknown as TraceSettings;\n}\nexport function imageSettingsKey(settings: TraceSettings, size: {width: number; height: number}) {\n const fields = Object.values(traceSchema).filter(field => field.kind === 'image');\n return JSON.stringify({...Object.fromEntries(fields.map(field => [field.key, settings[field.key]])),\n samples: settings.samplingPattern === 'zsobol' ? settings.maxSamples : undefined, ...size});\n}\n"]}
|
package/lib/util/fromTHREE.js
CHANGED
|
@@ -281,7 +281,7 @@ function convertMaterial(threeMaterial, textureCache) {
|
|
|
281
281
|
}
|
|
282
282
|
const dispersion = Number(physicalMaterial.dispersion);
|
|
283
283
|
material.transmissionDispersionScale = Number.isFinite(dispersion)
|
|
284
|
-
? Math.
|
|
284
|
+
? Math.max(0, dispersion)
|
|
285
285
|
: 0;
|
|
286
286
|
const realtimeIridescence = physicalMaterial;
|
|
287
287
|
material.thinFilmThickness = Math.min(20, Math.max(0, (realtimeIridescence.iridescenceThicknessRange?.[1] ?? 500) / 1000));
|
|
@@ -372,7 +372,7 @@ function convertMaterial(threeMaterial, textureCache) {
|
|
|
372
372
|
material.specularColor = color(openPbr.specularColor, material.specularColor);
|
|
373
373
|
material.transmissionScatter = color(openPbr.transmissionScatter, material.transmissionScatter);
|
|
374
374
|
material.transmissionScatterAnisotropy = finite(openPbr.transmissionScatterAnisotropy, material.transmissionScatterAnisotropy, -0.95, 0.95);
|
|
375
|
-
material.transmissionDispersionScale = finite(openPbr.transmissionDispersionScale, material.transmissionDispersionScale);
|
|
375
|
+
material.transmissionDispersionScale = finite(openPbr.transmissionDispersionScale, material.transmissionDispersionScale, 0, Infinity);
|
|
376
376
|
material.transmissionDispersionAbbeNumber = finite(openPbr.transmissionDispersionAbbeNumber, material.transmissionDispersionAbbeNumber, 1, 100);
|
|
377
377
|
material.subsurfaceWeight = finite(openPbr.subsurfaceWeight, material.subsurfaceWeight);
|
|
378
378
|
material.subsurfaceColor = color(openPbr.subsurfaceColor, material.subsurfaceColor);
|