@meimu/mutracer 0.1.5 → 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/LICENSES/Classroom-CC0.txt +121 -0
- package/README.md +10 -7
- package/THIRD_PARTY_NOTICES.md +13 -8
- package/dist/assets/bvh.worker.js +1 -1
- package/dist/mutracer.umd.js +12316 -9228
- package/docs/api.md +232 -21
- package/docs/caustics.md +80 -0
- package/docs/denoising.md +30 -15
- package/docs/materials.md +6 -5
- package/docs/performance.md +5 -2
- package/docs/progressive-rendering.md +73 -1
- package/docs/technical/continuous-spectrum.md +93 -0
- package/docs/threejs.md +14 -5
- package/docs/updates.md +46 -2
- package/docs/viewer-checklist.md +65 -0
- package/docs/viewer.md +15 -8
- package/lib/Display.d.ts +13 -2
- package/lib/Display.js +28 -7
- package/lib/Display.js.map +1 -1
- package/lib/Engine.d.ts +4 -0
- package/lib/Engine.js +99 -37
- package/lib/Engine.js.map +1 -1
- package/lib/Render.d.ts +98 -15
- package/lib/Render.js +275 -27
- package/lib/Render.js.map +1 -1
- package/lib/RenderLoop.d.ts +11 -1
- package/lib/RenderLoop.js +120 -15
- package/lib/RenderLoop.js.map +1 -1
- package/lib/main.d.ts +6 -3
- package/lib/main.js +1 -0
- package/lib/main.js.map +1 -1
- package/lib/math/access.d.ts +4 -0
- package/lib/math/access.js +10 -0
- package/lib/math/access.js.map +1 -0
- package/lib/math/mat3.d.ts +4 -0
- package/lib/math/mat3.js +54 -0
- package/lib/math/mat3.js.map +1 -0
- package/lib/math/mat4.d.ts +19 -0
- package/lib/math/mat4.js +413 -0
- package/lib/math/mat4.js.map +1 -0
- package/lib/math/storage.d.ts +6 -0
- package/lib/math/storage.js +22 -0
- package/lib/math/storage.js.map +1 -0
- package/lib/math/types.d.ts +3 -0
- package/lib/math/types.js +2 -0
- package/lib/math/types.js.map +1 -0
- package/lib/math/vec2.d.ts +13 -0
- package/lib/math/vec2.js +52 -0
- package/lib/math/vec2.js.map +1 -0
- package/lib/math/vec3.d.ts +23 -0
- package/lib/math/vec3.js +139 -0
- package/lib/math/vec3.js.map +1 -0
- package/lib/math.d.ts +5 -17
- package/lib/math.js +5 -55
- package/lib/math.js.map +1 -1
- package/lib/oidn.d.ts +1 -1
- package/lib/oidn.js +4 -2
- package/lib/oidn.js.map +1 -1
- package/lib/oidnBundledWeights.cdn.d.ts +2 -2
- package/lib/renderer/AovPrimitiveCache.d.ts +10 -0
- package/lib/renderer/AovPrimitiveCache.js +48 -0
- package/lib/renderer/AovPrimitiveCache.js.map +1 -0
- package/lib/renderer/Box3.d.ts +5 -0
- package/lib/renderer/Box3.js +9 -0
- package/lib/renderer/Box3.js.map +1 -0
- package/lib/renderer/GPUTraceQueue.d.ts +11 -0
- package/lib/renderer/GPUTraceQueue.js +33 -0
- package/lib/renderer/GPUTraceQueue.js.map +1 -0
- package/lib/renderer/MaterialData.d.ts +15 -0
- package/lib/renderer/MaterialData.js +61 -0
- package/lib/renderer/MaterialData.js.map +1 -0
- package/lib/renderer/MaterialTable.d.ts +11 -98
- package/lib/renderer/MaterialTable.js +34 -412
- package/lib/renderer/MaterialTable.js.map +1 -1
- package/lib/renderer/RenderingPipeline.d.ts +36 -13
- package/lib/renderer/RenderingPipeline.js +1 -17
- package/lib/renderer/RenderingPipeline.js.map +1 -1
- package/lib/renderer/TileRender.d.ts +0 -1
- package/lib/renderer/TileRender.js +14 -1
- package/lib/renderer/TileRender.js.map +1 -1
- package/lib/renderer/WGPUAov.d.ts +39 -0
- package/lib/renderer/WGPUAov.js +239 -0
- package/lib/renderer/WGPUAov.js.map +1 -0
- package/lib/renderer/WGPUBindlessMaterialData.d.ts +17 -0
- package/lib/renderer/WGPUBindlessMaterialData.js +48 -0
- package/lib/renderer/WGPUBindlessMaterialData.js.map +1 -0
- package/lib/renderer/WGPUBindlessTextures.d.ts +47 -0
- package/lib/renderer/WGPUBindlessTextures.js +180 -0
- package/lib/renderer/WGPUBindlessTextures.js.map +1 -0
- package/lib/renderer/WGPUBloomPass.js +17 -97
- package/lib/renderer/WGPUBloomPass.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/WGPUComputePass.d.ts +1 -1
- package/lib/renderer/WGPUComputePass.js.map +1 -1
- package/lib/renderer/WGPUEnvMapDistribution.d.ts +12 -0
- package/lib/renderer/WGPUEnvMapDistribution.js +150 -0
- package/lib/renderer/WGPUEnvMapDistribution.js.map +1 -0
- package/lib/renderer/WGPUFullQuadPass.js +10 -9
- package/lib/renderer/WGPUFullQuadPass.js.map +1 -1
- package/lib/renderer/WGPUJNLMDenoiser.d.ts +1 -20
- package/lib/renderer/WGPUJNLMDenoiser.js +4 -267
- package/lib/renderer/WGPUJNLMDenoiser.js.map +1 -1
- 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 +132 -29
- package/lib/renderer/WGPURayTraceOutput.js.map +1 -1
- package/lib/renderer/WGPURayTracePass.d.ts +83 -56
- package/lib/renderer/WGPURayTracePass.js +884 -837
- package/lib/renderer/WGPURayTracePass.js.map +1 -1
- package/lib/renderer/WGPURenderingPipeline.d.ts +5 -3
- package/lib/renderer/WGPURenderingPipeline.js +297 -82
- package/lib/renderer/WGPURenderingPipeline.js.map +1 -1
- package/lib/renderer/WGPUShaderCache.d.ts +2 -2
- package/lib/renderer/WGPUShaderCache.js +10 -5
- package/lib/renderer/WGPUShaderCache.js.map +1 -1
- package/lib/renderer/WGPUShaderCapabilities.d.ts +10 -0
- package/lib/renderer/WGPUShaderCapabilities.js +54 -0
- package/lib/renderer/WGPUShaderCapabilities.js.map +1 -0
- package/lib/renderer/WGPUSkyBaker.d.ts +13 -0
- package/lib/renderer/WGPUSkyBaker.js +85 -0
- package/lib/renderer/WGPUSkyBaker.js.map +1 -0
- package/lib/renderer/WebGLAov.d.ts +41 -0
- package/lib/renderer/WebGLAov.js +106 -0
- package/lib/renderer/WebGLAov.js.map +1 -0
- package/lib/renderer/WebGLFullQuadPass.d.ts +7 -4
- package/lib/renderer/WebGLFullQuadPass.js +22 -2
- package/lib/renderer/WebGLFullQuadPass.js.map +1 -1
- package/lib/renderer/WebGLJNLMDenoiser.d.ts +1 -1
- package/lib/renderer/WebGLJNLMDenoiser.js +83 -74
- package/lib/renderer/WebGLJNLMDenoiser.js.map +1 -1
- package/lib/renderer/WebGLRayTraceOutputPass.js +7 -4
- package/lib/renderer/WebGLRayTraceOutputPass.js.map +1 -1
- package/lib/renderer/WebGLRayTracePass.d.ts +18 -3
- package/lib/renderer/WebGLRayTracePass.js +121 -9
- package/lib/renderer/WebGLRayTracePass.js.map +1 -1
- package/lib/renderer/WebGLRenderingPipeline.js +126 -22
- package/lib/renderer/WebGLRenderingPipeline.js.map +1 -1
- package/lib/renderer/WebGLSkyBaker.d.ts +39 -0
- package/lib/renderer/WebGLSkyBaker.js +422 -0
- package/lib/renderer/WebGLSkyBaker.js.map +1 -0
- package/lib/renderer/WebGLSkySource.d.ts +9 -0
- package/lib/renderer/WebGLSkySource.js +28 -0
- package/lib/renderer/WebGLSkySource.js.map +1 -0
- package/lib/renderer/accumulationTarget.d.ts +6 -0
- package/lib/renderer/accumulationTarget.js.map +1 -1
- package/lib/renderer/aov.d.ts +42 -0
- package/lib/renderer/aov.js +50 -0
- package/lib/renderer/aov.js.map +1 -0
- package/lib/renderer/aovDisplay.d.ts +14 -0
- package/lib/renderer/aovDisplay.js +32 -0
- package/lib/renderer/aovDisplay.js.map +1 -0
- package/lib/renderer/aovScene.d.ts +21 -0
- package/lib/renderer/aovScene.js +139 -0
- package/lib/renderer/aovScene.js.map +1 -0
- package/lib/renderer/binnedSahBvh.js +2 -1
- package/lib/renderer/binnedSahBvh.js.map +1 -1
- package/lib/renderer/bvhAccel.d.ts +20 -10
- package/lib/renderer/bvhAccel.js +168 -58
- package/lib/renderer/bvhAccel.js.map +1 -1
- package/lib/renderer/bvhWorkerBuild.d.ts +1 -0
- package/lib/renderer/cameraSnapshot.js +2 -1
- package/lib/renderer/cameraSnapshot.js.map +1 -1
- package/lib/renderer/cameraUniform.d.ts +2 -0
- package/lib/renderer/cameraUniform.js +12 -0
- package/lib/renderer/cameraUniform.js.map +1 -0
- 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/committedScene.js +4 -0
- package/lib/renderer/committedScene.js.map +1 -1
- package/lib/renderer/denoiser.d.ts +3 -11
- package/lib/renderer/denoiser.js.map +1 -1
- package/lib/renderer/depthQuery.js +4 -1
- package/lib/renderer/depthQuery.js.map +1 -1
- package/lib/renderer/displacementPreparation.d.ts +11 -0
- package/lib/renderer/displacementPreparation.js +351 -0
- package/lib/renderer/displacementPreparation.js.map +1 -0
- package/lib/renderer/displacementRange.d.ts +1 -0
- package/lib/renderer/displacementRange.js +9 -0
- package/lib/renderer/displacementRange.js.map +1 -0
- package/lib/renderer/displayParityDiagnostics.d.ts +11 -0
- package/lib/renderer/displayParityDiagnostics.js +2 -0
- package/lib/renderer/displayParityDiagnostics.js.map +1 -0
- package/lib/renderer/envMapCreation.d.ts +9 -8
- package/lib/renderer/envMapCreation.js +36 -12
- package/lib/renderer/envMapCreation.js.map +1 -1
- package/lib/renderer/envMapDistribution.d.ts +12 -2
- package/lib/renderer/envMapDistribution.js +43 -34
- package/lib/renderer/envMapDistribution.js.map +1 -1
- package/lib/renderer/geometryBvhBounds.d.ts +4 -0
- package/lib/renderer/geometryBvhBounds.js +82 -0
- package/lib/renderer/geometryBvhBounds.js.map +1 -0
- package/lib/renderer/geometryUpdateState.d.ts +32 -0
- package/lib/renderer/geometryUpdateState.js +2 -0
- package/lib/renderer/geometryUpdateState.js.map +1 -0
- package/lib/renderer/geometryUpdates.d.ts +17 -9
- package/lib/renderer/geometryUpdates.js +92 -33
- package/lib/renderer/geometryUpdates.js.map +1 -1
- package/lib/renderer/glsl/aovDisplay.frag.d.ts +3 -0
- package/lib/renderer/glsl/aovDisplay.frag.js +54 -0
- package/lib/renderer/glsl/aovDisplay.frag.js.map +1 -0
- package/lib/renderer/glsl/aovReflectance.glsl.d.ts +2 -0
- package/lib/renderer/glsl/aovReflectance.glsl.js +87 -0
- package/lib/renderer/glsl/aovReflectance.glsl.js.map +1 -0
- package/lib/renderer/glsl/aovReflectanceSamples.glsl.d.ts +2 -0
- package/lib/renderer/glsl/aovReflectanceSamples.glsl.js +135 -0
- package/lib/renderer/glsl/aovReflectanceSamples.glsl.js.map +1 -0
- package/lib/renderer/glsl/aovTrace.frag.d.ts +2 -0
- package/lib/renderer/glsl/aovTrace.frag.js +121 -0
- package/lib/renderer/glsl/aovTrace.frag.js.map +1 -0
- package/lib/renderer/glsl/chunks/cameraRay.glsl.js +12 -12
- 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 -67
- package/lib/renderer/glsl/chunks/envMap.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/intersect.glsl.js +432 -434
- 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 +160 -157
- package/lib/renderer/glsl/chunks/materialBuffer.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/openPbrBsdf.glsl.js +802 -802
- 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 +72 -67
- 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 +143 -120
- package/lib/renderer/glsl/chunks/sampleMaterial.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js +21 -18
- package/lib/renderer/glsl/chunks/sampleShadowCatcher.glsl.js.map +1 -1
- package/lib/renderer/glsl/chunks/sampling.glsl.js +91 -94
- 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 +12 -12
- package/lib/renderer/glsl/chunks/uvProjection.glsl.js.map +1 -1
- package/lib/renderer/glsl/depthQuery.frag.js +7 -7
- package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js +17 -9
- package/lib/renderer/glsl/normalizeRayTraceOutput.frag.js.map +1 -1
- package/lib/renderer/glsl/rayTrace.frag.d.ts +1 -1
- package/lib/renderer/glsl/rayTrace.frag.js +271 -248
- package/lib/renderer/glsl/rayTrace.frag.js.map +1 -1
- package/lib/renderer/glsl/shadowCatcher.frag.js +37 -32
- package/lib/renderer/glsl/shadowCatcher.frag.js.map +1 -1
- package/lib/renderer/glsl/webglPipeline.frag.js +65 -60
- package/lib/renderer/glsl/webglPipeline.frag.js.map +1 -1
- package/lib/renderer/lightConstants.d.ts +10 -0
- package/lib/renderer/lightConstants.js +11 -0
- package/lib/renderer/lightConstants.js.map +1 -0
- package/lib/renderer/lightCore.d.ts +12 -0
- package/lib/renderer/lightCore.js +13 -0
- package/lib/renderer/lightCore.js.map +1 -0
- package/lib/renderer/lightSampling.d.ts +22 -18
- package/lib/renderer/lightSampling.js +135 -62
- package/lib/renderer/lightSampling.js.map +1 -1
- package/lib/renderer/lightsData.d.ts +21 -0
- package/lib/renderer/lightsData.js +192 -0
- package/lib/renderer/lightsData.js.map +1 -0
- package/lib/renderer/lightsUtil.d.ts +3 -26
- package/lib/renderer/lightsUtil.js +3 -153
- package/lib/renderer/lightsUtil.js.map +1 -1
- package/lib/renderer/materialBufferData.d.ts +70 -0
- package/lib/renderer/materialBufferData.js +2 -0
- package/lib/renderer/materialBufferData.js.map +1 -0
- package/lib/renderer/materialConstants.d.ts +11 -0
- package/lib/renderer/materialConstants.js +11 -0
- package/lib/renderer/materialConstants.js.map +1 -1
- package/lib/renderer/materialCore.d.ts +8 -0
- package/lib/renderer/materialCore.js +9 -0
- package/lib/renderer/materialCore.js.map +1 -0
- package/lib/renderer/materialEmission.d.ts +2 -2
- package/lib/renderer/materialEmission.js +3 -3
- package/lib/renderer/materialEmission.js.map +1 -1
- package/lib/renderer/materialLayout.d.ts +2 -101
- package/lib/renderer/materialLayout.js +110 -172
- package/lib/renderer/materialLayout.js.map +1 -1
- package/lib/renderer/materialLayoutSchema.d.ts +26 -0
- package/lib/renderer/materialLayoutSchema.js +172 -0
- package/lib/renderer/materialLayoutSchema.js.map +1 -0
- package/lib/renderer/materialParams.d.ts +69 -0
- package/lib/renderer/materialParams.js +272 -0
- package/lib/renderer/materialParams.js.map +1 -0
- package/lib/renderer/materialTextureSlots.d.ts +14 -324
- package/lib/renderer/materialTextureSlots.js +21 -8
- package/lib/renderer/materialTextureSlots.js.map +1 -1
- package/lib/renderer/materialTextures.d.ts +6 -0
- package/lib/renderer/{texturesFromMaterials.js → materialTextures.js} +1 -2
- package/lib/renderer/materialTextures.js.map +1 -0
- package/lib/renderer/mergeMeshesToGeometry.js +6 -147
- package/lib/renderer/mergeMeshesToGeometry.js.map +1 -1
- package/lib/renderer/meshInstances.d.ts +4 -21
- package/lib/renderer/meshInstances.js +10 -56
- package/lib/renderer/meshInstances.js.map +1 -1
- package/lib/renderer/oidnNetwork.d.ts +2 -2
- package/lib/renderer/oidnNetwork.js +1 -1
- package/lib/renderer/oidnNetwork.js.map +1 -1
- package/lib/renderer/openPbrEnergyLut.js +850 -846
- package/lib/renderer/openPbrEnergyLut.js.map +1 -1
- package/lib/renderer/ownedDenoiser.d.ts +9 -0
- package/lib/renderer/ownedDenoiser.js +2 -0
- package/lib/renderer/ownedDenoiser.js.map +1 -0
- package/lib/renderer/packedMeshInstances.d.ts +23 -0
- package/lib/renderer/packedMeshInstances.js +80 -0
- package/lib/renderer/packedMeshInstances.js.map +1 -0
- package/lib/renderer/prepareMeshBVH.d.ts +12 -47
- package/lib/renderer/prepareMeshBVH.js +21 -642
- package/lib/renderer/prepareMeshBVH.js.map +1 -1
- package/lib/renderer/proceduralEnvironmentPower.d.ts +3 -0
- package/lib/renderer/proceduralEnvironmentPower.js +8 -0
- package/lib/renderer/proceduralEnvironmentPower.js.map +1 -0
- package/lib/renderer/progressiveScheduler.d.ts +1 -0
- package/lib/renderer/progressiveScheduler.js +7 -3
- package/lib/renderer/progressiveScheduler.js.map +1 -1
- package/lib/renderer/renderPipeline.d.ts +4 -1
- package/lib/renderer/renderPipeline.js.map +1 -1
- package/lib/renderer/renderingLimits.d.ts +9 -0
- package/lib/renderer/renderingLimits.js +18 -0
- package/lib/renderer/renderingLimits.js.map +1 -0
- 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/sceneAssembly.d.ts +73 -0
- package/lib/renderer/sceneAssembly.js +109 -0
- package/lib/renderer/sceneAssembly.js.map +1 -0
- package/lib/renderer/sceneChangeCore.d.ts +39 -0
- package/lib/renderer/sceneChangeCore.js +307 -0
- package/lib/renderer/sceneChangeCore.js.map +1 -0
- package/lib/renderer/sceneChangeDetector.d.ts +3 -6
- package/lib/renderer/sceneChangeDetector.js +24 -150
- package/lib/renderer/sceneChangeDetector.js.map +1 -1
- package/lib/renderer/sceneLayoutSchema.d.ts +119 -0
- package/lib/renderer/sceneLayoutSchema.js +111 -0
- package/lib/renderer/sceneLayoutSchema.js.map +1 -0
- package/lib/renderer/sceneLightChanges.d.ts +3 -3
- package/lib/renderer/sceneLightChanges.js.map +1 -1
- package/lib/renderer/scenePreparation.d.ts +55 -0
- package/lib/renderer/scenePreparation.js +360 -0
- package/lib/renderer/scenePreparation.js.map +1 -0
- package/lib/renderer/scratchSampling.js +18 -18
- package/lib/renderer/scratchSampling.js.map +1 -1
- package/lib/renderer/shader.d.ts +2 -0
- package/lib/renderer/shader.js +2 -0
- package/lib/renderer/shader.js.map +1 -1
- package/lib/renderer/shaderCapabilities.d.ts +23 -0
- package/lib/renderer/shaderCapabilities.js +55 -0
- package/lib/renderer/shaderCapabilities.js.map +1 -0
- package/lib/renderer/shaderCapacity.d.ts +2 -0
- package/lib/renderer/shaderCapacity.js +5 -0
- package/lib/renderer/shaderCapacity.js.map +1 -0
- package/lib/renderer/skyModel.d.ts +9 -0
- package/lib/renderer/skyModel.js +10 -0
- package/lib/renderer/skyModel.js.map +1 -0
- 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/textureAtlas.d.ts +60 -0
- package/lib/renderer/textureAtlas.js +145 -0
- package/lib/renderer/textureAtlas.js.map +1 -0
- package/lib/renderer/textureAtlasPixels.d.ts +7 -0
- package/lib/renderer/textureAtlasPixels.js +68 -0
- package/lib/renderer/textureAtlasPixels.js.map +1 -0
- package/lib/renderer/util.d.ts +1 -1
- package/lib/renderer/util.js.map +1 -1
- package/lib/renderer/webgl.d.ts +9 -2
- package/lib/renderer/webgl.js +54 -9
- package/lib/renderer/webgl.js.map +1 -1
- 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/wgpuBindless.d.ts +26 -0
- package/lib/renderer/wgpuBindless.js +95 -0
- package/lib/renderer/wgpuBindless.js.map +1 -0
- package/lib/renderer/wgpuBindlessSource.d.ts +2 -0
- package/lib/renderer/wgpuBindlessSource.js +51 -0
- package/lib/renderer/wgpuBindlessSource.js.map +1 -0
- package/lib/renderer/wgpuMaterialConstants.d.ts +4 -0
- package/lib/renderer/wgpuMaterialConstants.js +52 -0
- package/lib/renderer/wgpuMaterialConstants.js.map +1 -0
- package/lib/renderer/wgpuSceneUniform.d.ts +42 -0
- package/lib/renderer/wgpuSceneUniform.js +125 -0
- package/lib/renderer/wgpuSceneUniform.js.map +1 -0
- package/lib/renderer/wgpuShaderSource.d.ts +22 -0
- package/lib/renderer/wgpuShaderSource.js +33 -0
- package/lib/renderer/wgpuShaderSource.js.map +1 -0
- package/lib/renderer/wgsl/aovDisplay.wgsl.d.ts +1 -0
- package/lib/renderer/wgsl/aovDisplay.wgsl.js +70 -0
- package/lib/renderer/wgsl/aovDisplay.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/aovReflectance.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/aovReflectance.wgsl.js +98 -0
- package/lib/renderer/wgsl/aovReflectance.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js +135 -0
- package/lib/renderer/wgsl/aovReflectanceSamples.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/aovResolve.wgsl.d.ts +5 -0
- package/lib/renderer/wgsl/aovResolve.wgsl.js +38 -0
- package/lib/renderer/wgsl/aovResolve.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/aovSnapshot.wgsl.d.ts +4 -0
- package/lib/renderer/wgsl/aovSnapshot.wgsl.js +21 -0
- package/lib/renderer/wgsl/aovSnapshot.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/aovTrace.wgsl.d.ts +2 -0
- package/lib/renderer/wgsl/aovTrace.wgsl.js +115 -0
- package/lib/renderer/wgsl/aovTrace.wgsl.js.map +1 -0
- package/lib/renderer/wgsl/bloom.wgsl.d.ts +13 -0
- package/lib/renderer/wgsl/bloom.wgsl.js +92 -0
- package/lib/renderer/wgsl/bloom.wgsl.js.map +1 -0
- 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.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js +41 -35
- package/lib/renderer/wgsl/chunks/envMap.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersect.wgsl.d.ts +13 -1
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js +747 -704
- package/lib/renderer/wgsl/chunks/intersect.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js +16 -16
- package/lib/renderer/wgsl/chunks/intersectionAlpha.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.d.ts +4 -1
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js +83 -79
- package/lib/renderer/wgsl/chunks/materialBuffer.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js +855 -857
- package/lib/renderer/wgsl/chunks/openPbrBsdf.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/pdm_displacement.wgsl.js +137 -137
- package/lib/renderer/wgsl/chunks/rand.wgsl.js +3 -3
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.d.ts +3 -1
- package/lib/renderer/wgsl/chunks/rayTraceCore.wgsl.js +52 -32
- 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 +152 -144
- 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 +269 -245
- 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 +27 -14
- 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 +91 -95
- package/lib/renderer/wgsl/chunks/sampling.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.d.ts +1 -1
- package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.js +1 -0
- package/lib/renderer/wgsl/chunks/shadowCatcherAccumulation.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/chunks/sobol.wgsl.js +83 -83
- package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js +12 -12
- package/lib/renderer/wgsl/chunks/uvProjection.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/compose.frag.wgsl.d.ts +8 -3
- package/lib/renderer/wgsl/compose.frag.wgsl.js +143 -118
- package/lib/renderer/wgsl/compose.frag.wgsl.js.map +1 -1
- package/lib/renderer/wgsl/customDenoise.wgsl.js +10 -10
- package/lib/renderer/wgsl/depthQuery.wgsl.js +7 -7
- package/lib/renderer/wgsl/jnlm.compute.d.ts +25 -0
- package/lib/renderer/wgsl/jnlm.compute.js +315 -0
- package/lib/renderer/wgsl/jnlm.compute.js.map +1 -0
- package/lib/renderer/wgsl/offlineAdaptive.compute.js +28 -28
- package/lib/renderer/wgsl/oidnAuxiliary.wgsl.d.ts +1 -0
- package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js +17 -0
- package/lib/renderer/wgsl/oidnAuxiliary.wgsl.js.map +1 -0
- 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.d.ts +1 -0
- package/lib/renderer/wgsl/proceduralSky.compute.js +198 -0
- package/lib/renderer/wgsl/proceduralSky.compute.js.map +1 -0
- package/lib/renderer/wgsl/rayTrace.compute.d.ts +25 -1
- package/lib/renderer/wgsl/rayTrace.compute.js +645 -500
- package/lib/renderer/wgsl/rayTrace.compute.js.map +1 -1
- package/lib/scene/Camera.js +7 -3
- package/lib/scene/Camera.js.map +1 -1
- package/lib/scene/EnvironmentLight.d.ts +5 -9
- package/lib/scene/EnvironmentLight.js +4 -14
- package/lib/scene/EnvironmentLight.js.map +1 -1
- package/lib/scene/EnvironmentLightState.d.ts +18 -0
- package/lib/scene/EnvironmentLightState.js +19 -0
- package/lib/scene/EnvironmentLightState.js.map +1 -0
- package/lib/scene/Geometry.d.ts +12 -6
- package/lib/scene/Geometry.js +37 -13
- package/lib/scene/Geometry.js.map +1 -1
- package/lib/scene/InstanceState.d.ts +39 -0
- package/lib/scene/InstanceState.js +77 -0
- package/lib/scene/InstanceState.js.map +1 -0
- package/lib/scene/InstancedMesh.d.ts +1 -1
- package/lib/scene/InstancedMesh.js +3 -0
- package/lib/scene/InstancedMesh.js.map +1 -1
- package/lib/scene/MaterialState.d.ts +209 -0
- package/lib/scene/MaterialState.js +231 -0
- package/lib/scene/MaterialState.js.map +1 -0
- package/lib/scene/Mesh.d.ts +4 -6
- package/lib/scene/Mesh.js +3 -11
- package/lib/scene/Mesh.js.map +1 -1
- package/lib/scene/MeshState.d.ts +20 -0
- package/lib/scene/MeshState.js +35 -0
- package/lib/scene/MeshState.js.map +1 -0
- package/lib/scene/OpenPBRMaterial.d.ts +6 -168
- package/lib/scene/OpenPBRMaterial.js +5 -223
- package/lib/scene/OpenPBRMaterial.js.map +1 -1
- package/lib/scene/PixelTexture.d.ts +6 -0
- package/lib/scene/PixelTexture.js +10 -0
- package/lib/scene/PixelTexture.js.map +1 -0
- package/lib/scene/ProceduralSky.d.ts +24 -0
- package/lib/scene/ProceduralSky.js +40 -0
- package/lib/scene/ProceduralSky.js.map +1 -0
- package/lib/scene/RectLight.d.ts +2 -13
- package/lib/scene/RectLight.js +3 -21
- package/lib/scene/RectLight.js.map +1 -1
- package/lib/scene/RectLightState.d.ts +31 -0
- package/lib/scene/RectLightState.js +26 -0
- package/lib/scene/RectLightState.js.map +1 -0
- package/lib/scene/Scene.d.ts +15 -4
- package/lib/scene/Scene.js.map +1 -1
- package/lib/scene/SceneNode.js +7 -6
- package/lib/scene/SceneNode.js.map +1 -1
- package/lib/scene/Texture.d.ts +10 -41
- package/lib/scene/Texture.js +4 -33
- package/lib/scene/Texture.js.map +1 -1
- package/lib/scene/TextureState.d.ts +35 -0
- package/lib/scene/TextureState.js +35 -0
- package/lib/scene/TextureState.js.map +1 -0
- package/lib/scene/attributeData.d.ts +4 -0
- package/lib/scene/attributeData.js +10 -0
- package/lib/scene/attributeData.js.map +1 -0
- package/lib/scene/lightState.d.ts +8 -0
- package/lib/scene/lightState.js +2 -0
- package/lib/scene/lightState.js.map +1 -0
- package/lib/scene/openPbrSchema.d.ts +7 -12
- package/lib/scene/openPbrSchema.js +273 -28
- package/lib/scene/openPbrSchema.js.map +1 -1
- package/lib/scene/skyParameters.d.ts +20 -0
- package/lib/scene/skyParameters.js +28 -0
- package/lib/scene/skyParameters.js.map +1 -0
- package/lib/scene/textureData.d.ts +9 -0
- package/lib/scene/textureData.js +2 -0
- package/lib/scene/textureData.js.map +1 -0
- package/lib/scene/texturePixels.d.ts +6 -0
- package/lib/scene/texturePixels.js +10 -0
- package/lib/scene/texturePixels.js.map +1 -0
- package/lib/settings/display.d.ts +33 -0
- package/lib/settings/display.js +7 -0
- package/lib/settings/display.js.map +1 -1
- package/lib/settings/render.d.ts +36 -3
- package/lib/settings/render.js +4 -1
- package/lib/settings/render.js.map +1 -1
- package/lib/tinybvh/tinybvh.wasm +0 -0
- package/lib/util/commonGeometry.js +7 -7
- package/lib/util/commonGeometry.js.map +1 -1
- package/lib/util/fromTHREE.d.ts +4 -1
- package/lib/util/fromTHREE.js +20 -7
- package/lib/util/fromTHREE.js.map +1 -1
- package/lib/util/geometry.js +42 -29
- package/lib/util/geometry.js.map +1 -1
- package/lib/util/loadRGBE.js +3 -4
- package/lib/util/loadRGBE.js.map +1 -1
- package/lib/util/studioLighting.d.ts +18 -0
- package/lib/util/studioLighting.js +36 -0
- package/lib/util/studioLighting.js.map +1 -0
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/llms.txt +5 -0
- package/package.json +33 -7
- package/lib/renderer/texturesFromMaterials.d.ts +0 -6
- package/lib/renderer/texturesFromMaterials.js.map +0 -1
|
@@ -1,7 +1,28 @@
|
|
|
1
|
+
import { causticDiagnostics } from './causticDiagnostics.js';
|
|
2
|
+
import { WGPUPhotonMap, photonRadius } from './WGPUPhotonMap.js';
|
|
3
|
+
import { defaultPhotonMapPhotons } from './wgsl/photonMap.compute.js';
|
|
4
|
+
import { researchTransport } from './researchTransport.js';
|
|
5
|
+
import { causticEmissionTable } from './causticEmission.js';
|
|
6
|
+
import { WGPUCausticLightPass } from './WGPUCausticLightPass.js';
|
|
7
|
+
import { causticLightCompute } from './wgsl/causticLight.compute.js';
|
|
8
|
+
import { causticCasterSpheres, hasStochasticCausticCaster, causticCasterSphere, isCausticSpecular } from './caustics.js';
|
|
9
|
+
import { AovPrimitiveCache } from './AovPrimitiveCache.js';
|
|
10
|
+
import { packAovScene } from './aovScene.js';
|
|
11
|
+
import { aovMask } from './aov.js';
|
|
12
|
+
import { proceduralEnvironmentPower } from './proceduralEnvironmentPower.js';
|
|
13
|
+
import { WGPUSkyBaker } from './WGPUSkyBaker.js';
|
|
14
|
+
import { initialSceneConstants, sceneAttributeLayout, sceneTraversalConstants, prepareSceneLightSampling, writeTraceLighting, packSceneAnalyticLights, writeTraceQuality, traceSampling, writeTraceSampling } from './sceneAssembly.js';
|
|
15
|
+
import { rayTraceBindGroupLayouts } from './sceneLayoutSchema.js';
|
|
16
|
+
import { WGPUBindlessMaterialData } from './WGPUBindlessMaterialData.js';
|
|
17
|
+
import { WGPUBindlessTextures } from './WGPUBindlessTextures.js';
|
|
18
|
+
import { bindlessBindGroupLayouts, bindlessShaderSource } from './wgpuBindlessSource.js';
|
|
19
|
+
import { bindlessLimitReason, resourceTableLayout, setResourceTable } from './wgpuBindless.js';
|
|
20
|
+
import { wgpuMaterialConstants } from './wgpuMaterialConstants.js';
|
|
21
|
+
import { wgpuShaderSource, wgpuPipelineConstantKeys, WGPU_SHADER_DEFINE_KEYS } from './wgpuShaderSource.js';
|
|
22
|
+
import { makeRayTraceUniform, WGPU_SCENE_BUFFER_MIN_BYTES } from './wgpuSceneUniform.js';
|
|
1
23
|
import { constants } from './webgl.js';
|
|
2
|
-
import { MATERIAL_FLOAT_COUNT } from './
|
|
24
|
+
import { MATERIAL_FLOAT_COUNT } from './materialLayoutSchema.js';
|
|
3
25
|
import { normalizeBounces } from './pathLimits.js';
|
|
4
|
-
import { MATERIAL_TEXTURE_DEFINES, materialTextureFeatures } from './materialTextureSlots.js';
|
|
5
26
|
import { updateMeshInstanceColors } from './prepareMeshBVH.js';
|
|
6
27
|
import { updateTLASInstanceColors } from './bvhAccel.js';
|
|
7
28
|
import { getMaterialHashKey } from '../scene/openPbrSchema.js';
|
|
@@ -9,31 +30,31 @@ import { sameInstanceMembership } from './meshInstances.js';
|
|
|
9
30
|
import InstancedMesh from '../scene/InstancedMesh.js';
|
|
10
31
|
import { dirtyMeshInstanceIndices } from './prepareMeshBVH.js';
|
|
11
32
|
import { cameraApertureRadius } from './cameraLens.js';
|
|
12
|
-
import { getRectLightUOrV, getRectLightArea, getSphereLightArea, getSpotLightArea, getSpotLightRadiance, getLightDirection, getRigidLightFrame, validateSceneLights } from './lightsUtil.js';
|
|
13
33
|
import { getGeometryBvh, setGeometryBvh } from './geometryBvh.js';
|
|
34
|
+
import { packCameraUniform } from './cameraUniform.js';
|
|
14
35
|
import { SHADOW_CATCHER_ACCUMULATION_BYTES } from './wgsl/chunks/shadowCatcherAccumulation.wgsl.js';
|
|
15
|
-
import { samplingPatternCode, zSobolLog2SPP } from './sampling.js';
|
|
16
36
|
import { DEPTH_SAMPLE_COUNT, packDepthSampleArea, unpackDepthSamples } from './depthQuery.js';
|
|
17
37
|
import { MeshMembershipCache } from './meshMembership.js';
|
|
18
38
|
import { mergeRefitRanges } from './bvhAccel.js';
|
|
19
39
|
import { updatePreparedGeometries, canUpdateGeometries, geometryTopologyChanged, packVertexAttributes } from './geometryUpdates.js';
|
|
20
40
|
import { SceneEnvironmentCache, RectLightTextureTracker } from './sceneLightChanges.js';
|
|
21
41
|
import { MaterialChangeTracker, materialIsEmissive } from './materialChanges.js';
|
|
42
|
+
import { WGPUEnvMapDistribution } from './WGPUEnvMapDistribution.js';
|
|
22
43
|
import { WGPUShaderCache, retainedShaderCapacity } from './WGPUShaderCache.js';
|
|
23
|
-
import {
|
|
44
|
+
import { WGPUShaderCapabilities } from './WGPUShaderCapabilities.js';
|
|
45
|
+
import { wgpuSceneFeatureFlags } from './WGPUSceneFeatures.js';
|
|
46
|
+
import { lightShaderCapabilities, retainedShaderCapabilities } from './shaderCapabilities.js';
|
|
24
47
|
import { mat4 } from '../math.js';
|
|
25
|
-
import MaterialTable, { DEFAULT_MAX_TEXTURE_ARRAY_BYTES
|
|
48
|
+
import MaterialTable, { DEFAULT_MAX_TEXTURE_ARRAY_BYTES } from './MaterialTable.js';
|
|
26
49
|
import { numberArraysEqual } from './util.js';
|
|
27
|
-
import { generateBackgroundMapFromSceneBackground, environmentRotation, backgroundMapTransform } from './envMapCreation.js';
|
|
28
|
-
import
|
|
29
|
-
import { CONSTANT_DIRECTIONAL_LIGHT, CONSTANT_RECT_LIGHT, CONSTANT_SPHERE_LIGHT, CONSTANT_SPOT_LIGHT } from './lightsUtil.js';
|
|
50
|
+
import { generateBackgroundMapFromSceneBackground, environmentRotation, backgroundMapTransform, backgroundMapRotation } from './envMapCreation.js';
|
|
51
|
+
import { WORKGROUP_SIZE } from './wgsl/rayTrace.compute.js';
|
|
30
52
|
import { envMapDistribution, environmentMapSourcesMatch } from './envMapDistribution.js';
|
|
31
53
|
import { updateMeshInstances, updateMeshInstanceTransforms } from './prepareMeshBVH.js';
|
|
32
54
|
import { buildTLAS, concatBLASBuffers, getTraversalStackSizes, refitBLAS, refitTLAS } from './bvhAccel.js';
|
|
33
55
|
import { isTextureDataSource } from '../scene/Texture.js';
|
|
34
|
-
import { resolveMaterialAlphaMode } from '../scene/OpenPBRMaterial.js';
|
|
35
56
|
import { DEFAULT_BEVEL_SAMPLES, DEFAULT_DIRECT_LIGHT_CANDIDATES, normalizeBevelSamples, normalizeDirectLightCandidates } from './RenderingPipeline.js';
|
|
36
|
-
import {
|
|
57
|
+
import { EmissiveTriangleCache, LightChoiceBuffer, estimateEnvironmentPower } from './lightSampling.js';
|
|
37
58
|
import { getOpenPbrEnergyLutData, OPENPBR_ENERGY_LUT_HEIGHT, OPENPBR_ENERGY_LUT_WIDTH } from './openPbrEnergyLut.js';
|
|
38
59
|
import { offlineAdaptiveCompute } from './wgsl/offlineAdaptive.compute.js';
|
|
39
60
|
import { normalizeOfflineAdaptiveSamplingOptions } from './OfflineAdaptiveSampling.js';
|
|
@@ -65,6 +86,10 @@ function joinWebGPUErrors(...errors) {
|
|
|
65
86
|
.filter(Boolean))
|
|
66
87
|
].join('\n');
|
|
67
88
|
}
|
|
89
|
+
const MAX_TRACE_VARIANTS = 32;
|
|
90
|
+
// Requests are cheap but drivers are not, and a running compilation cannot be cancelled.
|
|
91
|
+
const MAX_CONCURRENT_COMPILES = 2;
|
|
92
|
+
const MAX_QUEUED_COMPILES = 4;
|
|
68
93
|
function shaderCompilationNow() {
|
|
69
94
|
return typeof performance === 'undefined' ? Date.now() : performance.now();
|
|
70
95
|
}
|
|
@@ -87,124 +112,29 @@ export function isWebGPUPipelineSpecializationFailure(...errors) {
|
|
|
87
112
|
return errors.some((error) => /(?:compute )?library failed creation/i.test(getWebGPUErrorMessage(error)));
|
|
88
113
|
}
|
|
89
114
|
/** CPU-side views over the `RayTraceUniform` WGSL struct. */
|
|
90
|
-
export
|
|
91
|
-
// Created from https://webgpufundamentals.org/webgpu/lessons/resources/wgsl-offset-computer.html
|
|
92
|
-
const RayTraceUniformValues = new ArrayBuffer(256);
|
|
93
|
-
const RayTraceUniformViews = {
|
|
94
|
-
outputSize: new Float32Array(RayTraceUniformValues, 0, 2),
|
|
95
|
-
sampleCount: new Float32Array(RayTraceUniformValues, 8, 1),
|
|
96
|
-
scaleOffset: new Float32Array(RayTraceUniformValues, 16, 4),
|
|
97
|
-
lightsCount: new Int32Array(RayTraceUniformValues, 32, 1),
|
|
98
|
-
transmissiveOutputAlphaMin: new Float32Array(RayTraceUniformValues, 36, 1),
|
|
99
|
-
jitter: new Float32Array(RayTraceUniformValues, 40, 2),
|
|
100
|
-
backgroundColor: new Float32Array(RayTraceUniformValues, 48, 4),
|
|
101
|
-
groundedBackgroundParams: new Float32Array(RayTraceUniformValues, 64, 4),
|
|
102
|
-
groundedBackgroundCenter: new Float32Array(RayTraceUniformValues, 80, 2),
|
|
103
|
-
blasOffset: new Int32Array(RayTraceUniformValues, 88, 1),
|
|
104
|
-
isEmpty: new Int32Array(RayTraceUniformValues, 92, 1),
|
|
105
|
-
bevel: new Int32Array(RayTraceUniformValues, 96, 1),
|
|
106
|
-
dispersionWavelengths: new Int32Array(RayTraceUniformValues, 100, 1),
|
|
107
|
-
envLightPmf: new Float32Array(RayTraceUniformValues, 104, 1),
|
|
108
|
-
lightChoiceCount: new Int32Array(RayTraceUniformValues, 108, 1),
|
|
109
|
-
lightWeightTotal: new Float32Array(RayTraceUniformValues, 112, 1),
|
|
110
|
-
lightSamplingTextureWidth: new Int32Array(RayTraceUniformValues, 116, 1),
|
|
111
|
-
lightChoiceTexelOffset: new Int32Array(RayTraceUniformValues, 120, 1),
|
|
112
|
-
bounces: new Int32Array(RayTraceUniformValues, 124, 1),
|
|
113
|
-
varianceMoments: new Int32Array(RayTraceUniformValues, 128, 1),
|
|
114
|
-
adaptiveSampling: new Int32Array(RayTraceUniformValues, 132, 1),
|
|
115
|
-
clamping: new Float32Array(RayTraceUniformValues, 136, 1),
|
|
116
|
-
directLightCandidates: new Int32Array(RayTraceUniformValues, 140, 1),
|
|
117
|
-
backgroundFlags: new Int32Array(RayTraceUniformValues, 144, 1),
|
|
118
|
-
loopUnrollGuard: new Uint32Array(RayTraceUniformValues, 148, 1),
|
|
119
|
-
sceneFeatureFlags: new Uint32Array(RayTraceUniformValues, 152, 1),
|
|
120
|
-
sequenceSeed: new Uint32Array(RayTraceUniformValues, 156, 1),
|
|
121
|
-
zSobolLogSPP: new Uint32Array(RayTraceUniformValues, 160, 1),
|
|
122
|
-
sampleSequenceOffset: new Uint32Array(RayTraceUniformValues, 164, 1),
|
|
123
|
-
environmentRotation: new Float32Array(RayTraceUniformValues, 176, 16),
|
|
124
|
-
backgroundMapTransform: new Float32Array(RayTraceUniformValues, 240, 4)
|
|
125
|
-
};
|
|
126
|
-
// Fixed runtime trip bounds discourage thin-film loop unrolling. Keep 4 for
|
|
127
|
-
// 2 polarizations/substrates and 3 channels, independent of BSDF budgets.
|
|
128
|
-
RayTraceUniformViews.loopUnrollGuard[0] = 4;
|
|
129
|
-
RayTraceUniformViews.environmentRotation.set(environmentRotation([]));
|
|
130
|
-
RayTraceUniformViews.backgroundMapTransform.set(backgroundMapTransform());
|
|
131
|
-
RayTraceUniformViews.bevel[0] = 1;
|
|
132
|
-
RayTraceUniformViews.dispersionWavelengths[0] = 3;
|
|
133
|
-
return {
|
|
134
|
-
buffer: RayTraceUniformValues,
|
|
135
|
-
views: RayTraceUniformViews
|
|
136
|
-
};
|
|
137
|
-
}
|
|
115
|
+
export { makeRayTraceUniform } from './wgpuSceneUniform.js';
|
|
138
116
|
export function packAnalyticsLightsToBuffer(lights, texturesIndices, selectionPmfs, iesProfileTexelOffsets = []) {
|
|
139
|
-
|
|
140
|
-
const numLights = lights.length;
|
|
141
|
-
if (numLights > FIXED_LIGHT_COUNT) {
|
|
142
|
-
console.warn(`Too many lights, only the first ${FIXED_LIGHT_COUNT} lights will be used.`);
|
|
143
|
-
}
|
|
144
|
-
const data = new Float32Array(numLights * PIXELS_PER_LIGHT * 4);
|
|
145
|
-
function packVec3(idx, v) {
|
|
146
|
-
for (let i = 0; i < 3; i++) {
|
|
147
|
-
data[i + idx * 4] = v[i];
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
const lightTypeConstants = {
|
|
151
|
-
DirectionalLight: CONSTANT_DIRECTIONAL_LIGHT,
|
|
152
|
-
SphereLight: CONSTANT_SPHERE_LIGHT,
|
|
153
|
-
RectLight: CONSTANT_RECT_LIGHT,
|
|
154
|
-
SpotLight: CONSTANT_SPOT_LIGHT
|
|
155
|
-
};
|
|
156
|
-
for (let i = 0; i < numLights; i++) {
|
|
157
|
-
const light = lights[i];
|
|
158
|
-
const color = light.color;
|
|
159
|
-
const intensity = light.intensity;
|
|
160
|
-
const off = i * PIXELS_PER_LIGHT;
|
|
161
|
-
packVec3(off, light.position);
|
|
162
|
-
data[off * 4 + 3] = lightTypeConstants[light.type];
|
|
163
|
-
// emission
|
|
164
|
-
const emissionScale = light.type === 'SpotLight' ? getSpotLightRadiance(light) : intensity;
|
|
165
|
-
packVec3(off + 1, [
|
|
166
|
-
color[0] * emissionScale,
|
|
167
|
-
color[1] * emissionScale,
|
|
168
|
-
color[2] * emissionScale
|
|
169
|
-
]);
|
|
170
|
-
if (light.type === 'RectLight') {
|
|
171
|
-
data[(off + 1) * 4 + 3] = getRectLightArea(light);
|
|
172
|
-
packVec3(off + 2, getRectLightUOrV(light, 0));
|
|
173
|
-
data[(off + 2) * 4 + 3] = light.spread;
|
|
174
|
-
packVec3(off + 3, getRectLightUOrV(light, 1));
|
|
175
|
-
}
|
|
176
|
-
else if (light.type === 'SphereLight') {
|
|
177
|
-
data[(off + 1) * 4 + 3] = getSphereLightArea(light);
|
|
178
|
-
data[(off + 2) * 4 + 3] = light.radius;
|
|
179
|
-
}
|
|
180
|
-
else if (light.type === 'SpotLight') {
|
|
181
|
-
data[(off + 1) * 4 + 3] = getSpotLightArea(light);
|
|
182
|
-
packVec3(off + 2, getLightDirection(light));
|
|
183
|
-
data[(off + 2) * 4 + 3] = light.radius;
|
|
184
|
-
packVec3(off + 3, [
|
|
185
|
-
Math.cos((light.outerAngle * Math.PI) / 180),
|
|
186
|
-
Math.cos((Math.min(light.innerAngle, light.outerAngle) * Math.PI) / 180),
|
|
187
|
-
0
|
|
188
|
-
]);
|
|
189
|
-
}
|
|
190
|
-
else if (light.type === 'DirectionalLight') {
|
|
191
|
-
packVec3(off + 2, getLightDirection(light));
|
|
192
|
-
}
|
|
193
|
-
data[(off + 3) * 4 + 3] = light.type === 'SpotLight'
|
|
194
|
-
? iesProfileTexelOffsets[i] ?? -1
|
|
195
|
-
: texturesIndices[i] ?? -1;
|
|
196
|
-
data[(off + 4) * 4] = selectionPmfs[i] ?? 0;
|
|
197
|
-
packVec3(off + 5, getRigidLightFrame(light).x);
|
|
198
|
-
}
|
|
199
|
-
return data;
|
|
117
|
+
return packSceneAnalyticLights(lights, texturesIndices, selectionPmfs, i => iesProfileTexelOffsets[i]);
|
|
200
118
|
}
|
|
201
119
|
class WGPURayTracePass {
|
|
120
|
+
_textureSelection;
|
|
121
|
+
_aovPrimitiveCache = new AovPrimitiveCache();
|
|
202
122
|
_device;
|
|
203
123
|
_rayTracePipeline;
|
|
204
124
|
_targetAspect = 1;
|
|
205
125
|
_width = 0;
|
|
206
126
|
_height = 0;
|
|
207
127
|
_meshes = [];
|
|
128
|
+
_causticKey = '';
|
|
129
|
+
_photonMapping = false;
|
|
130
|
+
_photonMap;
|
|
131
|
+
_causticLight;
|
|
132
|
+
_causticSettings;
|
|
133
|
+
_causticPerspective = true;
|
|
134
|
+
_causticCameraScale = 1;
|
|
135
|
+
_causticBounds = [0, 0, 0, 0];
|
|
136
|
+
_transport;
|
|
137
|
+
_lightTracing = false;
|
|
208
138
|
_sceneEnvironment = new SceneEnvironmentCache();
|
|
209
139
|
_rectLightTextures = new RectLightTextureTracker();
|
|
210
140
|
_analyticLights = [];
|
|
@@ -212,6 +142,9 @@ class WGPURayTracePass {
|
|
|
212
142
|
_materialChanges = new MaterialChangeTracker();
|
|
213
143
|
_pendingMaterialUpdate;
|
|
214
144
|
_materialData;
|
|
145
|
+
_bindless;
|
|
146
|
+
_retiredBindless = new Set();
|
|
147
|
+
_disposed = false;
|
|
215
148
|
_maxTextureArrayBytes = DEFAULT_MAX_TEXTURE_ARRAY_BYTES;
|
|
216
149
|
_pendingGeometryUpdates = new Set();
|
|
217
150
|
_needsUpdateMeshData = true;
|
|
@@ -236,6 +169,8 @@ class WGPURayTracePass {
|
|
|
236
169
|
if (!output)
|
|
237
170
|
return;
|
|
238
171
|
this._targetOutputs.delete(target);
|
|
172
|
+
this._causticLight?.release(target);
|
|
173
|
+
this._photonMap?.release(target);
|
|
239
174
|
for (const buffer of output.buffers)
|
|
240
175
|
buffer.destroy();
|
|
241
176
|
if (this._activeTarget === target) {
|
|
@@ -244,17 +179,23 @@ class WGPURayTracePass {
|
|
|
244
179
|
}
|
|
245
180
|
}
|
|
246
181
|
getTargetMemoryBytes(target) {
|
|
247
|
-
return this._targetOutputs.get(target)?.buffers.reduce((sum, buffer) => sum + buffer.size, 0) ?? 0
|
|
182
|
+
return ((this._targetOutputs.get(target)?.buffers.reduce((sum, buffer) => sum + buffer.size, 0) ?? 0) +
|
|
183
|
+
(this._causticLight?.bytes(target) ?? 0) +
|
|
184
|
+
(this._photonMap?.bytes(target) ?? 0));
|
|
248
185
|
}
|
|
249
186
|
useTarget(target) {
|
|
187
|
+
if (this._photonMapping && this._activeTarget !== target)
|
|
188
|
+
this._needsPhotonTargetBindGroups = true;
|
|
250
189
|
this._activeTarget = target;
|
|
251
190
|
const output = this._targetOutputs.get(target);
|
|
252
|
-
[this._outColorBuffer, this._outNormalBuffer, this._outAlbedoBuffer, this._outShadowCatcherBuffer] =
|
|
191
|
+
[this._outColorBuffer, this._outNormalBuffer, this._outAlbedoBuffer, this._outShadowCatcherBuffer] =
|
|
192
|
+
output?.buffers ?? [];
|
|
253
193
|
if (output?.bindGroup && output.pipeline === this._rayTracePipeline && this._rayTraceBindGroups) {
|
|
254
|
-
this._rayTraceBindGroups[3] = output.bindGroup;
|
|
194
|
+
this._rayTraceBindGroups[this._bindless ? 2 : 3] = output.bindGroup;
|
|
195
|
+
this._needsUpdateOutputBindGroup = false;
|
|
255
196
|
}
|
|
256
197
|
else {
|
|
257
|
-
this.
|
|
198
|
+
this._needsUpdateOutputBindGroup = true;
|
|
258
199
|
}
|
|
259
200
|
}
|
|
260
201
|
_outColorBuffer;
|
|
@@ -281,6 +222,7 @@ class WGPURayTracePass {
|
|
|
281
222
|
_instancesBuffer;
|
|
282
223
|
_emissiveCache = new EmissiveTriangleCache();
|
|
283
224
|
_lightChoices = new LightChoiceBuffer();
|
|
225
|
+
_shadowCatcherLightChoices = new LightChoiceBuffer();
|
|
284
226
|
_lightSamplingData;
|
|
285
227
|
_lightSamplingScratch;
|
|
286
228
|
_lightSamplingTriangleCount = -1;
|
|
@@ -296,26 +238,29 @@ class WGPURayTracePass {
|
|
|
296
238
|
_envImageSource;
|
|
297
239
|
_envTex;
|
|
298
240
|
_envDistributionTex;
|
|
241
|
+
_envDistribution;
|
|
242
|
+
_skyBaker;
|
|
299
243
|
_backgroundImageSource;
|
|
300
244
|
_backgroundTex;
|
|
301
|
-
_rayTracePipelineLayout;
|
|
302
245
|
_rayTraceBindGroups;
|
|
246
|
+
_photonEmissionGroups;
|
|
303
247
|
_blasLayoutMask = 1;
|
|
304
|
-
_compiledBlasLayoutMask = 0;
|
|
305
|
-
_compiledAnisotropyDirections = false;
|
|
306
|
-
_compiledCoatNormals = false;
|
|
307
|
-
_compiledHasAlpha = true;
|
|
308
|
-
_compiledSceneHasAlpha = true;
|
|
309
248
|
_alphaBlendStrategy = 'stochastic';
|
|
310
|
-
_constants =
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
249
|
+
_constants = initialSceneConstants();
|
|
250
|
+
_retainedShaderCapabilities;
|
|
251
|
+
_retainedShaderStructure;
|
|
252
|
+
_shaderCapabilities = new WGPUShaderCapabilities();
|
|
253
|
+
_targetTransport = new WeakMap();
|
|
254
|
+
_traceVariants = new Map();
|
|
255
|
+
_queuedCompiles = [];
|
|
256
|
+
_activeCompiles = 0;
|
|
257
|
+
_traceModules = new Map();
|
|
258
|
+
_requestedVariant;
|
|
259
|
+
_activeVariant;
|
|
260
|
+
_activeInputs;
|
|
261
|
+
_awaitingRetirement;
|
|
262
|
+
_resourceError;
|
|
263
|
+
_aovPipelinesFailed = false;
|
|
319
264
|
_hasTransmission = false;
|
|
320
265
|
_hasShadowCatcher = false;
|
|
321
266
|
_hasDisplacement = false;
|
|
@@ -330,21 +275,26 @@ class WGPURayTracePass {
|
|
|
330
275
|
_apertureRadius = 0;
|
|
331
276
|
_focusDistance = 10;
|
|
332
277
|
_needsUpdateBindGroups = true;
|
|
333
|
-
|
|
278
|
+
// Photon maps are per target. Switching targets only rebinds them, which
|
|
279
|
+
// must not send steady frames through the asynchronous pipeline path.
|
|
280
|
+
_needsPhotonTargetBindGroups = false;
|
|
281
|
+
// Target selection is not a scene-resource edit. A stale fallback must not
|
|
282
|
+
// leave global bindings dirty after switching back to a prepared target.
|
|
283
|
+
_needsUpdateOutputBindGroup = false;
|
|
284
|
+
_aovPipelines = new WeakMap();
|
|
334
285
|
_depthCompute;
|
|
335
286
|
_depthTracePipeline;
|
|
336
287
|
_depthPipeline;
|
|
337
288
|
_shaderCache;
|
|
338
289
|
_debugBVHBuffer;
|
|
339
|
-
_isCompilingShader = false;
|
|
340
|
-
_shaderCompilationDiagnostics;
|
|
341
|
-
_pipelineError;
|
|
342
290
|
// Fold scene invariants before backend inlining; runtime edits use uniforms.
|
|
343
291
|
_bakePipelineConstants = true;
|
|
344
|
-
|
|
345
|
-
|
|
292
|
+
constructor(device, _textureSelection = { requested: 'atlas' }) {
|
|
293
|
+
this._textureSelection = _textureSelection;
|
|
346
294
|
this._device = device;
|
|
347
295
|
this._shaderCache = new WGPUShaderCache(device);
|
|
296
|
+
if (_textureSelection.support)
|
|
297
|
+
this._bindless = new WGPUBindlessTextures(device);
|
|
348
298
|
this._uniforms = {
|
|
349
299
|
cameraUniform: this._uniformBuffer(36 * 4),
|
|
350
300
|
rtUniform: this._uniformBuffer(this._rtUniform.buffer.byteLength),
|
|
@@ -382,8 +332,14 @@ class WGPURayTracePass {
|
|
|
382
332
|
device.queue.writeTexture({ texture: this._adaptiveSamplingTexture }, new Float32Array([Number.MAX_SAFE_INTEGER]), { bytesPerRow: 4 }, [1, 1]);
|
|
383
333
|
}
|
|
384
334
|
getOutputBuffer() {
|
|
335
|
+
// Another render may have traced since this target's samples were written.
|
|
336
|
+
const transport = this._targetTransport.get(this._activeTarget) ?? 0;
|
|
385
337
|
return {
|
|
386
338
|
color: this._outColorBuffer,
|
|
339
|
+
...(transport & 2 || ((this._transport?.debugGroup ?? -1) >= 0 || causticDiagnostics.debugGroup >= 0) ? { wideHdr: true } : {}),
|
|
340
|
+
...(transport & 1 && this._causticLight?.get(this._activeTarget)
|
|
341
|
+
? { caustic: this._causticLight.get(this._activeTarget) }
|
|
342
|
+
: {}),
|
|
387
343
|
normal: this._outNormalBuffer,
|
|
388
344
|
albedo: this._outAlbedoBuffer,
|
|
389
345
|
shadowCatcher: this._outShadowCatcherBuffer
|
|
@@ -578,130 +534,28 @@ class WGPURayTracePass {
|
|
|
578
534
|
varianceBuffer.destroy();
|
|
579
535
|
}
|
|
580
536
|
}
|
|
581
|
-
_createPipelineLayout() {
|
|
582
|
-
|
|
583
|
-
const commonBufferLayoutEntries = Array.from({ length: 4 }, (_, i) => ({
|
|
584
|
-
binding: i,
|
|
585
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
586
|
-
buffer: {
|
|
587
|
-
type: 'read-only-storage'
|
|
588
|
-
}
|
|
589
|
-
}));
|
|
590
|
-
const buffersBindGroupLayout = this._shaderCache.createBindGroupLayout({
|
|
591
|
-
label: 'Buffers',
|
|
592
|
-
entries: commonBufferLayoutEntries
|
|
593
|
-
});
|
|
594
|
-
const textureLayoutEntries = [
|
|
595
|
-
{
|
|
596
|
-
binding: 0,
|
|
597
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
598
|
-
sampler: {}
|
|
599
|
-
},
|
|
600
|
-
{
|
|
601
|
-
binding: 1,
|
|
602
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
603
|
-
texture: {
|
|
604
|
-
viewDimension: '2d-array'
|
|
605
|
-
}
|
|
606
|
-
},
|
|
607
|
-
// Map size
|
|
608
|
-
{
|
|
609
|
-
binding: 3,
|
|
610
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
611
|
-
buffer: {
|
|
612
|
-
type: 'uniform'
|
|
613
|
-
}
|
|
614
|
-
},
|
|
615
|
-
// Env map
|
|
616
|
-
{
|
|
617
|
-
binding: 4,
|
|
618
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
619
|
-
texture: {
|
|
620
|
-
viewDimension: '2d',
|
|
621
|
-
sampleType: 'unfilterable-float'
|
|
622
|
-
}
|
|
623
|
-
},
|
|
624
|
-
// Env map distribution
|
|
625
|
-
{
|
|
626
|
-
binding: 5,
|
|
627
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
628
|
-
texture: {
|
|
629
|
-
viewDimension: '2d',
|
|
630
|
-
sampleType: 'unfilterable-float'
|
|
631
|
-
}
|
|
632
|
-
},
|
|
633
|
-
// Background map
|
|
634
|
-
{
|
|
635
|
-
binding: 6,
|
|
636
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
637
|
-
texture: {
|
|
638
|
-
viewDimension: '2d',
|
|
639
|
-
sampleType: 'unfilterable-float'
|
|
640
|
-
}
|
|
641
|
-
},
|
|
642
|
-
{
|
|
643
|
-
binding: 8,
|
|
644
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
645
|
-
texture: {
|
|
646
|
-
viewDimension: '2d',
|
|
647
|
-
sampleType: 'unfilterable-float'
|
|
648
|
-
}
|
|
649
|
-
},
|
|
650
|
-
{
|
|
651
|
-
binding: 9,
|
|
652
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
653
|
-
texture: {
|
|
654
|
-
viewDimension: '2d',
|
|
655
|
-
sampleType: 'unfilterable-float'
|
|
656
|
-
}
|
|
657
|
-
},
|
|
658
|
-
{
|
|
659
|
-
binding: 10,
|
|
660
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
661
|
-
texture: {
|
|
662
|
-
viewDimension: '2d',
|
|
663
|
-
sampleType: 'unfilterable-float'
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
];
|
|
667
|
-
const texturesBindGroupLayout = this._shaderCache.createBindGroupLayout({
|
|
668
|
-
label: 'Textures', entries: textureLayoutEntries
|
|
669
|
-
});
|
|
670
|
-
const uniformsBindGroupLayout = this._shaderCache.createBindGroupLayout({
|
|
671
|
-
label: 'Uniforms',
|
|
672
|
-
entries: [
|
|
673
|
-
...Array.from({ length: Object.keys(this._uniforms).length }, (_, i) => ({
|
|
674
|
-
binding: i,
|
|
675
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
676
|
-
buffer: {
|
|
677
|
-
type: 'uniform'
|
|
678
|
-
}
|
|
679
|
-
})),
|
|
680
|
-
...([])
|
|
681
|
-
]
|
|
682
|
-
});
|
|
683
|
-
const rayTraceOutBuffersBindGroupLayout = this._shaderCache.createBindGroupLayout({
|
|
684
|
-
label: 'Ray Trace Out Buffers',
|
|
685
|
-
entries: [
|
|
686
|
-
...Array.from({ length: 4 }, (_, i) => ({
|
|
687
|
-
binding: i,
|
|
688
|
-
visibility: GPUShaderStage.COMPUTE,
|
|
689
|
-
buffer: { type: 'storage' }
|
|
690
|
-
})),
|
|
691
|
-
...([])
|
|
692
|
-
]
|
|
693
|
-
});
|
|
694
|
-
this._rayTracePipelineLayout = this._shaderCache.createPipelineLayout({
|
|
537
|
+
_createPipelineLayout(bindless, photonMapping) {
|
|
538
|
+
const descriptor = {
|
|
695
539
|
label: 'Ray Trace Pipeline Layout',
|
|
696
|
-
bindGroupLayouts:
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
540
|
+
bindGroupLayouts: (bindless ? bindlessBindGroupLayouts() : rayTraceBindGroupLayouts()).map((layout, index) => {
|
|
541
|
+
if (photonMapping && index === (bindless ? 0 : 1))
|
|
542
|
+
layout = {
|
|
543
|
+
...layout,
|
|
544
|
+
entries: [
|
|
545
|
+
...layout.entries,
|
|
546
|
+
...[11, 12].map((binding) => ({
|
|
547
|
+
binding: binding + (bindless ? 4 : 0),
|
|
548
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
549
|
+
texture: { sampleType: 'uint' }
|
|
550
|
+
}))
|
|
551
|
+
]
|
|
552
|
+
};
|
|
553
|
+
return this._shaderCache.createBindGroupLayout(layout);
|
|
554
|
+
})
|
|
555
|
+
};
|
|
556
|
+
return this._shaderCache.createPipelineLayout(bindless ? resourceTableLayout(descriptor) : descriptor);
|
|
703
557
|
}
|
|
704
|
-
_updateGPUBuffer(label, gpuBuffer, data, minSize =
|
|
558
|
+
_updateGPUBuffer(label, gpuBuffer, data, minSize = WGPU_SCENE_BUFFER_MIN_BYTES) {
|
|
705
559
|
const device = this._device;
|
|
706
560
|
const size = Math.max(data.byteLength, minSize);
|
|
707
561
|
if (gpuBuffer == null || gpuBuffer.size < size) {
|
|
@@ -727,17 +581,45 @@ class WGPURayTracePass {
|
|
|
727
581
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
728
582
|
});
|
|
729
583
|
}
|
|
730
|
-
|
|
731
|
-
if (this.
|
|
584
|
+
_preparePhotonTarget() {
|
|
585
|
+
if (!this._photonMapping)
|
|
732
586
|
return;
|
|
587
|
+
this._photonMap ??= new WGPUPhotonMap(this._device);
|
|
588
|
+
const before = this._photonMap.get(this._activeTarget)?.data;
|
|
589
|
+
const requested = this._causticSettings?.photonMapping;
|
|
590
|
+
const n = typeof requested === 'object' ? requested.photonsPerSample : undefined;
|
|
591
|
+
const after = this._photonMap.ensure(this._activeTarget, Number.isFinite(n) && n > 0 ? n : defaultPhotonMapPhotons(this._width, this._height, causticDiagnostics.photonCountDivisor));
|
|
592
|
+
if (before !== after.data)
|
|
593
|
+
this._needsUpdateBindGroups = true;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Prepares the pipeline for the settings currently written to this pass.
|
|
597
|
+
* Everything except compilation runs synchronously, and compilation only
|
|
598
|
+
* writes to its own variant. Renders with different settings can therefore
|
|
599
|
+
* interleave without invalidating each other. Returns a promise while the
|
|
600
|
+
* requested state waits on the GPU; undefined means nothing is outstanding.
|
|
601
|
+
*/
|
|
602
|
+
updatePipeline() {
|
|
603
|
+
if (this._disposed)
|
|
604
|
+
return;
|
|
605
|
+
if (this._needsUpdateMeshData || this._needsUpdateMeshMembership || this._pendingGeometryUpdates.size ||
|
|
606
|
+
this._needsRefitTLAS || this._dirtyColorMeshes.size || this._pendingMaterialUpdate || this._needsUpdateLightSamplingData) {
|
|
607
|
+
// A failure can have the same shader signature as its repaired scene.
|
|
608
|
+
// Retry after authored updates, while keeping an unchanged failure idle.
|
|
609
|
+
this._resourceError = undefined;
|
|
610
|
+
for (const [signature, variant] of this._traceVariants)
|
|
611
|
+
if (variant.status === 'failed')
|
|
612
|
+
this._traceVariants.delete(signature);
|
|
613
|
+
if (this._aovPipelinesFailed) {
|
|
614
|
+
this._aovPipelines = new WeakMap();
|
|
615
|
+
this._aovPipelinesFailed = false;
|
|
616
|
+
}
|
|
733
617
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
this.
|
|
738
|
-
|
|
739
|
-
let compilationStartedAt;
|
|
740
|
-
let pipelineCompilationStartedAt;
|
|
618
|
+
this._awaitingRetirement = this._bindless?.retirement;
|
|
619
|
+
if (this._awaitingRetirement)
|
|
620
|
+
return this._awaitingRetirement;
|
|
621
|
+
if (this._resourceError)
|
|
622
|
+
return;
|
|
741
623
|
try {
|
|
742
624
|
if (this._needsUpdateMeshData) {
|
|
743
625
|
this._updateMeshData();
|
|
@@ -753,314 +635,420 @@ class WGPURayTracePass {
|
|
|
753
635
|
this._updateMeshColors();
|
|
754
636
|
if (this._pendingMaterialUpdate)
|
|
755
637
|
this._updateMaterialData();
|
|
638
|
+
this._setCaustics(this._causticSettings);
|
|
756
639
|
if (this._needsUpdateLightSamplingData) {
|
|
757
640
|
this._updateLightSamplingData();
|
|
758
641
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
642
|
+
this._setCaustics(this._causticSettings);
|
|
643
|
+
if (this._lightTracing || this._photonMapping)
|
|
644
|
+
this._causticBounds = causticCasterSphere(this._meshes);
|
|
645
|
+
this._targetTransport.set(this._activeTarget, (+this._lightTracing) | (+this._photonMapping << 1));
|
|
646
|
+
this._preparePhotonTarget();
|
|
647
|
+
const pipelineInputs = JSON.stringify([
|
|
648
|
+
this._constants,
|
|
649
|
+
this._hasDisplacement,
|
|
650
|
+
this._alphaBlendStrategy,
|
|
651
|
+
this._blasLayoutMask,
|
|
652
|
+
this._bakePipelineConstants,
|
|
653
|
+
this._causticKey,
|
|
654
|
+
!!this._bindless
|
|
655
|
+
]);
|
|
656
|
+
// Steady rendering must not repeat capability selection and cache-key
|
|
657
|
+
// preparation. Dirty resources still take the normal bind-group path.
|
|
658
|
+
if (pipelineInputs === this._activeInputs &&
|
|
659
|
+
this._rayTracePipeline &&
|
|
660
|
+
this._rayTraceBindGroups &&
|
|
661
|
+
!this._needsUpdateBindGroups) {
|
|
662
|
+
if (this._needsPhotonTargetBindGroups || this._needsUpdateOutputBindGroup) {
|
|
663
|
+
this._needsPhotonTargetBindGroups = false;
|
|
664
|
+
this._needsUpdateOutputBindGroup = false;
|
|
665
|
+
this._updateBindGroups();
|
|
666
|
+
}
|
|
667
|
+
this._requestedVariant = this._activeVariant;
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
this._rtUniform.views.sceneFeatureFlags[0] = wgpuSceneFeatureFlags(this._constants);
|
|
671
|
+
const requiredCapabilities = retainedShaderCapabilities(this._constants);
|
|
672
|
+
const constants = { ...this._constants, ...requiredCapabilities };
|
|
673
|
+
const hasAlpha = Boolean(constants.HAS_ALPHA_BLEND) && this._alphaBlendStrategy === 'deterministic-source-over';
|
|
764
674
|
const hasSceneAlpha = Boolean(constants.HAS_ALPHA);
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
.map((key) => [key, constants[key]]),
|
|
675
|
+
const pipelineConstantsKeys = wgpuPipelineConstantKeys(constants);
|
|
676
|
+
const defineCode = WGPU_SHADER_DEFINE_KEYS.map((key) => `const ${key} = ${constants[key]};`).join('\n');
|
|
677
|
+
const structure = {
|
|
769
678
|
hasDisplacement: this._hasDisplacement,
|
|
679
|
+
caustics: this._causticKey,
|
|
680
|
+
bindless: !!this._bindless,
|
|
770
681
|
hasAlpha,
|
|
771
682
|
hasSceneAlpha,
|
|
772
683
|
hasAnisotropyDirections: Boolean(constants.USE_ANISOTROPY_DIRECTIONS),
|
|
773
684
|
hasCoatNormals: Boolean(constants.USE_COAT_NORMAL_FRAMES),
|
|
774
685
|
blasLayoutMask: this._blasLayoutMask,
|
|
775
686
|
bakePipelineConstants: this._bakePipelineConstants
|
|
687
|
+
};
|
|
688
|
+
const structureKey = JSON.stringify({
|
|
689
|
+
...structure,
|
|
690
|
+
defineCode,
|
|
691
|
+
constants: pipelineConstantsKeys
|
|
692
|
+
.filter((key) => !(key in requiredCapabilities))
|
|
693
|
+
.map((key) => [key, constants[key]])
|
|
694
|
+
});
|
|
695
|
+
// Prefer an existing compatible pipeline, including an earlier lean
|
|
696
|
+
// variant. Only grow the current variant when it cannot cover an edit;
|
|
697
|
+
// do not carry unused capabilities across different transport/layouts.
|
|
698
|
+
const capabilities = this._shaderCapabilities.find(structureKey, requiredCapabilities) ??
|
|
699
|
+
retainedShaderCapabilities(this._constants, structureKey === this._retainedShaderStructure ? this._retainedShaderCapabilities : undefined);
|
|
700
|
+
Object.assign(constants, capabilities);
|
|
701
|
+
// Runtime feature values live in uniforms, so edits can share a pipeline
|
|
702
|
+
// as long as its compiled defines and overrides are the same.
|
|
703
|
+
const signature = JSON.stringify({
|
|
704
|
+
...structure,
|
|
705
|
+
defineCode,
|
|
706
|
+
constants: pipelineConstantsKeys.map((key) => [key, constants[key]])
|
|
776
707
|
});
|
|
777
|
-
|
|
778
|
-
|
|
708
|
+
const variant = this._traceVariants.get(signature) ??
|
|
709
|
+
this._startTraceVariant(signature, constants, { hasAlpha, hasSceneAlpha, defineCode, pipelineConstantsKeys, structureKey, capabilities });
|
|
710
|
+
this._requestedVariant = variant;
|
|
711
|
+
if (variant.status === 'pending') {
|
|
712
|
+
this._requestQueuedCompile(variant);
|
|
713
|
+
return variant.settled;
|
|
714
|
+
}
|
|
715
|
+
if (variant.status === 'failed')
|
|
779
716
|
return;
|
|
717
|
+
this._installTraceVariant(variant, pipelineInputs);
|
|
718
|
+
}
|
|
719
|
+
catch (error) {
|
|
720
|
+
this._resourceError = error;
|
|
721
|
+
this._needsUpdateBindGroups = true;
|
|
722
|
+
console.error('Failed to update WebGPU ray tracing resources.', error);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
/** Runs only for a ready variant, without yielding, so tracing can start in this call. */
|
|
726
|
+
_installTraceVariant(variant, pipelineInputs) {
|
|
727
|
+
if (this._activeVariant !== variant) {
|
|
728
|
+
this._activeVariant = variant;
|
|
729
|
+
this._rayTracePipeline = variant.pipeline;
|
|
730
|
+
this._depthCompute = variant.depthCompute;
|
|
731
|
+
this._needsUpdateBindGroups = true;
|
|
732
|
+
}
|
|
733
|
+
const emission = () => causticEmissionTable(this._analyticLights, this._emissiveTriangles, this._causticBounds[3], this._envImageSource && 'sky' in this._envImageSource
|
|
734
|
+
? proceduralEnvironmentPower(this._envImageSource)
|
|
735
|
+
: estimateEnvironmentPower(this._envImageSource));
|
|
736
|
+
const casters = () => causticDiagnostics.aggregateCasters ? [this._causticBounds] : causticCasterSpheres(this._meshes);
|
|
737
|
+
if (this._lightTracing && this._causticLight) {
|
|
738
|
+
this._causticLight.setDistribution(casters(), emission());
|
|
739
|
+
this._causticLight.use(variant.lightEmitter);
|
|
740
|
+
}
|
|
741
|
+
if (this._photonMapping && this._photonMap) {
|
|
742
|
+
this._photonMap.emission.setDistribution(casters(), emission());
|
|
743
|
+
this._photonMap.emission.use(variant.photonEmitter);
|
|
744
|
+
}
|
|
745
|
+
// TODO rebind when lights, materials, bvh changed.
|
|
746
|
+
if (this._needsUpdateBindGroups || this._needsPhotonTargetBindGroups || this._needsUpdateOutputBindGroup) {
|
|
747
|
+
this._needsUpdateBindGroups = false;
|
|
748
|
+
this._needsPhotonTargetBindGroups = false;
|
|
749
|
+
this._needsUpdateOutputBindGroup = false;
|
|
750
|
+
this._updateBindGroups();
|
|
751
|
+
}
|
|
752
|
+
this._retainedShaderCapabilities = variant.capabilities;
|
|
753
|
+
this._retainedShaderStructure = variant.structureKey;
|
|
754
|
+
this._shaderCapabilities.remember(variant.structureKey, variant.capabilities);
|
|
755
|
+
this._activeInputs = pipelineInputs;
|
|
756
|
+
}
|
|
757
|
+
/** Snapshots everything compilation needs. The task must not read mutable pass state afterwards. */
|
|
758
|
+
_startTraceVariant(signature, constants, shader) {
|
|
759
|
+
const bake = this._bakePipelineConstants, bindless = this._bindless;
|
|
760
|
+
const hasDisplacement = this._hasDisplacement, blasLayoutMask = this._blasLayoutMask;
|
|
761
|
+
const lightTracing = this._lightTracing, photonMapping = this._photonMapping;
|
|
762
|
+
const causticKey = this._causticKey, transport = this._transport;
|
|
763
|
+
const { hasAlpha, hasSceneAlpha, defineCode, pipelineConstantsKeys } = shader;
|
|
764
|
+
const emitterSource = (mode) => {
|
|
765
|
+
const code = causticLightCompute(constants, blasLayoutMask, hasDisplacement, mode, '', causticDiagnostics.fullEmissionRandom !== false);
|
|
766
|
+
return bindless ? bindlessShaderSource(code, this._textureSelection.support.enable) : code;
|
|
767
|
+
};
|
|
768
|
+
const lightEmitter = lightTracing ? this._causticLight ??= new WGPUCausticLightPass(this._device) : undefined;
|
|
769
|
+
const photonEmitter = photonMapping ? this._photonMap?.emission : undefined;
|
|
770
|
+
const pipelineDiagnostic = {
|
|
771
|
+
label: 'Ray Trace', entryPoint: 'main', firstFrameRequired: true, status: 'compiling',
|
|
772
|
+
startedAtMs: 0, concurrentCompilationCount: 1
|
|
773
|
+
};
|
|
774
|
+
const variant = {
|
|
775
|
+
status: 'pending', capabilities: shader.capabilities, structureKey: shader.structureKey,
|
|
776
|
+
settled: undefined,
|
|
777
|
+
diagnostics: {
|
|
778
|
+
status: 'compiling', sourceBytes: 0, sourceLines: 0, pipelineCount: 1,
|
|
779
|
+
completedPipelineCount: 0, sourceGenerationTimeMs: 0, moduleCreationTimeMs: 0,
|
|
780
|
+
modules: [], pipelines: [{ ...pipelineDiagnostic }], maximumConcurrentPipelineCount: 1, peakConcurrentPipelineCount: 1
|
|
780
781
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
'USE_GROUNDED_BACKGROUND',
|
|
796
|
-
...MATERIAL_TEXTURE_DEFINES,
|
|
797
|
-
'USE_ANISOTROPY_DIRECTIONS',
|
|
798
|
-
'USE_COAT_NORMAL_FRAMES',
|
|
799
|
-
'HAS_ALPHA_BLEND',
|
|
800
|
-
'HAS_ALPHA_MASK'
|
|
801
|
-
];
|
|
802
|
-
const defines = definesKeys.reduce((obj, key) => {
|
|
803
|
-
obj[key] = constants[key];
|
|
804
|
-
return obj;
|
|
805
|
-
}, {});
|
|
806
|
-
const pipelineConstantsKeys = Object.keys(constants)
|
|
807
|
-
.sort()
|
|
808
|
-
.filter((key) => !definesKeys.includes(key) && !constantsNotUsed.includes(key));
|
|
809
|
-
const defineCode = definesKeys
|
|
810
|
-
.map((key) => `const ${key} = ${defines[key]};`)
|
|
811
|
-
.join('\n');
|
|
812
|
-
const previousPipelineConstantsKeys = Object.keys(this._prevPipelineConstants)
|
|
813
|
-
.filter((key) => !definesKeys.includes(key) && !constantsNotUsed.includes(key))
|
|
814
|
-
.sort();
|
|
815
|
-
const needsCreateShaderModule = !this._rayTracePipelineLayout ||
|
|
816
|
-
!this._computeShaderModule ||
|
|
817
|
-
pipelineConstantsKeys.join(',') !==
|
|
818
|
-
previousPipelineConstantsKeys.join(',') ||
|
|
819
|
-
defineCode !== this._prevDefinesCode ||
|
|
820
|
-
hasAlpha !== this._compiledHasAlpha ||
|
|
821
|
-
hasSceneAlpha !== this._compiledSceneHasAlpha ||
|
|
822
|
-
Boolean(constants.USE_ANISOTROPY_DIRECTIONS) !== this._compiledAnisotropyDirections ||
|
|
823
|
-
Boolean(constants.USE_COAT_NORMAL_FRAMES) !== this._compiledCoatNormals ||
|
|
824
|
-
this._blasLayoutMask !== this._compiledBlasLayoutMask ||
|
|
825
|
-
(this._bakePipelineConstants &&
|
|
826
|
-
pipelineConstantsKeys.some((key) => constants[key] !== this._prevPipelineConstants[key]));
|
|
827
|
-
const needsCreatePipeline = needsCreateShaderModule ||
|
|
828
|
-
pipelineConstantsKeys.some((key) => constants[key] !== this._prevPipelineConstants[key]);
|
|
829
|
-
compilationStartedAt = needsCreatePipeline
|
|
830
|
-
? shaderCompilationNow()
|
|
831
|
-
: undefined;
|
|
832
|
-
let sourceGenerationTimeMs = 0;
|
|
833
|
-
let moduleCreationTimeMs = 0;
|
|
834
|
-
let sourceBytes = this._shaderCompilationDiagnostics?.sourceBytes ?? 0;
|
|
835
|
-
let sourceLines = this._shaderCompilationDiagnostics?.sourceLines ?? 0;
|
|
836
|
-
let sourceFingerprint = this._shaderCompilationDiagnostics?.modules[0]?.sourceFingerprint ?? '';
|
|
837
|
-
if (needsCreateShaderModule) {
|
|
838
|
-
const sourceGenerationStartedAt = shaderCompilationNow();
|
|
839
|
-
const constantsCode = pipelineConstantsKeys
|
|
840
|
-
.map((key) => `${this._bakePipelineConstants ? 'const' : 'override'} ${key} = ${constants[key].toString()};`)
|
|
841
|
-
.join('\n');
|
|
842
|
-
const rayTraceShaderCode = /* wgsl */ `
|
|
843
|
-
${defineCode}
|
|
844
|
-
${constantsCode}
|
|
845
|
-
${rayTraceCompute({
|
|
846
|
-
hasDisplacement: this._hasDisplacement,
|
|
782
|
+
};
|
|
783
|
+
const device = this._device, groupIndices = bindless ? [0, 1] : [0, 1, 2];
|
|
784
|
+
let startedAt = 0;
|
|
785
|
+
const compile = async () => {
|
|
786
|
+
startedAt = shaderCompilationNow();
|
|
787
|
+
// Override values are not part of the WGSL unless they are baked, so a
|
|
788
|
+
// quality edit can share the module that the driver has already parsed.
|
|
789
|
+
const moduleKey = JSON.stringify([causticKey, !!bindless, hasAlpha, hasSceneAlpha, hasDisplacement,
|
|
790
|
+
constants.USE_ANISOTROPY_DIRECTIONS, constants.USE_COAT_NORMAL_FRAMES, blasLayoutMask, defineCode, pipelineConstantsKeys,
|
|
791
|
+
bake && pipelineConstantsKeys.map((key) => constants[key])]);
|
|
792
|
+
let source = this._traceModules.get(moduleKey);
|
|
793
|
+
if (!source) {
|
|
794
|
+
let code = wgpuShaderSource(constants, {
|
|
795
|
+
hasDisplacement,
|
|
847
796
|
hasAlpha,
|
|
848
797
|
hasSceneAlpha,
|
|
798
|
+
...transport?.features,
|
|
799
|
+
lightTracing,
|
|
800
|
+
photonMapping,
|
|
801
|
+
photonMaxDepth: causticDiagnostics.photonMaxDepth ?? 20,
|
|
802
|
+
photonDirectSubset: causticDiagnostics.photonDirectSubset,
|
|
803
|
+
debugGroup: causticDiagnostics.debugGroup,
|
|
804
|
+
workgroupSize: transport?.workgroupSize ?? WORKGROUP_SIZE,
|
|
849
805
|
hasAnisotropyDirections: Boolean(constants.USE_ANISOTROPY_DIRECTIONS),
|
|
850
806
|
hasCoatNormals: Boolean(constants.USE_COAT_NORMAL_FRAMES),
|
|
851
|
-
blasLayoutMask
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
sourceGenerationTimeMs =
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
this._compiledSceneHasAlpha = hasSceneAlpha;
|
|
867
|
-
this._compiledAnisotropyDirections = Boolean(constants.USE_ANISOTROPY_DIRECTIONS);
|
|
868
|
-
this._compiledCoatNormals = Boolean(constants.USE_COAT_NORMAL_FRAMES);
|
|
869
|
-
this._compiledBlasLayoutMask = this._blasLayoutMask;
|
|
807
|
+
blasLayoutMask,
|
|
808
|
+
dynamicEnvironment: true
|
|
809
|
+
}, bake);
|
|
810
|
+
if (bindless)
|
|
811
|
+
code = bindlessShaderSource(code, this._textureSelection.support.enable);
|
|
812
|
+
variant.diagnostics.sourceGenerationTimeMs = shaderCompilationNow() - startedAt;
|
|
813
|
+
const moduleStartedAt = shaderCompilationNow();
|
|
814
|
+
source = {
|
|
815
|
+
module: this._shaderCache.createShaderModule({ label: 'Ray Trace', code }),
|
|
816
|
+
bytes: code.length, lines: shaderSourceLineCount(code), fingerprint: shaderSourceFingerprint(code)
|
|
817
|
+
};
|
|
818
|
+
variant.diagnostics.moduleCreationTimeMs = shaderCompilationNow() - moduleStartedAt;
|
|
819
|
+
this._traceModules.set(moduleKey, source);
|
|
820
|
+
if (this._traceModules.size > 8)
|
|
821
|
+
this._traceModules.delete(this._traceModules.keys().next().value);
|
|
870
822
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
};
|
|
912
|
-
pipelineCompilationStartedAt = shaderCompilationNow();
|
|
913
|
-
diagnostic.startedAtMs = 0;
|
|
914
|
-
diagnostic.concurrentCompilationCount = 1;
|
|
915
|
-
this._shaderCompilationDiagnostics = {
|
|
916
|
-
status: 'compiling',
|
|
917
|
-
sourceBytes,
|
|
918
|
-
sourceLines,
|
|
919
|
-
pipelineCount: 1,
|
|
920
|
-
completedPipelineCount: 0,
|
|
921
|
-
sourceGenerationTimeMs,
|
|
922
|
-
moduleCreationTimeMs,
|
|
923
|
-
modules: [
|
|
924
|
-
{
|
|
925
|
-
label: 'Ray Trace',
|
|
926
|
-
sourceBytes,
|
|
927
|
-
sourceLines,
|
|
928
|
-
sourceFingerprint
|
|
929
|
-
}
|
|
930
|
-
],
|
|
931
|
-
pipelines: [{ ...diagnostic }],
|
|
932
|
-
maximumConcurrentPipelineCount: 1,
|
|
933
|
-
peakConcurrentPipelineCount: 1
|
|
934
|
-
};
|
|
935
|
-
let rayTracePipeline;
|
|
936
|
-
try {
|
|
937
|
-
rayTracePipeline = await this._shaderCache.createComputePipelineAsync({
|
|
938
|
-
label: 'Ray Trace',
|
|
939
|
-
layout: this._rayTracePipelineLayout,
|
|
940
|
-
compute: {
|
|
941
|
-
module: this._computeShaderModule,
|
|
942
|
-
entryPoint: 'main',
|
|
943
|
-
constants: pipelineConstants
|
|
944
|
-
}
|
|
945
|
-
});
|
|
946
|
-
diagnostic.status = 'ready';
|
|
947
|
-
}
|
|
948
|
-
catch (error) {
|
|
949
|
-
diagnostic.status = 'failed';
|
|
950
|
-
diagnostic.error = getWebGPUErrorMessage(error);
|
|
951
|
-
// Pipeline creation already reports WGSL failures. Pull the much
|
|
952
|
-
// heavier module diagnostics only on that failure path so normal
|
|
953
|
-
// startup does not schedule a second full-module inspection.
|
|
954
|
-
const info = await this._computeShaderModule
|
|
955
|
-
.getCompilationInfo()
|
|
956
|
-
.catch(() => undefined);
|
|
957
|
-
const shaderErrors = (info?.messages ?? [])
|
|
958
|
-
.filter((message) => message.type === 'error');
|
|
959
|
-
if (shaderErrors.length) {
|
|
960
|
-
throw new Error(shaderErrors
|
|
961
|
-
.map((message) => `Ray Trace WGSL ${message.lineNum}:${message.linePos} ${message.message}`)
|
|
962
|
-
.join('\n'));
|
|
963
|
-
}
|
|
964
|
-
throw error;
|
|
965
|
-
}
|
|
966
|
-
finally {
|
|
967
|
-
diagnostic.endedAtMs =
|
|
968
|
-
shaderCompilationNow() - pipelineCompilationStartedAt;
|
|
969
|
-
diagnostic.durationMs = diagnostic.endedAtMs;
|
|
970
|
-
}
|
|
971
|
-
this._rayTracePipeline = rayTracePipeline;
|
|
972
|
-
this._depthCompute = { module: this._computeShaderModule, constants: pipelineConstants };
|
|
973
|
-
const pipelineError = await device.popErrorScope();
|
|
974
|
-
pipelineScopeOpen = false;
|
|
975
|
-
if (pipelineError)
|
|
976
|
-
throw pipelineError;
|
|
977
|
-
const compilationCompletedAt = shaderCompilationNow();
|
|
978
|
-
this._shaderCompilationDiagnostics = {
|
|
979
|
-
...this._shaderCompilationDiagnostics,
|
|
980
|
-
status: 'ready',
|
|
981
|
-
completedPipelineCount: 1,
|
|
982
|
-
pipelines: [{ ...diagnostic }],
|
|
983
|
-
pipelineCompilationTimeMs: compilationCompletedAt - pipelineCompilationStartedAt,
|
|
984
|
-
totalTimeMs: compilationCompletedAt - compilationStartedAt
|
|
985
|
-
};
|
|
986
|
-
}
|
|
987
|
-
catch (error) {
|
|
988
|
-
const scopedError = pipelineScopeOpen
|
|
989
|
-
? await device.popErrorScope().catch(() => null)
|
|
990
|
-
: null;
|
|
991
|
-
const detail = joinWebGPUErrors(error, scopedError);
|
|
992
|
-
if (!this._bakePipelineConstants &&
|
|
993
|
-
isWebGPUPipelineSpecializationFailure(error, scopedError)) {
|
|
994
|
-
const failedAt = shaderCompilationNow();
|
|
995
|
-
if (this._shaderCompilationDiagnostics?.status === 'compiling') {
|
|
996
|
-
this._shaderCompilationDiagnostics = {
|
|
997
|
-
...this._shaderCompilationDiagnostics,
|
|
998
|
-
status: 'failed',
|
|
999
|
-
pipelineCompilationTimeMs: pipelineCompilationStartedAt == null
|
|
1000
|
-
? undefined
|
|
1001
|
-
: failedAt - pipelineCompilationStartedAt,
|
|
1002
|
-
totalTimeMs: compilationStartedAt == null
|
|
1003
|
-
? undefined
|
|
1004
|
-
: failedAt - compilationStartedAt,
|
|
1005
|
-
error: detail
|
|
1006
|
-
};
|
|
1007
|
-
}
|
|
1008
|
-
// WebKit can accept a large WGSL module but fail while translating
|
|
1009
|
-
// its specialization-constant variant to Metal. Baking the values
|
|
1010
|
-
// into WGSL lets its front end eliminate disabled feature branches
|
|
1011
|
-
// before creating the Metal library.
|
|
1012
|
-
this._bakePipelineConstants = true;
|
|
1013
|
-
this._rayTracePipeline = undefined;
|
|
1014
|
-
this._rayTraceBindGroups = undefined;
|
|
1015
|
-
this._computeShaderModule = undefined;
|
|
1016
|
-
this._prevPipelineConstants = {};
|
|
1017
|
-
console.warn('WebGPU pipeline specialization failed; retrying with constants baked into WGSL.');
|
|
1018
|
-
return;
|
|
1019
|
-
}
|
|
1020
|
-
throw new Error(detail);
|
|
823
|
+
Object.assign(variant.diagnostics, {
|
|
824
|
+
sourceBytes: source.bytes, sourceLines: source.lines,
|
|
825
|
+
modules: [{ label: 'Ray Trace', sourceBytes: source.bytes, sourceLines: source.lines, sourceFingerprint: source.fingerprint }]
|
|
826
|
+
});
|
|
827
|
+
const { module } = source;
|
|
828
|
+
const layout = this._createPipelineLayout(!!bindless, photonMapping);
|
|
829
|
+
const pipelineConstants = !bake
|
|
830
|
+
? pipelineConstantsKeys.reduce((obj, key) => {
|
|
831
|
+
obj[key] = +constants[key];
|
|
832
|
+
return obj;
|
|
833
|
+
}, {})
|
|
834
|
+
: undefined;
|
|
835
|
+
const lightSource = lightEmitter && emitterSource('lightTracing');
|
|
836
|
+
const photonSource = photonEmitter && emitterSource('photonMapping');
|
|
837
|
+
// Scopes capture all device errors while open, so one must never span an
|
|
838
|
+
// await: frames presented meanwhile would be blamed on this pipeline.
|
|
839
|
+
device.pushErrorScope('validation');
|
|
840
|
+
const request = this._shaderCache.createComputePipelineAsync({
|
|
841
|
+
label: 'Ray Trace', layout,
|
|
842
|
+
compute: { module, entryPoint: 'main', constants: pipelineConstants }
|
|
843
|
+
});
|
|
844
|
+
const scope = device.popErrorScope().catch(() => null);
|
|
845
|
+
let pipeline;
|
|
846
|
+
try {
|
|
847
|
+
pipeline = await request;
|
|
848
|
+
const scopedError = await scope;
|
|
849
|
+
if (scopedError)
|
|
850
|
+
throw scopedError;
|
|
851
|
+
}
|
|
852
|
+
catch (error) {
|
|
853
|
+
const scopedError = await scope;
|
|
854
|
+
// Pipeline creation already reports WGSL failures. Pull the much
|
|
855
|
+
// heavier module diagnostics only on that failure path so normal
|
|
856
|
+
// startup does not schedule a second full-module inspection.
|
|
857
|
+
const info = await module.getCompilationInfo().catch(() => undefined);
|
|
858
|
+
const shaderErrors = (info?.messages ?? []).filter((message) => message.type === 'error');
|
|
859
|
+
if (shaderErrors.length) {
|
|
860
|
+
throw new Error(shaderErrors
|
|
861
|
+
.map((message) => `Ray Trace WGSL ${message.lineNum}:${message.linePos} ${message.message}`)
|
|
862
|
+
.join('\n'));
|
|
1021
863
|
}
|
|
864
|
+
if (isWebGPUPipelineSpecializationFailure(error, scopedError))
|
|
865
|
+
throw error;
|
|
866
|
+
throw new Error(joinWebGPUErrors(error, scopedError));
|
|
1022
867
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
868
|
+
const groups = lightEmitter || photonEmitter ? groupIndices.map((i) => pipeline.getBindGroupLayout(i)) : [];
|
|
869
|
+
const [light, photon] = await Promise.all([
|
|
870
|
+
lightEmitter?.prepare(lightSource, groups, !!bindless),
|
|
871
|
+
photonEmitter?.prepare(photonSource, groups, !!bindless)
|
|
872
|
+
]);
|
|
873
|
+
Object.assign(variant, { pipeline, lightEmitter: light, photonEmitter: photon,
|
|
874
|
+
depthCompute: { module, constants: pipelineConstants } });
|
|
875
|
+
};
|
|
876
|
+
const finishDiagnostics = (status, error) => {
|
|
877
|
+
const totalTimeMs = shaderCompilationNow() - startedAt;
|
|
878
|
+
Object.assign(variant.diagnostics, {
|
|
879
|
+
status, completedPipelineCount: status === 'ready' ? 1 : 0, error,
|
|
880
|
+
pipelines: [{ ...pipelineDiagnostic, status, error, endedAtMs: totalTimeMs, durationMs: totalTimeMs }],
|
|
881
|
+
pipelineCompilationTimeMs: totalTimeMs, totalTimeMs
|
|
882
|
+
});
|
|
883
|
+
};
|
|
884
|
+
const run = () => compile().then(() => {
|
|
885
|
+
variant.status = 'ready';
|
|
886
|
+
finishDiagnostics('ready');
|
|
887
|
+
}, (error) => {
|
|
888
|
+
finishDiagnostics('failed', getWebGPUErrorMessage(error));
|
|
889
|
+
if (this._disposed)
|
|
890
|
+
return;
|
|
891
|
+
if (!bake && isWebGPUPipelineSpecializationFailure(error)) {
|
|
892
|
+
// WebKit can accept a large WGSL module but fail while translating
|
|
893
|
+
// its specialization-constant variant to Metal. Baking the values
|
|
894
|
+
// into WGSL lets its front end eliminate disabled feature branches
|
|
895
|
+
// before creating the Metal library. The next request has a new
|
|
896
|
+
// signature, so this one stays pending until it is replaced.
|
|
897
|
+
this._bakePipelineConstants = true;
|
|
898
|
+
this._traceVariants.delete(signature);
|
|
899
|
+
console.warn('WebGPU pipeline specialization failed; retrying with constants baked into WGSL.');
|
|
900
|
+
return;
|
|
1027
901
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
if (this._shaderCompilationDiagnostics?.status === 'compiling') {
|
|
1034
|
-
this._shaderCompilationDiagnostics = {
|
|
1035
|
-
...this._shaderCompilationDiagnostics,
|
|
1036
|
-
status: 'failed',
|
|
1037
|
-
pipelineCompilationTimeMs: pipelineCompilationStartedAt == null
|
|
1038
|
-
? undefined
|
|
1039
|
-
: failedAt - pipelineCompilationStartedAt,
|
|
1040
|
-
totalTimeMs: compilationStartedAt == null
|
|
1041
|
-
? undefined
|
|
1042
|
-
: failedAt - compilationStartedAt,
|
|
1043
|
-
error: getWebGPUErrorMessage(error)
|
|
1044
|
-
};
|
|
902
|
+
// Nobody needs an obsolete result, and a later request should try again.
|
|
903
|
+
if (this._requestedVariant !== variant) {
|
|
904
|
+
this._traceVariants.delete(signature);
|
|
905
|
+
variant.status = 'failed';
|
|
906
|
+
return;
|
|
1045
907
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
// The failed signature prevents retrying the same shader every frame;
|
|
1049
|
-
// changing a pipeline-affecting setting permits a new attempt.
|
|
1050
|
-
this._rayTracePipeline = undefined;
|
|
1051
|
-
this._rayTraceBindGroups = undefined;
|
|
1052
|
-
this._prevPipelineConstants = {};
|
|
1053
|
-
this._needsUpdateBindGroups = true;
|
|
908
|
+
variant.status = 'failed';
|
|
909
|
+
variant.error = error;
|
|
1054
910
|
console.error('Failed to update WebGPU ray tracing pipeline.', error);
|
|
911
|
+
});
|
|
912
|
+
variant.settled = new Promise((resolve) => {
|
|
913
|
+
this._queuedCompiles.push({ variant, signature, run, release: resolve });
|
|
914
|
+
});
|
|
915
|
+
this._traceVariants.set(signature, variant);
|
|
916
|
+
if (this._queuedCompiles.length > MAX_QUEUED_COMPILES) {
|
|
917
|
+
// A dropped variant stays pending, because a render that still wants it
|
|
918
|
+
// replaces it with a new request on its next frame.
|
|
919
|
+
const obsolete = this._queuedCompiles.shift();
|
|
920
|
+
if (this._traceVariants.get(obsolete.signature) === obsolete.variant)
|
|
921
|
+
this._traceVariants.delete(obsolete.signature);
|
|
922
|
+
obsolete.release();
|
|
923
|
+
}
|
|
924
|
+
for (const [key, retained] of this._traceVariants) {
|
|
925
|
+
if (this._traceVariants.size <= MAX_TRACE_VARIANTS)
|
|
926
|
+
break;
|
|
927
|
+
if (retained.status !== 'pending' && retained !== this._activeVariant)
|
|
928
|
+
this._traceVariants.delete(key);
|
|
929
|
+
}
|
|
930
|
+
this._pumpCompiles();
|
|
931
|
+
return variant;
|
|
932
|
+
}
|
|
933
|
+
/** Runs the most recently requested variants first: a dragged slider's last value is the one in use. */
|
|
934
|
+
_pumpCompiles() {
|
|
935
|
+
while (this._activeCompiles < MAX_CONCURRENT_COMPILES && this._queuedCompiles.length) {
|
|
936
|
+
const job = this._queuedCompiles.pop();
|
|
937
|
+
this._activeCompiles++;
|
|
938
|
+
void job.run().finally(() => {
|
|
939
|
+
this._activeCompiles--;
|
|
940
|
+
job.release();
|
|
941
|
+
if (!this._disposed)
|
|
942
|
+
this._pumpCompiles();
|
|
943
|
+
});
|
|
1055
944
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
945
|
+
}
|
|
946
|
+
_requestQueuedCompile(variant) {
|
|
947
|
+
const index = this._queuedCompiles.findIndex((job) => job.variant === variant);
|
|
948
|
+
if (index >= 0)
|
|
949
|
+
this._queuedCompiles.push(...this._queuedCompiles.splice(index, 1));
|
|
950
|
+
}
|
|
951
|
+
_prepareAovPipelines(kinds) {
|
|
952
|
+
const trace = this._rayTracePipeline;
|
|
953
|
+
if (!trace || !this._depthCompute)
|
|
954
|
+
return;
|
|
955
|
+
const reflectance = (aovMask(kinds) & 24) !== 0;
|
|
956
|
+
let variants = this._aovPipelines.get(trace);
|
|
957
|
+
if (!variants) {
|
|
958
|
+
variants = new Map();
|
|
959
|
+
this._aovPipelines.set(trace, variants);
|
|
960
|
+
}
|
|
961
|
+
let pipelines = variants.get(reflectance);
|
|
962
|
+
if (!pipelines) {
|
|
963
|
+
pipelines = {};
|
|
964
|
+
variants.set(reflectance, pipelines);
|
|
965
|
+
const pending = pipelines;
|
|
966
|
+
const textures = this._bindless, groupIndex = textures ? 2 : 3;
|
|
967
|
+
const output = this._device.createBindGroupLayout({ entries: [
|
|
968
|
+
{ binding: 5, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } },
|
|
969
|
+
{ binding: 6, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } },
|
|
970
|
+
{ binding: 7, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } },
|
|
971
|
+
{ binding: 8, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'uniform' } },
|
|
972
|
+
{ binding: 9, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } }
|
|
973
|
+
] });
|
|
974
|
+
const descriptor = { bindGroupLayouts: Array.from({ length: groupIndex }, (_, i) => trace.getBindGroupLayout(i)).concat(output) };
|
|
975
|
+
const layout = this._device.createPipelineLayout(textures ? resourceTableLayout(descriptor) : descriptor);
|
|
976
|
+
const compute = { ...this._depthCompute, constants: { ...this._depthCompute.constants, DEPTH_QUERY_MODE: 1, AOV_REFLECTANCE: reflectance ? 1 : 0 } };
|
|
977
|
+
// Keep the UI responsive during first use, and reuse both variants across
|
|
978
|
+
// preview/final target changes rather than recreating synchronous pipelines.
|
|
979
|
+
void Promise.all([
|
|
980
|
+
this._shaderCache.createComputePipelineAsync({ label: 'AOV accumulation', layout, compute: { ...compute, entryPoint: 'aovTrace' } }),
|
|
981
|
+
this._shaderCache.createComputePipelineAsync({ label: 'AOV center', layout, compute: { ...compute, entryPoint: 'aovTraceCenter' } })
|
|
982
|
+
]).then(([accumulation, center]) => { pending.accumulation = accumulation; pending.center = center; }, error => { pending.error = error; this._aovPipelinesFailed = true; });
|
|
983
|
+
}
|
|
984
|
+
if (pipelines.error)
|
|
985
|
+
throw pipelines.error;
|
|
986
|
+
return pipelines;
|
|
987
|
+
}
|
|
988
|
+
prepareAovs(kinds) {
|
|
989
|
+
const pipelines = this._prepareAovPipelines(kinds);
|
|
990
|
+
return !!pipelines?.accumulation && !!pipelines.center;
|
|
991
|
+
}
|
|
992
|
+
renderAovs(state, camera, samples, cap, kinds) {
|
|
993
|
+
const groups = this._rayTraceBindGroups;
|
|
994
|
+
if (!groups || !this._tlas || !this._preparedMeshData || !kinds.length)
|
|
995
|
+
return false;
|
|
996
|
+
const pipelines = this._prepareAovPipelines(kinds);
|
|
997
|
+
if (!pipelines?.accumulation || !pipelines.center)
|
|
998
|
+
return false;
|
|
999
|
+
state.resize(this._width, this._height);
|
|
1000
|
+
const textures = this._bindless, groupIndex = textures ? 2 : 3;
|
|
1001
|
+
const target = Math.min(samples, cap);
|
|
1002
|
+
if (state.centerReady && state.samples >= target)
|
|
1003
|
+
return true;
|
|
1004
|
+
if (!state.frame) {
|
|
1005
|
+
const primitiveIds = this._aovPrimitiveCache.get(this._preparedMeshData, this._tlas);
|
|
1006
|
+
const packed = packAovScene(this._preparedMeshData, this._tlas, camera, this._width, this._height, state.history, primitiveIds, false);
|
|
1007
|
+
state.frame = packed.frame;
|
|
1008
|
+
state.setScene(packed.words, primitiveIds);
|
|
1009
|
+
}
|
|
1010
|
+
this._device.queue.writeBuffer(this._uniforms.rtUniform, 0, this._rtUniform.buffer);
|
|
1011
|
+
this._device.queue.writeBuffer(this._uniforms.cameraUniform, 0, packCameraUniform(this._viewProjInv, this._cameraWorld, this._apertureRadius, this._focusDistance));
|
|
1012
|
+
const device = this._device;
|
|
1013
|
+
const run = (pipeline, sample) => {
|
|
1014
|
+
// A fresh uniform for each queued dispatch keeps batch samples independent.
|
|
1015
|
+
const parameters = device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM, mappedAtCreation: true });
|
|
1016
|
+
new Uint32Array(parameters.getMappedRange()).set([state.width, state.height, sample, aovMask(kinds)]);
|
|
1017
|
+
parameters.unmap();
|
|
1018
|
+
const output = device.createBindGroup({ layout: pipeline.getBindGroupLayout(groupIndex), entries: [
|
|
1019
|
+
{ binding: 5, resource: { buffer: state.accumulation } }, { binding: 6, resource: { buffer: state.center } },
|
|
1020
|
+
{ binding: 7, resource: { buffer: state.scene } }, { binding: 8, resource: { buffer: parameters } }, { binding: 9, resource: { buffer: state.previousCenter } }
|
|
1021
|
+
] });
|
|
1022
|
+
const encoder = device.createCommandEncoder({ label: 'AOV' }), pass = encoder.beginComputePass();
|
|
1023
|
+
if (textures?.table)
|
|
1024
|
+
setResourceTable(pass, textures.table);
|
|
1025
|
+
pass.setPipeline(pipeline);
|
|
1026
|
+
for (let i = 0; i < groupIndex; i++)
|
|
1027
|
+
pass.setBindGroup(i, groups[i]);
|
|
1028
|
+
pass.setBindGroup(groupIndex, output);
|
|
1029
|
+
pass.dispatchWorkgroups(Math.ceil(state.width / 8), Math.ceil(state.height / 8));
|
|
1030
|
+
pass.end();
|
|
1031
|
+
device.queue.submit([encoder.finish()]);
|
|
1032
|
+
parameters.destroy();
|
|
1033
|
+
};
|
|
1034
|
+
if (!state.centerReady) {
|
|
1035
|
+
run(pipelines.center, 0);
|
|
1036
|
+
state.centerReady = true;
|
|
1037
|
+
state.history.accept(state.frame);
|
|
1038
|
+
}
|
|
1039
|
+
if ((aovMask(kinds) & 31) === 0)
|
|
1040
|
+
state.samples = target;
|
|
1041
|
+
while (state.samples < target) {
|
|
1042
|
+
run(pipelines.accumulation, state.samples);
|
|
1043
|
+
state.samples++;
|
|
1058
1044
|
}
|
|
1045
|
+
return true;
|
|
1059
1046
|
}
|
|
1060
1047
|
async readDepthSamples(area, aspect) {
|
|
1061
1048
|
const tracePipeline = this._rayTracePipeline;
|
|
1062
1049
|
const groups = this._rayTraceBindGroups;
|
|
1063
|
-
|
|
1050
|
+
const textures = this._bindless, textureRevision = textures?.revision;
|
|
1051
|
+
if (!tracePipeline || !groups || !this._depthCompute || this.isCompilingShader())
|
|
1064
1052
|
return;
|
|
1065
1053
|
const packed = packDepthSampleArea(area, aspect ?? this._width / this._height);
|
|
1066
1054
|
if (this._depthTracePipeline !== tracePipeline || !this._depthPipeline) {
|
|
@@ -1071,10 +1059,11 @@ ${rayTraceCompute({
|
|
|
1071
1059
|
{ binding: 4, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'uniform' } }
|
|
1072
1060
|
]
|
|
1073
1061
|
});
|
|
1074
|
-
const
|
|
1062
|
+
const descriptor = {
|
|
1075
1063
|
label: 'Depth query layout',
|
|
1076
|
-
bindGroupLayouts: [0, 1, 2].map(i => tracePipeline.getBindGroupLayout(i)).concat(outputLayout)
|
|
1077
|
-
}
|
|
1064
|
+
bindGroupLayouts: (textures ? [0, 1] : [0, 1, 2]).map(i => tracePipeline.getBindGroupLayout(i)).concat(outputLayout)
|
|
1065
|
+
};
|
|
1066
|
+
const layout = this._shaderCache.createPipelineLayout(textures ? resourceTableLayout(descriptor) : descriptor);
|
|
1078
1067
|
this._depthTracePipeline = tracePipeline;
|
|
1079
1068
|
this._depthPipeline = this._shaderCache.createComputePipelineAsync({
|
|
1080
1069
|
label: 'Depth query', layout,
|
|
@@ -1084,7 +1073,7 @@ ${rayTraceCompute({
|
|
|
1084
1073
|
}
|
|
1085
1074
|
const pipeline = await this._depthPipeline;
|
|
1086
1075
|
// Scene uploads can replace the buffers while the small query compiles.
|
|
1087
|
-
if (tracePipeline !== this._rayTracePipeline || groups !== this._rayTraceBindGroups)
|
|
1076
|
+
if (tracePipeline !== this._rayTracePipeline || groups !== this._rayTraceBindGroups || textures !== this._bindless || textureRevision !== textures?.revision)
|
|
1088
1077
|
return;
|
|
1089
1078
|
const device = this._device;
|
|
1090
1079
|
const bytes = DEPTH_SAMPLE_COUNT * 16;
|
|
@@ -1096,16 +1085,18 @@ ${rayTraceCompute({
|
|
|
1096
1085
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
1097
1086
|
try {
|
|
1098
1087
|
device.queue.writeBuffer(params, 0, packed);
|
|
1099
|
-
const outputGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(3), entries: [
|
|
1088
|
+
const outputGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(textures ? 2 : 3), entries: [
|
|
1100
1089
|
{ binding: 0, resource: { buffer: output } },
|
|
1101
1090
|
{ binding: 4, resource: { buffer: params } }
|
|
1102
1091
|
] });
|
|
1103
1092
|
const encoder = device.createCommandEncoder({ label: 'Depth query' });
|
|
1104
1093
|
const pass = encoder.beginComputePass();
|
|
1094
|
+
if (textures?.table)
|
|
1095
|
+
setResourceTable(pass, textures.table);
|
|
1105
1096
|
pass.setPipeline(pipeline);
|
|
1106
|
-
for (let i = 0; i < 3; i++)
|
|
1097
|
+
for (let i = 0; i < (textures ? 2 : 3); i++)
|
|
1107
1098
|
pass.setBindGroup(i, groups[i]);
|
|
1108
|
-
pass.setBindGroup(3, outputGroup);
|
|
1099
|
+
pass.setBindGroup(textures ? 2 : 3, outputGroup);
|
|
1109
1100
|
pass.dispatchWorkgroups(1);
|
|
1110
1101
|
pass.end();
|
|
1111
1102
|
encoder.copyBufferToBuffer(output, 0, readback, 0, bytes);
|
|
@@ -1121,15 +1112,17 @@ ${rayTraceCompute({
|
|
|
1121
1112
|
params.destroy();
|
|
1122
1113
|
}
|
|
1123
1114
|
}
|
|
1115
|
+
/** Whether the latest request waits on GPU work that will settle on its own. */
|
|
1124
1116
|
isCompilingShader() {
|
|
1125
|
-
return this.
|
|
1117
|
+
return !!this._awaitingRetirement || this._requestedVariant?.status === 'pending';
|
|
1126
1118
|
}
|
|
1127
1119
|
isPipelineReady() {
|
|
1128
1120
|
return ((!!this._rayTracePipeline) &&
|
|
1129
1121
|
!!this._rayTraceBindGroups);
|
|
1130
1122
|
}
|
|
1123
|
+
/** Failure of the latest request. A failed variant does not affect other renders. */
|
|
1131
1124
|
getPipelineError() {
|
|
1132
|
-
return this.
|
|
1125
|
+
return this._resourceError ?? this._requestedVariant?.error;
|
|
1133
1126
|
}
|
|
1134
1127
|
_updateBindGroups() {
|
|
1135
1128
|
const { _device } = this;
|
|
@@ -1140,50 +1133,66 @@ ${rayTraceCompute({
|
|
|
1140
1133
|
if (!this._bvhBuffer || !this._instancesBuffer) {
|
|
1141
1134
|
throw new Error('WebGPU BVH buffers are not ready.');
|
|
1142
1135
|
}
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
binding: 1,
|
|
1166
|
-
resource: this._maps.createView()
|
|
1167
|
-
},
|
|
1168
|
-
{
|
|
1169
|
-
binding: 3,
|
|
1170
|
-
resource: {
|
|
1171
|
-
buffer: this._mapInfoUniform
|
|
1136
|
+
const bufferEntries = [
|
|
1137
|
+
this._bvhBuffer,
|
|
1138
|
+
this._instancesBuffer,
|
|
1139
|
+
this._attrBuffers,
|
|
1140
|
+
this._materialsBuffer
|
|
1141
|
+
].map((buffer, i) => ({
|
|
1142
|
+
binding: i,
|
|
1143
|
+
resource: {
|
|
1144
|
+
buffer: buffer
|
|
1145
|
+
}
|
|
1146
|
+
}));
|
|
1147
|
+
const textureEntries = [
|
|
1148
|
+
...(this._bindless
|
|
1149
|
+
? []
|
|
1150
|
+
: [
|
|
1151
|
+
{
|
|
1152
|
+
binding: 0,
|
|
1153
|
+
resource: this._mapsSampler
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
binding: 1,
|
|
1157
|
+
resource: this._maps.createView()
|
|
1172
1158
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
{ binding: 9, resource: this._openPbrEnergyLut.createView() },
|
|
1179
|
-
{
|
|
1180
|
-
binding: 10,
|
|
1181
|
-
resource: this._adaptiveSamplingTexture.createView()
|
|
1159
|
+
]),
|
|
1160
|
+
{
|
|
1161
|
+
binding: 3,
|
|
1162
|
+
resource: {
|
|
1163
|
+
buffer: this._bindless?.metadata ?? this._mapInfoUniform
|
|
1182
1164
|
}
|
|
1183
|
-
|
|
1165
|
+
},
|
|
1166
|
+
{ binding: 4, resource: this._envTex.createView() },
|
|
1167
|
+
{ binding: 5, resource: this._envDistributionTex.createView() },
|
|
1168
|
+
{ binding: 6, resource: this._backgroundTex.createView() },
|
|
1169
|
+
...(this._photonMapping
|
|
1170
|
+
? [
|
|
1171
|
+
{ binding: 11, resource: this._photonMap.get(this._activeTarget).data.createView() },
|
|
1172
|
+
{ binding: 12, resource: this._photonMap.get(this._activeTarget).headers.createView() }
|
|
1173
|
+
]
|
|
1174
|
+
: []),
|
|
1175
|
+
{ binding: 8, resource: this._lightSamplingTexture.createView() },
|
|
1176
|
+
{ binding: 9, resource: this._openPbrEnergyLut.createView() },
|
|
1177
|
+
{
|
|
1178
|
+
binding: 10,
|
|
1179
|
+
resource: this._adaptiveSamplingTexture.createView()
|
|
1180
|
+
}
|
|
1181
|
+
];
|
|
1182
|
+
const bufferBindGroup = _device.createBindGroup({
|
|
1183
|
+
layout: bindGroupPipeline.getBindGroupLayout(0),
|
|
1184
|
+
entries: this._bindless
|
|
1185
|
+
? bufferEntries.concat(textureEntries.map((e) => ({ ...e, binding: e.binding + 4 })))
|
|
1186
|
+
: bufferEntries
|
|
1184
1187
|
});
|
|
1188
|
+
const texturesBindGroup = this._bindless
|
|
1189
|
+
? undefined
|
|
1190
|
+
: _device.createBindGroup({
|
|
1191
|
+
layout: bindGroupPipeline.getBindGroupLayout(1),
|
|
1192
|
+
entries: textureEntries
|
|
1193
|
+
});
|
|
1185
1194
|
const uniformBindGroup = _device.createBindGroup({
|
|
1186
|
-
layout: bindGroupPipeline.getBindGroupLayout(2),
|
|
1195
|
+
layout: bindGroupPipeline.getBindGroupLayout(this._bindless ? 1 : 2),
|
|
1187
1196
|
entries: Object.keys(this._uniforms).map((key, i) => ({
|
|
1188
1197
|
binding: i,
|
|
1189
1198
|
resource: {
|
|
@@ -1191,24 +1200,41 @@ ${rayTraceCompute({
|
|
|
1191
1200
|
}
|
|
1192
1201
|
}))
|
|
1193
1202
|
});
|
|
1203
|
+
if (this._photonMapping) {
|
|
1204
|
+
// Explicit layouts still track unused textures. Bind a placeholder while
|
|
1205
|
+
// emission writes the real packed map through its storage view.
|
|
1206
|
+
const emissionEntries = textureEntries.map((entry) => entry.binding === 11 || entry.binding === 12
|
|
1207
|
+
? { ...entry, resource: this._photonMap.placeholder.createView() }
|
|
1208
|
+
: entry);
|
|
1209
|
+
const sceneGroup = _device.createBindGroup({
|
|
1210
|
+
layout: bindGroupPipeline.getBindGroupLayout(this._bindless ? 0 : 1),
|
|
1211
|
+
entries: this._bindless
|
|
1212
|
+
? bufferEntries.concat(emissionEntries.map((e) => ({ ...e, binding: e.binding + 4 })))
|
|
1213
|
+
: emissionEntries
|
|
1214
|
+
});
|
|
1215
|
+
this._photonEmissionGroups = this._bindless
|
|
1216
|
+
? [sceneGroup, uniformBindGroup]
|
|
1217
|
+
: [bufferBindGroup, sceneGroup, uniformBindGroup];
|
|
1218
|
+
}
|
|
1194
1219
|
const target = this._targetOutputs.get(this._activeTarget);
|
|
1195
|
-
const outputBindGroup = target.pipeline === bindGroupPipeline && target.bindGroup ||
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1220
|
+
const outputBindGroup = (target.pipeline === bindGroupPipeline && target.bindGroup) ||
|
|
1221
|
+
_device.createBindGroup({
|
|
1222
|
+
layout: bindGroupPipeline.getBindGroupLayout(this._bindless ? 2 : 3),
|
|
1223
|
+
entries: [
|
|
1224
|
+
this._outColorBuffer,
|
|
1225
|
+
this._outNormalBuffer,
|
|
1226
|
+
this._outAlbedoBuffer,
|
|
1227
|
+
this._outShadowCatcherBuffer
|
|
1228
|
+
].map((buffer, i) => ({
|
|
1229
|
+
binding: i,
|
|
1230
|
+
resource: { buffer }
|
|
1231
|
+
}))
|
|
1232
|
+
});
|
|
1207
1233
|
target.bindGroup = outputBindGroup;
|
|
1208
1234
|
target.pipeline = bindGroupPipeline;
|
|
1209
1235
|
this._rayTraceBindGroups = [
|
|
1210
1236
|
bufferBindGroup,
|
|
1211
|
-
texturesBindGroup,
|
|
1237
|
+
...(texturesBindGroup ? [texturesBindGroup] : []),
|
|
1212
1238
|
uniformBindGroup,
|
|
1213
1239
|
outputBindGroup
|
|
1214
1240
|
];
|
|
@@ -1230,6 +1256,36 @@ ${rayTraceCompute({
|
|
|
1230
1256
|
encoder.clearBuffer(this._outAlbedoBuffer);
|
|
1231
1257
|
encoder.clearBuffer(this._outShadowCatcherBuffer);
|
|
1232
1258
|
}
|
|
1259
|
+
const offset = this._rtUniform.views.scaleOffset;
|
|
1260
|
+
const causticBatch = this._lightTracing && offset[2] === tiles[0] - 1 && offset[3] === tiles[1] - 1;
|
|
1261
|
+
const photonBatch = this._photonMapping && offset[2] === 0 && offset[3] === 0;
|
|
1262
|
+
if (photonBatch && this._photonMap) {
|
|
1263
|
+
const requested = this._causticSettings?.photonMapping, initial = typeof requested === 'object' ? requested.initialRadius : undefined;
|
|
1264
|
+
const radius = Number.isFinite(initial) && initial > 0 ? initial : Math.max((causticDiagnostics.photonPerCasterRadius
|
|
1265
|
+
? causticCasterSpheres(this._meshes).reduce((maximum, s) => Math.max(maximum, s[3]), 0)
|
|
1266
|
+
: this._causticBounds[3]) * 0.03, 1e-4);
|
|
1267
|
+
const batch = Math.max(1, this._rtUniform.views.sampleCount[0] - this._rtUniform.views.sampleSequenceOffset[0] + 1);
|
|
1268
|
+
const photonViewProjection = mat4.create();
|
|
1269
|
+
mat4.invert(photonViewProjection, this._viewProjInv);
|
|
1270
|
+
this._photonMap.encode(encoder, this._photonEmissionGroups, this._activeTarget, photonRadius(radius, causticDiagnostics.photonDebugBatch ?? batch, causticDiagnostics.photonAlpha ?? 2 / 3,
|
|
1271
|
+
// Allocation limits may reduce the requested count.
|
|
1272
|
+
this._photonMap.get(this._activeTarget).count, causticDiagnostics.photonRadiusScaling), {
|
|
1273
|
+
querySet: timing?.querySet,
|
|
1274
|
+
resourceTable: this._bindless?.table,
|
|
1275
|
+
orthographic: !this._causticPerspective,
|
|
1276
|
+
progressiveBatch: batch,
|
|
1277
|
+
width: this._width,
|
|
1278
|
+
height: this._height,
|
|
1279
|
+
clear,
|
|
1280
|
+
run: true,
|
|
1281
|
+
photons: 1,
|
|
1282
|
+
batch: Math.floor(this._rtUniform.views.sampleCount[0]),
|
|
1283
|
+
...this._transport?.uniform,
|
|
1284
|
+
viewProjection: photonViewProjection,
|
|
1285
|
+
casterSphere: this._causticBounds,
|
|
1286
|
+
cameraScale: this._causticCameraScale
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1233
1289
|
const descriptor = {};
|
|
1234
1290
|
const querySet = timing?.querySet;
|
|
1235
1291
|
if (querySet) {
|
|
@@ -1240,6 +1296,8 @@ ${rayTraceCompute({
|
|
|
1240
1296
|
};
|
|
1241
1297
|
}
|
|
1242
1298
|
const pass = encoder.beginComputePass(descriptor);
|
|
1299
|
+
if (this._bindless?.table)
|
|
1300
|
+
setResourceTable(pass, this._bindless.table);
|
|
1243
1301
|
if (!this._rayTracePipeline || !this._rayTraceBindGroups) {
|
|
1244
1302
|
throw new Error('WebGPU ray tracing pass was submitted before it was ready.');
|
|
1245
1303
|
}
|
|
@@ -1247,8 +1305,32 @@ ${rayTraceCompute({
|
|
|
1247
1305
|
this._rayTraceBindGroups.forEach((bindGroup, idx) => {
|
|
1248
1306
|
pass.setBindGroup(idx, bindGroup);
|
|
1249
1307
|
});
|
|
1250
|
-
|
|
1308
|
+
researchTransport()?.capture(this);
|
|
1309
|
+
pass.dispatchWorkgroups(Math.ceil(dispatchWidth / (this._transport?.workgroupSize ?? WORKGROUP_SIZE)), Math.ceil(dispatchHeight / (this._transport?.workgroupSize ?? WORKGROUP_SIZE)), 1);
|
|
1251
1310
|
pass.end();
|
|
1311
|
+
if (this._lightTracing && this._causticLight) {
|
|
1312
|
+
const viewProjection = mat4.create();
|
|
1313
|
+
mat4.invert(viewProjection, this._viewProjInv);
|
|
1314
|
+
const requested = this._causticSettings?.lightTracing;
|
|
1315
|
+
const count = typeof requested === 'object' ? requested.photonsPerSample : undefined;
|
|
1316
|
+
const photons = Math.min(this._device.limits.maxComputeWorkgroupsPerDimension * 64, Math.max(1, Math.round(Number.isFinite(count) ? count : (this._width * this._height) / 4)));
|
|
1317
|
+
this._causticLight.encode(encoder, this._rayTraceBindGroups.slice(0, this._bindless ? 2 : 3), this._activeTarget, {
|
|
1318
|
+
querySet: causticBatch ? querySet : undefined,
|
|
1319
|
+
width: this._width,
|
|
1320
|
+
height: this._height,
|
|
1321
|
+
clear,
|
|
1322
|
+
run: !!causticBatch,
|
|
1323
|
+
photons,
|
|
1324
|
+
batch: Math.floor(this._rtUniform.views.sampleCount[0]),
|
|
1325
|
+
...this._transport?.uniform,
|
|
1326
|
+
resourceTable: this._bindless?.table,
|
|
1327
|
+
orthographic: !this._causticPerspective,
|
|
1328
|
+
viewProjection,
|
|
1329
|
+
casterSphere: this._causticBounds,
|
|
1330
|
+
cameraScale: this._causticCameraScale
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
return (causticBatch ? 1 : 0) | (photonBatch ? 2 : 0);
|
|
1252
1334
|
}
|
|
1253
1335
|
async getDebugBVHData() {
|
|
1254
1336
|
if (!this._debugBVHBuffer) {
|
|
@@ -1259,9 +1341,19 @@ ${rayTraceCompute({
|
|
|
1259
1341
|
};
|
|
1260
1342
|
}
|
|
1261
1343
|
getDiagnostics() {
|
|
1344
|
+
const atlas = this._materialData instanceof MaterialTable ? this._materialData.textureAtlasStats : undefined;
|
|
1262
1345
|
return {
|
|
1263
|
-
textureAtlas:
|
|
1264
|
-
|
|
1346
|
+
textureAtlas: atlas,
|
|
1347
|
+
materialTextures: {
|
|
1348
|
+
requested: this._textureSelection.requested,
|
|
1349
|
+
active: this._bindless ? 'bindless' : 'atlas',
|
|
1350
|
+
fallbackReason: this._textureSelection.fallbackReason,
|
|
1351
|
+
...(this._bindless?.diagnostics ?? {
|
|
1352
|
+
textureCount: atlas?.textureCount ?? 0, estimatedBytes: atlas?.estimatedBytes ?? 0,
|
|
1353
|
+
scale: atlas?.scale ?? 1, metadataBytes: 0, resourceTableCapacity: 0, retiredBytes: 0
|
|
1354
|
+
})
|
|
1355
|
+
},
|
|
1356
|
+
shaderCompilation: this._requestedVariant?.diagnostics,
|
|
1265
1357
|
lightSampling: {
|
|
1266
1358
|
emissiveTriangles: this._emissiveTriangles.length,
|
|
1267
1359
|
activeChoices: this._rtUniform.views.lightChoiceCount[0],
|
|
@@ -1281,15 +1373,6 @@ ${rayTraceCompute({
|
|
|
1281
1373
|
if (next === this._alphaBlendStrategy)
|
|
1282
1374
|
return false;
|
|
1283
1375
|
this._alphaBlendStrategy = next;
|
|
1284
|
-
if (this._constants.HAS_ALPHA_BLEND) {
|
|
1285
|
-
this._computeShaderModule = undefined;
|
|
1286
|
-
this._rayTracePipeline = undefined;
|
|
1287
|
-
this._rayTracePipelineLayout = undefined;
|
|
1288
|
-
this._rayTraceBindGroups = undefined;
|
|
1289
|
-
this._prevPipelineConstants = {};
|
|
1290
|
-
this._failedPipelineSignature = undefined;
|
|
1291
|
-
this._needsUpdateBindGroups = true;
|
|
1292
|
-
}
|
|
1293
1376
|
return true;
|
|
1294
1377
|
}
|
|
1295
1378
|
hasDepthOfField() {
|
|
@@ -1325,15 +1408,14 @@ ${rayTraceCompute({
|
|
|
1325
1408
|
return changed;
|
|
1326
1409
|
}
|
|
1327
1410
|
setSamplingPattern(pattern, maxSamples, sequenceSeed = 0) {
|
|
1328
|
-
const
|
|
1329
|
-
const
|
|
1411
|
+
const sampling = traceSampling(pattern, maxSamples, sequenceSeed);
|
|
1412
|
+
const { code, logSPP } = sampling;
|
|
1330
1413
|
const views = this._rtUniform.views;
|
|
1331
1414
|
const changed = this._constants.SAMPLING_PATTERN !== code ||
|
|
1332
1415
|
views.sequenceSeed[0] !== (sequenceSeed >>> 0) ||
|
|
1333
1416
|
(code === 1 && views.zSobolLogSPP[0] !== logSPP);
|
|
1334
1417
|
this._constants.SAMPLING_PATTERN = code;
|
|
1335
|
-
views
|
|
1336
|
-
views.zSobolLogSPP[0] = logSPP;
|
|
1418
|
+
writeTraceSampling(views, sampling);
|
|
1337
1419
|
return changed;
|
|
1338
1420
|
}
|
|
1339
1421
|
setSampleSequenceOffset(offset) {
|
|
@@ -1354,10 +1436,27 @@ ${rayTraceCompute({
|
|
|
1354
1436
|
this._device.queue.writeTexture({ texture: this._adaptiveSamplingTexture }, data, { bytesPerRow: width * Float32Array.BYTES_PER_ELEMENT }, [width, height]);
|
|
1355
1437
|
}
|
|
1356
1438
|
setQuality(options) {
|
|
1357
|
-
|
|
1358
|
-
this._rtUniform.views
|
|
1359
|
-
|
|
1360
|
-
|
|
1439
|
+
WGPURayTracePass.prototype._setCaustics.call(this, options.caustics);
|
|
1440
|
+
writeTraceQuality(this._rtUniform.views, options);
|
|
1441
|
+
}
|
|
1442
|
+
_setCaustics(settings) {
|
|
1443
|
+
this._causticSettings = settings;
|
|
1444
|
+
const transport = researchTransport()?.configure(this, 'webgpu');
|
|
1445
|
+
if (!!transport?.enabled !== !!this._transport?.enabled)
|
|
1446
|
+
this._needsUpdateLightSamplingData = true;
|
|
1447
|
+
this._transport = transport;
|
|
1448
|
+
const hasCaster = !!(settings?.lightTracing || settings?.photonMapping) &&
|
|
1449
|
+
this._meshes.some((mesh) => mesh.materials.some(isCausticSpecular));
|
|
1450
|
+
const photonSupported = !(settings?.lightTracing || settings?.photonMapping) ||
|
|
1451
|
+
!hasCaster ||
|
|
1452
|
+
!hasStochasticCausticCaster(this._meshes);
|
|
1453
|
+
this._lightTracing = !!settings?.lightTracing && hasCaster && photonSupported;
|
|
1454
|
+
const photonMapping = !!settings?.photonMapping && hasCaster && photonSupported;
|
|
1455
|
+
this._photonMapping = photonMapping;
|
|
1456
|
+
this._causticKey =
|
|
1457
|
+
this._transport?.enabled || this._lightTracing || this._photonMapping
|
|
1458
|
+
? `${this._lightTracing}/${this._photonMapping}/${causticDiagnostics.fullEmissionRandom !== false}/${causticDiagnostics.photonMaxDepth ?? 20}/${causticDiagnostics.debugGroup}/${causticDiagnostics.photonDirectSubset !== false}/${this._transport?.key ?? ''}`
|
|
1459
|
+
: '';
|
|
1361
1460
|
}
|
|
1362
1461
|
_setEnvMap(envImage) {
|
|
1363
1462
|
const device = this._device;
|
|
@@ -1365,53 +1464,74 @@ ${rayTraceCompute({
|
|
|
1365
1464
|
if (!environmentMapSourcesMatch(envImage, this._envImageSource) ||
|
|
1366
1465
|
!this._envTex) {
|
|
1367
1466
|
this._envImageSource = envImage;
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
const
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
}, envImage.data.buffer, {
|
|
1398
|
-
bytesPerRow: envImage.width * 4 * 4
|
|
1399
|
-
}, {
|
|
1400
|
-
width: envImage.width,
|
|
1401
|
-
height: envImage.height,
|
|
1402
|
-
depthOrArrayLayers: 1
|
|
1467
|
+
const sky = envImage && 'sky' in envImage ? envImage : undefined;
|
|
1468
|
+
if (sky && (sky.width + 2 > device.limits.maxTextureDimension2D || sky.height + 1 > device.limits.maxTextureDimension2D))
|
|
1469
|
+
throw new RangeError('ProceduralSky resolution exceeds the device texture size limit.');
|
|
1470
|
+
const gpuDistribution = envImage && (sky || envImage.width * envImage.height >= 65536) &&
|
|
1471
|
+
WGPUEnvMapDistribution.supported(device, envImage.width, envImage.height);
|
|
1472
|
+
if (sky && !gpuDistribution)
|
|
1473
|
+
throw new RangeError('ProceduralSky resolution exceeds the GPU sampling storage limit; reduce resolution.');
|
|
1474
|
+
const distribution = envImage && !('sky' in envImage) && !gpuDistribution ? envMapDistribution(envImage) : undefined;
|
|
1475
|
+
const distributionWidth = envImage ? envImage.width + 2 : 1;
|
|
1476
|
+
const distributionHeight = envImage ? envImage.height + 1 : 1;
|
|
1477
|
+
const width = envImage?.width ?? 1;
|
|
1478
|
+
const height = envImage?.height ?? 1;
|
|
1479
|
+
const resized = !this._envTex || this._envTex.width !== width || this._envTex.height !== height ||
|
|
1480
|
+
!this._envDistributionTex || this._envDistributionTex.width !== distributionWidth || this._envDistributionTex.height !== distributionHeight;
|
|
1481
|
+
if (resized) {
|
|
1482
|
+
this._envTex?.destroy();
|
|
1483
|
+
this._envDistributionTex?.destroy();
|
|
1484
|
+
const usage = GPUTextureUsage.TEXTURE_BINDING |
|
|
1485
|
+
GPUTextureUsage.COPY_DST |
|
|
1486
|
+
GPUTextureUsage.RENDER_ATTACHMENT;
|
|
1487
|
+
this._envTex = device.createTexture({
|
|
1488
|
+
format: 'rgba32float',
|
|
1489
|
+
usage: usage | GPUTextureUsage.STORAGE_BINDING,
|
|
1490
|
+
label: 'Env',
|
|
1491
|
+
size: {
|
|
1492
|
+
width,
|
|
1493
|
+
height,
|
|
1494
|
+
depthOrArrayLayers: 1
|
|
1495
|
+
}
|
|
1403
1496
|
});
|
|
1404
|
-
device.
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1497
|
+
this._envDistributionTex = device.createTexture({
|
|
1498
|
+
format: 'rg32float',
|
|
1499
|
+
usage: usage | GPUTextureUsage.STORAGE_BINDING,
|
|
1500
|
+
label: 'Env Distribution',
|
|
1501
|
+
size: {
|
|
1502
|
+
width: distributionWidth,
|
|
1503
|
+
height: distributionHeight,
|
|
1504
|
+
depthOrArrayLayers: 1
|
|
1505
|
+
}
|
|
1412
1506
|
});
|
|
1413
1507
|
}
|
|
1414
|
-
|
|
1508
|
+
if (envImage) {
|
|
1509
|
+
if ('sky' in envImage) {
|
|
1510
|
+
this._skyBaker ??= new WGPUSkyBaker(device);
|
|
1511
|
+
this._skyBaker.bake(envImage, this._envTex);
|
|
1512
|
+
}
|
|
1513
|
+
else
|
|
1514
|
+
device.queue.writeTexture({
|
|
1515
|
+
texture: this._envTex
|
|
1516
|
+
}, envImage.data.buffer, {
|
|
1517
|
+
bytesPerRow: envImage.width * 4 * 4
|
|
1518
|
+
}, {
|
|
1519
|
+
width: envImage.width,
|
|
1520
|
+
height: envImage.height,
|
|
1521
|
+
depthOrArrayLayers: 1
|
|
1522
|
+
});
|
|
1523
|
+
if (gpuDistribution) {
|
|
1524
|
+
this._envDistribution ??= new WGPUEnvMapDistribution(device);
|
|
1525
|
+
this._envDistribution.build(this._envTex, this._envDistributionTex);
|
|
1526
|
+
}
|
|
1527
|
+
else if (distribution) {
|
|
1528
|
+
this._envDistribution?.dispose();
|
|
1529
|
+
device.queue.writeTexture({ texture: this._envDistributionTex }, distribution.data.buffer, { bytesPerRow: distribution.width * 4 * 2 }, { width: distribution.width, height: distribution.height, depthOrArrayLayers: 1 });
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
else
|
|
1533
|
+
this._envDistribution?.dispose();
|
|
1534
|
+
this._needsUpdateBindGroups ||= resized;
|
|
1415
1535
|
}
|
|
1416
1536
|
this._constants.USE_ENVMAP = !!envImage;
|
|
1417
1537
|
}
|
|
@@ -1624,6 +1744,7 @@ ${rayTraceCompute({
|
|
|
1624
1744
|
}
|
|
1625
1745
|
setBackground(background) {
|
|
1626
1746
|
this._rtUniform.views.backgroundMapTransform.set(backgroundMapTransform(background));
|
|
1747
|
+
this._rtUniform.views.backgroundMapRotation.set(backgroundMapRotation(background));
|
|
1627
1748
|
this._constants.USE_BACKGROUND = !!background;
|
|
1628
1749
|
const backgroundImage = background && !Array.isArray(background)
|
|
1629
1750
|
? generateBackgroundMapFromSceneBackground(background)
|
|
@@ -1658,74 +1779,51 @@ ${rayTraceCompute({
|
|
|
1658
1779
|
return;
|
|
1659
1780
|
}
|
|
1660
1781
|
this._backgroundImageSource = backgroundImage;
|
|
1661
|
-
this._backgroundTex?.destroy();
|
|
1662
1782
|
const width = backgroundImage?.width ?? 1;
|
|
1663
1783
|
const height = backgroundImage?.height ?? 1;
|
|
1664
|
-
this.
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1784
|
+
if (backgroundImage && 'sky' in backgroundImage && (width > this._device.limits.maxTextureDimension2D || height > this._device.limits.maxTextureDimension2D))
|
|
1785
|
+
throw new RangeError('ProceduralSky resolution exceeds the device texture size limit.');
|
|
1786
|
+
const resized = !this._backgroundTex || this._backgroundTex.width !== width || this._backgroundTex.height !== height;
|
|
1787
|
+
if (resized) {
|
|
1788
|
+
this._backgroundTex?.destroy();
|
|
1789
|
+
this._backgroundTex = this._device.createTexture({
|
|
1790
|
+
format: 'rgba32float',
|
|
1791
|
+
usage: GPUTextureUsage.TEXTURE_BINDING |
|
|
1792
|
+
GPUTextureUsage.COPY_DST |
|
|
1793
|
+
GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.STORAGE_BINDING,
|
|
1794
|
+
label: 'Background',
|
|
1795
|
+
size: { width, height, depthOrArrayLayers: 1 }
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
if (backgroundImage && 'sky' in backgroundImage) {
|
|
1799
|
+
this._skyBaker ??= new WGPUSkyBaker(this._device);
|
|
1800
|
+
this._skyBaker.bake(backgroundImage, this._backgroundTex);
|
|
1801
|
+
}
|
|
1802
|
+
else if (backgroundImage) {
|
|
1673
1803
|
const source = backgroundImage.data;
|
|
1674
1804
|
const data = source instanceof Float32Array
|
|
1675
1805
|
? source
|
|
1676
|
-
:
|
|
1806
|
+
: source instanceof Uint8Array || source instanceof Uint8ClampedArray
|
|
1807
|
+
? Float32Array.from(source, value => value / 255)
|
|
1808
|
+
: Float32Array.from(source);
|
|
1677
1809
|
this._device.queue.writeTexture({ texture: this._backgroundTex }, data.buffer, { bytesPerRow: width * 4 * 4 }, { width, height, depthOrArrayLayers: 1 });
|
|
1678
1810
|
}
|
|
1679
|
-
this._needsUpdateBindGroups
|
|
1811
|
+
this._needsUpdateBindGroups ||= resized;
|
|
1680
1812
|
}
|
|
1681
1813
|
_updateLightSamplingData() {
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
const domains = [domainDiagnostics.environment, domainDiagnostics.infinite, domainDiagnostics.localAndEmissive];
|
|
1696
|
-
for (let i = 0; i < choices.count; i++) {
|
|
1697
|
-
const type = choices.sourceTypes[i], source = choices.sourceIndices[i];
|
|
1698
|
-
const pmf = distribution.pmfs[i];
|
|
1699
|
-
const domainIndex = type === LIGHT_CHOICE_ENVIRONMENT ? LIGHT_DOMAIN_ENVIRONMENT :
|
|
1700
|
-
type === LIGHT_CHOICE_ANALYTIC && this._analyticLights[source]?.type === 'DirectionalLight' ?
|
|
1701
|
-
LIGHT_DOMAIN_INFINITE : LIGHT_DOMAIN_LOCAL_AND_EMISSIVE;
|
|
1702
|
-
const domain = domains[domainIndex];
|
|
1703
|
-
domain.count++;
|
|
1704
|
-
domain.weight += choices.weights[i];
|
|
1705
|
-
domain.pmf += pmf;
|
|
1706
|
-
if (type === LIGHT_CHOICE_ENVIRONMENT)
|
|
1707
|
-
this._rtUniform.views.envLightPmf[0] = pmf;
|
|
1708
|
-
else if (type === LIGHT_CHOICE_ANALYTIC)
|
|
1709
|
-
this._analyticLightSelectionPmfs[source] = pmf;
|
|
1710
|
-
else
|
|
1711
|
-
this._emissiveTriangles[source].selectionPmf = pmf;
|
|
1712
|
-
}
|
|
1713
|
-
this._lightDomainDiagnostics = domainDiagnostics;
|
|
1714
|
-
this._rtUniform.views.lightChoiceCount[0] = choices.count;
|
|
1715
|
-
this._rtUniform.views.lightWeightTotal[0] = distribution.totalWeight;
|
|
1716
|
-
const emissiveTexels = this._emissiveTriangles.length * 6;
|
|
1717
|
-
const choiceTexels = Math.max(choices.count, 1);
|
|
1718
|
-
const profileStartTexel = emissiveTexels + choiceTexels;
|
|
1719
|
-
const packedIES = packIESProfiles(this._analyticLights, profileStartTexel);
|
|
1720
|
-
this._iesProfileTexelOffsets = packedIES.offsets;
|
|
1721
|
-
const totalTexels = Math.max(profileStartTexel + packedIES.texelCount, 1);
|
|
1722
|
-
const maxDimension = this._device.limits.maxTextureDimension2D;
|
|
1723
|
-
const width = Math.min(maxDimension, Math.max(16, Math.ceil(Math.sqrt(totalTexels) / 16) * 16));
|
|
1724
|
-
const height = Math.ceil(totalTexels / width);
|
|
1725
|
-
if (height > maxDimension) {
|
|
1726
|
-
throw new Error('Too many emissive triangles for the light sampling texture.');
|
|
1727
|
-
}
|
|
1728
|
-
const resized = !this._lightSamplingTexture || width !== this._lightSamplingTextureSize[0] || height !== this._lightSamplingTextureSize[1];
|
|
1814
|
+
this._constants.LIGHT_CAPABILITIES = lightShaderCapabilities(this._analyticLights.map((light) => light.type), this._emissiveTriangles.length);
|
|
1815
|
+
const lighting = prepareSceneLightSampling(this._analyticLights, this._emissiveTriangles, this._envImageSource && 'sky' in this._envImageSource
|
|
1816
|
+
? proceduralEnvironmentPower(this._envImageSource)
|
|
1817
|
+
: estimateEnvironmentPower(this._envImageSource), this._device.limits.maxTextureDimension2D, this._lightChoices, this._lightSamplingScratch, this._shadowCatcherLightChoices);
|
|
1818
|
+
researchTransport()?.prepare(this, 'webgpu', lighting);
|
|
1819
|
+
this._analyticLightSelectionPmfs = lighting.analyticPmfs;
|
|
1820
|
+
this._iesProfileTexelOffsets = lighting.profiles.offsets;
|
|
1821
|
+
this._lightDomainDiagnostics = lighting.domains;
|
|
1822
|
+
const { lightWidth: width, lightHeight: height, lightData: textureData } = lighting;
|
|
1823
|
+
writeTraceLighting(this._rtUniform.views, lighting);
|
|
1824
|
+
const resized = !this._lightSamplingTexture ||
|
|
1825
|
+
width !== this._lightSamplingTextureSize[0] ||
|
|
1826
|
+
height !== this._lightSamplingTextureSize[1];
|
|
1729
1827
|
if (!this._lightSamplingTexture ||
|
|
1730
1828
|
width !== this._lightSamplingTextureSize[0] ||
|
|
1731
1829
|
height !== this._lightSamplingTextureSize[1]) {
|
|
@@ -1739,14 +1837,6 @@ ${rayTraceCompute({
|
|
|
1739
1837
|
this._lightSamplingTextureSize = [width, height];
|
|
1740
1838
|
this._needsUpdateBindGroups = true;
|
|
1741
1839
|
}
|
|
1742
|
-
const textureData = this._lightSamplingScratch?.length === width * height * 4
|
|
1743
|
-
? this._lightSamplingScratch : new Float32Array(width * height * 4);
|
|
1744
|
-
textureData.fill(0);
|
|
1745
|
-
if (this._emissiveTriangles.length) {
|
|
1746
|
-
packEmissiveTriangles(this._emissiveTriangles, textureData);
|
|
1747
|
-
}
|
|
1748
|
-
new Uint32Array(textureData.buffer).set(distribution.choices, emissiveTexels * 4);
|
|
1749
|
-
textureData.set(packedIES.data, profileStartTexel * 4);
|
|
1750
1840
|
const previous = this._lightSamplingData;
|
|
1751
1841
|
const full = resized || !previous || this._lightSamplingTriangleCount !== this._emissiveTriangles.length;
|
|
1752
1842
|
const words = new Uint32Array(textureData.buffer);
|
|
@@ -1772,8 +1862,6 @@ ${rayTraceCompute({
|
|
|
1772
1862
|
this._lightSamplingScratch = previous;
|
|
1773
1863
|
this._lightSamplingData = textureData;
|
|
1774
1864
|
this._lightSamplingTriangleCount = this._emissiveTriangles.length;
|
|
1775
|
-
this._rtUniform.views.lightSamplingTextureWidth[0] = width;
|
|
1776
|
-
this._rtUniform.views.lightChoiceTexelOffset[0] = emissiveTexels;
|
|
1777
1865
|
this._needsUpdateAnalyticLightsBuffer = true;
|
|
1778
1866
|
this._needsUpdateLightSamplingData = false;
|
|
1779
1867
|
}
|
|
@@ -1823,33 +1911,15 @@ ${rayTraceCompute({
|
|
|
1823
1911
|
const tlas = buildTLAS(res.instances, res.blases);
|
|
1824
1912
|
this._tlas = tlas;
|
|
1825
1913
|
this._uploadBVH(tlas, res.blases);
|
|
1826
|
-
const materialData = (this._materialData =
|
|
1914
|
+
const materialData = (this._materialData = this._createMaterialData(materials, this._analyticLights, {
|
|
1827
1915
|
maxTextureArrayBytes: this._maxTextureArrayBytes,
|
|
1828
1916
|
maxTextureArrayLayers: this._device.limits.maxTextureArrayLayers,
|
|
1829
1917
|
maxTextureDimension2D: this._device.limits.maxTextureDimension2D
|
|
1830
1918
|
}));
|
|
1831
1919
|
this._emissiveTriangles = this._emissiveCache.collect(res.emissiveBindings, materials);
|
|
1832
1920
|
this._needsUpdateLightSamplingData = true;
|
|
1833
|
-
const
|
|
1834
|
-
|
|
1835
|
-
'normal',
|
|
1836
|
-
'uv',
|
|
1837
|
-
'uv1',
|
|
1838
|
-
'color',
|
|
1839
|
-
'tangent',
|
|
1840
|
-
'materialIndex'
|
|
1841
|
-
];
|
|
1842
|
-
const attributes = attrNames.map((a) => mergedGeometry[a]);
|
|
1843
|
-
const fixedSize = 4;
|
|
1844
|
-
let stride = 0;
|
|
1845
|
-
const offsets = attrNames.map((attrName, idx) => {
|
|
1846
|
-
const attr = attributes[idx];
|
|
1847
|
-
const offset = stride;
|
|
1848
|
-
if (attr) {
|
|
1849
|
-
stride += fixedSize;
|
|
1850
|
-
}
|
|
1851
|
-
return offset;
|
|
1852
|
-
});
|
|
1921
|
+
const attributeLayout = sceneAttributeLayout(mergedGeometry);
|
|
1922
|
+
const { stride, offsets } = attributeLayout;
|
|
1853
1923
|
const attrBufferData = new F32(mergedGeometry.vertexCount * stride);
|
|
1854
1924
|
packVertexAttributes(mergedGeometry, attrBufferData, stride, offsets, 0, mergedGeometry.vertexCount);
|
|
1855
1925
|
this._attributeData = attrBufferData;
|
|
@@ -1869,17 +1939,7 @@ ${rayTraceCompute({
|
|
|
1869
1939
|
cwbvh: 8
|
|
1870
1940
|
};
|
|
1871
1941
|
this._blasLayoutMask = res.blases.reduce((mask, blas) => mask | blasLayoutBits[blas.bvh.layout ?? 'bvh2'], 0);
|
|
1872
|
-
Object.assign(this._constants,
|
|
1873
|
-
TLAS_STACK_SIZE: retainedShaderCapacity(traversalStackSizes.tlas, Number(this._constants.TLAS_STACK_SIZE ?? 0)),
|
|
1874
|
-
BLAS_STACK_SIZE: retainedShaderCapacity(traversalStackSizes.blas, Number(this._constants.BLAS_STACK_SIZE ?? 0), 32),
|
|
1875
|
-
USE_UV: mergedGeometry.uv != null,
|
|
1876
|
-
USE_VERTEX_COLOR: mergedGeometry.color != null,
|
|
1877
|
-
ATTR_STRIDE: stride / fixedSize,
|
|
1878
|
-
...offsets.reduce((obj, off, idx) => {
|
|
1879
|
-
obj[attrNames[idx].toUpperCase() + '_OFF'] = off / fixedSize;
|
|
1880
|
-
return obj;
|
|
1881
|
-
}, {})
|
|
1882
|
-
});
|
|
1942
|
+
Object.assign(this._constants, sceneTraversalConstants(traversalStackSizes.tlas, traversalStackSizes.blas, this._constants), attributeLayout.constants);
|
|
1883
1943
|
this._updateMaterialConstants(materials, materialData, mergedGeometry);
|
|
1884
1944
|
this._updateTextures(materialData);
|
|
1885
1945
|
this._needsUpdateBindGroups = true;
|
|
@@ -1905,52 +1965,11 @@ ${rayTraceCompute({
|
|
|
1905
1965
|
}
|
|
1906
1966
|
this._hasShadowCatcher = materials.some((m) => m.shadowCatcher);
|
|
1907
1967
|
const hasDisplacement = materialData.getDisplacementMaps().length > 0;
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
const HAS_BEVEL = materials.some((m) => m.bevelSize > 0);
|
|
1914
|
-
const SPECTRAL = materials.some((m) => m.transmissionDispersionScale > 0);
|
|
1915
|
-
const HAS_MEDIUM = materials.some(materialUsesMedium);
|
|
1916
|
-
// Retain the atlas uniform capacity when a stage/material is removed.
|
|
1917
|
-
// Shrinking the array would rebuild every pipeline again on the next edit.
|
|
1918
|
-
const NUM_MAPS = Math.max(Math.ceil(+materialData.defines.NUM_MAPS / 10) * 10, Number(this._constants.NUM_MAPS ?? 0), 20);
|
|
1919
|
-
// Either layer's map requires independent normals, including a smooth
|
|
1920
|
-
// coat over a mapped base. Unmapped materials retain the shared path.
|
|
1921
|
-
const USE_COAT_NORMAL_FRAMES = materials.some(m => m.coatWeight > 0 && (!!m.normalMap || !!m.coatNormalMap));
|
|
1922
|
-
Object.assign(this._constants, {
|
|
1923
|
-
// Match the atlas UV selector. Keep the second set when any texture
|
|
1924
|
-
// needs it; material/texture edits already rebuild these constants.
|
|
1925
|
-
USE_UV1: materialData.bufferData.mapUvChannel.some(channel => channel > 0.5),
|
|
1926
|
-
// Isotropic OpenPBR constructs its own basis. Normal mapping uses
|
|
1927
|
-
// triangle UV differentials rather than this vertex tangent.
|
|
1928
|
-
USE_TANGENT: mergedGeometry.tangent != null &&
|
|
1929
|
-
materials.some(material => material.specularAnisotropy > 0 || material.coatAnisotropy > 0 || (!!material.coatNormalMap && material.coatWeight > 0)),
|
|
1930
|
-
USE_COAT_NORMAL_FRAMES,
|
|
1931
|
-
USE_ANISOTROPY_DIRECTIONS: USE_COAT_NORMAL_FRAMES || materials.some(m => m.specularAnisotropy > 0 && (m.specularAnisotropyModel === 'gltf' || !!m.anisotropyMap || m.specularAnisotropyRotation !== 0) ||
|
|
1932
|
-
m.coatAnisotropy > 0 && m.coatAnisotropyRotation !== 0),
|
|
1933
|
-
HAS_MEDIUM,
|
|
1934
|
-
HAS_SHADOW_CATCHER: this._hasShadowCatcher,
|
|
1935
|
-
HAS_THIN_FILM: materials.some((material) => material.thinFilmWeight > 0),
|
|
1936
|
-
HAS_COAT: materials.some((material) => material.coatWeight > 0),
|
|
1937
|
-
HAS_FUZZ: materials.some((material) => material.fuzzWeight > 0),
|
|
1938
|
-
HAS_SUBSURFACE: materials.some((material) => material.subsurfaceWeight > 0),
|
|
1939
|
-
HAS_BEVEL,
|
|
1940
|
-
HAS_ALPHA_MASK: materials.some((m) => resolveMaterialAlphaMode(m) === 'mask'),
|
|
1941
|
-
HAS_ALPHA_BLEND: materials.some((m) => resolveMaterialAlphaMode(m) === 'blend'),
|
|
1942
|
-
HAS_ALPHA: materials.some((m) => resolveMaterialAlphaMode(m) !== 'opaque'),
|
|
1943
|
-
SPECTRAL,
|
|
1944
|
-
BEVEL_SAMPLES: this._bevelSamples,
|
|
1945
|
-
USE_RR: true,
|
|
1946
|
-
// Match WebGL: defer roulette for multiple-scattering SSS paths.
|
|
1947
|
-
RR_MAX: HAS_MEDIUM ? 12 : 2,
|
|
1948
|
-
NUM_UNIFORM_LIGHTS: FIXED_LIGHT_COUNT,
|
|
1949
|
-
...materialData.defines,
|
|
1950
|
-
NUM_MAPS,
|
|
1951
|
-
...materialTextureFeatures(materialData.defines),
|
|
1952
|
-
USE_NORMAL_MAPS: +materialData.defines.NUM_NORMAL_MAPS > 0
|
|
1953
|
-
});
|
|
1968
|
+
this._hasDisplacement = hasDisplacement;
|
|
1969
|
+
Object.assign(this._constants, wgpuMaterialConstants(materials, materialData, mergedGeometry, this._bevelSamples, Number(this._constants.NUM_MAPS ?? 0)));
|
|
1970
|
+
// Runtime storage metadata has no texture-count specialization.
|
|
1971
|
+
if (this._bindless)
|
|
1972
|
+
this._constants.NUM_MAPS = 20;
|
|
1954
1973
|
}
|
|
1955
1974
|
_updateGeometryData() {
|
|
1956
1975
|
const res = this._preparedMeshData, tlas = this._tlas;
|
|
@@ -1996,7 +2015,7 @@ ${rayTraceCompute({
|
|
|
1996
2015
|
const res = this._preparedMeshData;
|
|
1997
2016
|
let materialData = this._materialData;
|
|
1998
2017
|
if (update.level === 'textures') {
|
|
1999
|
-
materialData = this._materialData =
|
|
2018
|
+
materialData = this._materialData = this._createMaterialData(res.materials, this._analyticLights, {
|
|
2000
2019
|
maxTextureArrayBytes: this._maxTextureArrayBytes,
|
|
2001
2020
|
maxTextureArrayLayers: this._device.limits.maxTextureArrayLayers,
|
|
2002
2021
|
maxTextureDimension2D: this._device.limits.maxTextureDimension2D
|
|
@@ -2106,7 +2125,26 @@ ${rayTraceCompute({
|
|
|
2106
2125
|
this._dirtyTransformMeshes.clear();
|
|
2107
2126
|
this._needsRefitTLAS = false;
|
|
2108
2127
|
}
|
|
2128
|
+
_createMaterialData(...args) {
|
|
2129
|
+
if (this._bindless) {
|
|
2130
|
+
const data = new WGPUBindlessMaterialData(args[0], args[1]);
|
|
2131
|
+
const reason = bindlessLimitReason(this._device, data.textures.length);
|
|
2132
|
+
if (!reason)
|
|
2133
|
+
return data;
|
|
2134
|
+
this._textureSelection.fallbackReason = reason;
|
|
2135
|
+
const retired = this._bindless;
|
|
2136
|
+
this._retiredBindless.add(retired);
|
|
2137
|
+
void this._device.queue.onSubmittedWorkDone().catch(() => { }).then(() => { retired.dispose(); this._retiredBindless.delete(retired); });
|
|
2138
|
+
this._bindless = undefined;
|
|
2139
|
+
}
|
|
2140
|
+
return new MaterialTable(...args);
|
|
2141
|
+
}
|
|
2109
2142
|
_updateTextures(materialData) {
|
|
2143
|
+
if (materialData instanceof WGPUBindlessMaterialData) {
|
|
2144
|
+
this._bindless.update(materialData.textures, this._maxTextureArrayBytes);
|
|
2145
|
+
this._needsUpdateBindGroups = true;
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
2110
2148
|
// Textures
|
|
2111
2149
|
// TODO Avoid zero length array.
|
|
2112
2150
|
const mapInfo = materialData.bufferData.mapInfo ?? [0, 0, 0, 0];
|
|
@@ -2209,6 +2247,10 @@ ${rayTraceCompute({
|
|
|
2209
2247
|
}
|
|
2210
2248
|
// TODO reuse the code of RayTraceNode
|
|
2211
2249
|
setCamera(camera) {
|
|
2250
|
+
this._causticPerspective = camera.projection === 'perspective';
|
|
2251
|
+
const projection = camera.getProjectionMatrix(this._targetAspect);
|
|
2252
|
+
this._causticCameraScale = Math.abs(projection[0] * projection[5]) / 4;
|
|
2253
|
+
this._setCaustics(this._causticSettings);
|
|
2212
2254
|
const viewProjectionInvMat = this._viewProjInv;
|
|
2213
2255
|
this._apertureRadius = cameraApertureRadius(camera);
|
|
2214
2256
|
this._focusDistance = camera.focusDistance;
|
|
@@ -2268,14 +2310,7 @@ ${rayTraceCompute({
|
|
|
2268
2310
|
const arr = this._rtUniform.views.jitter;
|
|
2269
2311
|
arr[0] = x;
|
|
2270
2312
|
arr[1] = y;
|
|
2271
|
-
this._device.queue.writeBuffer(this._uniforms.cameraUniform, 0,
|
|
2272
|
-
...this._viewProjInv,
|
|
2273
|
-
...this._cameraWorld,
|
|
2274
|
-
this._apertureRadius,
|
|
2275
|
-
this._focusDistance,
|
|
2276
|
-
0,
|
|
2277
|
-
0
|
|
2278
|
-
]));
|
|
2313
|
+
this._device.queue.writeBuffer(this._uniforms.cameraUniform, 0, packCameraUniform(this._viewProjInv, this._cameraWorld, this._apertureRadius, this._focusDistance));
|
|
2279
2314
|
}
|
|
2280
2315
|
setSampleTiles(columns, rows, tileX, tileY) {
|
|
2281
2316
|
const arr = this._rtUniform.views.scaleOffset;
|
|
@@ -2301,9 +2336,18 @@ ${rayTraceCompute({
|
|
|
2301
2336
|
return this._hasShadowCatcher;
|
|
2302
2337
|
}
|
|
2303
2338
|
dispose() {
|
|
2339
|
+
this._disposed = true;
|
|
2340
|
+
this._envDistribution?.dispose();
|
|
2341
|
+
this._skyBaker?.dispose();
|
|
2342
|
+
this._bindless?.dispose();
|
|
2343
|
+
this._retiredBindless.forEach((textures) => textures.dispose());
|
|
2344
|
+
this._retiredBindless.clear();
|
|
2345
|
+
this._causticLight?.dispose();
|
|
2346
|
+
this._photonMap?.dispose();
|
|
2304
2347
|
this._shaderCache.clear();
|
|
2348
|
+
this._shaderCapabilities.clear();
|
|
2305
2349
|
const buffers = new Set([
|
|
2306
|
-
...Array.from(this._targetOutputs.values()).flatMap(target => target.buffers),
|
|
2350
|
+
...Array.from(this._targetOutputs.values()).flatMap((target) => target.buffers),
|
|
2307
2351
|
this._mapInfoUniform,
|
|
2308
2352
|
this._attrBuffers,
|
|
2309
2353
|
this._materialsBuffer,
|
|
@@ -2323,6 +2367,9 @@ ${rayTraceCompute({
|
|
|
2323
2367
|
this._adaptiveSamplingTexture
|
|
2324
2368
|
]);
|
|
2325
2369
|
textures.forEach((texture) => texture?.destroy());
|
|
2370
|
+
for (const job of this._queuedCompiles.splice(0))
|
|
2371
|
+
job.release();
|
|
2372
|
+
this._traceVariants.clear();
|
|
2326
2373
|
this._rayTracePipeline = undefined;
|
|
2327
2374
|
this._rayTraceBindGroups = undefined;
|
|
2328
2375
|
}
|