@forgeax/engine-render 0.1.33 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1062 -127
- package/dist/assembly/backend-contract.d.ts +2 -0
- package/dist/assembly/backend-contract.d.ts.map +1 -1
- package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
- package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
- package/dist/assembly/canvas-draw-guard.d.ts +3 -0
- package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
- package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
- package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
- package/dist/assembly/device-feature-admission.d.ts +2 -2
- package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
- package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
- package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
- package/dist/assembly/factory.d.ts +1 -1
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +6 -5
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
- package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
- package/dist/assembly/material-shader-policy.d.ts +16 -2
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
- package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/render-feature-post-processes.d.ts +6 -0
- package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
- package/dist/assembly/render-target-host.d.ts +5 -0
- package/dist/assembly/render-target-host.d.ts.map +1 -1
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
- package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-inspection.d.ts +11 -0
- package/dist/assembly/renderer-inspection.d.ts.map +1 -1
- package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
- package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +1 -1
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
- package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +2 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/authoring.mjs +2 -4
- package/dist/authoring.mjs.map +1 -1
- package/dist/barrel-distortion.d.ts +47 -0
- package/dist/barrel-distortion.d.ts.map +1 -0
- package/dist/chunk-3XUBWRY2.mjs +1842 -0
- package/dist/chunk-3XUBWRY2.mjs.map +1 -0
- package/dist/{chunk-OO3DRYK7.mjs → chunk-G6HDNGY7.mjs} +2400 -1767
- package/dist/chunk-G6HDNGY7.mjs.map +1 -0
- package/dist/{chunk-ZDQWGQW7.mjs → chunk-LM5IE7GF.mjs} +4 -4
- package/dist/{chunk-ZDQWGQW7.mjs.map → chunk-LM5IE7GF.mjs.map} +1 -1
- package/dist/{chunk-VOZWDM3L.mjs → chunk-LNIUIKQ7.mjs} +1514 -498
- package/dist/chunk-LNIUIKQ7.mjs.map +1 -0
- package/dist/chunk-MXTWKZO4.mjs +3268 -0
- package/dist/chunk-MXTWKZO4.mjs.map +1 -0
- package/dist/chunk-MZ4C7EHF.mjs +6942 -0
- package/dist/chunk-MZ4C7EHF.mjs.map +1 -0
- package/dist/chunk-RIPAEGI6.mjs +48 -0
- package/dist/chunk-RIPAEGI6.mjs.map +1 -0
- package/dist/{chunk-LEAXOMYJ.mjs → chunk-SGPUJW3F.mjs} +117 -5
- package/dist/chunk-SGPUJW3F.mjs.map +1 -0
- package/dist/{chunk-PA5DRJNI.mjs → chunk-X2KA6WHM.mjs} +29 -4
- package/dist/chunk-X2KA6WHM.mjs.map +1 -0
- package/dist/{chunk-BLXGONR2.mjs → chunk-XSFB7XXQ.mjs} +51 -4
- package/dist/chunk-XSFB7XXQ.mjs.map +1 -0
- package/dist/cloud/density.d.ts +55 -0
- package/dist/cloud/density.d.ts.map +1 -0
- package/dist/cloud/extract.d.ts +25 -0
- package/dist/cloud/extract.d.ts.map +1 -0
- package/dist/cloud/feature.d.ts +57 -0
- package/dist/cloud/feature.d.ts.map +1 -0
- package/dist/cloud/frame-context.d.ts +8 -0
- package/dist/cloud/frame-context.d.ts.map +1 -0
- package/dist/cloud/inspection.d.ts +59 -0
- package/dist/cloud/inspection.d.ts.map +1 -0
- package/dist/cloud/optics.d.ts +56 -0
- package/dist/cloud/optics.d.ts.map +1 -0
- package/dist/cloud/parameters.d.ts +73 -0
- package/dist/cloud/parameters.d.ts.map +1 -0
- package/dist/cloud/resources.d.ts +60 -0
- package/dist/cloud/resources.d.ts.map +1 -0
- package/dist/cloud/shadow.d.ts +42 -0
- package/dist/cloud/shadow.d.ts.map +1 -0
- package/dist/cloud/temporal.d.ts +74 -0
- package/dist/cloud/temporal.d.ts.map +1 -0
- package/dist/components/barrel-distortion.d.ts +13 -0
- package/dist/components/barrel-distortion.d.ts.map +1 -0
- package/dist/components/cloud-layer.d.ts +29 -0
- package/dist/components/cloud-layer.d.ts.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/instances.d.ts +20 -6
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/construct-renderer.d.ts +1 -1
- package/dist/construct-renderer.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +8406 -5785
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +19 -19
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/dynamic-geometry.d.ts +5 -1
- package/dist/dynamic-geometry.d.ts.map +1 -1
- package/dist/errors/cloud.d.ts +59 -0
- package/dist/errors/cloud.d.ts.map +1 -0
- package/dist/errors/render.d.ts +20 -5
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/examples/single-layer-medium-surface.d.ts +47 -0
- package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +5 -1
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/extract/material-context.d.ts +7 -0
- package/dist/extract/material-context.d.ts.map +1 -0
- package/dist/features/barrel-distortion.d.ts +29 -0
- package/dist/features/barrel-distortion.d.ts.map +1 -0
- package/dist/features/error-reporting.d.ts +6 -2
- package/dist/features/error-reporting.d.ts.map +1 -1
- package/dist/features/host.d.ts +15 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/plan.d.ts +10 -0
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts +5 -0
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +1 -0
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/targets.d.ts +2 -0
- package/dist/features/targets.d.ts.map +1 -1
- package/dist/features/types.d.ts +48 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +15 -7
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/glyph-text-layout-system.d.ts +1 -1
- package/dist/glyph-text-layout-system.d.ts.map +1 -1
- package/dist/gpu-dirty-ranges.d.ts +11 -0
- package/dist/gpu-dirty-ranges.d.ts.map +1 -0
- package/dist/gpu-driven/batch-topology.d.ts +17 -0
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts +6 -0
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster-material.d.ts +178 -0
- package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
- package/dist/gpu-driven/production-raster.d.ts +47 -3
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/resource-allocation.d.ts +31 -0
- package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
- package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
- package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
- package/dist/gpu-driven/view-gpu.d.ts +26 -2
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +5 -0
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +2 -2
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/frame-resources.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +14 -5
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +30 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +780 -9
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +135 -1
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instance-buffer-cache.d.ts +4 -4
- package/dist/instance-buffer-cache.d.ts.map +1 -1
- package/dist/instances.d.ts +47 -100
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +7 -8
- package/dist/internal.mjs.map +1 -1
- package/dist/pbr-pipeline.d.ts +22 -4
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
- package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
- package/dist/pipeline/standard-output/lut-gpu.d.ts +2 -2
- package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -1
- package/dist/pipeline/standard-output/resources.d.ts +2 -2
- package/dist/pipeline/standard-output/resources.d.ts.map +1 -1
- package/dist/pipeline/standard-output/types.d.ts +4 -2
- package/dist/pipeline/standard-output/types.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +1 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
- package/dist/publication/contract.d.ts +77 -0
- package/dist/publication/contract.d.ts.map +1 -0
- package/dist/publication/dependencies.d.ts +7 -0
- package/dist/publication/dependencies.d.ts.map +1 -0
- package/dist/publication/prepare-geometry.d.ts +9 -0
- package/dist/publication/prepare-geometry.d.ts.map +1 -0
- package/dist/publication/programs.d.ts +5 -0
- package/dist/publication/programs.d.ts.map +1 -0
- package/dist/publication/publisher.d.ts +33 -0
- package/dist/publication/publisher.d.ts.map +1 -0
- package/dist/publication/receiver.d.ts +29 -0
- package/dist/publication/receiver.d.ts.map +1 -0
- package/dist/publication/resource-scope.d.ts +18 -0
- package/dist/publication/resource-scope.d.ts.map +1 -0
- package/dist/publication/targets.d.ts +33 -0
- package/dist/publication/targets.d.ts.map +1 -0
- package/dist/publication/video.d.ts +5 -0
- package/dist/publication/video.d.ts.map +1 -0
- package/dist/record/barrel-distortion-frame.d.ts +15 -0
- package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
- package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
- package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
- package/dist/record/fold-buckets.d.ts +95 -0
- package/dist/record/fold-buckets.d.ts.map +1 -0
- package/dist/record/fold-instance-buffer.d.ts +6 -0
- package/dist/record/fold-instance-buffer.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +1 -0
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +43 -9
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame.d.ts +15 -65
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-draw-receipts.d.ts +11 -0
- package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
- package/dist/record/gpu-timing.d.ts +8 -0
- package/dist/record/gpu-timing.d.ts.map +1 -1
- package/dist/record/helpers.d.ts +2 -2
- package/dist/record/helpers.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +22 -6
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +14 -10
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +3 -16
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +13 -2
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/motion-blur-receipt.d.ts +11 -0
- package/dist/record/motion-blur-receipt.d.ts.map +1 -0
- package/dist/record/prepared-material-bindings.d.ts +3 -3
- package/dist/record/prepared-material-bindings.d.ts.map +1 -1
- package/dist/record/probe-blend-buffer.d.ts +16 -0
- package/dist/record/probe-blend-buffer.d.ts.map +1 -0
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +24 -5
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +2 -0
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +1 -1
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/sprite-instance-buffer.d.ts +31 -0
- package/dist/record/sprite-instance-buffer.d.ts.map +1 -0
- package/dist/record/target-capture-graph.d.ts +19 -0
- package/dist/record/target-capture-graph.d.ts.map +1 -0
- package/dist/record/typed-frame-graph.d.ts +24 -15
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +11 -3
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/record/volume-params.d.ts +9 -0
- package/dist/record/volume-params.d.ts.map +1 -0
- package/dist/recovery/render-system-candidate.d.ts +14 -4
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +46 -5
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +103 -2
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +3 -2
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +18 -9
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-presentation.d.ts +23 -0
- package/dist/render-system-presentation.d.ts.map +1 -0
- package/dist/render-system-producer-roots.d.ts +10 -0
- package/dist/render-system-producer-roots.d.ts.map +1 -0
- package/dist/render-system-projections.d.ts +3 -2
- package/dist/render-system-projections.d.ts.map +1 -1
- package/dist/render-system-residency.d.ts +12 -0
- package/dist/render-system-residency.d.ts.map +1 -0
- package/dist/render-system.d.ts +22 -21
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/probe-blend.d.ts +27 -0
- package/dist/scene/probe-blend.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +18 -7
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/render-source.d.ts +42 -0
- package/dist/scene/render-source.d.ts.map +1 -0
- package/dist/scene/source-systems.d.ts +6 -0
- package/dist/scene/source-systems.d.ts.map +1 -0
- package/dist/ssao-buffers.d.ts +9 -0
- package/dist/ssao-buffers.d.ts.map +1 -1
- package/dist/surface/dynamic-input.d.ts +133 -0
- package/dist/surface/dynamic-input.d.ts.map +1 -0
- package/dist/surface/gpu-driven.d.ts +41 -0
- package/dist/surface/gpu-driven.d.ts.map +1 -0
- package/dist/surface/submission-observation.d.ts +136 -0
- package/dist/surface/submission-observation.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/systems/skin-palette-types.d.ts +10 -0
- package/dist/systems/skin-palette-types.d.ts.map +1 -1
- package/dist/systems/transparent-dispatch.d.ts +2 -2
- package/dist/systems/transparent-dispatch.d.ts.map +1 -1
- package/dist/systems/transparent-sort-config.d.ts +2 -1
- package/dist/systems/transparent-sort-config.d.ts.map +1 -1
- package/dist/temporal/gpu.d.ts +13 -1
- package/dist/temporal/gpu.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -7
- package/dist/temporal/inspection.d.ts.map +1 -1
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
- package/dist/transmission/single-layer-medium.d.ts +89 -0
- package/dist/transmission/single-layer-medium.d.ts.map +1 -0
- package/dist/typed-render-graph-primitives.d.ts +14 -2
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/component.d.ts +9 -4
- package/dist/volume/component.d.ts.map +1 -1
- package/dist/volume/extract.d.ts +1 -1
- package/dist/volume/extract.d.ts.map +1 -1
- package/dist/volume/inspection.d.ts +2 -0
- package/dist/volume/inspection.d.ts.map +1 -1
- package/dist/volume/passes.d.ts +4 -1
- package/dist/volume/passes.d.ts.map +1 -1
- package/dist/volume/resources.d.ts.map +1 -1
- package/package.json +22 -21
- package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
- package/src/__tests__/auto-exposure-transaction.integration.test.ts +4 -9
- package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
- package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
- package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
- package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
- package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +3 -2
- package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
- package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
- package/src/__tests__/factory-contract.integration.test.ts +295 -45
- package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
- package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
- package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +5 -6
- package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +6 -5
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +94 -6
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
- package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
- package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
- package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
- package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
- package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
- package/src/__tests__/gpu-scene.unit.test.ts +119 -9
- package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
- package/src/__tests__/instances-culling.integration.test.ts +10 -36
- package/src/__tests__/instances-store.unit.test.ts +97 -305
- package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +4 -1
- package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +145 -0
- package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
- package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
- package/src/__tests__/materials-standard-contract.unit.test.ts +0 -1
- package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
- package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
- package/src/__tests__/motion-blur-compute.dawn.test.ts +13 -7
- package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
- package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
- package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
- package/src/__tests__/physical-clearcoat.integration.test.ts +66 -8
- package/src/__tests__/physical-material-budget.unit.test.ts +40 -0
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
- package/src/__tests__/probe-bindings.unit.test.ts +582 -1
- package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
- package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +8 -0
- package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
- package/src/__tests__/render-feature-error-reporting.unit.test.ts +21 -1
- package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
- package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
- package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
- package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +20 -20
- package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
- package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
- package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +42 -8
- package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +26 -10
- package/src/__tests__/resource-allocation.unit.test.ts +123 -0
- package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
- package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +15 -0
- package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
- package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
- package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
- package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
- package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
- package/src/__tests__/standard-pipeline.integration.test.ts +182 -1
- package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
- package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
- package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
- package/src/__tests__/temporal-performance.integration.test.ts +3 -3
- package/src/__tests__/visibility-instances.integration.test.ts +7 -19
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
- package/src/__tests__/volume-optics-oracle.unit.test.ts +4 -2
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +30 -20
- package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
- package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
- package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +113 -9
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +5 -3
- package/src/assembly/backend-contract.ts +30 -0
- package/src/assembly/barrel-distortion-inspection.ts +22 -0
- package/src/assembly/canvas-draw-guard.ts +11 -0
- package/src/assembly/cloud-shadow-entry.ts +22 -0
- package/src/assembly/device-feature-admission.ts +2 -2
- package/src/assembly/dynamic-geometry-host.ts +65 -37
- package/src/assembly/dynamic-geometry-runtime.ts +7 -1
- package/src/assembly/factory.ts +2 -30
- package/src/assembly/host-contract.ts +8 -5
- package/src/assembly/material/assembly.ts +25 -13
- package/src/assembly/material/pipeline-helpers.ts +53 -12
- package/src/assembly/material-shader-policy.ts +55 -0
- package/src/assembly/material-shader-prewarm.ts +21 -10
- package/src/assembly/recovery/device-loss-fanout.ts +13 -0
- package/src/assembly/recovery/renderer-recover.ts +50 -18
- package/src/assembly/render-feature-post-processes.ts +54 -0
- package/src/assembly/render-target-host.ts +57 -47
- package/src/assembly/renderer-facade.ts +7 -2
- package/src/assembly/renderer-feature-inspection.ts +8 -2
- package/src/assembly/renderer-frame-transaction.ts +23 -0
- package/src/assembly/renderer-inspection.ts +28 -0
- package/src/assembly/skin-palette-owner.ts +1 -1
- package/src/assembly/temporal-post-process-prewarm.ts +3 -3
- package/src/assembly/webgpu-pbr-ready.ts +9 -1
- package/src/assembly/webgpu-ready-per-pass.ts +104 -0
- package/src/assembly/webgpu-ready.ts +116 -62
- package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
- package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
- package/src/assembly/webgpu-renderer.ts +303 -313
- package/src/assembly/webgpu-vertex-layouts.ts +1 -1
- package/src/barrel-distortion.ts +247 -0
- package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
- package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
- package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
- package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
- package/src/cloud/density.ts +600 -0
- package/src/cloud/extract.ts +110 -0
- package/src/cloud/feature.ts +1810 -0
- package/src/cloud/frame-context.ts +42 -0
- package/src/cloud/inspection.ts +130 -0
- package/src/cloud/optics.ts +285 -0
- package/src/cloud/parameters.ts +255 -0
- package/src/cloud/resources.ts +122 -0
- package/src/cloud/shadow.ts +167 -0
- package/src/cloud/temporal.ts +226 -0
- package/src/components/barrel-distortion.ts +46 -0
- package/src/components/cloud-layer.ts +47 -0
- package/src/components/index.ts +2 -0
- package/src/components/instances.ts +18 -45
- package/src/components/sprite-instances.ts +1 -1
- package/src/construct-renderer.ts +1 -1
- package/src/device/gpu-residency.ts +26 -25
- package/src/dynamic-geometry.ts +12 -1
- package/src/errors/cloud.ts +103 -0
- package/src/errors/render.ts +47 -8
- package/src/examples/single-layer-medium-surface.ts +59 -0
- package/src/extract/camera.ts +15 -1
- package/src/extract/contracts.ts +5 -0
- package/src/extract/material-context.ts +21 -0
- package/src/features/barrel-distortion.ts +172 -0
- package/src/features/error-reporting.ts +26 -6
- package/src/features/host.ts +128 -19
- package/src/features/plan.ts +10 -0
- package/src/features/render-graph-contribution.ts +9 -0
- package/src/features/render-graph-raster.ts +45 -3
- package/src/features/targets.ts +66 -0
- package/src/features/types.ts +51 -0
- package/src/fullscreen-post-process-pass.ts +41 -9
- package/src/glyph-text-layout-system.ts +2 -2
- package/src/gpu-dirty-ranges.ts +48 -0
- package/src/gpu-driven/batch-topology.ts +53 -0
- package/src/gpu-driven/material-artifacts.ts +33 -25
- package/src/gpu-driven/prepared-draw.ts +12 -4
- package/src/gpu-driven/production-raster-material.ts +1894 -0
- package/src/gpu-driven/production-raster.ts +825 -784
- package/src/gpu-driven/resource-allocation.ts +142 -0
- package/src/gpu-driven/shadow-ownership.ts +71 -0
- package/src/gpu-driven/view-gpu.ts +164 -10
- package/src/gpu-scene.ts +75 -84
- package/src/hdrp-buffers.ts +4 -3
- package/src/ibl/frame-resources.ts +0 -1
- package/src/ibl/skylight-bind-group.ts +62 -32
- package/src/index.ts +205 -9
- package/src/inspection-types.ts +162 -0
- package/src/instance-buffer-cache.ts +4 -4
- package/src/instances.ts +152 -569
- package/src/pbr-pipeline.ts +230 -30
- package/src/pipeline/single-layer-medium-passes.ts +267 -0
- package/src/pipeline/standard-forward-lane.ts +313 -57
- package/src/pipeline/standard-output/graph.ts +8 -2
- package/src/pipeline/standard-output/lut-gpu.ts +3 -3
- package/src/pipeline/standard-output/resources.ts +2 -2
- package/src/pipeline/standard-output/types.ts +12 -1
- package/src/pipeline/standard-pipeline.ts +291 -18
- package/src/pipeline/standard-post.ts +90 -24
- package/src/pipeline-builder.ts +1 -1
- package/src/plugin.ts +4 -0
- package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
- package/src/prepare/prepared-graphics-resolver.ts +2 -0
- package/src/publication/__tests__/derived-geometry.integration.test.ts +177 -0
- package/src/publication/__tests__/features.integration.test.ts +122 -0
- package/src/publication/__tests__/programs.unit.test.ts +25 -0
- package/src/publication/__tests__/publication.integration.test.ts +370 -0
- package/src/publication/__tests__/targets.integration.test.ts +120 -0
- package/src/publication/__tests__/video.browser.test.ts +50 -0
- package/src/publication/contract.ts +104 -0
- package/src/publication/dependencies.ts +54 -0
- package/src/publication/prepare-geometry.ts +94 -0
- package/src/publication/programs.ts +45 -0
- package/src/publication/publisher.ts +618 -0
- package/src/publication/receiver.ts +297 -0
- package/src/publication/resource-scope.ts +31 -0
- package/src/publication/targets.ts +182 -0
- package/src/publication/video.ts +33 -0
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
- package/src/record/__tests__/material-bg-cache.unit.test.ts +15 -1
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +9 -11
- package/src/record/__tests__/temporal-payload.unit.test.ts +1 -1
- package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
- package/src/record/__tests__/video-texture-view.unit.test.ts +9 -9
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
- package/src/record/barrel-distortion-frame.ts +110 -0
- package/src/record/dynamic-geometry-consumption.ts +39 -9
- package/src/record/fold-buckets.ts +855 -0
- package/src/record/fold-instance-buffer.ts +51 -0
- package/src/record/frame-lighting.ts +36 -2
- package/src/record/frame-snapshot.ts +45 -9
- package/src/record/frame.ts +258 -1105
- package/src/record/gpu-draw-receipts.ts +38 -0
- package/src/record/gpu-timing.ts +48 -5
- package/src/record/helpers.ts +2 -2
- package/src/record/main-pass-geometry.ts +272 -84
- package/src/record/main-pass-material.ts +102 -36
- package/src/record/main-pass-sprite-draws.ts +13 -217
- package/src/record/main-pass.ts +164 -27
- package/src/record/motion-blur-receipt.ts +91 -0
- package/src/record/prepared-material-bindings.ts +11 -6
- package/src/record/probe-blend-buffer.ts +180 -0
- package/src/record/recovery-pipeline.ts +35 -3
- package/src/record/render-context.ts +41 -5
- package/src/record/shadow-pass.ts +30 -4
- package/src/record/skybox-post-pass.ts +28 -4
- package/src/record/sprite-instance-buffer.ts +155 -0
- package/src/record/target-capture-graph.ts +258 -0
- package/src/record/typed-frame-graph.ts +776 -340
- package/src/record/view-ubo.ts +27 -4
- package/src/record/volume-params.ts +225 -0
- package/src/recovery/render-system-candidate.ts +243 -50
- package/src/reflection/record-owner.ts +7 -3
- package/src/render-contract.ts +54 -5
- package/src/render-graph-primitives.ts +7 -1
- package/src/render-pipeline.ts +130 -0
- package/src/render-system-extract-tail.ts +90 -78
- package/src/render-system-extract.ts +76 -76
- package/src/render-system-presentation.ts +56 -0
- package/src/render-system-producer-roots.ts +96 -0
- package/src/render-system-projections.ts +3 -2
- package/src/render-system-residency.ts +159 -0
- package/src/render-system.ts +421 -408
- package/src/scene/probe-blend.ts +96 -4
- package/src/scene/render-scene.ts +416 -337
- package/src/scene/render-source.ts +90 -0
- package/src/scene/source-systems.ts +33 -0
- package/src/ssao-buffers.ts +95 -2
- package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
- package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
- package/src/surface/dynamic-input.ts +658 -0
- package/src/surface/gpu-driven.ts +147 -0
- package/src/surface/submission-observation.ts +517 -0
- package/src/systems/skin-palette-allocator.ts +5 -1
- package/src/systems/skin-palette-types.ts +11 -0
- package/src/systems/transparent-dispatch.ts +2 -2
- package/src/systems/transparent-sort-config.ts +3 -1
- package/src/temporal/gpu.ts +101 -17
- package/src/temporal/inspection.ts +7 -1
- package/src/temporal/standard-scene-data.ts +5 -0
- package/src/temporal/target-coverage-attachment.ts +4 -0
- package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
- package/src/transmission/single-layer-medium.ts +322 -0
- package/src/typed-render-graph-primitives.ts +70 -2
- package/src/volume/component.ts +10 -9
- package/src/volume/extract.ts +11 -5
- package/src/volume/inspection.ts +3 -0
- package/src/volume/passes.ts +87 -4
- package/src/volume/resources.ts +2 -1
- package/dist/chunk-3RTCJTH4.mjs +0 -1610
- package/dist/chunk-3RTCJTH4.mjs.map +0 -1
- package/dist/chunk-BLXGONR2.mjs.map +0 -1
- package/dist/chunk-ILQ5MEZF.mjs +0 -32
- package/dist/chunk-ILQ5MEZF.mjs.map +0 -1
- package/dist/chunk-LEAXOMYJ.mjs.map +0 -1
- package/dist/chunk-NB2S7ZRX.mjs +0 -1783
- package/dist/chunk-NB2S7ZRX.mjs.map +0 -1
- package/dist/chunk-NNQL7CB3.mjs +0 -1713
- package/dist/chunk-NNQL7CB3.mjs.map +0 -1
- package/dist/chunk-OO3DRYK7.mjs.map +0 -1
- package/dist/chunk-PA5DRJNI.mjs.map +0 -1
- package/dist/chunk-PLAXOOX5.mjs +0 -1907
- package/dist/chunk-PLAXOOX5.mjs.map +0 -1
- package/dist/chunk-RXZ3RGOU.mjs +0 -53
- package/dist/chunk-RXZ3RGOU.mjs.map +0 -1
- package/dist/chunk-VOZWDM3L.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/assembly/material/surface-projection.ts","../src/materials.ts","../src/systems/transparent-sort-config.ts"],"names":["pass"],"mappings":"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","file":"chunk-XSFB7XXQ.mjs","sourcesContent":["import { DEFAULT_STANDARD_SURFACE_MODULE } from '@forgeax/engine-shader';\nimport type {\n MaterialPass,\n MaterialRenderState,\n MaterialValue,\n StandardLayerPlan,\n} from '@forgeax/engine-types';\n\nexport const STANDARD_MATERIAL_MODULE = 'forgeax_material::standard';\nexport { DEFAULT_STANDARD_SURFACE_MODULE };\n\nexport type StandardSurfacePassName = 'forward' | 'deferred' | 'shadow-caster';\nexport type StandardSurfaceGeometryVariant = 'rigid' | 'skinned' | 'instanced';\nexport type StandardSurfaceLightingLane = 'direct' | 'clustered';\nexport type StandardSurfaceKind = 'standard' | 'full-custom';\n\nexport interface StandardSurfaceProjectionOptions {\n readonly surfaceModule: string;\n readonly values: Readonly<Record<string, MaterialValue | null>>;\n readonly renderState?: MaterialRenderState;\n readonly castShadow?: boolean;\n readonly queue?: number;\n readonly geometryVariant?: StandardSurfaceGeometryVariant;\n readonly lightingLane?: StandardSurfaceLightingLane;\n readonly alphaClip?: boolean;\n readonly surfaceKind?: StandardSurfaceKind;\n readonly declaredPasses?: readonly StandardSurfacePassName[];\n readonly layerPlan?: StandardLayerPlan;\n}\n\nfunction pass(\n name: StandardSurfacePassName,\n options: StandardSurfaceProjectionOptions,\n fragmentEntry: string,\n): MaterialPass {\n const authoredState = options.renderState ?? {};\n const authoredRecord = authoredState as Readonly<Record<string, unknown>>;\n const tags = (authoredRecord.tags ?? {}) as Readonly<Record<string, string>>;\n const lightMode =\n name === 'shadow-caster' ? 'ShadowCaster' : name === 'forward' ? 'Forward' : 'Deferred';\n return {\n name,\n program: {\n module:\n name === 'shadow-caster' ? 'forgeax::default-shadow-caster' : STANDARD_MATERIAL_MODULE,\n fragmentEntry,\n moduleSlots: { surface: options.surfaceModule },\n },\n renderState: {\n ...authoredState,\n tags:\n options.surfaceModule === DEFAULT_STANDARD_SURFACE_MODULE\n ? { LightMode: lightMode, ...tags }\n : {\n ...tags,\n LightMode: lightMode,\n SurfaceModule: options.surfaceModule,\n SurfaceKind: options.surfaceKind ?? 'standard',\n GeometryVariant: options.geometryVariant ?? 'rigid',\n LightingLane: options.lightingLane ?? 'direct',\n AlphaClip: options.alphaClip === true ? 'enabled' : 'disabled',\n },\n ...(options.queue === undefined ? {} : { queue: options.queue }),\n },\n };\n}\n\n/** Project one custom or default Surface into the existing Standard pass family. */\nexport function projectStandardSurfacePasses(\n options: StandardSurfaceProjectionOptions,\n): [MaterialPass, ...MaterialPass[]] {\n const authoredPasses = options.declaredPasses;\n const passNames: StandardSurfacePassName[] =\n options.surfaceKind === 'full-custom' && authoredPasses !== undefined\n ? [...authoredPasses]\n : (['forward'] as StandardSurfacePassName[]);\n if (authoredPasses === undefined || options.surfaceKind !== 'full-custom') {\n const blended = options.renderState?.blend !== undefined;\n if (!blended && options.layerPlan?.mode !== 'physical') passNames.push('deferred');\n if (options.castShadow !== false) passNames.push('shadow-caster');\n }\n const entries = passNames.map((name) =>\n pass(\n name,\n options,\n name === 'forward' ? 'fs_main' : name === 'deferred' ? 'fs_gbuffer' : 'fs_shadow',\n ),\n );\n const passes = entries as [MaterialPass, ...MaterialPass[]];\n return passes;\n}\n","import { color } from '@forgeax/engine-math';\nimport { DEFAULT_STANDARD_PBR_PARAM_SCHEMA } from '@forgeax/engine-shader';\nimport type {\n MaterialAsset,\n MaterialColorSpace,\n MaterialParameter,\n MaterialPass,\n MaterialRenderState,\n MaterialValue,\n} from '@forgeax/engine-types';\nimport { deriveStandardLayerPlan, standardSurfaceParameters } from '@forgeax/engine-types';\nimport {\n DEFAULT_STANDARD_SURFACE_MODULE,\n projectStandardSurfacePasses,\n} from './assembly/material/surface-projection';\n\nexport type MaterialTransmissionValidationReason =\n | 'non-finite'\n | 'range'\n | 'shape'\n | 'blend'\n | 'depth-write';\n\nexport type MaterialAuthoringValidationReason = 'non-finite' | 'range';\n\n/** Structured authoring failure for a built-in Standard material parameter. */\nexport class MaterialAuthoringContractError extends Error {\n readonly code = 'material-authoring-contract-invalid' as const;\n readonly expected =\n 'Standard material authoring values are finite and within their declared ranges';\n readonly hint =\n 'repair the named Standard material parameter alphaCutoff before publishing the material';\n readonly detail: {\n readonly code: 'material-authoring-contract-invalid';\n readonly material: 'Standard';\n readonly parameter: 'alphaCutoff';\n readonly reason: MaterialAuthoringValidationReason;\n readonly actual?: unknown;\n };\n\n constructor(reason: MaterialAuthoringValidationReason, actual?: unknown) {\n super(`Materials.standard: alphaCutoff violates the authoring contract (${reason})`);\n this.name = 'MaterialAuthoringContractError';\n this.detail = {\n code: 'material-authoring-contract-invalid',\n material: 'Standard',\n parameter: 'alphaCutoff',\n reason,\n ...(actual === undefined ? {} : { actual }),\n };\n }\n}\n\n/** Structured authoring failure for the Standard transmission contract. */\nexport class MaterialTransmissionContractError extends Error {\n readonly code = 'material-transmission-contract-invalid' as const;\n readonly expected = 'transmission material values satisfy finite ranges and Forward depth rules';\n readonly hint =\n 'repair the named transmission value or pass state before publishing the material';\n readonly detail: {\n readonly code: 'material-transmission-contract-invalid';\n readonly material: 'Standard';\n readonly parameter:\n | 'transmission'\n | 'ior'\n | 'thickness'\n | 'attenuationColor'\n | 'attenuationDistance';\n readonly reason: MaterialTransmissionValidationReason;\n readonly actual?: unknown;\n };\n\n constructor(\n parameter: (typeof MaterialTransmissionContractError.prototype.detail)['parameter'],\n reason: MaterialTransmissionValidationReason,\n actual?: unknown,\n ) {\n super(`Materials.standard: ${parameter} violates the transmission contract (${reason})`);\n this.name = 'MaterialTransmissionContractError';\n this.detail = {\n code: 'material-transmission-contract-invalid',\n material: 'Standard',\n parameter,\n reason,\n ...(actual === undefined ? {} : { actual }),\n };\n }\n}\n\nexport const SPRITE_PREMULTIPLIED_ALPHA_BLEND: GPUBlendState = {\n color: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },\n alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' },\n};\n\nconst UNLIT_MODULE = 'forgeax_material::unlit';\nconst SPRITE_MODULE = 'forgeax_material::sprite';\n\nexport type MaterialColorTuple3 = readonly [number, number, number];\nexport type MaterialColorTuple4 = readonly [number, number, number, number];\nexport type MaterialColorInput3 = MaterialColorTuple3 | string | number;\nexport type MaterialColorInput4 = MaterialColorTuple4 | string | number;\n\nfunction colorHexFromNumber(value: number): string {\n if (!Number.isInteger(value) || value < 0 || value > 0xffffff) {\n throw new Error(`material color number must be an integer in [0, 0xffffff], got ${value}`);\n }\n return `#${value.toString(16).padStart(6, '0')}`;\n}\n\nfunction linearColorFromInput(\n input: MaterialColorInput3 | MaterialColorInput4,\n channels: 3 | 4,\n): readonly number[] {\n if (typeof input === 'number' || typeof input === 'string') {\n const parsed = color.create();\n color.fromCss(parsed, typeof input === 'number' ? colorHexFromNumber(input) : input);\n return channels === 3\n ? [parsed[0] as number, parsed[1] as number, parsed[2] as number]\n : Array.from(parsed);\n }\n if (input.length !== channels) {\n throw new Error(`material color tuple must contain ${channels} channels, got ${input.length}`);\n }\n return [...input];\n}\n\n/** Convert an explicit sRGB numeric tuple into the linear tuple factories store. */\nexport function srgb(input: MaterialColorTuple3): MaterialColorTuple3;\nexport function srgb(input: MaterialColorTuple4): MaterialColorTuple4;\nexport function srgb(input: readonly number[]): readonly number[] {\n if (input.length !== 3 && input.length !== 4) {\n throw new Error(`sRGB material color tuple must contain 3 or 4 channels, got ${input.length}`);\n }\n const parsed = color.create(input[0] ?? 0, input[1] ?? 0, input[2] ?? 0, input[3] ?? 1);\n color.srgbToLinear(parsed, parsed);\n return input.length === 3\n ? [parsed[0] as number, parsed[1] as number, parsed[2] as number]\n : [parsed[0] as number, parsed[1] as number, parsed[2] as number, parsed[3] as number];\n}\n\nfunction authoredColorParameter(\n name: string,\n type: 'color' | 'vec3',\n colorSpace: MaterialColorSpace,\n optional = false,\n): MaterialParameter {\n return {\n name,\n type,\n ...(type === 'color' && colorSpace === 'srgb' ? {} : { colorSpace }),\n ...(optional ? { optional: true } : {}),\n };\n}\n\nfunction standardParameters(\n colorSpace: MaterialColorSpace,\n opts: StandardOpts,\n): readonly MaterialParameter[] {\n const physicalNames = new Set<string>();\n const addPhysical = (...names: readonly string[]) => {\n for (const name of names) physicalNames.add(name);\n };\n if (\n opts.clearcoat !== undefined ||\n opts.clearcoatRoughness !== undefined ||\n opts.clearcoatNormalScale !== undefined ||\n opts.clearcoatTexture !== undefined ||\n opts.clearcoatRoughnessTexture !== undefined ||\n opts.clearcoatNormalTexture !== undefined\n ) {\n addPhysical(\n 'clearcoat',\n 'clearcoatRoughness',\n 'clearcoatNormalScale',\n ...(opts.clearcoatTexture === undefined ? [] : ['clearcoatTexture']),\n ...(opts.clearcoatRoughnessTexture === undefined ? [] : ['clearcoatRoughnessTexture']),\n ...(opts.clearcoatNormalTexture === undefined ? [] : ['clearcoatNormalTexture']),\n );\n }\n if (\n opts.anisotropyStrength !== undefined ||\n opts.anisotropyRotation !== undefined ||\n opts.anisotropyTexture !== undefined\n ) {\n addPhysical('anisotropyStrength', 'anisotropyRotation');\n if (opts.anisotropyTexture !== undefined) addPhysical('anisotropyTexture');\n }\n if (\n opts.sheenColor !== undefined ||\n opts.sheenRoughness !== undefined ||\n opts.sheenColorTexture !== undefined ||\n opts.sheenRoughnessTexture !== undefined\n ) {\n addPhysical('sheenColor', 'sheenRoughness');\n if (opts.sheenColorTexture !== undefined) addPhysical('sheenColorTexture');\n if (opts.sheenRoughnessTexture !== undefined) addPhysical('sheenRoughnessTexture');\n }\n if (\n opts.iridescence !== undefined ||\n opts.iridescenceIor !== undefined ||\n opts.iridescenceThicknessMinimum !== undefined ||\n opts.iridescenceThicknessMaximum !== undefined ||\n opts.iridescenceTexture !== undefined ||\n opts.iridescenceThicknessTexture !== undefined\n ) {\n addPhysical(\n 'iridescence',\n 'iridescenceIor',\n 'iridescenceThicknessMinimum',\n 'iridescenceThicknessMaximum',\n ...(opts.iridescenceTexture === undefined ? [] : ['iridescenceTexture']),\n ...(opts.iridescenceThicknessTexture === undefined ? [] : ['iridescenceThicknessTexture']),\n );\n }\n const baseNames = new Set([\n 'baseColor',\n 'metallic',\n 'roughness',\n 'metallicChannel',\n 'roughnessChannel',\n 'aoChannel',\n 'extraChannel',\n 'emissive',\n 'emissiveIntensity',\n 'occlusionStrength',\n 'alphaCutoff',\n 'normalScale',\n 'specular',\n 'specularColor',\n // IOR is part of the always-present dielectric F0 contract. The\n // transmission layer may be absent, but the Standard shader still\n // consumes this scalar for the base reflection path.\n 'ior',\n ]);\n if (opts.specularTexture !== undefined) baseNames.add('specularTexture');\n if (opts.specularColorTexture !== undefined) baseNames.add('specularColorTexture');\n if (\n opts.transmission !== undefined ||\n opts.ior !== undefined ||\n opts.thickness !== undefined ||\n opts.transmissionTexture !== undefined ||\n opts.thicknessTexture !== undefined ||\n opts.attenuationColor !== undefined ||\n opts.attenuationDistance !== undefined\n ) {\n for (const name of [\n 'transmission',\n 'ior',\n 'thickness',\n 'attenuationColor',\n 'attenuationDistance',\n ]) {\n baseNames.add(name);\n }\n }\n // A texture declaration selects its compiled sampling path. Loading state\n // does not: an authored but not-yet-resident slot still needs its fallback.\n for (const parameter of DEFAULT_STANDARD_PBR_PARAM_SCHEMA) {\n if (\n parameter.type === 'texture2d' &&\n opts[parameter.name as keyof StandardOpts] !== undefined\n ) {\n baseNames.add(parameter.name);\n }\n }\n // All Standard fields come from one root schema. Physical fields are\n // projected by the authored option membership above; appending a second\n // physical schema would create a parallel ABI authority.\n const availableSchema = DEFAULT_STANDARD_PBR_PARAM_SCHEMA;\n // Keep the generated Standard UBO's numeric run contiguous before any\n // texture coordinate records. The built-in WGSL places the complete\n // physical root (including clearcoat scalars) after the base numeric run\n // and before all coordinate pairs; appending physical entries after the\n // base texture records would otherwise derive a different ABI.\n const numericSchema = availableSchema.filter((entry) => !entry.type.startsWith('texture'));\n const textureSchema = availableSchema.filter((entry) => entry.type.startsWith('texture'));\n const orderedSchema = [...numericSchema, ...textureSchema];\n return orderedSchema\n .filter((entry) => baseNames.has(entry.name) || physicalNames.has(entry.name))\n .map((entry) => {\n const type = entry.type.startsWith('texture') ? 'texture' : entry.type;\n const isRequired =\n entry.name === 'baseColor' || entry.name === 'metallic' || entry.name === 'roughness';\n const authoredColor =\n entry.type === 'color' || (entry.type === 'vec3' && entry.name !== 'attenuationColor');\n const schemaColorSpace = 'colorSpace' in entry ? entry.colorSpace : undefined;\n const colorSpaceProjection: { colorSpace?: MaterialColorSpace } = {};\n if (authoredColor && !(type === 'color' && colorSpace === 'srgb')) {\n colorSpaceProjection.colorSpace = colorSpace;\n } else if (!authoredColor && schemaColorSpace !== undefined) {\n colorSpaceProjection.colorSpace = schemaColorSpace;\n }\n return {\n name: entry.name,\n type,\n ...colorSpaceProjection,\n ...(isRequired ? {} : { optional: true }),\n } as MaterialParameter;\n });\n}\n\nfunction unlitParameters(colorSpace: MaterialColorSpace): readonly MaterialParameter[] {\n return [\n authoredColorParameter('baseColor', 'color', colorSpace),\n { name: 'alphaCutoff', type: 'f32', optional: true },\n { name: 'baseColorTexture', type: 'texture', optional: true },\n ];\n}\n\nfunction pass(\n name: string,\n module: string,\n renderState: MaterialRenderState | undefined,\n fragmentEntry?: string,\n queue?: number,\n): MaterialPass {\n const lightMode =\n name === 'shadow-caster' ? 'ShadowCaster' : name === 'deferred' ? 'Deferred' : 'Forward';\n const authoredState = (renderState ?? {}) as Readonly<Record<string, unknown>>;\n const authoredTags = authoredState.tags as Readonly<Record<string, string>> | undefined;\n return {\n name,\n program: {\n module,\n ...(fragmentEntry === undefined ? {} : { fragmentEntry }),\n },\n renderState: {\n ...authoredState,\n tags: { LightMode: lightMode, ...authoredTags },\n ...(queue === undefined ? {} : { queue }),\n },\n };\n}\n\nfunction shadowCasterRenderState(\n renderState: MaterialRenderState | undefined,\n): MaterialRenderState | undefined {\n if (renderState?.cullMode === undefined && renderState?.frontFace === undefined) return undefined;\n return {\n ...(renderState.cullMode === undefined ? {} : { cullMode: renderState.cullMode }),\n ...(renderState.frontFace === undefined ? {} : { frontFace: renderState.frontFace }),\n };\n}\n\ninterface UnlitOpts {\n readonly castShadow?: boolean;\n readonly baseColorTexture?: MaterialValue;\n readonly alphaCutoff?: number;\n readonly renderState?: MaterialRenderState;\n readonly queue?: number;\n}\n\nfunction unlit(rgba: MaterialColorInput4, opts?: UnlitOpts): MaterialAsset {\n if (opts?.alphaCutoff !== undefined && (opts.alphaCutoff < 0 || opts.alphaCutoff > 1)) {\n throw new Error(`Materials.unlit: alphaCutoff must be in [0, 1], got ${opts.alphaCutoff}`);\n }\n const values: Record<string, MaterialValue> = { baseColor: linearColorFromInput(rgba, 4) };\n if (opts?.baseColorTexture !== undefined) values.baseColorTexture = opts.baseColorTexture;\n if (opts?.alphaCutoff !== undefined) values.alphaCutoff = opts.alphaCutoff;\n const passes: [MaterialPass, ...MaterialPass[]] = [\n pass('forward', UNLIT_MODULE, opts?.renderState, undefined, opts?.queue),\n ];\n if (opts?.castShadow !== false) {\n passes.push(pass('shadow-caster', UNLIT_MODULE, shadowCasterRenderState(opts?.renderState)));\n }\n return {\n kind: 'material',\n colorSpace: 'linear',\n passes,\n parameters: unlitParameters('linear'),\n values,\n };\n}\n\ninterface StandardOpts {\n /** Numeric tuples are linear; Hex/CSS strings and numbers are sRGB inputs. */\n readonly baseColor: MaterialColorInput4;\n readonly metallic?: number;\n readonly roughness?: number;\n /** Texture channel used for the metallic factor; glTF defaults to B (2). */\n readonly metallicChannel?: number;\n /** Texture channel used for the roughness factor; glTF defaults to G (1). */\n readonly roughnessChannel?: number;\n readonly clearcoat?: number;\n readonly clearcoatRoughness?: number;\n readonly clearcoatTexture?: MaterialValue;\n readonly clearcoatRoughnessTexture?: MaterialValue;\n readonly clearcoatNormalTexture?: MaterialValue;\n readonly clearcoatNormalScale?: number;\n readonly anisotropyStrength?: number;\n readonly anisotropyRotation?: number;\n readonly anisotropyTexture?: MaterialValue;\n readonly sheenColor?: MaterialColorInput3;\n readonly sheenRoughness?: number;\n readonly sheenColorTexture?: MaterialValue;\n readonly sheenRoughnessTexture?: MaterialValue;\n readonly iridescence?: number;\n readonly iridescenceIor?: number;\n readonly iridescenceThicknessMinimum?: number;\n readonly iridescenceThicknessMaximum?: number;\n readonly iridescenceTexture?: MaterialValue;\n readonly iridescenceThicknessTexture?: MaterialValue;\n readonly specular?: number;\n readonly specularColor?: MaterialColorInput3;\n readonly specularTexture?: MaterialValue;\n readonly specularColorTexture?: MaterialValue;\n readonly emissive?: MaterialColorInput3;\n readonly emissiveIntensity?: number;\n readonly emissiveTexture?: MaterialValue;\n readonly baseColorTexture?: MaterialValue;\n readonly metallicRoughnessTexture?: MaterialValue;\n readonly normalTexture?: MaterialValue;\n readonly occlusionTexture?: MaterialValue;\n readonly occlusionStrength?: number;\n readonly alphaCutoff?: number;\n readonly transmission?: number;\n readonly transmissionTexture?: MaterialValue;\n readonly ior?: number;\n readonly thickness?: number;\n readonly thicknessTexture?: MaterialValue;\n readonly attenuationColor?: readonly [number, number, number];\n readonly attenuationDistance?: number;\n readonly renderState?: MaterialRenderState;\n readonly castShadow?: boolean;\n readonly queue?: number;\n}\n\n/** Built-in Standard authoring surface with engine-owned parameter defaults. */\nexport interface DefaultStandardOptions extends StandardOpts {\n readonly surfaceModule?: never;\n readonly parameters?: never;\n readonly values?: never;\n}\n\n/** Import-first Standard Surface authoring; the root parameter contract stays caller-owned. */\nexport interface CustomStandardSurfaceOptions {\n readonly surfaceModule: string;\n readonly parameters: readonly MaterialParameter[];\n readonly values: Readonly<Record<string, MaterialValue>>;\n readonly colorSpace?: MaterialColorSpace;\n readonly renderState?: MaterialRenderState;\n readonly castShadow?: boolean;\n readonly queue?: number;\n readonly baseColor?: never;\n readonly metallic?: never;\n readonly roughness?: never;\n readonly metallicChannel?: never;\n readonly roughnessChannel?: never;\n readonly clearcoat?: never;\n readonly clearcoatRoughness?: never;\n readonly clearcoatTexture?: never;\n readonly clearcoatRoughnessTexture?: never;\n readonly clearcoatNormalTexture?: never;\n readonly clearcoatNormalScale?: never;\n readonly anisotropyStrength?: never;\n readonly anisotropyRotation?: never;\n readonly anisotropyTexture?: never;\n readonly sheenColor?: never;\n readonly sheenRoughness?: never;\n readonly sheenColorTexture?: never;\n readonly sheenRoughnessTexture?: never;\n readonly iridescence?: never;\n readonly iridescenceIor?: never;\n readonly iridescenceThicknessMinimum?: never;\n readonly iridescenceThicknessMaximum?: never;\n readonly iridescenceTexture?: never;\n readonly iridescenceThicknessTexture?: never;\n readonly specular?: never;\n readonly specularColor?: never;\n readonly specularTexture?: never;\n readonly specularColorTexture?: never;\n readonly emissive?: never;\n readonly emissiveIntensity?: never;\n readonly emissiveTexture?: never;\n readonly baseColorTexture?: never;\n readonly metallicRoughnessTexture?: never;\n readonly normalTexture?: never;\n readonly occlusionTexture?: never;\n readonly occlusionStrength?: never;\n readonly alphaCutoff?: never;\n readonly transmission?: never;\n readonly transmissionTexture?: never;\n readonly ior?: never;\n readonly thickness?: never;\n readonly thicknessTexture?: never;\n readonly attenuationColor?: never;\n readonly attenuationDistance?: never;\n}\n\nexport type StandardOptions = DefaultStandardOptions | CustomStandardSurfaceOptions;\n\nfunction validateChannel(\n name: 'metallicChannel' | 'roughnessChannel',\n value: number | undefined,\n): void {\n if (value !== undefined && (!Number.isInteger(value) || value < 0 || value > 3)) {\n throw new Error(`Materials.standard: ${name} must be an integer in [0, 3], got ${value}`);\n }\n}\n\nfunction validateFiniteRange(\n name: 'transmission' | 'ior' | 'thickness' | 'attenuationDistance',\n value: number | undefined,\n minimum: number,\n maximum?: number,\n): void {\n if (value === undefined || !Number.isFinite(value)) {\n if (value === undefined) return;\n throw new MaterialTransmissionContractError(name, 'non-finite', value);\n }\n if (value < minimum || (maximum !== undefined && value > maximum)) {\n throw new MaterialTransmissionContractError(name, 'range', value);\n }\n}\n\nfunction standardDefault(opts: DefaultStandardOptions): MaterialAsset {\n const occlusionStrength = opts.occlusionStrength ?? 1;\n if (occlusionStrength < 0 || occlusionStrength > 1) {\n throw new Error(\n `Materials.standard: occlusionStrength must be in [0, 1], got ${occlusionStrength}`,\n );\n }\n if (opts.alphaCutoff !== undefined && !Number.isFinite(opts.alphaCutoff)) {\n throw new MaterialAuthoringContractError('non-finite', opts.alphaCutoff);\n }\n if (opts.alphaCutoff !== undefined && (opts.alphaCutoff < 0 || opts.alphaCutoff > 1)) {\n throw new MaterialAuthoringContractError('range', opts.alphaCutoff);\n }\n validateChannel('metallicChannel', opts.metallicChannel);\n validateChannel('roughnessChannel', opts.roughnessChannel);\n const transmission = opts.transmission ?? 0;\n const ior = opts.ior ?? 1.5;\n const thickness = opts.thickness ?? 0;\n const attenuationColor = opts.attenuationColor ?? [1, 1, 1];\n validateFiniteRange('transmission', transmission, 0, 1);\n validateFiniteRange('ior', ior, 1);\n validateFiniteRange('thickness', thickness, 0);\n validateFiniteRange('attenuationDistance', opts.attenuationDistance, Number.MIN_VALUE);\n if (\n attenuationColor.length !== 3 ||\n attenuationColor.some((value) => !Number.isFinite(value) || value < 0 || value > 1)\n ) {\n throw new MaterialTransmissionContractError('attenuationColor', 'shape', attenuationColor);\n }\n if (transmission > 0 && opts.renderState?.blend !== undefined) {\n throw new MaterialTransmissionContractError('transmission', 'blend', opts.renderState.blend);\n }\n if (transmission > 0 && opts.renderState?.depthWriteEnabled === true) {\n throw new MaterialTransmissionContractError('transmission', 'depth-write', true);\n }\n const values: Record<string, MaterialValue> = {\n baseColor: linearColorFromInput(opts.baseColor, 4),\n metallic: opts.metallic ?? 0,\n roughness: opts.roughness ?? 0.5,\n occlusionStrength,\n specular: opts.specular ?? 1,\n specularColor:\n opts.specularColor === undefined ? [1, 1, 1] : linearColorFromInput(opts.specularColor, 3),\n transmission,\n ior,\n thickness,\n attenuationColor,\n };\n if (opts.metallicChannel !== undefined) values.metallicChannel = opts.metallicChannel;\n if (opts.roughnessChannel !== undefined) values.roughnessChannel = opts.roughnessChannel;\n if (opts.clearcoat !== undefined) values.clearcoat = opts.clearcoat;\n if (opts.clearcoatRoughness !== undefined) values.clearcoatRoughness = opts.clearcoatRoughness;\n if (opts.clearcoatTexture !== undefined) values.clearcoatTexture = opts.clearcoatTexture;\n if (opts.clearcoatRoughnessTexture !== undefined) {\n values.clearcoatRoughnessTexture = opts.clearcoatRoughnessTexture;\n }\n if (opts.clearcoatNormalTexture !== undefined) {\n values.clearcoatNormalTexture = opts.clearcoatNormalTexture;\n }\n if (opts.clearcoatNormalScale !== undefined)\n values.clearcoatNormalScale = opts.clearcoatNormalScale;\n if (opts.anisotropyStrength !== undefined) values.anisotropyStrength = opts.anisotropyStrength;\n if (opts.anisotropyRotation !== undefined) values.anisotropyRotation = opts.anisotropyRotation;\n if (opts.anisotropyTexture !== undefined) values.anisotropyTexture = opts.anisotropyTexture;\n if (opts.sheenColor !== undefined) values.sheenColor = linearColorFromInput(opts.sheenColor, 3);\n if (opts.sheenRoughness !== undefined) values.sheenRoughness = opts.sheenRoughness;\n if (opts.sheenColorTexture !== undefined) values.sheenColorTexture = opts.sheenColorTexture;\n if (opts.sheenRoughnessTexture !== undefined) {\n values.sheenRoughnessTexture = opts.sheenRoughnessTexture;\n }\n if (opts.iridescence !== undefined) values.iridescence = opts.iridescence;\n if (opts.iridescenceIor !== undefined) values.iridescenceIor = opts.iridescenceIor;\n if (opts.iridescenceThicknessMinimum !== undefined) {\n values.iridescenceThicknessMinimum = opts.iridescenceThicknessMinimum;\n }\n if (opts.iridescenceThicknessMaximum !== undefined) {\n values.iridescenceThicknessMaximum = opts.iridescenceThicknessMaximum;\n }\n if (opts.iridescenceTexture !== undefined) values.iridescenceTexture = opts.iridescenceTexture;\n if (opts.iridescenceThicknessTexture !== undefined) {\n values.iridescenceThicknessTexture = opts.iridescenceThicknessTexture;\n }\n if (opts.emissive !== undefined) values.emissive = linearColorFromInput(opts.emissive, 3);\n if (opts.emissiveIntensity !== undefined) values.emissiveIntensity = opts.emissiveIntensity;\n if (opts.emissiveTexture !== undefined) values.emissiveTexture = opts.emissiveTexture;\n if (opts.baseColorTexture !== undefined) values.baseColorTexture = opts.baseColorTexture;\n if (opts.metallicRoughnessTexture !== undefined) {\n values.metallicRoughnessTexture = opts.metallicRoughnessTexture;\n }\n if (opts.normalTexture !== undefined) values.normalTexture = opts.normalTexture;\n if (opts.occlusionTexture !== undefined) values.occlusionTexture = opts.occlusionTexture;\n if (opts.specularTexture !== undefined) values.specularTexture = opts.specularTexture;\n if (opts.specularColorTexture !== undefined) {\n values.specularColorTexture = opts.specularColorTexture;\n }\n if (opts.alphaCutoff !== undefined) values.alphaCutoff = opts.alphaCutoff;\n if (opts.transmissionTexture !== undefined) values.transmissionTexture = opts.transmissionTexture;\n if (opts.thicknessTexture !== undefined) values.thicknessTexture = opts.thicknessTexture;\n if (opts.attenuationDistance !== undefined) values.attenuationDistance = opts.attenuationDistance;\n const forwardRenderState =\n transmission > 0 ? { ...(opts.renderState ?? {}), depthWriteEnabled: false } : opts.renderState;\n const parameters = standardSurfaceParameters(standardParameters('linear', opts));\n const declaredNames = new Set(parameters.map((parameter) => parameter.name));\n for (const name of Object.keys(values)) {\n if (!declaredNames.has(name)) delete values[name];\n }\n const layerPlan = deriveStandardLayerPlan(parameters);\n const projected = projectStandardSurfacePasses({\n surfaceModule: DEFAULT_STANDARD_SURFACE_MODULE,\n values,\n ...(forwardRenderState === undefined ? {} : { renderState: forwardRenderState }),\n ...(opts.castShadow === undefined ? {} : { castShadow: opts.castShadow }),\n ...(opts.queue === undefined ? {} : { queue: opts.queue }),\n layerPlan,\n });\n const passes = (\n transmission > 0 ? projected.filter((entry) => entry.name !== 'deferred') : projected\n ) as [MaterialPass, ...MaterialPass[]];\n return {\n kind: 'material',\n colorSpace: 'linear',\n passes,\n parameters,\n values,\n };\n}\n\nfunction standardCustom(opts: CustomStandardSurfaceOptions): MaterialAsset {\n const layerPlan = deriveStandardLayerPlan(\n opts.parameters.filter((parameter) => parameter.optional !== true),\n );\n return {\n kind: 'material',\n ...(opts.colorSpace === undefined || opts.colorSpace === 'srgb'\n ? {}\n : { colorSpace: opts.colorSpace }),\n passes: projectStandardSurfacePasses({\n surfaceModule: opts.surfaceModule,\n values: opts.values,\n ...(opts.renderState === undefined ? {} : { renderState: opts.renderState }),\n ...(opts.castShadow === undefined ? {} : { castShadow: opts.castShadow }),\n ...(opts.queue === undefined ? {} : { queue: opts.queue }),\n layerPlan,\n }),\n parameters: standardSurfaceParameters(opts.parameters),\n values: opts.values,\n };\n}\n\nexport function standard(options: StandardOptions): MaterialAsset {\n return options.surfaceModule === undefined ? standardDefault(options) : standardCustom(options);\n}\n\nexport const Materials = { unlit, standard, srgb } as const;\n\nexport { SPRITE_MODULE };\n","import type { RenderResourceScope } from '../publication/resource-scope';\n// @forgeax/engine-runtime - TransparentSortConfig KV resource + helpers.\n//\n// feat-20260520-2d-sprite-layer-mvp M-2 w14 / requirements AC-08 + AC-18\n// path (2)/(3) + AC-19 derivation audit (5).\n//\n// Surface:\n// - interface TransparentSortConfig { mode: number; yzAlpha: number }\n// - constant TRANSPARENT_SORT_CONFIG_KEY = 'TransparentSortConfig'\n// - constants TRANSPARENT_SORT_MODE_LAYER_Z = 0 (horizontal-z)\n// TRANSPARENT_SORT_MODE_LAYER_Y = 1 (Y-sort)\n// TRANSPARENT_SORT_MODE_LAYER_YZ = 2 (Y-Z blend)\n// - helper getTransparentSortConfig(world): TransparentSortConfig\n// - helper setTransparentSortConfig(world, cfg):\n// Result<void, ResourceInvalidValueError>\n//\n// 5-view selection table (charter F1 progressive disclosure for AI users\n// reading IDE hover on this header; same SSOT mirrored in\n// apps/hello/sprite/README.md M-4 w33):\n//\n// | view | mode | yzAlpha | sortValue formula |\n// |:------------------|:----:|:-------:|:--------------------------------|\n// | horizontal (side) | 0 | --- | posZ |\n// | top-down | 1 | --- | -(posY - pivot.y * size.y) |\n// | Don't Starve | 2 | 1.0 | (posY - pivot.y * size.y) + posZ|\n// | isometric | 2 | 0.5 | (posY - pivot.y * size.y) + 0.5*posZ |\n// | JRPG (foot pivot) | 1 | --- | -(posY - pivot.y * size.y) |\n//\n// AC-18 path (2): @fallback getTransparentSortConfig KV missing returns\n// default; no warn; no throw (legal default state, not an error state).\n// AC-18 path (3): @fallback setTransparentSortConfig mode out-of-range\n// does NOT silently coerce to mode=0 — returns Result.err with the\n// 4 SSOT fields locked by plan-strategy D-4 so AI users can self-repair\n// via .code / .expected / .hint / .detail property access (charter P3).\n// AC-18 path (3) carve-out: yzAlpha range is intentionally NOT validated;\n// mode=0 / mode=1 ignore the field entirely, mode=2 reads it, mode=3\n// (distance) also reads it for compat but ignores it (squared-distance\n// formula does not use yzAlpha). The silent-ignore is the documented\n// charter P3 \"legal default\" boundary.\n//\n// @new-surface KV resource: ECS has no defineResource factory at this\n// feat's commit time; the TS POD interface + string KV key form is the\n// minimum-new-surface route. The world resource store\n// (insertResource / getResource / hasResource) is reused unchanged\n// (requirements §2.2.G derivation row 5).\n// @derives world.hasResource / world.insertResource / world.getResource\n// KV API (packages/ecs/src/resource.ts + world.ts: insertResource l.490,\n// getResource l.498, hasResource l.503).\n// @fallback getTransparentSortConfig KV missing returns\n// { mode: 0, yzAlpha: 1.0 }; no warn; no throw.\n// @fallback setTransparentSortConfig mode out-of-range returns Result.err\n// with code='resource-invalid-value'; never silently coerced.\n//\n// charter mapping: F1 (single-import barrel + 5-view JSDoc table on\n// hover) + P3 (structured failure on mode out-of-range; legal-default\n// fallback distinguished from error fallback) + P4 (consistent\n// abstraction — same world.{has,get,insert}Resource KV API as every\n// other engine resource consumer).\n\nimport type { World } from '@forgeax/engine-ecs';\nimport { ResourceInvalidValueError } from '@forgeax/engine-ecs/projection';\nimport { err, ok, type Result } from '@forgeax/engine-types';\n\n// ────────────────────────────────────────────────────────────────────────────\n// POD interface + KV key + 3 named mode constants\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Transparent-bucket sort configuration (plain-data POD). Lives as a\n * world-level resource keyed by `TRANSPARENT_SORT_CONFIG_KEY`.\n *\n * Fields:\n * - `mode \\u2208 {0, 1, 2, 3}` selects the sort formula\n * (`TRANSPARENT_SORT_MODE_LAYER_Z` / `_LAYER_Y` / `_LAYER_YZ` / `_DISTANCE`).\n * - `yzAlpha` (mode=2 only) blends Y-axis into Z-axis: 1.0 weights\n * posZ equally with `posY - pivot.y * size.y` (Don't-Starve style),\n * 0.5 weights posZ at half (isometric). mode=0 / mode=1 ignore the\n * field (documented charter P3 legal-default boundary).\n */\nexport interface TransparentSortConfig {\n readonly mode: number;\n readonly yzAlpha: number;\n}\n\n/** Extract-stage POD consumed by the transparent-sort owner. */\nexport interface TransparentEntry {\n readonly entityIndex: number;\n readonly materialHandle: number;\n readonly layer: number;\n readonly posX: number;\n readonly posY: number;\n readonly posZ: number;\n readonly pivotY: number;\n readonly sizeY: number;\n readonly sortKey?: number | undefined;\n readonly renderableIndex?: number | undefined;\n}\n\n/** World resource key for `TransparentSortConfig` KV entry. */\nexport const TRANSPARENT_SORT_CONFIG_KEY = 'TransparentSortConfig' as const;\n\n/** horizontal / side-scroller: sortValue = posZ. */\nexport const TRANSPARENT_SORT_MODE_LAYER_Z = 0;\n\n/** top-down / JRPG: sortValue = -(posY - pivot.y * size.y) (foot pivot Y-sort). */\nexport const TRANSPARENT_SORT_MODE_LAYER_Y = 1;\n\n/** Don't-Starve / isometric: sortValue = (posY - pivot.y * size.y) + yzAlpha * posZ. */\nexport const TRANSPARENT_SORT_MODE_LAYER_YZ = 2;\n\n/** 3D distance: sortValue = -(dist^2) to cameraPos, back-to-front (far first). */\nexport const TRANSPARENT_SORT_MODE_DISTANCE = 3;\n\n/** Grouped authoring values and the single configuration operation. */\nexport const TransparentSort = Object.freeze({\n layerZ: TRANSPARENT_SORT_MODE_LAYER_Z,\n layerY: TRANSPARENT_SORT_MODE_LAYER_Y,\n layerYZ: TRANSPARENT_SORT_MODE_LAYER_YZ,\n distance: TRANSPARENT_SORT_MODE_DISTANCE,\n configure: setTransparentSortConfig,\n} as const);\n\n// ────────────────────────────────────────────────────────────────────────────\n// Internal: silent default + mode validity check\n// ────────────────────────────────────────────────────────────────────────────\n\nconst DEFAULT_CONFIG: TransparentSortConfig = Object.freeze({\n mode: TRANSPARENT_SORT_MODE_LAYER_Z,\n yzAlpha: 1.0,\n});\n\nconst VALID_MODES: ReadonlySet<number> = new Set<number>([\n TRANSPARENT_SORT_MODE_LAYER_Z,\n TRANSPARENT_SORT_MODE_LAYER_Y,\n TRANSPARENT_SORT_MODE_LAYER_YZ,\n TRANSPARENT_SORT_MODE_DISTANCE,\n]);\n\n// The expected / hint literals are the SSOT locked by plan-strategy D-4.\n// They MUST round-trip byte-for-byte into ResourceInvalidValueError so AI\n// users can read err.expected / err.hint for self-repair (charter P3).\n// `\\u2208` is the math \"is-element-of\" symbol; the ASCII source escape\n// keeps the file English-only per AGENTS.md §Conventions.\nconst EXPECTED_MODE = 'mode \\u2208 {0, 1, 2, 3}';\nconst HINT_MODE = '0=layer-z, 1=layer-y, 2=layer-yz, 3=distance';\n\n// ────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Read the world's `TransparentSortConfig` resource.\n *\n * @fallback KV missing returns `{ mode: 0, yzAlpha: 1.0 }`\n * (`TRANSPARENT_SORT_MODE_LAYER_Z`, horizontal-z safe default).\n * NO warn, NO throw — KV missing is a legal state (not an error state),\n * mirroring how a sprite-free scene is a legal state for the renderer\n * even though render-system has a transparent bucket. plan-strategy D-2:\n * `hasResource` guard precedes the read so this helper never trips\n * `ResourceNotFoundError` from `world.getResource`.\n *\n * @example\n * const cfg = getTransparentSortConfig(world);\n * // cfg.mode === 0, cfg.yzAlpha === 1.0 when no resource inserted.\n *\n * @example\n * world.insertResource(TRANSPARENT_SORT_CONFIG_KEY,\n * { mode: TRANSPARENT_SORT_MODE_LAYER_Y, yzAlpha: 1.0 });\n * const cfg = getTransparentSortConfig(world);\n * // cfg.mode === 1, cfg.yzAlpha === 1.0 (yzAlpha ignored by mode=1).\n */\nexport function getTransparentSortConfig(world: RenderResourceScope): TransparentSortConfig {\n if ('resolveAsset' in world) return world.transparentSort;\n if (!world.hasResource(TRANSPARENT_SORT_CONFIG_KEY)) {\n return DEFAULT_CONFIG;\n }\n return world.getResource<TransparentSortConfig>(TRANSPARENT_SORT_CONFIG_KEY);\n}\n\n/**\n * Write the world's `TransparentSortConfig` resource, validating `mode`.\n *\n * Returns `Result<void, ResourceInvalidValueError>`:\n *\n * - `ok` — mode is in `{0, 1, 2, 3}`; the resource is inserted /\n * overwritten via `world.insertResource`.\n * - `err` — mode is out of range; the resource is NOT mutated;\n * `err.error` is a `ResourceInvalidValueError` instance carrying:\n * `.code === 'resource-invalid-value'`\n * `.expected === 'mode \\u2208 {0, 1, 2, 3}'`\n * `.hint === '0=layer-z, 1=layer-y, 2=layer-yz, 3=distance'`\n * `.detail.receivedMode === <the rejected mode>`\n *\n * @fallback mode out-of-range is NEVER silently coerced to mode=0 —\n * `Result.err` is the only fail-mode (charter P3 structured failure).\n * AI users read `.code / .expected / .hint / .detail` properties to\n * self-repair, NOT the human-readable `.message` string.\n *\n * @fallback yzAlpha range is intentionally not validated; mode=0 / mode=1\n * ignore the field, mode=2 reads it. The silent-ignore is the\n * documented charter P3 \"legal default\" boundary (plan-strategy D-4 +\n * AC-18 path (3) carve-out).\n *\n * @example success path:\n * const r = setTransparentSortConfig(world,\n * { mode: TRANSPARENT_SORT_MODE_LAYER_YZ, yzAlpha: 0.5 });\n * if (!r.ok) return r;\n *\n * @example failure path:\n * const r = setTransparentSortConfig(world, { mode: 99, yzAlpha: 1.0 });\n * if (!r.ok) {\n * // r.error.code === 'resource-invalid-value'\n * // r.error.expected === 'mode \\u2208 {0, 1, 2, 3}'\n * // r.error.hint === '0=layer-z, 1=layer-y, 2=layer-yz, 3=distance'\n * // r.error.detail.receivedMode === 99\n * }\n */\nexport function setTransparentSortConfig(\n world: World,\n cfg: TransparentSortConfig,\n): Result<void, ResourceInvalidValueError> {\n if (!VALID_MODES.has(cfg.mode)) {\n return err(\n new ResourceInvalidValueError(EXPECTED_MODE, HINT_MODE, {\n receivedMode: cfg.mode,\n receivedKey: TRANSPARENT_SORT_CONFIG_KEY,\n }),\n );\n }\n world.insertResource<TransparentSortConfig>(TRANSPARENT_SORT_CONFIG_KEY, {\n mode: cfg.mode,\n yzAlpha: cfg.yzAlpha,\n });\n return ok(undefined);\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type Result } from '@forgeax/engine-types';
|
|
2
|
+
import { type CloudLayerError } from '../errors/cloud';
|
|
3
|
+
import { type CloudQualityProfile, type ValidatedCloudLayer } from './parameters';
|
|
4
|
+
export interface CloudDensityCache {
|
|
5
|
+
readonly sourceKey: string;
|
|
6
|
+
readonly resolution: number;
|
|
7
|
+
/** R8 normalized density values in z-major, y, x order. */
|
|
8
|
+
readonly data: Uint8Array;
|
|
9
|
+
/**
|
|
10
|
+
* Weather, body and erosion bases in three contiguous R8 planes. `data` is
|
|
11
|
+
* retained as a diagnostic projection for callers that still need the
|
|
12
|
+
* composed density; runtime sampling uses these formation planes so coverage,
|
|
13
|
+
* density and wind remain evaluation controls.
|
|
14
|
+
*/
|
|
15
|
+
readonly formationData: Uint8Array;
|
|
16
|
+
readonly formationByteLength: number;
|
|
17
|
+
/** Bytes uploaded for the reusable formation planes. */
|
|
18
|
+
readonly byteLength: number;
|
|
19
|
+
readonly digest: string;
|
|
20
|
+
}
|
|
21
|
+
export interface CloudDensityCacheSnapshot {
|
|
22
|
+
readonly sourceKey: string;
|
|
23
|
+
readonly resolution: number;
|
|
24
|
+
readonly data: Uint8Array;
|
|
25
|
+
readonly formationData?: Uint8Array;
|
|
26
|
+
readonly digest: string;
|
|
27
|
+
}
|
|
28
|
+
export interface CloudDensitySample {
|
|
29
|
+
readonly density: number;
|
|
30
|
+
readonly heightFraction: number;
|
|
31
|
+
readonly noise: number;
|
|
32
|
+
readonly weather?: number;
|
|
33
|
+
readonly body?: number;
|
|
34
|
+
readonly erosion?: number;
|
|
35
|
+
}
|
|
36
|
+
export interface CloudFormationSample {
|
|
37
|
+
readonly weather: number;
|
|
38
|
+
readonly body: number;
|
|
39
|
+
readonly erosion: number;
|
|
40
|
+
readonly noise: number;
|
|
41
|
+
}
|
|
42
|
+
export declare const CLOUD_VERTICAL_CELLS = 1;
|
|
43
|
+
/** Evaluate the reusable weather/body/erosion bases at one world position. */
|
|
44
|
+
export declare function evaluateCloudFormation(params: ValidatedCloudLayer, position: ArrayLike<number>, timeSeconds?: number): CloudFormationSample;
|
|
45
|
+
/** Analytic field used for cache generation, CPU diagnostics and recovery. */
|
|
46
|
+
export declare function evaluateCloudDensity(params: ValidatedCloudLayer, position: ArrayLike<number>, timeSeconds?: number): CloudDensitySample;
|
|
47
|
+
/** Build one canonical, periodic formation cache. */
|
|
48
|
+
export declare function buildCloudDensityCache(params: ValidatedCloudLayer, profile?: CloudQualityProfile): CloudDensityCache;
|
|
49
|
+
/** Export a detached snapshot suitable for persistence or a worker boundary. */
|
|
50
|
+
export declare function snapshotCloudDensityCache(cache: CloudDensityCache): CloudDensityCacheSnapshot;
|
|
51
|
+
/** Reconstruct a cache without running the procedural generator again. */
|
|
52
|
+
export declare function reconstructCloudDensityCache(params: ValidatedCloudLayer, snapshot: CloudDensityCacheSnapshot): Result<CloudDensityCache, CloudLayerError>;
|
|
53
|
+
/** Sample the cached field with periodic x/z addressing and wind advection. */
|
|
54
|
+
export declare function sampleCloudDensity(cache: CloudDensityCache, params: ValidatedCloudLayer, position: ArrayLike<number>, timeSeconds?: number): number;
|
|
55
|
+
//# sourceMappingURL=density.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"density.d.ts","sourceRoot":"","sources":["../../src/cloud/density.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAA+B,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EAEL,KAAK,mBAAmB,EAExB,KAAK,mBAAmB,EACzB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC;IACnC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,wDAAwD;IACxD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,UAAU,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAcD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAkQtC,8EAA8E;AAC9E,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,EAC3B,WAAW,SAAI,GACd,oBAAoB,CActB;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,EAC3B,WAAW,SAAI,GACd,kBAAkB,CAwBpB;AAiBD,qDAAqD;AACrD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,GAAE,mBAA4D,GACpE,iBAAiB,CA2CnB;AAED,gFAAgF;AAChF,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,iBAAiB,GAAG,yBAAyB,CAQ7F;AAED,0EAA0E;AAC1E,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,yBAAyB,GAClC,MAAM,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAqC5C;AAOD,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,EAC3B,WAAW,SAAI,GACd,MAAM,CAiGR"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EntityHandle, World } from '@forgeax/engine-ecs';
|
|
2
|
+
import { type Result } from '@forgeax/engine-types';
|
|
3
|
+
import { type CloudLayerData } from '../components/cloud-layer';
|
|
4
|
+
import { type CloudLayerError } from '../errors/cloud';
|
|
5
|
+
import { type ValidatedCloudLayer } from './parameters';
|
|
6
|
+
export interface CloudLayerCandidate {
|
|
7
|
+
readonly entityKey: EntityHandle;
|
|
8
|
+
readonly data: CloudLayerData;
|
|
9
|
+
}
|
|
10
|
+
export interface ExtractedCloudLayer {
|
|
11
|
+
readonly status: 'available';
|
|
12
|
+
readonly entityKey: EntityHandle;
|
|
13
|
+
readonly sourceKey: string;
|
|
14
|
+
readonly params: ValidatedCloudLayer;
|
|
15
|
+
readonly worldTimeSeconds: number;
|
|
16
|
+
/** First World-owned sun selected by the same extraction boundary. */
|
|
17
|
+
readonly sunDirection: readonly [number, number, number] | undefined;
|
|
18
|
+
readonly sunRadiance: readonly [number, number, number] | undefined;
|
|
19
|
+
readonly revision: number;
|
|
20
|
+
}
|
|
21
|
+
/** Select the one resource owner without creating a second World registry. */
|
|
22
|
+
export declare function selectCloudLayerFrame(candidates: readonly CloudLayerCandidate[], worldTimeSeconds?: number, sunDirection?: readonly [number, number, number] | undefined, sunRadiance?: readonly [number, number, number] | undefined): Result<ExtractedCloudLayer | undefined, CloudLayerError>;
|
|
23
|
+
/** Read the World-owned component and Time resource at the extraction boundary. */
|
|
24
|
+
export declare function extractCloudLayer(world: World): Result<ExtractedCloudLayer | undefined, CloudLayerError>;
|
|
25
|
+
//# sourceMappingURL=extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/cloud/extract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE5E,OAAO,EAAE,KAAK,eAAe,EAAgC,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAuB,KAAK,mBAAmB,EAAsB,MAAM,cAAc,CAAC;AAEjG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACrE,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,SAAS,mBAAmB,EAAE,EAC1C,gBAAgB,SAAI,EACpB,YAAY,GAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAqB,EACvE,WAAW,GAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAqB,GACrE,MAAM,CAAC,mBAAmB,GAAG,SAAS,EAAE,eAAe,CAAC,CAyB1D;AAED,mFAAmF;AACnF,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,KAAK,GACX,MAAM,CAAC,mBAAmB,GAAG,SAAS,EAAE,eAAe,CAAC,CAgD1D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { RenderFeature } from '../features/types';
|
|
2
|
+
import { type CloudDensityCache } from './density';
|
|
3
|
+
import { type CloudLayerInspection } from './inspection';
|
|
4
|
+
import { type ValidatedCloudLayer } from './parameters';
|
|
5
|
+
import { type CloudShadowProjection } from './shadow';
|
|
6
|
+
import { type CloudTemporalFrame } from './temporal';
|
|
7
|
+
export declare const CLOUD_LAYER_FEATURE_IDENTITY = "forgeax.cloud-layer";
|
|
8
|
+
export declare const CLOUD_LAYER_DENSITY_PROGRAM = "cloud-layer-density";
|
|
9
|
+
export declare const CLOUD_LAYER_DENSITY_BINDINGS = "cloud-layer-density-bindings";
|
|
10
|
+
export declare const CLOUD_LAYER_DENSITY_CACHE = "cloud-layer-density-cache";
|
|
11
|
+
export declare const CLOUD_LAYER_DENSITY_OUTPUT = "cloud-layer-density-output";
|
|
12
|
+
export declare const CLOUD_VIEW_PARAMS_BYTES = 176;
|
|
13
|
+
/** Minimal copy compute; one invocation owns one packed u32 cache element. */
|
|
14
|
+
export declare const CLOUD_DENSITY_COMPUTE_WGSL = "\nstruct CloudCacheParams { count: u32, }\n@group(0) @binding(0) var<storage, read> sourceDensity: array<u32>;\n@group(0) @binding(1) var<storage, read_write> cachedDensity: array<u32>;\n@group(0) @binding(2) var<uniform> params: CloudCacheParams;\n@compute @workgroup_size(64)\nfn cloud_density_cache(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= params.count) { return; }\n cachedDensity[id.x] = sourceDensity[id.x];\n}\n";
|
|
15
|
+
/**
|
|
16
|
+
* Cloud view transport. The View group is the renderer-owned camera/light
|
|
17
|
+
* source of truth; group(1) carries frame-local cloud parameters, depth and
|
|
18
|
+
* the bounded packed 3D cache. Camera, solar-column and cloud-interior
|
|
19
|
+
* integration all call cloud_density(), so wind only changes the sample
|
|
20
|
+
* coordinate and never the authored field.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CLOUD_VIEW_FULLSCREEN_WGSL = "\nconst CLOUD_VERTICAL_CELLS: f32 = 1;\nconst CLOUD_VERTICAL_NOISE_CELLS: f32 = 2.0;\n\nstruct CloudCameraView {\n worldViewProj: mat4x4<f32>,\n lightDir: vec3<f32>,\n lightColor: vec3<f32>,\n cameraPos: vec3<f32>,\n lightViewProj_A: mat4x4<f32>,\n inverseViewProj: mat4x4<f32>,\n lightViewProj_B: mat4x4<f32>,\n lightViewProj_C: mat4x4<f32>,\n lightViewProj_D: mat4x4<f32>,\n splitPlanes: array<vec4<f32>, 4>,\n cascadeCount: f32,\n cascadeBlend: f32,\n depthBias: f32,\n normalBias: f32,\n directionalShadowFilter: vec4<f32>,\n spotLightViewProj: array<mat4x4<f32>, 4>,\n temporalCurrentViewProj: mat4x4<f32>,\n temporalPreviousViewProj: mat4x4<f32>,\n temporalProjection: vec4<f32>,\n temporalPreviousCameraPos: vec4<f32>,\n ssrParams: vec4<f32>,\n};\n\nstruct CloudViewParams {\n layer: vec4<f32>, // baseHeight, thickness, scale, density\n field: vec4<f32>, // coverage, timeSeconds, maxDistance, seed\n wind: vec4<f32>, // xyz m/s\n sunDirection: vec4<f32>,\n sunRadiance: vec4<f32>,\n integration: vec4<u32>, // cacheResolution, viewSteps, solarSteps, reserved\n shadowOrigin: vec4<f32>,\n shadowRight: vec4<f32>,\n shadowUp: vec4<f32>,\n shadowProjection: vec4<f32>, // range, valid, lowSun, texelSize\n temporal: vec4<f32>, // valid, reset, historyWeight, reserved\n};\n\n@group(0) @binding(0) var<uniform> view: CloudCameraView;\n@group(1) @binding(0) var sceneColor: texture_2d<f32>;\n@group(1) @binding(1) var sceneSampler: sampler;\n@group(1) @binding(2) var<uniform> cloud: CloudViewParams;\n@group(1) @binding(3) var sceneDepth: texture_depth_2d;\n@group(1) @binding(4) var depthSampler: sampler;\n@group(1) @binding(5) var previousRadiance: texture_2d<f32>;\n@group(1) @binding(6) var previousTransmittance: texture_2d<f32>;\n@group(1) @binding(7) var previousDepth: texture_2d<f32>;\n@group(1) @binding(8) var<storage, read> densityCache: array<u32>;\n\nstruct CloudViewOut {\n @builtin(position) position: vec4<f32>,\n @location(0) uv: vec2<f32>,\n};\n\nfn cloud_hash3(cell: vec3<i32>, seed: u32) -> f32 {\n var h = bitcast<u32>(cell.x) * 0x45d9f3bu;\n h = (h ^ (bitcast<u32>(cell.y) * 0x119de1f3u)) * 0x45d9f3bu;\n h = (h ^ (bitcast<u32>(cell.z) * 0x3449fu)) * 0x45d9f3bu;\n h = h ^ seed;\n h = (h ^ (h >> 16u)) * 0x45d9f3bu;\n h = (h ^ (h >> 13u)) * 0x27d4eb2du;\n return f32(h ^ (h >> 16u)) / 4294967296.0;\n}\n\nfn cloud_wrap_cell(value: i32, period: i32) -> i32 {\n let wrapped = value % period;\n return select(wrapped, wrapped + period, wrapped < 0);\n}\n\nfn cloud_hash3_periodic(\n cell: vec3<i32>,\n seed: u32,\n periodX: i32,\n periodY: i32,\n periodZ: i32,\n) -> f32 {\n return cloud_hash3(\n vec3<i32>(\n cloud_wrap_cell(cell.x, periodX),\n cloud_wrap_cell(cell.y, periodY),\n cloud_wrap_cell(cell.z, periodZ),\n ),\n seed,\n );\n}\n\nfn cloud_value_noise(\n position: vec3<f32>,\n seed: u32,\n periodX: i32,\n periodY: i32,\n periodZ: i32,\n) -> f32 {\n let cell = vec3<i32>(floor(position));\n let fraction = fract(position);\n let smoothFraction = fraction * fraction * (3.0 - 2.0 * fraction);\n let c000 = cloud_hash3_periodic(cell + vec3<i32>(0, 0, 0), seed, periodX, periodY, periodZ);\n let c100 = cloud_hash3_periodic(cell + vec3<i32>(1, 0, 0), seed, periodX, periodY, periodZ);\n let c010 = cloud_hash3_periodic(cell + vec3<i32>(0, 1, 0), seed, periodX, periodY, periodZ);\n let c110 = cloud_hash3_periodic(cell + vec3<i32>(1, 1, 0), seed, periodX, periodY, periodZ);\n let c001 = cloud_hash3_periodic(cell + vec3<i32>(0, 0, 1), seed, periodX, periodY, periodZ);\n let c101 = cloud_hash3_periodic(cell + vec3<i32>(1, 0, 1), seed, periodX, periodY, periodZ);\n let c011 = cloud_hash3_periodic(cell + vec3<i32>(0, 1, 1), seed, periodX, periodY, periodZ);\n let c111 = cloud_hash3_periodic(cell + vec3<i32>(1, 1, 1), seed, periodX, periodY, periodZ);\n let x00 = mix(c000, c100, smoothFraction.x);\n let x10 = mix(c010, c110, smoothFraction.x);\n let x01 = mix(c001, c101, smoothFraction.x);\n let x11 = mix(c011, c111, smoothFraction.x);\n return mix(mix(x00, x10, smoothFraction.y), mix(x01, x11, smoothFraction.y), smoothFraction.z);\n}\n\nfn cloud_cellular_noise(position: vec3<f32>, seed: u32, periodXZ: i32, periodY: i32) -> f32 {\n let cell = vec3<i32>(floor(position));\n let fraction = fract(position);\n var nearest = 1e9;\n var dz: i32 = -1;\n loop {\n var dy: i32 = -1;\n loop {\n var dx: i32 = -1;\n loop {\n let random = cloud_hash3_periodic(\n cell + vec3<i32>(dx, dy, dz),\n seed ^ 0x9e3779b9u,\n periodXZ,\n periodY,\n periodXZ,\n );\n let point = vec3<f32>(\n f32(dx) + fract(random * 17.0) - fraction.x,\n f32(dy) + fract(random * 31.0) - fraction.y,\n f32(dz) + fract(random * 47.0) - fraction.z,\n );\n nearest = min(nearest, dot(point, point));\n if (dx >= 1) { break; }\n dx = dx + 1;\n }\n if (dy >= 1) { break; }\n dy = dy + 1;\n }\n if (dz >= 1) { break; }\n dz = dz + 1;\n }\n return 1.0 - clamp(sqrt(nearest) * 1.25, 0.0, 1.0);\n}\n\nfn cloud_weather_field(position: vec3<f32>, seed: u32) -> f32 {\n return smoothstep(\n 0.34,\n 0.66,\n cloud_value_noise(\n vec3<f32>(position.x * 2.0, 0.37, position.z * 2.0),\n seed + 17041u,\n 2,\n 1,\n 2,\n ),\n );\n}\n\nfn cloud_formation_field(position: vec3<f32>, seed: u32) -> vec3<f32> {\n let macroWarpX = cloud_value_noise(\n vec3<f32>(position.x * 2.0, position.y * CLOUD_VERTICAL_NOISE_CELLS + 3.0, position.z * 2.0),\n seed + 41023u,\n 2,\n 2,\n 2,\n ) - 0.5;\n let macroWarpZ = cloud_value_noise(\n vec3<f32>(position.x * 2.0 + 5.0, position.y * CLOUD_VERTICAL_NOISE_CELLS - 7.0, position.z * 2.0),\n seed + 41023u,\n 2,\n 2,\n 2,\n ) - 0.5;\n let bodyX = position.x + macroWarpX * 0.3;\n let bodyZ = position.z + macroWarpZ * 0.3;\n var noise = 0.0;\n var weight = 0.0;\n var amplitude = 0.5;\n var frequency = 1.0;\n for (var octave = 0u; octave < 4u; octave = octave + 1u) {\n let tilePeriod = 4.0 * frequency;\n noise = noise + cloud_value_noise(\n vec3<f32>(bodyX * tilePeriod, position.y * frequency * CLOUD_VERTICAL_NOISE_CELLS, bodyZ * tilePeriod),\n seed + octave * 1013u,\n i32(tilePeriod),\n i32(frequency * CLOUD_VERTICAL_NOISE_CELLS),\n i32(tilePeriod),\n ) * amplitude;\n weight = weight + amplitude;\n amplitude = amplitude * 0.4;\n frequency = frequency * 2.0;\n }\n let broad = noise / max(0.0001, weight);\n let cells = cloud_cellular_noise(\n vec3<f32>(bodyX * 4.0, position.y * CLOUD_VERTICAL_NOISE_CELLS, bodyZ * 4.0),\n seed + 5011u,\n 4,\n i32(CLOUD_VERTICAL_NOISE_CELLS),\n );\n // Keep the broad field responsible for the cloud silhouette and use the\n // cellular term as bounded breakup. A broad-dominant mix reads as fewer,\n // wider formations instead of evenly spaced foam balls.\n let base = clamp(broad * 0.85 + cells * 0.15 + 0.15, 0.0, 1.0);\n let weather = cloud_weather_field(position, seed);\n let detailPosition = position * 3.0;\n let warpX = cloud_value_noise(\n vec3<f32>(detailPosition.x * 2.0, detailPosition.y * 2.0 + 11.0, detailPosition.z * 2.0),\n seed + 29011u,\n 2,\n 2,\n 2,\n ) - 0.5;\n let warpZ = cloud_value_noise(\n vec3<f32>(detailPosition.x * 2.0 + 7.0, detailPosition.y * 2.0 - 5.0, detailPosition.z * 2.0),\n seed + 29011u,\n 2,\n 2,\n 2,\n ) - 0.5;\n let erosion = cloud_cellular_noise(\n vec3<f32>(\n (detailPosition.x + warpX * 0.22) * 12.0,\n detailPosition.y * 3.0,\n (detailPosition.z + warpZ * 0.22) * 12.0,\n ),\n seed + 7919u,\n 12,\n 3,\n );\n return vec3<f32>(weather, base, erosion);\n}\n\nfn cloud_height_profile(height: f32, body: f32, weather: f32) -> f32 {\n // A body-dependent crown breaks the fixed horizontal slab silhouette while\n // keeping the profile smooth enough for the bounded ray step budget.\n let lowerEdge = 0.05 + (1.0 - body) * 0.1;\n let crownEdge = min(0.96, 0.6 + body * 0.3 + weather * 0.08);\n let lower = smoothstep(lowerEdge, min(1.0, lowerEdge + 0.12), height);\n let upper = 1.0 - smoothstep(crownEdge, min(1.0, crownEdge + 0.14), height);\n return lower * upper;\n}\n\nfn cloud_compose_density(height: f32, formation: vec3<f32>, coverage: f32) -> f32 {\n let threshold = 1.0 - coverage * (0.45 + formation.x * 0.55);\n let covered = smoothstep(0.0, 0.4, (formation.y - threshold) / max(0.001, 1.0 - threshold));\n let shaped = covered * cloud_height_profile(height, formation.y, formation.x);\n let erosion = (1.0 - formation.z) * 0.18;\n return clamp((shaped - erosion) / (1.0 - erosion), 0.0, 1.0);\n}\n\nfn cloud_analytic_density(position: vec3<f32>) -> f32 {\n let height = (position.y - cloud.layer.x) / max(0.000001, cloud.layer.y);\n if (height <= 0.0 || height >= 1.0 || cloud.layer.w <= 0.0 || cloud.field.x <= 0.0) { return 0.0; }\n let advected = position + cloud.wind.xyz * cloud.field.y;\n let advectedHeight = height +\n cloud.wind.y * cloud.field.y * cloud.layer.z / max(CLOUD_VERTICAL_CELLS, 0.000001);\n let p = vec3<f32>(\n advected.x * cloud.layer.z,\n fract(advectedHeight) * CLOUD_VERTICAL_CELLS,\n advected.z * cloud.layer.z,\n );\n let formation = cloud_formation_field(p, u32(round(cloud.field.w)));\n return cloud_compose_density(height, formation, cloud.field.x) * cloud.layer.w;\n}\n\nfn cloud_cache_byte(index: u32) -> f32 {\n let word = densityCache[index >> 2u];\n let shift = (index & 3u) * 8u;\n return f32((word >> shift) & 255u) / 255.0;\n}\n\nfn cloud_cache_component(plane: u32, index: u32, stride: u32) -> f32 {\n return cloud_cache_byte(plane * stride + index);\n}\n\nfn cloud_cache_trilinear(\n plane: u32,\n resolution: u32,\n x0: u32,\n x1: u32,\n y0: u32,\n y1: u32,\n z0: u32,\n z1: u32,\n fx: f32,\n fy: f32,\n fz: f32,\n) -> f32 {\n let stride = resolution * resolution * resolution;\n let layer = resolution * resolution;\n let c000 = cloud_cache_component(plane, z0 * layer + y0 * resolution + x0, stride);\n let c100 = cloud_cache_component(plane, z0 * layer + y0 * resolution + x1, stride);\n let c010 = cloud_cache_component(plane, z0 * layer + y1 * resolution + x0, stride);\n let c110 = cloud_cache_component(plane, z0 * layer + y1 * resolution + x1, stride);\n let c001 = cloud_cache_component(plane, z1 * layer + y0 * resolution + x0, stride);\n let c101 = cloud_cache_component(plane, z1 * layer + y0 * resolution + x1, stride);\n let c011 = cloud_cache_component(plane, z1 * layer + y1 * resolution + x0, stride);\n let c111 = cloud_cache_component(plane, z1 * layer + y1 * resolution + x1, stride);\n let x00 = mix(c000, c100, fx);\n let x10 = mix(c010, c110, fx);\n let x01 = mix(c001, c101, fx);\n let x11 = mix(c011, c111, fx);\n return mix(mix(x00, x10, fy), mix(x01, x11, fy), fz);\n}\n\nfn cloud_density(position: vec3<f32>) -> f32 {\n let height = (position.y - cloud.layer.x) / max(0.000001, cloud.layer.y);\n if (height <= 0.0 || height >= 1.0 || cloud.layer.w <= 0.0 || cloud.field.x <= 0.0) { return 0.0; }\n let resolution = cloud.integration.x;\n if (resolution < 4u) { return max(cloud_analytic_density(position), 0.0); }\n let periodCoordinate = fract((position.xz + cloud.wind.xz * cloud.field.y) * cloud.layer.z);\n let x = fract(periodCoordinate.x - 0.5 / f32(resolution)) * f32(resolution);\n let z = fract(periodCoordinate.y - 0.5 / f32(resolution)) * f32(resolution);\n let advectedHeight = height +\n cloud.wind.y * cloud.field.y * cloud.layer.z / max(CLOUD_VERTICAL_CELLS, 0.000001);\n let y = fract(advectedHeight - 0.5 / f32(resolution)) * f32(resolution);\n let x0 = u32(floor(x));\n let y0 = u32(floor(y));\n let z0 = u32(floor(z));\n let x1 = (x0 + 1u) % resolution;\n let y1 = (y0 + 1u) % resolution;\n let z1 = (z0 + 1u) % resolution;\n let fx = x - f32(x0);\n let fy = y - f32(y0);\n let fz = z - f32(z0);\n let weather = cloud_cache_trilinear(0u, resolution, x0, x1, y0, y1, z0, z1, fx, fy, fz);\n let body = cloud_cache_trilinear(1u, resolution, x0, x1, y0, y1, z0, z1, fx, fy, fz);\n // Repeat only the independent erosion plane: fine boundary structure with\n // the same eight packed reads and the same cache allocation.\n let detail = fract(vec3<f32>(periodCoordinate.x, advectedHeight, periodCoordinate.y) * 3.0 - vec3<f32>(0.5 / f32(resolution))) * f32(resolution);\n let d0 = vec3<u32>(floor(detail));\n let d1 = (d0 + vec3<u32>(1u)) % vec3<u32>(resolution);\n let df = fract(detail);\n let erosion = cloud_cache_trilinear(2u, resolution, d0.x, d1.x, d0.y, d1.y, d0.z, d1.z, df.x, df.y, df.z);\n return cloud_compose_density(height, vec3<f32>(weather, body, erosion), cloud.field.x) * cloud.layer.w;\n}\n\nfn cloud_layer_interval(origin: vec3<f32>, direction: vec3<f32>, maxDistance: f32) -> vec2<f32> {\n if (abs(direction.y) < 0.000001) {\n if (origin.y < cloud.layer.x || origin.y > cloud.layer.x + cloud.layer.y) {\n return vec2<f32>(1.0, 0.0);\n }\n return vec2<f32>(0.0, maxDistance);\n }\n let lower = (cloud.layer.x - origin.y) / direction.y;\n let upper = (cloud.layer.x + cloud.layer.y - origin.y) / direction.y;\n return vec2<f32>(\n max(0.0, min(lower, upper)),\n min(maxDistance, max(lower, upper)),\n );\n}\n\nfn cloud_solar_transmittance(position: vec3<f32>) -> f32 {\n let sunDirection = normalize(-view.lightDir);\n let solarDistance = select(cloud.field.z, cloud.shadowProjection.x, cloud.shadowProjection.x > 0.0);\n let interval = cloud_layer_interval(position, sunDirection, solarDistance);\n if (interval.y <= interval.x) { return 1.0; }\n let steps = min(cloud.integration.z, 64u);\n let stepLength = (interval.y - interval.x) / max(1.0, f32(steps));\n var opticalDepth = 0.0;\n let solarJitter = cloud_solar_jitter(position);\n for (var index = 0u; index < 64u; index = index + 1u) {\n if (index >= steps) { break; }\n let samplePosition = position + sunDirection * (interval.x + (f32(index) + solarJitter) * stepLength);\n opticalDepth = opticalDepth + cloud_density(samplePosition) * 0.03 * stepLength;\n }\n return exp(-max(opticalDepth, 0.0));\n}\n\nfn cloud_henyey_greenstein(cosTheta: f32, g: f32) -> f32 {\n let denominator = max(0.0001, 1.0 + g * g - 2.0 * g * cosTheta);\n return (1.0 - g * g) / (denominator * sqrt(denominator)) * 0.0795774715;\n}\n\nfn cloud_phase(cosTheta: f32) -> f32 {\n // A bounded dual lobe gives a readable forward highlight and a small\n // back-scatter rim without another solar march.\n return cloud_henyey_greenstein(cosTheta, 0.55) * 0.8 +\n cloud_henyey_greenstein(cosTheta, -0.2) * 0.2;\n}\n\n// Reuse the same remaining-path optical depth for two bounded scattering\n// orders. This adds arithmetic only; no extra density or light-ray samples.\nfn cloud_incident_light(solarT: f32, cosTheta: f32, height: f32) -> vec3<f32> {\n let direct = solarT * cloud_phase(cosTheta);\n let multiple = (0.5 * pow(solarT, 0.5) + 0.25 * pow(solarT, 0.25)) * 0.0795774715;\n let skyFill = mix(0.025, 0.075, smoothstep(0.1, 0.85, height));\n // Match volume-integrate: the shared DirectionalLight value uses the\n // unnormalized phase convention. Convert the normalized HG basis once.\n return cloud.sunRadiance.xyz *\n (vec3<f32>((direct + multiple) * 12.5663706144) + vec3<f32>(0.72, 0.84, 1.0) * skyFill);\n}\n\nfn cloud_ray_jitter(pixel: vec2<f32>) -> f32 {\n // A stable per-pixel offset removes view-step contour bands without adding\n // a frame-varying noise source that would fight temporal reprojection.\n return fract(sin(dot(pixel, vec2<f32>(12.9898, 78.233))) * 43758.5453);\n}\n\nfn cloud_solar_jitter(position: vec3<f32>) -> f32 {\n // Dither the light-column samples in world space so low shadow-step\n // profiles do not paint parallel contour bands across cloud bases.\n return fract(sin(dot(position.xz, vec2<f32>(19.193, 47.117))) * 15731.743);\n}\n\nfn reconstruct_world(uv: vec2<f32>, depth: f32) -> vec3<f32> {\n let ndc = vec4<f32>(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth, 1.0);\n let world = view.inverseViewProj * ndc;\n return world.xyz / max(abs(world.w), 0.00001);\n}\n\n@vertex\nfn vs_main(@builtin(vertex_index) index: u32) -> CloudViewOut {\n var positions = array<vec2<f32>, 3>(\n vec2<f32>(-1.0, -1.0),\n vec2<f32>(3.0, -1.0),\n vec2<f32>(-1.0, 3.0),\n );\n var output: CloudViewOut;\n output.position = vec4<f32>(positions[index], 0.0, 1.0);\n // WebGPU clip-space Y grows upward while sampled framebuffer rows and the\n // depth texture use a top-left origin. Keep this one top-left UV for color,\n // depth and inverse-projection reconstruction; flipping only the final\n // color would leave the camera ray and depth cut mirrored.\n output.uv = vec2<f32>(\n positions[index].x * 0.5 + 0.5,\n 1.0 - (positions[index].y * 0.5 + 0.5),\n );\n return output;\n}\n\n@fragment\nfn fs_main(input: CloudViewOut) -> @location(0) vec4<f32> {\n // The shadow producer uses this same production density/cache contract but\n // rasterizes a camera-independent, texel-snapped light-space map. It does\n // not consume scene colour; the generic fullscreen host still supplies the\n // input binding to keep the feature binding ABI single and validated.\n if (cloud.integration.w == 1u) {\n let projection = cloud.shadowProjection;\n if (projection.y < 0.5 || projection.z > 0.5 || projection.x <= 0.0) {\n return vec4<f32>(1.0);\n }\n let offset = (input.uv - vec2<f32>(0.5)) * projection.x;\n let receiver = cloud.shadowOrigin.xyz +\n cloud.shadowRight.xyz * offset.x + cloud.shadowUp.xyz * offset.y;\n let sunDirection = normalize(-view.lightDir);\n let interval = cloud_layer_interval(receiver, sunDirection, projection.x);\n if (interval.y <= interval.x) { return vec4<f32>(1.0); }\n let steps = min(cloud.integration.z, 64u);\n let stepLength = (interval.y - interval.x) / max(1.0, f32(steps));\n var opticalDepth = 0.0;\n var firstHeight = 1.0;\n var lastHeight = 0.0;\n var occupied = false;\n let solarJitter = cloud_solar_jitter(receiver);\n for (var index = 0u; index < 64u; index = index + 1u) {\n if (index >= steps) { break; }\n let samplePosition = receiver + sunDirection *\n (interval.x + (f32(index) + solarJitter) * stepLength);\n let localDensity = cloud_density(samplePosition);\n opticalDepth = opticalDepth + localDensity * 0.03 * stepLength;\n if (localDensity > 0.0001) {\n let sampleHeight = clamp(\n (samplePosition.y - cloud.layer.x) / max(0.0001, cloud.layer.y),\n 0.0,\n 1.0,\n );\n if (!occupied) { firstHeight = sampleHeight; }\n lastHeight = sampleHeight;\n occupied = true;\n }\n }\n let transmittance = exp(-max(opticalDepth, 0.0));\n // R stores full-column transmittance. G/B preserve the first/last\n // occupied normalized layer heights so interior samples can estimate the\n // remaining segment without pretending optical depth is uniform through\n // the whole layer. A stores the integrated optical depth and distinguishes\n // an empty projected column from a valid, nearly-clear one.\n return vec4<f32>(\n transmittance,\n select(1.0, firstHeight, occupied),\n select(0.0, lastHeight, occupied),\n select(0.0, opticalDepth, occupied),\n );\n }\n // The shadow producer branch above is per-fragment (its light-space\n // interval depends on the rasterized receiver), so derivative-based\n // sampling here would violate WGSL uniform-control-flow rules. The cloud\n // scene path already owns an explicit level-0 sampler contract.\n // Keep point semantics for the current full-resolution scene path. The\n // half-resolution transport contract is introduced with its own resolve\n // shader so this path retains the established depth convention.\n let depthPixel = vec2<i32>(input.position.xy);\n let depth = textureLoad(sceneDepth, depthPixel, 0);\n let farWorld = reconstruct_world(input.uv, 1.0);\n let sceneWorld = reconstruct_world(input.uv, clamp(depth, 0.0, 1.0));\n let ray = normalize(farWorld - view.cameraPos);\n let rayJitter = cloud_ray_jitter(input.position.xy);\n let sceneDistance = select(cloud.field.z, length(sceneWorld - view.cameraPos), depth < 0.999999);\n let interval = cloud_layer_interval(view.cameraPos, ray, sceneDistance);\n\n // The depth producer shares the same camera ray, interval and density\n // contract as the display path. It writes a cloud representative world\n // position and a validity bit so history remains comparable after camera\n // translation or rotation and never uses an opaque scene distance as a\n // proxy for a participating medium.\n if (cloud.integration.w == 2u) {\n if (interval.y <= interval.x) { return vec4<f32>(sceneWorld, 0.0); }\n let depthSteps = min(cloud.integration.y, 64u);\n let depthStepLength = (interval.y - interval.x) / max(1.0, f32(depthSteps));\n var depthTransmittance = 1.0;\n var weightedDistance = 0.0;\n var opticalWeight = 0.0;\n for (var depthIndex = 0u; depthIndex < 64u; depthIndex = depthIndex + 1u) {\n if (depthIndex >= depthSteps) { break; }\n let sampleDistance = interval.x + (f32(depthIndex) + rayJitter) * depthStepLength;\n let samplePosition = view.cameraPos + ray * sampleDistance;\n let localDensity = cloud_density(samplePosition);\n let localTransmittance = exp(-max(localDensity * 0.03 * depthStepLength, 0.0));\n let weight = depthTransmittance * (1.0 - localTransmittance);\n weightedDistance = weightedDistance + sampleDistance * weight;\n opticalWeight = opticalWeight + weight;\n depthTransmittance = depthTransmittance * localTransmittance;\n }\n let hasCloud = opticalWeight > 0.00001;\n let representativeDistance = select(sceneDistance, weightedDistance / opticalWeight, hasCloud);\n let representativeWorld = view.cameraPos + ray * representativeDistance;\n return vec4<f32>(representativeWorld, select(0.0, 1.0, hasCloud));\n }\n\n let scene = textureSampleLevel(sceneColor, sceneSampler, input.uv, 0.0);\n if (interval.y <= interval.x) { return scene; }\n let steps = min(cloud.integration.y, 64u);\n let stepLength = (interval.y - interval.x) / max(1.0, f32(steps));\n var transmittance = 1.0;\n var scattering = vec3<f32>(0.0);\n var weightedDistance = 0.0;\n var opticalWeight = 0.0;\n let sunDirection = normalize(-view.lightDir);\n for (var index = 0u; index < 64u; index = index + 1u) {\n if (index >= steps) { break; }\n let distance = interval.x + (f32(index) + rayJitter) * stepLength;\n let samplePosition = view.cameraPos + ray * distance;\n let localDensity = cloud_density(samplePosition);\n let extinction = localDensity * 0.03 * stepLength;\n let localTransmittance = exp(-max(extinction, 0.0));\n let depthWeight = transmittance * (1.0 - localTransmittance);\n weightedDistance = weightedDistance + distance * depthWeight;\n opticalWeight = opticalWeight + depthWeight;\n // Empty samples are common in the separated-cloud field. Avoid a nested\n // solar march for them and stop once the camera path is effectively opaque.\n if (localDensity > 0.0001 && transmittance > 0.01) {\n let solarTransmittance = cloud_solar_transmittance(samplePosition);\n let cloudHeight = clamp(\n (samplePosition.y - cloud.layer.x) / max(0.0001, cloud.layer.y), 0.0, 1.0,\n );\n scattering = scattering + cloud_incident_light(\n solarTransmittance, dot(ray, sunDirection), cloudHeight,\n ) * depthWeight;\n }\n transmittance = transmittance * localTransmittance;\n if (transmittance < 0.01) { break; }\n }\n let hasCloud = opticalWeight > 0.00001;\n let representativeDistance = select(sceneDistance, weightedDistance / opticalWeight, hasCloud);\n let representativeWorld = view.cameraPos + ray * representativeDistance;\n // Wind is an advective coordinate change. Reproject the cloud point into\n // the previous frame's world before applying the previous camera matrix;\n // authored field values remain immutable.\n let windDelta = cloud.wind.xyz * max(0.0, cloud.field.y - cloud.temporal.w);\n // cloud_density evaluates the authored field at position + wind*time.\n // Keeping the same field sample in the previous frame therefore advances\n // the previous world point by the elapsed wind delta as well.\n let previousCloudWorld = representativeWorld + windDelta;\n let previousCloudClip = view.temporalPreviousViewProj * vec4<f32>(previousCloudWorld, 1.0);\n let previousCloudNdc = previousCloudClip.xyz / max(abs(previousCloudClip.w), 0.00001);\n let previousCloudUv = vec2<f32>(\n previousCloudNdc.x * 0.5 + 0.5,\n 1.0 - (previousCloudNdc.y * 0.5 + 0.5),\n );\n let cloudInBounds = all(previousCloudUv >= vec2<f32>(0.0)) && all(previousCloudUv <= vec2<f32>(1.0));\n let cloudHistoryUv = clamp(previousCloudUv, vec2<f32>(0.0), vec2<f32>(1.0));\n let cloudHistoryDepthSample = textureSampleLevel(previousDepth, depthSampler, cloudHistoryUv, 0.0);\n let cloudHistoryWorld = cloudHistoryDepthSample.xyz;\n let previous = textureSampleLevel(previousRadiance, sceneSampler, cloudHistoryUv, 0.0);\n let previousTransport = textureSampleLevel(\n previousTransmittance,\n sceneSampler,\n cloudHistoryUv,\n 0.0,\n );\n let worldTolerance = max(0.05, length(previousCloudWorld - view.cameraPos) * 0.02);\n let depthAccepted = cloudHistoryDepthSample.a > 0.5 &&\n distance(cloudHistoryWorld, previousCloudWorld) <= worldTolerance;\n let accepted =\n hasCloud && cloud.temporal.x > 0.5 && cloud.temporal.y < 0.5 && cloudInBounds &&\n depthAccepted && previousTransport.a > 0.0;\n let historyWeight = select(0.0, clamp(cloud.temporal.z, 0.0, 0.95), accepted);\n let cloudRadiance = mix(scattering, previous.rgb, historyWeight);\n // Preserve scene-linear HDR output for the downstream material lane. The\n // history owner receives cloudRadiance separately through the history pass.\n return vec4<f32>(scene.rgb * transmittance + cloudRadiance, transmittance);\n}\n\n// The light-space shadow producer already paid for a bounded column integral.\n// Reuse that texel for camera samples instead of starting a full solar march\n// for every occupied camera step. Outside the occupied interval the cached\n// full-column transmittance is exact for the projected column. Inside the\n// interval, the cache supplies the stable projection/bounds and a short\n// residual march resolves the non-uniform density that a linear optical-depth\n// interpolation would lose. Invalid/out-of-range projections retain the\n// analytic path as an explicit fallback.\nfn cloud_solar_cached_transmittance(position: vec3<f32>) -> f32 {\n let projection = cloud.shadowProjection;\n if (projection.y < 0.5 || projection.x <= 0.0) {\n return cloud_solar_transmittance(position);\n }\n let relative = position - cloud.shadowOrigin.xyz;\n let uv = vec2<f32>(\n dot(relative, cloud.shadowRight.xyz) / projection.x + 0.5,\n dot(relative, cloud.shadowUp.xyz) / projection.x + 0.5,\n );\n if (any(uv < vec2<f32>(0.0)) || any(uv > vec2<f32>(1.0))) {\n return cloud_solar_transmittance(position);\n }\n let cachedShadow = textureSampleLevel(previousRadiance, sceneSampler, uv, 0.0);\n let cachedOpticalDepth = max(cachedShadow.a, 0.0);\n let layerHeight = clamp(\n (position.y - cloud.layer.x) / max(0.0001, cloud.layer.y),\n 0.0,\n 1.0,\n );\n if (cachedOpticalDepth <= 0.0001) { return 1.0; }\n let firstHeight = clamp(cachedShadow.g, 0.0, 1.0);\n let lastHeight = clamp(cachedShadow.b, 0.0, 1.0);\n let occupiedMin = min(firstHeight, lastHeight);\n let occupiedMax = max(firstHeight, lastHeight);\n let sunDirection = normalize(-view.lightDir);\n // G/B are ordered along the light ray, not necessarily bottom-to-top. Only\n // use the cached full/empty fast paths when the light has enough vertical\n // component to make that ordering meaningful; grazing light stays on the\n // residual path so a low-sun column cannot invert the cache bounds.\n if (abs(sunDirection.y) > 0.2) {\n if (sunDirection.y > 0.0) {\n if (layerHeight <= occupiedMin) { return exp(-cachedOpticalDepth); }\n if (layerHeight >= occupiedMax) { return 1.0; }\n } else {\n if (layerHeight >= occupiedMax) { return exp(-cachedOpticalDepth); }\n if (layerHeight <= occupiedMin) { return 1.0; }\n }\n }\n\n // A short residual march is only paid for samples inside the projected\n // cloud span. Vertical light uses twelve taps, oblique light sixteen, and\n // grazing light up to thirty-two because the same layer projects across a\n // much longer solar segment. This removes the large error caused by\n // assuming optical depth is uniform between the first and last occupied\n // heights while keeping the normal camera path far below a second full\n // solar integration.\n let solarDistance = select(cloud.field.z, projection.x, projection.x > 0.0);\n let interval = cloud_layer_interval(position, sunDirection, solarDistance);\n if (interval.y <= interval.x) { return 1.0; }\n let absLightY = abs(sunDirection.y);\n let directionBudget = select(\n 32u,\n select(16u, 12u, absLightY > 0.5),\n absLightY > 0.2,\n );\n let residualSteps = min(directionBudget, max(8u, cloud.integration.z * 3u));\n let stepLength = (interval.y - interval.x) / max(1.0, f32(residualSteps));\n var opticalDepth = 0.0;\n let solarJitter = cloud_solar_jitter(position);\n for (var index = 0u; index < 32u; index = index + 1u) {\n if (index >= residualSteps) { break; }\n let samplePosition = position + sunDirection *\n (interval.x + (f32(index) + solarJitter) * stepLength);\n opticalDepth = opticalDepth + cloud_density(samplePosition) *\n 0.03 * stepLength;\n }\n return exp(-max(opticalDepth, 0.0));\n}\n\nstruct CloudTransportOutput {\n @location(0) radiance: vec4<f32>,\n @location(1) transmittance: vec4<f32>,\n @location(2) depth: vec4<f32>,\n};\n\n// One half-resolution transport pass writes all three reusable cloud fields.\n// The full-resolution resolve below reconstructs colour and applies temporal\n// history; no second full raymarch exists solely to obtain cloud depth.\n@fragment\nfn fs_transport(input: CloudViewOut) -> CloudTransportOutput {\n let depth = textureSampleLevel(sceneDepth, depthSampler, input.uv, 0u);\n let farWorld = reconstruct_world(input.uv, 1.0);\n let sceneWorld = reconstruct_world(input.uv, clamp(depth, 0.0, 1.0));\n let ray = normalize(farWorld - view.cameraPos);\n let rayJitter = cloud_ray_jitter(input.position.xy);\n let sceneDistance = select(cloud.field.z, length(sceneWorld - view.cameraPos), depth < 0.999999);\n let interval = cloud_layer_interval(view.cameraPos, ray, sceneDistance);\n var output: CloudTransportOutput;\n output.radiance = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n output.transmittance = vec4<f32>(1.0, 1.0, 1.0, 1.0);\n output.depth = vec4<f32>(sceneWorld, 0.0);\n if (interval.y <= interval.x) { return output; }\n\n let steps = min(cloud.integration.y, 64u);\n let stepLength = (interval.y - interval.x) / max(1.0, f32(steps));\n var transmittance = 1.0;\n var scattering = vec3<f32>(0.0);\n var weightedDistance = 0.0;\n var opticalWeight = 0.0;\n let sunDirection = normalize(-view.lightDir);\n for (var index = 0u; index < 64u; index = index + 1u) {\n if (index >= steps) { break; }\n let distance = interval.x + (f32(index) + rayJitter) * stepLength;\n let samplePosition = view.cameraPos + ray * distance;\n let localDensity = cloud_density(samplePosition);\n let extinction = localDensity * 0.03 * stepLength;\n let localTransmittance = exp(-max(extinction, 0.0));\n let depthWeight = transmittance * (1.0 - localTransmittance);\n weightedDistance = weightedDistance + distance * depthWeight;\n opticalWeight = opticalWeight + depthWeight;\n if (localDensity > 0.0001 && transmittance > 0.01) {\n let solarTransmittance = cloud_solar_cached_transmittance(samplePosition);\n let cloudHeight = clamp(\n (samplePosition.y - cloud.layer.x) / max(0.0001, cloud.layer.y), 0.0, 1.0,\n );\n scattering = scattering + cloud_incident_light(\n solarTransmittance, dot(ray, sunDirection), cloudHeight,\n ) * depthWeight;\n }\n transmittance = transmittance * localTransmittance;\n if (transmittance < 0.01) { break; }\n }\n let hasCloud = opticalWeight > 0.00001;\n let representativeDistance = select(sceneDistance, weightedDistance / max(opticalWeight, 0.00001), hasCloud);\n output.radiance = vec4<f32>(scattering, select(0.0, 1.0, hasCloud));\n output.transmittance = vec4<f32>(transmittance, transmittance, transmittance, 1.0);\n output.depth = vec4<f32>(view.cameraPos + ray * representativeDistance, select(0.0, 1.0, hasCloud));\n return output;\n}\n";
|
|
23
|
+
/** Shared cloud transport module; `fs_transport` is selected by the MRT PSO. */
|
|
24
|
+
export declare const CLOUD_TRANSPORT_FULLSCREEN_WGSL = "\nconst CLOUD_VERTICAL_CELLS: f32 = 1;\nconst CLOUD_VERTICAL_NOISE_CELLS: f32 = 2.0;\n\nstruct CloudCameraView {\n worldViewProj: mat4x4<f32>,\n lightDir: vec3<f32>,\n lightColor: vec3<f32>,\n cameraPos: vec3<f32>,\n lightViewProj_A: mat4x4<f32>,\n inverseViewProj: mat4x4<f32>,\n lightViewProj_B: mat4x4<f32>,\n lightViewProj_C: mat4x4<f32>,\n lightViewProj_D: mat4x4<f32>,\n splitPlanes: array<vec4<f32>, 4>,\n cascadeCount: f32,\n cascadeBlend: f32,\n depthBias: f32,\n normalBias: f32,\n directionalShadowFilter: vec4<f32>,\n spotLightViewProj: array<mat4x4<f32>, 4>,\n temporalCurrentViewProj: mat4x4<f32>,\n temporalPreviousViewProj: mat4x4<f32>,\n temporalProjection: vec4<f32>,\n temporalPreviousCameraPos: vec4<f32>,\n ssrParams: vec4<f32>,\n};\n\nstruct CloudViewParams {\n layer: vec4<f32>, // baseHeight, thickness, scale, density\n field: vec4<f32>, // coverage, timeSeconds, maxDistance, seed\n wind: vec4<f32>, // xyz m/s\n sunDirection: vec4<f32>,\n sunRadiance: vec4<f32>,\n integration: vec4<u32>, // cacheResolution, viewSteps, solarSteps, reserved\n shadowOrigin: vec4<f32>,\n shadowRight: vec4<f32>,\n shadowUp: vec4<f32>,\n shadowProjection: vec4<f32>, // range, valid, lowSun, texelSize\n temporal: vec4<f32>, // valid, reset, historyWeight, reserved\n};\n\n@group(0) @binding(0) var<uniform> view: CloudCameraView;\n@group(1) @binding(0) var sceneColor: texture_2d<f32>;\n@group(1) @binding(1) var sceneSampler: sampler;\n@group(1) @binding(2) var<uniform> cloud: CloudViewParams;\n@group(1) @binding(3) var sceneDepth: texture_depth_2d;\n@group(1) @binding(4) var depthSampler: sampler;\n@group(1) @binding(5) var previousRadiance: texture_2d<f32>;\n@group(1) @binding(6) var previousTransmittance: texture_2d<f32>;\n@group(1) @binding(7) var previousDepth: texture_2d<f32>;\n@group(1) @binding(8) var<storage, read> densityCache: array<u32>;\n\nstruct CloudViewOut {\n @builtin(position) position: vec4<f32>,\n @location(0) uv: vec2<f32>,\n};\n\nfn cloud_hash3(cell: vec3<i32>, seed: u32) -> f32 {\n var h = bitcast<u32>(cell.x) * 0x45d9f3bu;\n h = (h ^ (bitcast<u32>(cell.y) * 0x119de1f3u)) * 0x45d9f3bu;\n h = (h ^ (bitcast<u32>(cell.z) * 0x3449fu)) * 0x45d9f3bu;\n h = h ^ seed;\n h = (h ^ (h >> 16u)) * 0x45d9f3bu;\n h = (h ^ (h >> 13u)) * 0x27d4eb2du;\n return f32(h ^ (h >> 16u)) / 4294967296.0;\n}\n\nfn cloud_wrap_cell(value: i32, period: i32) -> i32 {\n let wrapped = value % period;\n return select(wrapped, wrapped + period, wrapped < 0);\n}\n\nfn cloud_hash3_periodic(\n cell: vec3<i32>,\n seed: u32,\n periodX: i32,\n periodY: i32,\n periodZ: i32,\n) -> f32 {\n return cloud_hash3(\n vec3<i32>(\n cloud_wrap_cell(cell.x, periodX),\n cloud_wrap_cell(cell.y, periodY),\n cloud_wrap_cell(cell.z, periodZ),\n ),\n seed,\n );\n}\n\nfn cloud_value_noise(\n position: vec3<f32>,\n seed: u32,\n periodX: i32,\n periodY: i32,\n periodZ: i32,\n) -> f32 {\n let cell = vec3<i32>(floor(position));\n let fraction = fract(position);\n let smoothFraction = fraction * fraction * (3.0 - 2.0 * fraction);\n let c000 = cloud_hash3_periodic(cell + vec3<i32>(0, 0, 0), seed, periodX, periodY, periodZ);\n let c100 = cloud_hash3_periodic(cell + vec3<i32>(1, 0, 0), seed, periodX, periodY, periodZ);\n let c010 = cloud_hash3_periodic(cell + vec3<i32>(0, 1, 0), seed, periodX, periodY, periodZ);\n let c110 = cloud_hash3_periodic(cell + vec3<i32>(1, 1, 0), seed, periodX, periodY, periodZ);\n let c001 = cloud_hash3_periodic(cell + vec3<i32>(0, 0, 1), seed, periodX, periodY, periodZ);\n let c101 = cloud_hash3_periodic(cell + vec3<i32>(1, 0, 1), seed, periodX, periodY, periodZ);\n let c011 = cloud_hash3_periodic(cell + vec3<i32>(0, 1, 1), seed, periodX, periodY, periodZ);\n let c111 = cloud_hash3_periodic(cell + vec3<i32>(1, 1, 1), seed, periodX, periodY, periodZ);\n let x00 = mix(c000, c100, smoothFraction.x);\n let x10 = mix(c010, c110, smoothFraction.x);\n let x01 = mix(c001, c101, smoothFraction.x);\n let x11 = mix(c011, c111, smoothFraction.x);\n return mix(mix(x00, x10, smoothFraction.y), mix(x01, x11, smoothFraction.y), smoothFraction.z);\n}\n\nfn cloud_cellular_noise(position: vec3<f32>, seed: u32, periodXZ: i32, periodY: i32) -> f32 {\n let cell = vec3<i32>(floor(position));\n let fraction = fract(position);\n var nearest = 1e9;\n var dz: i32 = -1;\n loop {\n var dy: i32 = -1;\n loop {\n var dx: i32 = -1;\n loop {\n let random = cloud_hash3_periodic(\n cell + vec3<i32>(dx, dy, dz),\n seed ^ 0x9e3779b9u,\n periodXZ,\n periodY,\n periodXZ,\n );\n let point = vec3<f32>(\n f32(dx) + fract(random * 17.0) - fraction.x,\n f32(dy) + fract(random * 31.0) - fraction.y,\n f32(dz) + fract(random * 47.0) - fraction.z,\n );\n nearest = min(nearest, dot(point, point));\n if (dx >= 1) { break; }\n dx = dx + 1;\n }\n if (dy >= 1) { break; }\n dy = dy + 1;\n }\n if (dz >= 1) { break; }\n dz = dz + 1;\n }\n return 1.0 - clamp(sqrt(nearest) * 1.25, 0.0, 1.0);\n}\n\nfn cloud_weather_field(position: vec3<f32>, seed: u32) -> f32 {\n return smoothstep(\n 0.34,\n 0.66,\n cloud_value_noise(\n vec3<f32>(position.x * 2.0, 0.37, position.z * 2.0),\n seed + 17041u,\n 2,\n 1,\n 2,\n ),\n );\n}\n\nfn cloud_formation_field(position: vec3<f32>, seed: u32) -> vec3<f32> {\n let macroWarpX = cloud_value_noise(\n vec3<f32>(position.x * 2.0, position.y * CLOUD_VERTICAL_NOISE_CELLS + 3.0, position.z * 2.0),\n seed + 41023u,\n 2,\n 2,\n 2,\n ) - 0.5;\n let macroWarpZ = cloud_value_noise(\n vec3<f32>(position.x * 2.0 + 5.0, position.y * CLOUD_VERTICAL_NOISE_CELLS - 7.0, position.z * 2.0),\n seed + 41023u,\n 2,\n 2,\n 2,\n ) - 0.5;\n let bodyX = position.x + macroWarpX * 0.3;\n let bodyZ = position.z + macroWarpZ * 0.3;\n var noise = 0.0;\n var weight = 0.0;\n var amplitude = 0.5;\n var frequency = 1.0;\n for (var octave = 0u; octave < 4u; octave = octave + 1u) {\n let tilePeriod = 4.0 * frequency;\n noise = noise + cloud_value_noise(\n vec3<f32>(bodyX * tilePeriod, position.y * frequency * CLOUD_VERTICAL_NOISE_CELLS, bodyZ * tilePeriod),\n seed + octave * 1013u,\n i32(tilePeriod),\n i32(frequency * CLOUD_VERTICAL_NOISE_CELLS),\n i32(tilePeriod),\n ) * amplitude;\n weight = weight + amplitude;\n amplitude = amplitude * 0.4;\n frequency = frequency * 2.0;\n }\n let broad = noise / max(0.0001, weight);\n let cells = cloud_cellular_noise(\n vec3<f32>(bodyX * 4.0, position.y * CLOUD_VERTICAL_NOISE_CELLS, bodyZ * 4.0),\n seed + 5011u,\n 4,\n i32(CLOUD_VERTICAL_NOISE_CELLS),\n );\n // Keep the broad field responsible for the cloud silhouette and use the\n // cellular term as bounded breakup. A broad-dominant mix reads as fewer,\n // wider formations instead of evenly spaced foam balls.\n let base = clamp(broad * 0.85 + cells * 0.15 + 0.15, 0.0, 1.0);\n let weather = cloud_weather_field(position, seed);\n let detailPosition = position * 3.0;\n let warpX = cloud_value_noise(\n vec3<f32>(detailPosition.x * 2.0, detailPosition.y * 2.0 + 11.0, detailPosition.z * 2.0),\n seed + 29011u,\n 2,\n 2,\n 2,\n ) - 0.5;\n let warpZ = cloud_value_noise(\n vec3<f32>(detailPosition.x * 2.0 + 7.0, detailPosition.y * 2.0 - 5.0, detailPosition.z * 2.0),\n seed + 29011u,\n 2,\n 2,\n 2,\n ) - 0.5;\n let erosion = cloud_cellular_noise(\n vec3<f32>(\n (detailPosition.x + warpX * 0.22) * 12.0,\n detailPosition.y * 3.0,\n (detailPosition.z + warpZ * 0.22) * 12.0,\n ),\n seed + 7919u,\n 12,\n 3,\n );\n return vec3<f32>(weather, base, erosion);\n}\n\nfn cloud_height_profile(height: f32, body: f32, weather: f32) -> f32 {\n // A body-dependent crown breaks the fixed horizontal slab silhouette while\n // keeping the profile smooth enough for the bounded ray step budget.\n let lowerEdge = 0.05 + (1.0 - body) * 0.1;\n let crownEdge = min(0.96, 0.6 + body * 0.3 + weather * 0.08);\n let lower = smoothstep(lowerEdge, min(1.0, lowerEdge + 0.12), height);\n let upper = 1.0 - smoothstep(crownEdge, min(1.0, crownEdge + 0.14), height);\n return lower * upper;\n}\n\nfn cloud_compose_density(height: f32, formation: vec3<f32>, coverage: f32) -> f32 {\n let threshold = 1.0 - coverage * (0.45 + formation.x * 0.55);\n let covered = smoothstep(0.0, 0.4, (formation.y - threshold) / max(0.001, 1.0 - threshold));\n let shaped = covered * cloud_height_profile(height, formation.y, formation.x);\n let erosion = (1.0 - formation.z) * 0.18;\n return clamp((shaped - erosion) / (1.0 - erosion), 0.0, 1.0);\n}\n\nfn cloud_analytic_density(position: vec3<f32>) -> f32 {\n let height = (position.y - cloud.layer.x) / max(0.000001, cloud.layer.y);\n if (height <= 0.0 || height >= 1.0 || cloud.layer.w <= 0.0 || cloud.field.x <= 0.0) { return 0.0; }\n let advected = position + cloud.wind.xyz * cloud.field.y;\n let advectedHeight = height +\n cloud.wind.y * cloud.field.y * cloud.layer.z / max(CLOUD_VERTICAL_CELLS, 0.000001);\n let p = vec3<f32>(\n advected.x * cloud.layer.z,\n fract(advectedHeight) * CLOUD_VERTICAL_CELLS,\n advected.z * cloud.layer.z,\n );\n let formation = cloud_formation_field(p, u32(round(cloud.field.w)));\n return cloud_compose_density(height, formation, cloud.field.x) * cloud.layer.w;\n}\n\nfn cloud_cache_byte(index: u32) -> f32 {\n let word = densityCache[index >> 2u];\n let shift = (index & 3u) * 8u;\n return f32((word >> shift) & 255u) / 255.0;\n}\n\nfn cloud_cache_component(plane: u32, index: u32, stride: u32) -> f32 {\n return cloud_cache_byte(plane * stride + index);\n}\n\nfn cloud_cache_trilinear(\n plane: u32,\n resolution: u32,\n x0: u32,\n x1: u32,\n y0: u32,\n y1: u32,\n z0: u32,\n z1: u32,\n fx: f32,\n fy: f32,\n fz: f32,\n) -> f32 {\n let stride = resolution * resolution * resolution;\n let layer = resolution * resolution;\n let c000 = cloud_cache_component(plane, z0 * layer + y0 * resolution + x0, stride);\n let c100 = cloud_cache_component(plane, z0 * layer + y0 * resolution + x1, stride);\n let c010 = cloud_cache_component(plane, z0 * layer + y1 * resolution + x0, stride);\n let c110 = cloud_cache_component(plane, z0 * layer + y1 * resolution + x1, stride);\n let c001 = cloud_cache_component(plane, z1 * layer + y0 * resolution + x0, stride);\n let c101 = cloud_cache_component(plane, z1 * layer + y0 * resolution + x1, stride);\n let c011 = cloud_cache_component(plane, z1 * layer + y1 * resolution + x0, stride);\n let c111 = cloud_cache_component(plane, z1 * layer + y1 * resolution + x1, stride);\n let x00 = mix(c000, c100, fx);\n let x10 = mix(c010, c110, fx);\n let x01 = mix(c001, c101, fx);\n let x11 = mix(c011, c111, fx);\n return mix(mix(x00, x10, fy), mix(x01, x11, fy), fz);\n}\n\nfn cloud_density(position: vec3<f32>) -> f32 {\n let height = (position.y - cloud.layer.x) / max(0.000001, cloud.layer.y);\n if (height <= 0.0 || height >= 1.0 || cloud.layer.w <= 0.0 || cloud.field.x <= 0.0) { return 0.0; }\n let resolution = cloud.integration.x;\n if (resolution < 4u) { return max(cloud_analytic_density(position), 0.0); }\n let periodCoordinate = fract((position.xz + cloud.wind.xz * cloud.field.y) * cloud.layer.z);\n let x = fract(periodCoordinate.x - 0.5 / f32(resolution)) * f32(resolution);\n let z = fract(periodCoordinate.y - 0.5 / f32(resolution)) * f32(resolution);\n let advectedHeight = height +\n cloud.wind.y * cloud.field.y * cloud.layer.z / max(CLOUD_VERTICAL_CELLS, 0.000001);\n let y = fract(advectedHeight - 0.5 / f32(resolution)) * f32(resolution);\n let x0 = u32(floor(x));\n let y0 = u32(floor(y));\n let z0 = u32(floor(z));\n let x1 = (x0 + 1u) % resolution;\n let y1 = (y0 + 1u) % resolution;\n let z1 = (z0 + 1u) % resolution;\n let fx = x - f32(x0);\n let fy = y - f32(y0);\n let fz = z - f32(z0);\n let weather = cloud_cache_trilinear(0u, resolution, x0, x1, y0, y1, z0, z1, fx, fy, fz);\n let body = cloud_cache_trilinear(1u, resolution, x0, x1, y0, y1, z0, z1, fx, fy, fz);\n // Repeat only the independent erosion plane: fine boundary structure with\n // the same eight packed reads and the same cache allocation.\n let detail = fract(vec3<f32>(periodCoordinate.x, advectedHeight, periodCoordinate.y) * 3.0 - vec3<f32>(0.5 / f32(resolution))) * f32(resolution);\n let d0 = vec3<u32>(floor(detail));\n let d1 = (d0 + vec3<u32>(1u)) % vec3<u32>(resolution);\n let df = fract(detail);\n let erosion = cloud_cache_trilinear(2u, resolution, d0.x, d1.x, d0.y, d1.y, d0.z, d1.z, df.x, df.y, df.z);\n return cloud_compose_density(height, vec3<f32>(weather, body, erosion), cloud.field.x) * cloud.layer.w;\n}\n\nfn cloud_layer_interval(origin: vec3<f32>, direction: vec3<f32>, maxDistance: f32) -> vec2<f32> {\n if (abs(direction.y) < 0.000001) {\n if (origin.y < cloud.layer.x || origin.y > cloud.layer.x + cloud.layer.y) {\n return vec2<f32>(1.0, 0.0);\n }\n return vec2<f32>(0.0, maxDistance);\n }\n let lower = (cloud.layer.x - origin.y) / direction.y;\n let upper = (cloud.layer.x + cloud.layer.y - origin.y) / direction.y;\n return vec2<f32>(\n max(0.0, min(lower, upper)),\n min(maxDistance, max(lower, upper)),\n );\n}\n\nfn cloud_solar_transmittance(position: vec3<f32>) -> f32 {\n let sunDirection = normalize(-view.lightDir);\n let solarDistance = select(cloud.field.z, cloud.shadowProjection.x, cloud.shadowProjection.x > 0.0);\n let interval = cloud_layer_interval(position, sunDirection, solarDistance);\n if (interval.y <= interval.x) { return 1.0; }\n let steps = min(cloud.integration.z, 64u);\n let stepLength = (interval.y - interval.x) / max(1.0, f32(steps));\n var opticalDepth = 0.0;\n let solarJitter = cloud_solar_jitter(position);\n for (var index = 0u; index < 64u; index = index + 1u) {\n if (index >= steps) { break; }\n let samplePosition = position + sunDirection * (interval.x + (f32(index) + solarJitter) * stepLength);\n opticalDepth = opticalDepth + cloud_density(samplePosition) * 0.03 * stepLength;\n }\n return exp(-max(opticalDepth, 0.0));\n}\n\nfn cloud_henyey_greenstein(cosTheta: f32, g: f32) -> f32 {\n let denominator = max(0.0001, 1.0 + g * g - 2.0 * g * cosTheta);\n return (1.0 - g * g) / (denominator * sqrt(denominator)) * 0.0795774715;\n}\n\nfn cloud_phase(cosTheta: f32) -> f32 {\n // A bounded dual lobe gives a readable forward highlight and a small\n // back-scatter rim without another solar march.\n return cloud_henyey_greenstein(cosTheta, 0.55) * 0.8 +\n cloud_henyey_greenstein(cosTheta, -0.2) * 0.2;\n}\n\n// Reuse the same remaining-path optical depth for two bounded scattering\n// orders. This adds arithmetic only; no extra density or light-ray samples.\nfn cloud_incident_light(solarT: f32, cosTheta: f32, height: f32) -> vec3<f32> {\n let direct = solarT * cloud_phase(cosTheta);\n let multiple = (0.5 * pow(solarT, 0.5) + 0.25 * pow(solarT, 0.25)) * 0.0795774715;\n let skyFill = mix(0.025, 0.075, smoothstep(0.1, 0.85, height));\n // Match volume-integrate: the shared DirectionalLight value uses the\n // unnormalized phase convention. Convert the normalized HG basis once.\n return cloud.sunRadiance.xyz *\n (vec3<f32>((direct + multiple) * 12.5663706144) + vec3<f32>(0.72, 0.84, 1.0) * skyFill);\n}\n\nfn cloud_ray_jitter(pixel: vec2<f32>) -> f32 {\n // A stable per-pixel offset removes view-step contour bands without adding\n // a frame-varying noise source that would fight temporal reprojection.\n return fract(sin(dot(pixel, vec2<f32>(12.9898, 78.233))) * 43758.5453);\n}\n\nfn cloud_solar_jitter(position: vec3<f32>) -> f32 {\n // Dither the light-column samples in world space so low shadow-step\n // profiles do not paint parallel contour bands across cloud bases.\n return fract(sin(dot(position.xz, vec2<f32>(19.193, 47.117))) * 15731.743);\n}\n\nfn reconstruct_world(uv: vec2<f32>, depth: f32) -> vec3<f32> {\n let ndc = vec4<f32>(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth, 1.0);\n let world = view.inverseViewProj * ndc;\n return world.xyz / max(abs(world.w), 0.00001);\n}\n\n@vertex\nfn vs_main(@builtin(vertex_index) index: u32) -> CloudViewOut {\n var positions = array<vec2<f32>, 3>(\n vec2<f32>(-1.0, -1.0),\n vec2<f32>(3.0, -1.0),\n vec2<f32>(-1.0, 3.0),\n );\n var output: CloudViewOut;\n output.position = vec4<f32>(positions[index], 0.0, 1.0);\n // WebGPU clip-space Y grows upward while sampled framebuffer rows and the\n // depth texture use a top-left origin. Keep this one top-left UV for color,\n // depth and inverse-projection reconstruction; flipping only the final\n // color would leave the camera ray and depth cut mirrored.\n output.uv = vec2<f32>(\n positions[index].x * 0.5 + 0.5,\n 1.0 - (positions[index].y * 0.5 + 0.5),\n );\n return output;\n}\n\n@fragment\nfn fs_main(input: CloudViewOut) -> @location(0) vec4<f32> {\n // The shadow producer uses this same production density/cache contract but\n // rasterizes a camera-independent, texel-snapped light-space map. It does\n // not consume scene colour; the generic fullscreen host still supplies the\n // input binding to keep the feature binding ABI single and validated.\n if (cloud.integration.w == 1u) {\n let projection = cloud.shadowProjection;\n if (projection.y < 0.5 || projection.z > 0.5 || projection.x <= 0.0) {\n return vec4<f32>(1.0);\n }\n let offset = (input.uv - vec2<f32>(0.5)) * projection.x;\n let receiver = cloud.shadowOrigin.xyz +\n cloud.shadowRight.xyz * offset.x + cloud.shadowUp.xyz * offset.y;\n let sunDirection = normalize(-view.lightDir);\n let interval = cloud_layer_interval(receiver, sunDirection, projection.x);\n if (interval.y <= interval.x) { return vec4<f32>(1.0); }\n let steps = min(cloud.integration.z, 64u);\n let stepLength = (interval.y - interval.x) / max(1.0, f32(steps));\n var opticalDepth = 0.0;\n var firstHeight = 1.0;\n var lastHeight = 0.0;\n var occupied = false;\n let solarJitter = cloud_solar_jitter(receiver);\n for (var index = 0u; index < 64u; index = index + 1u) {\n if (index >= steps) { break; }\n let samplePosition = receiver + sunDirection *\n (interval.x + (f32(index) + solarJitter) * stepLength);\n let localDensity = cloud_density(samplePosition);\n opticalDepth = opticalDepth + localDensity * 0.03 * stepLength;\n if (localDensity > 0.0001) {\n let sampleHeight = clamp(\n (samplePosition.y - cloud.layer.x) / max(0.0001, cloud.layer.y),\n 0.0,\n 1.0,\n );\n if (!occupied) { firstHeight = sampleHeight; }\n lastHeight = sampleHeight;\n occupied = true;\n }\n }\n let transmittance = exp(-max(opticalDepth, 0.0));\n // R stores full-column transmittance. G/B preserve the first/last\n // occupied normalized layer heights so interior samples can estimate the\n // remaining segment without pretending optical depth is uniform through\n // the whole layer. A stores the integrated optical depth and distinguishes\n // an empty projected column from a valid, nearly-clear one.\n return vec4<f32>(\n transmittance,\n select(1.0, firstHeight, occupied),\n select(0.0, lastHeight, occupied),\n select(0.0, opticalDepth, occupied),\n );\n }\n // The shadow producer branch above is per-fragment (its light-space\n // interval depends on the rasterized receiver), so derivative-based\n // sampling here would violate WGSL uniform-control-flow rules. The cloud\n // scene path already owns an explicit level-0 sampler contract.\n // Keep point semantics for the current full-resolution scene path. The\n // half-resolution transport contract is introduced with its own resolve\n // shader so this path retains the established depth convention.\n let depthPixel = vec2<i32>(input.position.xy);\n let depth = textureLoad(sceneDepth, depthPixel, 0);\n let farWorld = reconstruct_world(input.uv, 1.0);\n let sceneWorld = reconstruct_world(input.uv, clamp(depth, 0.0, 1.0));\n let ray = normalize(farWorld - view.cameraPos);\n let rayJitter = cloud_ray_jitter(input.position.xy);\n let sceneDistance = select(cloud.field.z, length(sceneWorld - view.cameraPos), depth < 0.999999);\n let interval = cloud_layer_interval(view.cameraPos, ray, sceneDistance);\n\n // The depth producer shares the same camera ray, interval and density\n // contract as the display path. It writes a cloud representative world\n // position and a validity bit so history remains comparable after camera\n // translation or rotation and never uses an opaque scene distance as a\n // proxy for a participating medium.\n if (cloud.integration.w == 2u) {\n if (interval.y <= interval.x) { return vec4<f32>(sceneWorld, 0.0); }\n let depthSteps = min(cloud.integration.y, 64u);\n let depthStepLength = (interval.y - interval.x) / max(1.0, f32(depthSteps));\n var depthTransmittance = 1.0;\n var weightedDistance = 0.0;\n var opticalWeight = 0.0;\n for (var depthIndex = 0u; depthIndex < 64u; depthIndex = depthIndex + 1u) {\n if (depthIndex >= depthSteps) { break; }\n let sampleDistance = interval.x + (f32(depthIndex) + rayJitter) * depthStepLength;\n let samplePosition = view.cameraPos + ray * sampleDistance;\n let localDensity = cloud_density(samplePosition);\n let localTransmittance = exp(-max(localDensity * 0.03 * depthStepLength, 0.0));\n let weight = depthTransmittance * (1.0 - localTransmittance);\n weightedDistance = weightedDistance + sampleDistance * weight;\n opticalWeight = opticalWeight + weight;\n depthTransmittance = depthTransmittance * localTransmittance;\n }\n let hasCloud = opticalWeight > 0.00001;\n let representativeDistance = select(sceneDistance, weightedDistance / opticalWeight, hasCloud);\n let representativeWorld = view.cameraPos + ray * representativeDistance;\n return vec4<f32>(representativeWorld, select(0.0, 1.0, hasCloud));\n }\n\n let scene = textureSampleLevel(sceneColor, sceneSampler, input.uv, 0.0);\n if (interval.y <= interval.x) { return scene; }\n let steps = min(cloud.integration.y, 64u);\n let stepLength = (interval.y - interval.x) / max(1.0, f32(steps));\n var transmittance = 1.0;\n var scattering = vec3<f32>(0.0);\n var weightedDistance = 0.0;\n var opticalWeight = 0.0;\n let sunDirection = normalize(-view.lightDir);\n for (var index = 0u; index < 64u; index = index + 1u) {\n if (index >= steps) { break; }\n let distance = interval.x + (f32(index) + rayJitter) * stepLength;\n let samplePosition = view.cameraPos + ray * distance;\n let localDensity = cloud_density(samplePosition);\n let extinction = localDensity * 0.03 * stepLength;\n let localTransmittance = exp(-max(extinction, 0.0));\n let depthWeight = transmittance * (1.0 - localTransmittance);\n weightedDistance = weightedDistance + distance * depthWeight;\n opticalWeight = opticalWeight + depthWeight;\n // Empty samples are common in the separated-cloud field. Avoid a nested\n // solar march for them and stop once the camera path is effectively opaque.\n if (localDensity > 0.0001 && transmittance > 0.01) {\n let solarTransmittance = cloud_solar_transmittance(samplePosition);\n let cloudHeight = clamp(\n (samplePosition.y - cloud.layer.x) / max(0.0001, cloud.layer.y), 0.0, 1.0,\n );\n scattering = scattering + cloud_incident_light(\n solarTransmittance, dot(ray, sunDirection), cloudHeight,\n ) * depthWeight;\n }\n transmittance = transmittance * localTransmittance;\n if (transmittance < 0.01) { break; }\n }\n let hasCloud = opticalWeight > 0.00001;\n let representativeDistance = select(sceneDistance, weightedDistance / opticalWeight, hasCloud);\n let representativeWorld = view.cameraPos + ray * representativeDistance;\n // Wind is an advective coordinate change. Reproject the cloud point into\n // the previous frame's world before applying the previous camera matrix;\n // authored field values remain immutable.\n let windDelta = cloud.wind.xyz * max(0.0, cloud.field.y - cloud.temporal.w);\n // cloud_density evaluates the authored field at position + wind*time.\n // Keeping the same field sample in the previous frame therefore advances\n // the previous world point by the elapsed wind delta as well.\n let previousCloudWorld = representativeWorld + windDelta;\n let previousCloudClip = view.temporalPreviousViewProj * vec4<f32>(previousCloudWorld, 1.0);\n let previousCloudNdc = previousCloudClip.xyz / max(abs(previousCloudClip.w), 0.00001);\n let previousCloudUv = vec2<f32>(\n previousCloudNdc.x * 0.5 + 0.5,\n 1.0 - (previousCloudNdc.y * 0.5 + 0.5),\n );\n let cloudInBounds = all(previousCloudUv >= vec2<f32>(0.0)) && all(previousCloudUv <= vec2<f32>(1.0));\n let cloudHistoryUv = clamp(previousCloudUv, vec2<f32>(0.0), vec2<f32>(1.0));\n let cloudHistoryDepthSample = textureSampleLevel(previousDepth, depthSampler, cloudHistoryUv, 0.0);\n let cloudHistoryWorld = cloudHistoryDepthSample.xyz;\n let previous = textureSampleLevel(previousRadiance, sceneSampler, cloudHistoryUv, 0.0);\n let previousTransport = textureSampleLevel(\n previousTransmittance,\n sceneSampler,\n cloudHistoryUv,\n 0.0,\n );\n let worldTolerance = max(0.05, length(previousCloudWorld - view.cameraPos) * 0.02);\n let depthAccepted = cloudHistoryDepthSample.a > 0.5 &&\n distance(cloudHistoryWorld, previousCloudWorld) <= worldTolerance;\n let accepted =\n hasCloud && cloud.temporal.x > 0.5 && cloud.temporal.y < 0.5 && cloudInBounds &&\n depthAccepted && previousTransport.a > 0.0;\n let historyWeight = select(0.0, clamp(cloud.temporal.z, 0.0, 0.95), accepted);\n let cloudRadiance = mix(scattering, previous.rgb, historyWeight);\n // Preserve scene-linear HDR output for the downstream material lane. The\n // history owner receives cloudRadiance separately through the history pass.\n return vec4<f32>(scene.rgb * transmittance + cloudRadiance, transmittance);\n}\n\n// The light-space shadow producer already paid for a bounded column integral.\n// Reuse that texel for camera samples instead of starting a full solar march\n// for every occupied camera step. Outside the occupied interval the cached\n// full-column transmittance is exact for the projected column. Inside the\n// interval, the cache supplies the stable projection/bounds and a short\n// residual march resolves the non-uniform density that a linear optical-depth\n// interpolation would lose. Invalid/out-of-range projections retain the\n// analytic path as an explicit fallback.\nfn cloud_solar_cached_transmittance(position: vec3<f32>) -> f32 {\n let projection = cloud.shadowProjection;\n if (projection.y < 0.5 || projection.x <= 0.0) {\n return cloud_solar_transmittance(position);\n }\n let relative = position - cloud.shadowOrigin.xyz;\n let uv = vec2<f32>(\n dot(relative, cloud.shadowRight.xyz) / projection.x + 0.5,\n dot(relative, cloud.shadowUp.xyz) / projection.x + 0.5,\n );\n if (any(uv < vec2<f32>(0.0)) || any(uv > vec2<f32>(1.0))) {\n return cloud_solar_transmittance(position);\n }\n let cachedShadow = textureSampleLevel(previousRadiance, sceneSampler, uv, 0.0);\n let cachedOpticalDepth = max(cachedShadow.a, 0.0);\n let layerHeight = clamp(\n (position.y - cloud.layer.x) / max(0.0001, cloud.layer.y),\n 0.0,\n 1.0,\n );\n if (cachedOpticalDepth <= 0.0001) { return 1.0; }\n let firstHeight = clamp(cachedShadow.g, 0.0, 1.0);\n let lastHeight = clamp(cachedShadow.b, 0.0, 1.0);\n let occupiedMin = min(firstHeight, lastHeight);\n let occupiedMax = max(firstHeight, lastHeight);\n let sunDirection = normalize(-view.lightDir);\n // G/B are ordered along the light ray, not necessarily bottom-to-top. Only\n // use the cached full/empty fast paths when the light has enough vertical\n // component to make that ordering meaningful; grazing light stays on the\n // residual path so a low-sun column cannot invert the cache bounds.\n if (abs(sunDirection.y) > 0.2) {\n if (sunDirection.y > 0.0) {\n if (layerHeight <= occupiedMin) { return exp(-cachedOpticalDepth); }\n if (layerHeight >= occupiedMax) { return 1.0; }\n } else {\n if (layerHeight >= occupiedMax) { return exp(-cachedOpticalDepth); }\n if (layerHeight <= occupiedMin) { return 1.0; }\n }\n }\n\n // A short residual march is only paid for samples inside the projected\n // cloud span. Vertical light uses twelve taps, oblique light sixteen, and\n // grazing light up to thirty-two because the same layer projects across a\n // much longer solar segment. This removes the large error caused by\n // assuming optical depth is uniform between the first and last occupied\n // heights while keeping the normal camera path far below a second full\n // solar integration.\n let solarDistance = select(cloud.field.z, projection.x, projection.x > 0.0);\n let interval = cloud_layer_interval(position, sunDirection, solarDistance);\n if (interval.y <= interval.x) { return 1.0; }\n let absLightY = abs(sunDirection.y);\n let directionBudget = select(\n 32u,\n select(16u, 12u, absLightY > 0.5),\n absLightY > 0.2,\n );\n let residualSteps = min(directionBudget, max(8u, cloud.integration.z * 3u));\n let stepLength = (interval.y - interval.x) / max(1.0, f32(residualSteps));\n var opticalDepth = 0.0;\n let solarJitter = cloud_solar_jitter(position);\n for (var index = 0u; index < 32u; index = index + 1u) {\n if (index >= residualSteps) { break; }\n let samplePosition = position + sunDirection *\n (interval.x + (f32(index) + solarJitter) * stepLength);\n opticalDepth = opticalDepth + cloud_density(samplePosition) *\n 0.03 * stepLength;\n }\n return exp(-max(opticalDepth, 0.0));\n}\n\nstruct CloudTransportOutput {\n @location(0) radiance: vec4<f32>,\n @location(1) transmittance: vec4<f32>,\n @location(2) depth: vec4<f32>,\n};\n\n// One half-resolution transport pass writes all three reusable cloud fields.\n// The full-resolution resolve below reconstructs colour and applies temporal\n// history; no second full raymarch exists solely to obtain cloud depth.\n@fragment\nfn fs_transport(input: CloudViewOut) -> CloudTransportOutput {\n let depth = textureSampleLevel(sceneDepth, depthSampler, input.uv, 0u);\n let farWorld = reconstruct_world(input.uv, 1.0);\n let sceneWorld = reconstruct_world(input.uv, clamp(depth, 0.0, 1.0));\n let ray = normalize(farWorld - view.cameraPos);\n let rayJitter = cloud_ray_jitter(input.position.xy);\n let sceneDistance = select(cloud.field.z, length(sceneWorld - view.cameraPos), depth < 0.999999);\n let interval = cloud_layer_interval(view.cameraPos, ray, sceneDistance);\n var output: CloudTransportOutput;\n output.radiance = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n output.transmittance = vec4<f32>(1.0, 1.0, 1.0, 1.0);\n output.depth = vec4<f32>(sceneWorld, 0.0);\n if (interval.y <= interval.x) { return output; }\n\n let steps = min(cloud.integration.y, 64u);\n let stepLength = (interval.y - interval.x) / max(1.0, f32(steps));\n var transmittance = 1.0;\n var scattering = vec3<f32>(0.0);\n var weightedDistance = 0.0;\n var opticalWeight = 0.0;\n let sunDirection = normalize(-view.lightDir);\n for (var index = 0u; index < 64u; index = index + 1u) {\n if (index >= steps) { break; }\n let distance = interval.x + (f32(index) + rayJitter) * stepLength;\n let samplePosition = view.cameraPos + ray * distance;\n let localDensity = cloud_density(samplePosition);\n let extinction = localDensity * 0.03 * stepLength;\n let localTransmittance = exp(-max(extinction, 0.0));\n let depthWeight = transmittance * (1.0 - localTransmittance);\n weightedDistance = weightedDistance + distance * depthWeight;\n opticalWeight = opticalWeight + depthWeight;\n if (localDensity > 0.0001 && transmittance > 0.01) {\n let solarTransmittance = cloud_solar_cached_transmittance(samplePosition);\n let cloudHeight = clamp(\n (samplePosition.y - cloud.layer.x) / max(0.0001, cloud.layer.y), 0.0, 1.0,\n );\n scattering = scattering + cloud_incident_light(\n solarTransmittance, dot(ray, sunDirection), cloudHeight,\n ) * depthWeight;\n }\n transmittance = transmittance * localTransmittance;\n if (transmittance < 0.01) { break; }\n }\n let hasCloud = opticalWeight > 0.00001;\n let representativeDistance = select(sceneDistance, weightedDistance / max(opticalWeight, 0.00001), hasCloud);\n output.radiance = vec4<f32>(scattering, select(0.0, 1.0, hasCloud));\n output.transmittance = vec4<f32>(transmittance, transmittance, transmittance, 1.0);\n output.depth = vec4<f32>(view.cameraPos + ray * representativeDistance, select(0.0, 1.0, hasCloud));\n return output;\n}\n";
|
|
25
|
+
/** Analytic fallback for adapters without a projected cloud-shadow target. */
|
|
26
|
+
export declare const CLOUD_TRANSPORT_ANALYTIC_FULLSCREEN_WGSL: string;
|
|
27
|
+
/**
|
|
28
|
+
* History writer. It is a separate MRT pass so the cloud composite remains a
|
|
29
|
+
* normal scene-linear color producer while the renderer owns the ping-pong
|
|
30
|
+
* radiance/transmittance/depth attachments and advances them only on submit.
|
|
31
|
+
*/
|
|
32
|
+
export declare const CLOUD_HISTORY_FULLSCREEN_WGSL = "\nstruct CloudCameraView {\n worldViewProj: mat4x4<f32>,\n lightDir: vec3<f32>,\n lightColor: vec3<f32>,\n cameraPos: vec3<f32>,\n lightViewProj_A: mat4x4<f32>,\n inverseViewProj: mat4x4<f32>,\n lightViewProj_B: mat4x4<f32>,\n lightViewProj_C: mat4x4<f32>,\n lightViewProj_D: mat4x4<f32>,\n splitPlanes: array<vec4<f32>, 4>,\n cascadeCount: f32,\n cascadeBlend: f32,\n depthBias: f32,\n normalBias: f32,\n directionalShadowFilter: vec4<f32>,\n spotLightViewProj: array<mat4x4<f32>, 4>,\n temporalCurrentViewProj: mat4x4<f32>,\n temporalPreviousViewProj: mat4x4<f32>,\n temporalProjection: vec4<f32>,\n temporalPreviousCameraPos: vec4<f32>,\n ssrParams: vec4<f32>,\n};\n\nstruct CloudHistoryParams {\n temporal: vec4<f32>, // valid, reset, historyWeight, reserved\n windTime: vec4<f32>, // wind.xyz, elapsed seconds since previous submit\n};\n\n@group(0) @binding(0) var<uniform> view: CloudCameraView;\n@group(1) @binding(0) var currentRadiance: texture_2d<f32>;\n@group(1) @binding(1) var sceneSampler: sampler;\n@group(1) @binding(2) var<uniform> params: CloudHistoryParams;\n@group(1) @binding(3) var sceneDepth: texture_depth_2d;\n@group(1) @binding(4) var depthSampler: sampler;\n@group(1) @binding(5) var currentCloudDepth: texture_2d<f32>;\n@group(1) @binding(6) var sceneBackground: texture_2d<f32>;\n\nstruct CloudHistoryInput {\n @builtin(position) position: vec4<f32>,\n @location(0) uv: vec2<f32>,\n};\n\nstruct CloudHistoryOutput {\n @location(0) radiance: vec4<f32>,\n @location(1) transmittance: vec4<f32>,\n};\n\nfn reconstruct_world(uv: vec2<f32>, depth: f32) -> vec3<f32> {\n let ndc = vec4<f32>(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth, 1.0);\n let world = view.inverseViewProj * ndc;\n return world.xyz / max(abs(world.w), 0.00001);\n}\n\n@vertex\nfn vs_main(@builtin(vertex_index) index: u32) -> CloudHistoryInput {\n var positions = array<vec2<f32>, 3>(\n vec2<f32>(-1.0, -1.0),\n vec2<f32>(3.0, -1.0),\n vec2<f32>(-1.0, 3.0),\n );\n var output: CloudHistoryInput;\n output.position = vec4<f32>(positions[index], 0.0, 1.0);\n output.uv = vec2<f32>(\n positions[index].x * 0.5 + 0.5,\n 1.0 - (positions[index].y * 0.5 + 0.5),\n );\n return output;\n}\n\n@fragment\nfn fs_main(input: CloudHistoryInput) -> CloudHistoryOutput {\n let current = textureSampleLevel(currentRadiance, sceneSampler, input.uv, 0.0);\n let background = textureSampleLevel(sceneBackground, sceneSampler, input.uv, 0.0);\n let cloudDepth = textureSampleLevel(currentCloudDepth, depthSampler, input.uv, 0.0);\n let currentValid = cloudDepth.a > 0.5;\n // currentRadiance is the scene-linear cloud composite. Remove the\n // unchanged scene background before writing history so the persistent owner\n // stores raw cloud radiance only. The view pass is the sole temporal blend\n // owner; blending here as well would accumulate history twice per frame.\n let currentCloudRadiance = max(current.rgb - background.rgb * current.a, vec3<f32>(0.0));\n let currentTransmittance = vec4<f32>(current.a, current.a, current.a, 1.0);\n var output: CloudHistoryOutput;\n output.radiance = vec4<f32>(currentCloudRadiance, select(0.0, 1.0, currentValid));\n output.transmittance = currentTransmittance;\n return output;\n}\n";
|
|
33
|
+
/** Full-resolution resolve for the half-resolution transport MRT. */
|
|
34
|
+
export declare const CLOUD_RESOLVE_FULLSCREEN_WGSL = "\nstruct CloudCameraView {\n worldViewProj: mat4x4<f32>,\n lightDir: vec3<f32>,\n lightColor: vec3<f32>,\n cameraPos: vec3<f32>,\n lightViewProj_A: mat4x4<f32>,\n inverseViewProj: mat4x4<f32>,\n lightViewProj_B: mat4x4<f32>,\n lightViewProj_C: mat4x4<f32>,\n lightViewProj_D: mat4x4<f32>,\n splitPlanes: array<vec4<f32>, 4>,\n cascadeCount: f32,\n cascadeBlend: f32,\n depthBias: f32,\n normalBias: f32,\n directionalShadowFilter: vec4<f32>,\n spotLightViewProj: array<mat4x4<f32>, 4>,\n temporalCurrentViewProj: mat4x4<f32>,\n temporalPreviousViewProj: mat4x4<f32>,\n temporalProjection: vec4<f32>,\n temporalPreviousCameraPos: vec4<f32>,\n ssrParams: vec4<f32>,\n};\n\nstruct CloudResolveParams {\n layer: vec4<f32>,\n field: vec4<f32>,\n wind: vec4<f32>,\n sunDirection: vec4<f32>,\n sunRadiance: vec4<f32>,\n integration: vec4<u32>,\n shadowOrigin: vec4<f32>,\n shadowRight: vec4<f32>,\n shadowUp: vec4<f32>,\n shadowProjection: vec4<f32>,\n temporal: vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> view: CloudCameraView;\n@group(1) @binding(0) var sceneColor: texture_2d<f32>;\n@group(1) @binding(1) var sceneSampler: sampler;\n@group(1) @binding(2) var<uniform> cloud: CloudResolveParams;\n@group(1) @binding(3) var sceneDepth: texture_depth_2d;\n@group(1) @binding(4) var depthSampler: sampler;\n@group(1) @binding(5) var currentRadiance: texture_2d<f32>;\n@group(1) @binding(6) var currentTransmittance: texture_2d<f32>;\n@group(1) @binding(7) var currentDepth: texture_2d<f32>;\n@group(1) @binding(8) var previousRadiance: texture_2d<f32>;\n@group(1) @binding(9) var previousTransmittance: texture_2d<f32>;\n@group(1) @binding(10) var previousDepth: texture_2d<f32>;\n\nstruct CloudResolveInput {\n @builtin(position) position: vec4<f32>,\n @location(0) uv: vec2<f32>,\n};\n\nfn reconstruct_world(uv: vec2<f32>, depth: f32) -> vec3<f32> {\n let ndc = vec4<f32>(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth, 1.0);\n let world = view.inverseViewProj * ndc;\n return world.xyz / max(abs(world.w), 0.00001);\n}\n\n@vertex\nfn vs_main(@builtin(vertex_index) index: u32) -> CloudResolveInput {\n var positions = array<vec2<f32>, 3>(\n vec2<f32>(-1.0, -1.0),\n vec2<f32>(3.0, -1.0),\n vec2<f32>(-1.0, 3.0),\n );\n var output: CloudResolveInput;\n output.position = vec4<f32>(positions[index], 0.0, 1.0);\n output.uv = vec2<f32>(\n positions[index].x * 0.5 + 0.5,\n 1.0 - (positions[index].y * 0.5 + 0.5),\n );\n return output;\n}\n\n@fragment\nfn fs_main(input: CloudResolveInput) -> @location(0) vec4<f32> {\n let scene = textureSampleLevel(sceneColor, sceneSampler, input.uv, 0.0);\n // The resolve owns the full-resolution foreground test. Reject the current\n // low-resolution transport before any history or radiance is composited;\n // gating history alone still lets a newly sampled cloud bleed over geometry.\n let sceneDepthValue = textureLoad(sceneDepth, vec2<i32>(input.position.xy), 0);\n let sceneWorld = reconstruct_world(input.uv, clamp(sceneDepthValue, 0.0, 1.0));\n let sceneDistance = length(sceneWorld - view.cameraPos);\n let transport = textureSampleLevel(currentRadiance, sceneSampler, input.uv, 0.0);\n let transportT = textureSampleLevel(currentTransmittance, sceneSampler, input.uv, 0.0);\n // The transport depth is an rgba16float color attachment, not the scene's\n // depth texture. Sample it with the filtering color sampler; binding the\n // non-filtering scene-depth sampler here makes the shader contract depend on\n // an incompatible sampler/texture pair on strict WebGPU implementations.\n let cloudDepth = textureSampleLevel(currentDepth, sceneSampler, input.uv, 0.0);\n let currentValid = transport.a > 0.5 && cloudDepth.a > 0.5;\n let cloudDistance = length(cloudDepth.xyz - view.cameraPos);\n let foregroundAccepted = sceneDepthValue >= 0.999999 ||\n cloudDistance <= sceneDistance + max(0.05, sceneDistance * 0.01);\n if (!currentValid || !foregroundAccepted) { return scene; }\n\n let previousClip = view.temporalPreviousViewProj * vec4<f32>(\n cloudDepth.xyz + cloud.wind.xyz * max(0.0, cloud.field.y - cloud.temporal.w),\n 1.0,\n );\n let previousNdc = previousClip.xyz / max(abs(previousClip.w), 0.00001);\n let previousUv = vec2<f32>(\n previousNdc.x * 0.5 + 0.5,\n 1.0 - (previousNdc.y * 0.5 + 0.5),\n );\n let inBounds = all(previousUv >= vec2<f32>(0.0)) && all(previousUv <= vec2<f32>(1.0));\n let historyUv = clamp(previousUv, vec2<f32>(0.0), vec2<f32>(1.0));\n let historyDepth = textureSampleLevel(previousDepth, sceneSampler, historyUv, 0.0);\n let previous = textureSampleLevel(previousRadiance, sceneSampler, historyUv, 0.0);\n let previousTransport = textureSampleLevel(previousTransmittance, sceneSampler, historyUv, 0.0);\n let dims = vec2<f32>(textureDimensions(previousRadiance));\n let texel = 1.0 / max(dims, vec2<f32>(1.0));\n // Clamp reprojected history against the current low-resolution transport\n // neighborhood. Using only previous-frame neighbors (and including the\n // previous center in both extrema) is a no-op and lets stale bright cloud\n // pixels leak across a newly exposed edge.\n let c0 = transport.rgb;\n let c1 = textureSampleLevel(currentRadiance, sceneSampler, clamp(input.uv + vec2<f32>(-texel.x, 0.0), vec2<f32>(0.0), vec2<f32>(1.0)), 0.0).rgb;\n let c2 = textureSampleLevel(currentRadiance, sceneSampler, clamp(input.uv + vec2<f32>(texel.x, 0.0), vec2<f32>(0.0), vec2<f32>(1.0)), 0.0).rgb;\n let c3 = textureSampleLevel(currentRadiance, sceneSampler, clamp(input.uv + vec2<f32>(0.0, -texel.y), vec2<f32>(0.0), vec2<f32>(1.0)), 0.0).rgb;\n let c4 = textureSampleLevel(currentRadiance, sceneSampler, clamp(input.uv + vec2<f32>(0.0, texel.y), vec2<f32>(0.0), vec2<f32>(1.0)), 0.0).rgb;\n let minRadiance = min(c0, min(min(c1, c2), min(c3, c4)));\n let maxRadiance = max(c0, max(max(c1, c2), max(c3, c4)));\n let t1 = textureSampleLevel(currentTransmittance, sceneSampler, clamp(input.uv + vec2<f32>(-texel.x, 0.0), vec2<f32>(0.0), vec2<f32>(1.0)), 0.0).r;\n let t2 = textureSampleLevel(currentTransmittance, sceneSampler, clamp(input.uv + vec2<f32>(texel.x, 0.0), vec2<f32>(0.0), vec2<f32>(1.0)), 0.0).r;\n let t3 = textureSampleLevel(currentTransmittance, sceneSampler, clamp(input.uv + vec2<f32>(0.0, -texel.y), vec2<f32>(0.0), vec2<f32>(1.0)), 0.0).r;\n let t4 = textureSampleLevel(currentTransmittance, sceneSampler, clamp(input.uv + vec2<f32>(0.0, texel.y), vec2<f32>(0.0), vec2<f32>(1.0)), 0.0).r;\n let minTransport = min(transportT.r, min(min(t1, t2), min(t3, t4)));\n let maxTransport = max(transportT.r, max(max(t1, t2), max(t3, t4)));\n let worldTolerance = max(0.05, distance(cloudDepth.xyz, view.cameraPos) * 0.02);\n let depthAccepted = historyDepth.a > 0.5 && distance(historyDepth.xyz, cloudDepth.xyz) <= worldTolerance;\n let accepted = cloud.temporal.x > 0.5 && cloud.temporal.y < 0.5 && inBounds && depthAccepted;\n let historyWeight = select(0.0, clamp(cloud.temporal.z, 0.0, 0.92), accepted);\n let stableHistory = clamp(previous.rgb, minRadiance, maxRadiance);\n let stableTransport = clamp(previousTransport.r, minTransport, maxTransport);\n let radiance = mix(transport.rgb, stableHistory, historyWeight);\n let transmittance = mix(transportT.r, stableTransport, historyWeight);\n return vec4<f32>(scene.rgb * transmittance + radiance, transmittance);\n}\n";
|
|
35
|
+
export interface CloudLayerFeatureFrame {
|
|
36
|
+
readonly params: ValidatedCloudLayer | undefined;
|
|
37
|
+
readonly cache: CloudDensityCache | undefined;
|
|
38
|
+
readonly sourceKey: string | undefined;
|
|
39
|
+
readonly timeSeconds: number;
|
|
40
|
+
readonly sunDirection: readonly [number, number, number] | undefined;
|
|
41
|
+
readonly sunRadiance: readonly [number, number, number] | undefined;
|
|
42
|
+
readonly shadowProjection: CloudShadowProjection | undefined;
|
|
43
|
+
readonly temporal: CloudTemporalFrame | undefined;
|
|
44
|
+
readonly generation: number;
|
|
45
|
+
readonly inspection: CloudLayerInspection;
|
|
46
|
+
}
|
|
47
|
+
export interface CloudLayerFeatureOptions {
|
|
48
|
+
/** Disable the renderer-owned CloudLayer projection while retaining the component. */
|
|
49
|
+
readonly enabled?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Renderer-installed cloud producer. Authoring is read once from World at
|
|
53
|
+
* extraction; resource preparation, graph ordering and retirement stay with
|
|
54
|
+
* RenderFeatureHost.
|
|
55
|
+
*/
|
|
56
|
+
export declare function createCloudLayerFeature(options?: CloudLayerFeatureOptions): RenderFeature<CloudLayerFeatureFrame>;
|
|
57
|
+
//# sourceMappingURL=feature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../../src/cloud/feature.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAA+B,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAAgD,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACjG,OAAO,EACL,KAAK,oBAAoB,EAG1B,MAAM,cAAc,CAAC;AACtB,OAAO,EAML,KAAK,mBAAmB,EACzB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,KAAK,qBAAqB,EAA+B,MAAM,UAAU,CAAC;AACnF,OAAO,EAEL,KAAK,kBAAkB,EAGxB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,4BAA4B,wBAAwB,CAAC;AAClE,eAAO,MAAM,2BAA2B,wBAAwB,CAAC;AACjE,eAAO,MAAM,4BAA4B,iCAAiC,CAAC;AAC3E,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AACrE,eAAO,MAAM,0BAA0B,+BAA+B,CAAC;AAWvE,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAa3C,8EAA8E;AAC9E,eAAO,MAAM,0BAA0B,8bAUtC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,45gCA4uBtC,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,+BAA+B,45gCAA6B,CAAC;AAC1E,8EAA8E;AAC9E,eAAO,MAAM,wCAAwC,QAGpD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,ysGAwFzC,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,6BAA6B,+uOAgJzC,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACrE,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACpE,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAC7D,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;CAC3C;AAED,MAAM,WAAW,wBAAwB;IACvC,sFAAsF;IACtF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAqgBD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B,GACrC,aAAa,CAAC,sBAAsB,CAAC,CAkMvC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RenderFeatureFrameContext, RenderFeatureViewContext } from '../features/types';
|
|
2
|
+
import type { RenderResourceScope } from '../publication/resource-scope';
|
|
3
|
+
import type { CameraSnapshot } from '../render-contract';
|
|
4
|
+
import type { ExtractedCloudLayer } from './extract';
|
|
5
|
+
/** Project the stable camera/world facts consumed by the cloud feature owner. */
|
|
6
|
+
export declare function createCloudFeatureView(camera: CameraSnapshot | undefined, worlds: readonly RenderResourceScope[], cameraOwner: number, width: number, height: number, deviceGeneration: number, cameraCut: boolean, recovery: boolean): RenderFeatureViewContext | undefined;
|
|
7
|
+
export declare function createCloudFeatureFrameContext(cloudLayer: ExtractedCloudLayer | undefined, view: RenderFeatureViewContext | undefined): RenderFeatureFrameContext;
|
|
8
|
+
//# sourceMappingURL=frame-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-context.d.ts","sourceRoot":"","sources":["../../src/cloud/frame-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAErD,iFAAiF;AACjF,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,MAAM,EAAE,SAAS,mBAAmB,EAAE,EACtC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,SAAS,EAAE,OAAO,EAClB,QAAQ,EAAE,OAAO,GAChB,wBAAwB,GAAG,SAAS,CAgBtC;AAED,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,mBAAmB,GAAG,SAAS,EAC3C,IAAI,EAAE,wBAAwB,GAAG,SAAS,GACzC,yBAAyB,CAK3B"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { RhiCaps } from '@forgeax/engine-rhi';
|
|
2
|
+
import type { CloudLayerResourceFacts } from './resources';
|
|
3
|
+
import type { CloudTemporalResetReason } from './temporal';
|
|
4
|
+
export type CloudLayerInspectionStatus = 'off' | 'available' | 'unavailable' | 'degraded';
|
|
5
|
+
export type CloudLayerResourceStage = 'none' | 'candidate' | 'accepted' | 'lkg' | 'recovering';
|
|
6
|
+
/** Physical-adapter evidence is explicit; RHI backend labels cannot prove it. */
|
|
7
|
+
export type CloudPhysicalGpuEvidence = 'unknown' | 'available' | 'unavailable';
|
|
8
|
+
export interface CloudLayerBudgetObservation {
|
|
9
|
+
readonly measured: boolean;
|
|
10
|
+
readonly physicalGpu: CloudPhysicalGpuEvidence;
|
|
11
|
+
readonly gpuTimestamp: boolean;
|
|
12
|
+
readonly quality: 'low' | 'medium' | 'high' | undefined;
|
|
13
|
+
readonly warmupFrames: number;
|
|
14
|
+
readonly sampleFrames: number;
|
|
15
|
+
readonly p50GpuMs: number | undefined;
|
|
16
|
+
readonly p95GpuMs: number | undefined;
|
|
17
|
+
readonly coldGenerationMs: number | undefined;
|
|
18
|
+
readonly frameBudgetMs: number | undefined;
|
|
19
|
+
readonly reason?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface CloudLayerInspection {
|
|
22
|
+
readonly status: CloudLayerInspectionStatus;
|
|
23
|
+
readonly resourceStage: CloudLayerResourceStage;
|
|
24
|
+
readonly sourceKey: string | undefined;
|
|
25
|
+
readonly generation: number | undefined;
|
|
26
|
+
readonly candidateGeneration: number | undefined;
|
|
27
|
+
readonly lkgGeneration: number | undefined;
|
|
28
|
+
readonly shadowRevision: number | undefined;
|
|
29
|
+
readonly temporalResets: number;
|
|
30
|
+
readonly lastTemporalReset: CloudTemporalResetReason | undefined;
|
|
31
|
+
readonly resourceFacts: CloudLayerResourceFacts | undefined;
|
|
32
|
+
readonly capability: {
|
|
33
|
+
readonly compute: boolean;
|
|
34
|
+
readonly storageBuffer: boolean;
|
|
35
|
+
readonly rgba16floatRenderable: boolean;
|
|
36
|
+
readonly physicalGpu: CloudPhysicalGpuEvidence;
|
|
37
|
+
readonly timestampQuery: boolean;
|
|
38
|
+
};
|
|
39
|
+
readonly budget: CloudLayerBudgetObservation;
|
|
40
|
+
}
|
|
41
|
+
export interface CloudLayerInspectionInput {
|
|
42
|
+
readonly authored: boolean;
|
|
43
|
+
readonly sourceKey?: string;
|
|
44
|
+
readonly generation?: number;
|
|
45
|
+
readonly candidateGeneration?: number;
|
|
46
|
+
readonly lkgGeneration?: number;
|
|
47
|
+
readonly shadowRevision?: number;
|
|
48
|
+
readonly temporalResets?: number;
|
|
49
|
+
readonly lastTemporalReset?: CloudTemporalResetReason;
|
|
50
|
+
readonly resourceFacts?: CloudLayerResourceFacts;
|
|
51
|
+
readonly capability?: Partial<CloudLayerInspection['capability']>;
|
|
52
|
+
readonly degraded?: boolean;
|
|
53
|
+
readonly budget?: Partial<CloudLayerBudgetObservation>;
|
|
54
|
+
}
|
|
55
|
+
/** Capability and lifecycle facts are inspectable even when the lane is unavailable. */
|
|
56
|
+
export declare function inspectCloudLayer(input: CloudLayerInspectionInput): CloudLayerInspection;
|
|
57
|
+
/** Convert RHI facts without letting inspection invent physical measurements. */
|
|
58
|
+
export declare function cloudCapabilitiesFromRhi(caps: Readonly<RhiCaps>): CloudLayerInspection['capability'];
|
|
59
|
+
//# sourceMappingURL=inspection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspection.d.ts","sourceRoot":"","sources":["../../src/cloud/inspection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,MAAM,0BAA0B,GAAG,KAAK,GAAG,WAAW,GAAG,aAAa,GAAG,UAAU,CAAC;AAC1F,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,KAAK,GAAG,YAAY,CAAC;AAC/F,iFAAiF;AACjF,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,WAAW,GAAG,aAAa,CAAC;AAE/E,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,wBAAwB,CAAC;IAC/C,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACxD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,iBAAiB,EAAE,wBAAwB,GAAG,SAAS,CAAC;IACjE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,GAAG,SAAS,CAAC;IAC5D,QAAQ,CAAC,UAAU,EAAE;QACnB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;QAChC,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;QACxC,QAAQ,CAAC,WAAW,EAAE,wBAAwB,CAAC;QAC/C,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;KAClC,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAC;CAC9C;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IACtD,QAAQ,CAAC,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACjD,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAC,CAAC;IAClE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,CAAC;CACxD;AASD,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,yBAAyB,GAAG,oBAAoB,CA+CxF;AAED,iFAAiF;AACjF,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GACtB,oBAAoB,CAAC,YAAY,CAAC,CAUpC"}
|