@forgeax/engine-render 0.1.31 → 0.1.33
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 +447 -34
- package/dist/assembly/analytic-fog-registration.d.ts +3 -0
- package/dist/assembly/analytic-fog-registration.d.ts.map +1 -0
- package/dist/assembly/fxaa-registration.d.ts +4 -0
- package/dist/assembly/fxaa-registration.d.ts.map +1 -0
- package/dist/assembly/lod-observation.d.ts +4 -0
- package/dist/assembly/lod-observation.d.ts.map +1 -0
- package/dist/assembly/motion-blur-registration.d.ts +3 -0
- package/dist/assembly/motion-blur-registration.d.ts.map +1 -0
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/temporal-post-process-prewarm.d.ts +13 -0
- package/dist/assembly/temporal-post-process-prewarm.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-contract.d.ts +4 -3
- package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready.d.ts +3 -2
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/bloom-admission.d.ts +13 -0
- package/dist/bloom-admission.d.ts.map +1 -0
- package/dist/{chunk-FUUO4UND.mjs → chunk-3RTCJTH4.mjs} +12 -9
- package/dist/chunk-3RTCJTH4.mjs.map +1 -0
- package/dist/{chunk-NO4WZCAH.mjs → chunk-LEAXOMYJ.mjs} +3 -3
- package/dist/{chunk-NO4WZCAH.mjs.map → chunk-LEAXOMYJ.mjs.map} +1 -1
- package/dist/{chunk-JRBDO2KB.mjs → chunk-NB2S7ZRX.mjs} +166 -10
- package/dist/chunk-NB2S7ZRX.mjs.map +1 -0
- package/dist/{chunk-7MQ3XBSP.mjs → chunk-NNQL7CB3.mjs} +4 -4
- package/dist/{chunk-7MQ3XBSP.mjs.map → chunk-NNQL7CB3.mjs.map} +1 -1
- package/dist/{chunk-C3DDSXYK.mjs → chunk-OO3DRYK7.mjs} +171 -230
- package/dist/chunk-OO3DRYK7.mjs.map +1 -0
- package/dist/{chunk-S5UE2T5P.mjs → chunk-PLAXOOX5.mjs} +608 -7
- package/dist/chunk-PLAXOOX5.mjs.map +1 -0
- package/dist/{chunk-PSEQ6QPH.mjs → chunk-VOZWDM3L.mjs} +295 -198
- package/dist/chunk-VOZWDM3L.mjs.map +1 -0
- package/dist/{chunk-MDQJPY3T.mjs → chunk-ZDQWGQW7.mjs} +10 -4
- package/dist/chunk-ZDQWGQW7.mjs.map +1 -0
- package/dist/components/atmosphere.d.ts +2 -0
- package/dist/components/atmosphere.d.ts.map +1 -1
- package/dist/components/camera.d.ts +20 -4
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/depth-of-field.d.ts +28 -0
- package/dist/components/depth-of-field.d.ts.map +1 -0
- package/dist/components/directional-light.d.ts +2 -1
- package/dist/components/directional-light.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/mesh-filter.d.ts +6 -5
- package/dist/components/mesh-filter.d.ts.map +1 -1
- package/dist/components/motion-blur.d.ts +1 -0
- package/dist/components/motion-blur.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +3746 -837
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +6 -0
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/environment/analytic-fog.d.ts +6 -0
- package/dist/environment/analytic-fog.d.ts.map +1 -0
- package/dist/errors/render.d.ts +6 -2
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +7 -0
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/extract/environment.d.ts +2 -0
- package/dist/extract/environment.d.ts.map +1 -1
- package/dist/features/depth-of-field/depth-of-field-assembly.d.ts +19 -0
- package/dist/features/depth-of-field/depth-of-field-assembly.d.ts.map +1 -0
- package/dist/features/depth-of-field/depth-of-field-feature.d.ts +43 -0
- package/dist/features/depth-of-field/depth-of-field-feature.d.ts.map +1 -0
- package/dist/features/depth-of-field/depth-of-field-inspection.d.ts +34 -0
- package/dist/features/depth-of-field/depth-of-field-inspection.d.ts.map +1 -0
- package/dist/features/depth-of-field/depth-of-field-params.d.ts +83 -0
- package/dist/features/depth-of-field/depth-of-field-params.d.ts.map +1 -0
- package/dist/features/error-reporting.d.ts +13 -0
- package/dist/features/error-reporting.d.ts.map +1 -0
- package/dist/features/host.d.ts +5 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-feature.d.ts +20 -2
- package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-params.d.ts +21 -0
- package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-runtime.d.ts +54 -0
- package/dist/features/motion-blur/motion-blur-runtime.d.ts.map +1 -0
- package/dist/features/plan.d.ts +7 -4
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +9 -5
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/types.d.ts +19 -1
- package/dist/features/types.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts +7 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +3 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +9 -0
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +6 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +6 -5
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +78 -2
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances.d.ts +15 -1
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +6 -6
- package/dist/mesh-material-bindings.d.ts +3 -0
- package/dist/mesh-material-bindings.d.ts.map +1 -1
- package/dist/pipeline/standard-output/graph.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-post.d.ts +28 -19
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +2 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +24 -1
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +5 -2
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +6 -5
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +12 -28
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +5 -3
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +8 -1
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/render-contract.d.ts +32 -5
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts +2 -0
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +15 -1
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +26 -5
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +9 -2
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +2 -0
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/frame.d.ts +7 -0
- package/dist/temporal/frame.d.ts.map +1 -1
- package/dist/temporal/index.mjs +5 -5
- package/dist/temporal/view.d.ts +6 -1
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +10 -7
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/component.d.ts +0 -1
- package/dist/volume/component.d.ts.map +1 -1
- package/dist/volume/passes.d.ts.map +1 -1
- package/dist/volume/temporal.d.ts +3 -1
- package/dist/volume/temporal.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/__tests__/analytic-fog-graph.unit.test.ts +199 -0
- package/src/__tests__/analytic-fog-optics.unit.test.ts +38 -0
- package/src/__tests__/bloom-exact-zero.unit.test.ts +112 -18
- package/src/__tests__/bloom-lifecycle.integration.test.ts +17 -4
- package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +20 -0
- package/src/__tests__/environment-fog-lanes.integration.test.ts +14 -0
- package/src/__tests__/environment-selection.unit.test.ts +12 -0
- package/src/__tests__/factory-contract.integration.test.ts +331 -68
- package/src/__tests__/frame-camera-selection.unit.test.ts +39 -1
- package/src/__tests__/frame-plan-contract.unit.test.ts +2 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +12 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +2 -1
- package/src/__tests__/gpu-driven-production.integration.test.ts +27 -1
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +1 -1
- package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +1 -1
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +11 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +12 -0
- package/src/__tests__/gpu-scene.unit.test.ts +91 -0
- package/src/__tests__/instances-store.unit.test.ts +90 -0
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +31 -0
- package/src/__tests__/motion-blur-compute.dawn.test.ts +583 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +49 -0
- package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +86 -0
- package/src/__tests__/public-surface.test-d.ts +8 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +2 -0
- package/src/__tests__/render-feature-error-reporting.unit.test.ts +24 -0
- package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +23 -0
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +31 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +7 -1
- package/src/__tests__/shader-manifest-fixture.ts +4 -4
- package/src/__tests__/skinned-shadow-caster.test.ts +2 -0
- package/src/__tests__/standard-lighting-write.unit.test.ts +2 -1
- package/src/__tests__/standard-pipeline.dawn.test.ts +1 -1
- package/src/__tests__/standard-pipeline.integration.test.ts +18 -8
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +10 -0
- package/src/__tests__/temporal-view.unit.test.ts +2 -1
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +20 -4
- package/src/__tests__/volume-optics-oracle.unit.test.ts +3 -1
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +177 -13
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +447 -61
- package/src/__tests__/volumetric-fog-temporal.unit.test.ts +27 -0
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +19 -3
- package/src/assembly/analytic-fog-registration.ts +14 -0
- package/src/assembly/fxaa-registration.ts +17 -0
- package/src/assembly/lod-observation.ts +12 -0
- package/src/assembly/motion-blur-registration.ts +17 -0
- package/src/assembly/renderer-facade.ts +2 -0
- package/src/assembly/temporal-post-process-prewarm.ts +40 -0
- package/src/assembly/webgpu-ready-contract.ts +8 -3
- package/src/assembly/webgpu-ready.ts +186 -250
- package/src/assembly/webgpu-renderer.ts +30 -45
- package/src/bloom-admission.ts +21 -0
- package/src/components/__tests__/motion-blur.unit.test.ts +1 -0
- package/src/components/atmosphere.ts +4 -0
- package/src/components/camera.ts +66 -10
- package/src/components/depth-of-field.ts +45 -0
- package/src/components/directional-light.ts +2 -1
- package/src/components/index.ts +1 -0
- package/src/components/mesh-filter.ts +11 -9
- package/src/components/motion-blur.ts +1 -0
- package/src/device/gpu-residency.ts +56 -12
- package/src/environment/analytic-fog.ts +44 -0
- package/src/environment/background.ts +2 -2
- package/src/errors/render.ts +14 -1
- package/src/extract/camera.ts +42 -9
- package/src/extract/contracts.ts +7 -0
- package/src/extract/environment.ts +4 -0
- package/src/features/depth-of-field/__tests__/depth-of-field-params.unit.test.ts +943 -0
- package/src/features/depth-of-field/depth-of-field-assembly.ts +100 -0
- package/src/features/depth-of-field/depth-of-field-feature.ts +295 -0
- package/src/features/depth-of-field/depth-of-field-inspection.ts +237 -0
- package/src/features/depth-of-field/depth-of-field-params.ts +556 -0
- package/src/features/error-reporting.ts +78 -0
- package/src/features/host.ts +26 -13
- package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +111 -1
- package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +58 -3
- package/src/features/motion-blur/motion-blur-feature.ts +1519 -15
- package/src/features/motion-blur/motion-blur-params.ts +70 -2
- package/src/features/motion-blur/motion-blur-runtime.ts +176 -0
- package/src/features/plan.ts +25 -8
- package/src/features/prepared-gpu-work.ts +86 -35
- package/src/features/render-graph-compute.ts +13 -5
- package/src/features/render-graph-contribution.ts +2 -0
- package/src/features/types.ts +18 -1
- package/src/gpu-driven/material-artifacts.ts +2 -7
- package/src/gpu-driven/prepared-draw.ts +15 -10
- package/src/gpu-driven/production-raster.ts +83 -7
- package/src/gpu-driven/view-gpu.ts +17 -0
- package/src/gpu-scene.ts +188 -21
- package/src/index.ts +64 -0
- package/src/inspection-types.ts +98 -2
- package/src/instances.ts +93 -0
- package/src/mesh-material-bindings.ts +4 -0
- package/src/pipeline/standard-output/graph.ts +3 -0
- package/src/pipeline/standard-output/types.ts +4 -1
- package/src/pipeline/standard-post.ts +324 -76
- package/src/pipeline-spec.ts +5 -5
- package/src/plugin.ts +2 -0
- package/src/points-lines/__tests__/view-ubo.unit.test.ts +2 -1
- package/src/record/__tests__/temporal-payload.unit.test.ts +2 -1
- package/src/record/frame-snapshot.ts +31 -2
- package/src/record/frame-targets.ts +10 -6
- package/src/record/frame.ts +202 -28
- package/src/record/main-pass-geometry.ts +30 -1
- package/src/record/main-pass-sprite-draws.ts +6 -1
- package/src/record/main-pass.ts +1 -5
- package/src/record/mesh-ssbo.ts +43 -6
- package/src/record/render-context.ts +12 -28
- package/src/record/shadow-pass.ts +6 -1
- package/src/record/skybox-post-pass.ts +122 -196
- package/src/record/typed-frame-graph.ts +259 -15
- package/src/recovery/render-system-candidate.ts +28 -10
- package/src/render-contract.ts +34 -4
- package/src/render-graph-primitives.ts +8 -1
- package/src/render-pipeline.ts +22 -1
- package/src/render-system-extract-tail.ts +103 -13
- package/src/render-system-extract.ts +148 -38
- package/src/render-system.ts +332 -119
- package/src/scene/render-scene.ts +85 -4
- package/src/temporal/__tests__/temporal-frame-transaction.unit.test.ts +36 -0
- package/src/temporal/frame-coordinator.ts +8 -1
- package/src/temporal/frame.ts +20 -0
- package/src/temporal/view.ts +13 -2
- package/src/typed-render-graph-primitives.ts +124 -48
- package/src/volume/component.ts +0 -1
- package/src/volume/passes.ts +8 -4
- package/src/volume/resources.ts +1 -1
- package/src/volume/temporal.ts +11 -0
- package/dist/chunk-C3DDSXYK.mjs.map +0 -1
- package/dist/chunk-FUUO4UND.mjs.map +0 -1
- package/dist/chunk-JRBDO2KB.mjs.map +0 -1
- package/dist/chunk-MDQJPY3T.mjs.map +0 -1
- package/dist/chunk-PSEQ6QPH.mjs.map +0 -1
- package/dist/chunk-S5UE2T5P.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# `@forgeax/engine-render`
|
|
2
2
|
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> This package-owner README contains physical `@forgeax/engine-render` imports
|
|
5
|
+
> in internal ownership examples. Game-facing code should use the public
|
|
6
|
+
> facade `@forgeax/engine/render`; the Bloom consumer route is documented in
|
|
7
|
+
> [hello-bloom](../../apps/hello/bloom/README.md).
|
|
8
|
+
|
|
3
9
|
> [!IMPORTANT]
|
|
4
10
|
> The Standard pipeline keeps dark-gradient color in `rgba16float` until one
|
|
5
11
|
> `outputTransform` reaches the `surface.storage.raw` endpoint; `inspect()` and
|
|
@@ -8,6 +14,16 @@
|
|
|
8
14
|
|
|
9
15
|
## Public frame contract
|
|
10
16
|
|
|
17
|
+
`Fog` provides distance fog with exponential height density through one
|
|
18
|
+
finite-depth fullscreen pass before temporal resolve, Bloom and tone mapping.
|
|
19
|
+
`density`, `heightFalloff`, linear `color` and `maxOpacity` are selected from
|
|
20
|
+
the frame's resource owner. Disabled or zero-density fog allocates no fog target;
|
|
21
|
+
there is no 3D texture, froxel grid or temporal history. The authored sky remains
|
|
22
|
+
unchanged. FXAA, TAA and no-AA use single-sample depth; MSAA is rejected until a
|
|
23
|
+
matching depth resolve is available. Translucent surfaces that do not write depth
|
|
24
|
+
use the opaque background depth. Use `VolumetricFog` for spatial density and
|
|
25
|
+
lighting/scattering effects; the meadow-style distance haze needs only `Fog`.
|
|
26
|
+
|
|
11
27
|
## 灯光最短入口
|
|
12
28
|
|
|
13
29
|
三条最短入口:
|
|
@@ -101,6 +117,14 @@ plan. Register it at construction with
|
|
|
101
117
|
`createRenderer(canvas, { features: [feature] })`. A feature never receives a
|
|
102
118
|
device, queue, encoder, staging builder, or submit callback.
|
|
103
119
|
|
|
120
|
+
After a frame reaches queue submission, the optional `onFrameSubmitted` callback
|
|
121
|
+
receives a bounded `RenderFeatureSubmission` projection. Its `passes` contain
|
|
122
|
+
only work admitted after prepared-resource resolution, so producer diagnostics
|
|
123
|
+
can distinguish submitted compute dispatches and graphics draws from a plan
|
|
124
|
+
that was still pending. This is a receipt of admitted commands, not a GPU
|
|
125
|
+
readback or proof that an indirect draw produced non-zero instances; producers
|
|
126
|
+
that need those facts must use their existing readback or tape evidence.
|
|
127
|
+
|
|
104
128
|
## RenderFeature: the producer seam (first-read index)
|
|
105
129
|
|
|
106
130
|
The public route is one `RenderFeature<FrameData>` through the active RenderGraph
|
|
@@ -157,6 +181,13 @@ surface receiver position along its world-space normal before projection (defaul
|
|
|
157
181
|
Normal offset and depth bias are independent: increasing the CSM depth span must
|
|
158
182
|
not magnify the world-space normal offset. Both PCF and PCSS project the offset
|
|
159
183
|
receiver; volumetric samples have no surface normal and use only depth bias.
|
|
184
|
+
Surface receivers also get a per-cascade depth correction derived from the
|
|
185
|
+
world size of a shadow texel, the filter footprint, and the receiver's depth
|
|
186
|
+
slope along the light's two image axes. The existing normal offset counts
|
|
187
|
+
toward this coverage; only the missing amount is added to `depthBias`, after
|
|
188
|
+
conversion through that cascade's light-space depth span. PCSS derives its
|
|
189
|
+
blocker-search and comparison coverage separately from their actual radii.
|
|
190
|
+
This does not change either author field's units or add shadow texture taps.
|
|
160
191
|
Cascade projections include their incoming blend band, and PCSS depth spans
|
|
161
192
|
match the actual projection, including toward-light caster reach. The normal
|
|
162
193
|
position offset follows the approach in
|
|
@@ -192,6 +223,8 @@ index path, not a second schema.
|
|
|
192
223
|
import {
|
|
193
224
|
ANTIALIAS_TAA,
|
|
194
225
|
Atmosphere,
|
|
226
|
+
BLOOM_DISABLED,
|
|
227
|
+
CAMERA_EXPOSURE_MODE_MANUAL,
|
|
195
228
|
Camera,
|
|
196
229
|
Fog,
|
|
197
230
|
type FramePlan,
|
|
@@ -214,6 +247,17 @@ code-specific `detail`; they do not create a second registry or silently pick
|
|
|
214
247
|
the first entity. Frame facts contain IDs, revisions, and POD values only, not
|
|
215
248
|
textures, buffers, devices, or other live GPU objects.
|
|
216
249
|
|
|
250
|
+
An Atmosphere also requires exactly one `DirectionalLight` sun. A direct render
|
|
251
|
+
extraction with no sun routes `SunCardinalityError` through the World-owned
|
|
252
|
+
error boundary (`console.error` with the structured error) before extraction
|
|
253
|
+
throws; that internal route is deliberately separate from `app.onError`.
|
|
254
|
+
`app.onError` receives errors returned by the App frame loop and renderer event
|
|
255
|
+
stream. Inspect `error.code` and `error.detail.value`, repair the owning World,
|
|
256
|
+
then retry the same extraction or the next frame. For zero suns, spawn one
|
|
257
|
+
`DirectionalLight`; for multiple suns, remove the extras so the Atmosphere has
|
|
258
|
+
one owner. The renderer does not manufacture a fallback sun or retain a
|
|
259
|
+
partially selected environment.
|
|
260
|
+
|
|
217
261
|
The Standard graph renders an Atmosphere source as a 128-by-128, six-face
|
|
218
262
|
`rgba16float` sky cube and a background pass before scene geometry. The selected
|
|
219
263
|
DirectionalLight supplies the sun direction, color, and illuminance; the sun
|
|
@@ -225,6 +269,42 @@ Its color and intensity remain the lighting controls; no Skylight means no
|
|
|
225
269
|
global ambient contribution. An explicit equirect keeps its image source.
|
|
226
270
|
Use a neutral Skylight tint when comparing local captures against the sky.
|
|
227
271
|
|
|
272
|
+
### Atmosphere controls and fixed-exposure comparisons
|
|
273
|
+
|
|
274
|
+
`Atmosphere` keeps the visible disc and the analytic sky response as separate
|
|
275
|
+
controls. `circumsolarStrength` and `circumsolarWidth` affect only the Mie
|
|
276
|
+
forward lobe used while producing the cached sky cube. The defaults preserve the
|
|
277
|
+
baseline response; `sunAngularRadius` still controls the separate disc in the
|
|
278
|
+
background pass and does not grow a halo.
|
|
279
|
+
|
|
280
|
+
| Field | Default | Valid range | Meaning |
|
|
281
|
+
|:--|:--:|:--|:--|
|
|
282
|
+
| `circumsolarStrength` | `1` | `[0, 4]` | Multiplier for the circumsolar Mie lobe |
|
|
283
|
+
| `circumsolarWidth` | `1` | `[0.25, 4]` | Relative lobe width; larger values broaden it, smaller values narrow it |
|
|
284
|
+
| `sunAngularRadius` | `0.004675` rad | `[0, +∞)` | Radius of the separate visible sun disc; `0` disables the disc |
|
|
285
|
+
|
|
286
|
+
For an angular or sunset sweep, hold the camera exposure in manual mode and
|
|
287
|
+
disable Bloom so the measurement belongs to the Atmosphere response. Vary the
|
|
288
|
+
two circumsolar fields while leaving `sunAngularRadius` unchanged:
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
world.set(atmosphere, Atmosphere, {
|
|
292
|
+
circumsolarStrength: 1.5,
|
|
293
|
+
circumsolarWidth: 2,
|
|
294
|
+
sunAngularRadius: 0.004675,
|
|
295
|
+
}).unwrap();
|
|
296
|
+
world.set(camera, Camera, {
|
|
297
|
+
exposureMode: CAMERA_EXPOSURE_MODE_MANUAL,
|
|
298
|
+
exposure: 1,
|
|
299
|
+
bloom: BLOOM_DISABLED,
|
|
300
|
+
}).unwrap();
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The fixed exposure and Bloom setting are comparison setup, not additional
|
|
304
|
+
Atmosphere state. Use the renderer's submitted frame or a real Browser/Dawn
|
|
305
|
+
readback for visual claims; source values and a sky-cube upload do not prove a
|
|
306
|
+
pixel response.
|
|
307
|
+
|
|
228
308
|
The cube has 786,432 texture payload bytes. Its 16-square irradiance cube and
|
|
229
309
|
64-square, five-mip prefilter add 274,176 bytes; parameters and vertices total
|
|
230
310
|
1,560 bytes, before backend allocation alignment. DeviceScope owns one cached
|
|
@@ -259,6 +339,8 @@ import { createRenderer } from '@forgeax/engine-runtime';
|
|
|
259
339
|
const created = await createRenderer(canvas, { gpuPassTiming: {} });
|
|
260
340
|
if (!created.ok) throw created.error;
|
|
261
341
|
const renderer = created.value;
|
|
342
|
+
const ready = await renderer.initialization;
|
|
343
|
+
if (!ready.ok) throw ready.error;
|
|
262
344
|
const attached = renderer.attach(world);
|
|
263
345
|
if (!attached.ok) throw attached.error;
|
|
264
346
|
const drawn = renderer.draw({
|
|
@@ -332,6 +414,168 @@ before publication; an invalid update keeps the renderer's last-known-good
|
|
|
332
414
|
fog frame and exposes the structured failure through the existing inspection
|
|
333
415
|
path. There is no app-local fog state.
|
|
334
416
|
|
|
417
|
+
### VolumetricFog authoring, World time, and recovery
|
|
418
|
+
|
|
419
|
+
`VolumetricFog` is an authored, one-owner component backed by a linear 3D
|
|
420
|
+
`TextureAsset`. The selected light must be a live same-World
|
|
421
|
+
`DirectionalLight`, `PointLight`, or `SpotLight`; Point/Spot selection also
|
|
422
|
+
requires the corresponding `Transform`. Validate the authoring POD before
|
|
423
|
+
spawning the component, then let the existing RenderSystem extract it. The
|
|
424
|
+
World `Time` resource is the only simulation clock: `world.update(delta)`
|
|
425
|
+
advances it, and the volume parameter block receives that elapsed value.
|
|
426
|
+
|
|
427
|
+
```ts
|
|
428
|
+
import { Time, World } from '@forgeax/engine-ecs';
|
|
429
|
+
import {
|
|
430
|
+
Atmosphere,
|
|
431
|
+
BLOOM_DISABLED,
|
|
432
|
+
CAMERA_EXPOSURE_MODE_MANUAL,
|
|
433
|
+
Camera,
|
|
434
|
+
DirectionalLight,
|
|
435
|
+
VolumetricFog,
|
|
436
|
+
extractVolumetricFog,
|
|
437
|
+
perspective,
|
|
438
|
+
resolveSelectedVolumetricLight,
|
|
439
|
+
validateVolumetricFog,
|
|
440
|
+
type VolumetricFogAuthoring,
|
|
441
|
+
type Renderer,
|
|
442
|
+
} from '@forgeax/engine-render';
|
|
443
|
+
import { Transform } from '@forgeax/engine-scene';
|
|
444
|
+
import type { TextureAsset } from '@forgeax/engine-types';
|
|
445
|
+
|
|
446
|
+
const world = new World();
|
|
447
|
+
const densityAsset: TextureAsset = {
|
|
448
|
+
kind: 'texture',
|
|
449
|
+
shape: { viewDimension: '3d', extent: { width: 4, height: 4, depth: 4 } },
|
|
450
|
+
format: 'r8unorm',
|
|
451
|
+
colorSpace: 'linear',
|
|
452
|
+
mips: { kind: 'none' },
|
|
453
|
+
data: new Uint8Array(4 * 4 * 4).fill(32),
|
|
454
|
+
};
|
|
455
|
+
const density = world.allocSharedRef('TextureAsset', densityAsset);
|
|
456
|
+
// A production project normally obtains this TextureAsset from its existing
|
|
457
|
+
// Pack/Catalog load path, then allocates the same shared handle in this World.
|
|
458
|
+
// The inline payload keeps this authoring example executable and deterministic.
|
|
459
|
+
const camera = world.spawn(
|
|
460
|
+
{ component: Transform, data: { pos: [0, 0, 3] } },
|
|
461
|
+
{ component: Camera, data: {
|
|
462
|
+
...perspective({ fov: Math.PI / 4, aspect: 16 / 9 }),
|
|
463
|
+
exposureMode: CAMERA_EXPOSURE_MODE_MANUAL,
|
|
464
|
+
exposure: 1,
|
|
465
|
+
bloom: BLOOM_DISABLED,
|
|
466
|
+
} },
|
|
467
|
+
).unwrap();
|
|
468
|
+
const sun = world.spawn({
|
|
469
|
+
component: DirectionalLight,
|
|
470
|
+
data: { direction: [-0.4, -0.8, -0.3], castShadow: true },
|
|
471
|
+
}).unwrap();
|
|
472
|
+
const atmosphere = world.spawn({
|
|
473
|
+
component: Atmosphere,
|
|
474
|
+
data: { circumsolarStrength: 1, circumsolarWidth: 1, sunAngularRadius: 0.004675 },
|
|
475
|
+
}).unwrap();
|
|
476
|
+
|
|
477
|
+
const authored: VolumetricFogAuthoring = {
|
|
478
|
+
light: sun,
|
|
479
|
+
density: {
|
|
480
|
+
guid: 'density-demo',
|
|
481
|
+
generation: 1,
|
|
482
|
+
shape: { viewDimension: '3d', extent: { width: 4, height: 4, depth: 4 } },
|
|
483
|
+
format: 'r8unorm',
|
|
484
|
+
colorSpace: 'linear',
|
|
485
|
+
},
|
|
486
|
+
bounds: { min: [-1, -1, -1], max: [1, 1, 1] },
|
|
487
|
+
extinction: [0.2, 0.2, 0.2],
|
|
488
|
+
albedo: [0.8, 0.8, 0.8],
|
|
489
|
+
emission: [0, 0, 0],
|
|
490
|
+
anisotropy: 0,
|
|
491
|
+
maxDistance: 50,
|
|
492
|
+
};
|
|
493
|
+
const checked = validateVolumetricFog(authored);
|
|
494
|
+
if (!checked.ok) throw checked.error;
|
|
495
|
+
const extracted = extractVolumetricFog([checked.value]);
|
|
496
|
+
if (!extracted.ok) throw extracted.error;
|
|
497
|
+
const selected = resolveSelectedVolumetricLight(world, sun);
|
|
498
|
+
if (selected.status === 'unresolved') {
|
|
499
|
+
throw new Error(`${selected.reason}: ${selected.hint}`);
|
|
500
|
+
}
|
|
501
|
+
world.spawn({
|
|
502
|
+
component: VolumetricFog,
|
|
503
|
+
data: {
|
|
504
|
+
light: sun,
|
|
505
|
+
density,
|
|
506
|
+
boundsMin: authored.bounds.min,
|
|
507
|
+
boundsMax: authored.bounds.max,
|
|
508
|
+
extinction: authored.extinction,
|
|
509
|
+
albedo: authored.albedo,
|
|
510
|
+
emission: authored.emission,
|
|
511
|
+
anisotropy: authored.anisotropy,
|
|
512
|
+
maxDistance: authored.maxDistance,
|
|
513
|
+
},
|
|
514
|
+
}).unwrap();
|
|
515
|
+
|
|
516
|
+
world.update(1 / 60).unwrap();
|
|
517
|
+
const elapsed = world.getResource(Time).elapsed; // the projected volume time
|
|
518
|
+
void camera;
|
|
519
|
+
void atmosphere;
|
|
520
|
+
void elapsed;
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
If validation fails, read its structured `code`, `expected`, `hint`, and typed
|
|
524
|
+
`detail`. If light resolution is unresolved, read its `reason`, `expected`,
|
|
525
|
+
`actual`, and `hint`; repair the owning authoring field, and extract again.
|
|
526
|
+
Do not manufacture a light or a density fallback. After a submitted frame,
|
|
527
|
+
`renderer.inspect().volumetricFog` reports `status`, `resourceStage`, the
|
|
528
|
+
selected-light facts, and candidate/LKG generations. For a renderer device
|
|
529
|
+
loss, wait for the host recovery boundary, call `await renderer.recover()`,
|
|
530
|
+
then submit the same World again and inspect the replacement generation:
|
|
531
|
+
|
|
532
|
+
```ts
|
|
533
|
+
import type { World } from '@forgeax/engine-ecs';
|
|
534
|
+
import type { Renderer } from '@forgeax/engine-render';
|
|
535
|
+
|
|
536
|
+
// After the existing createRenderer(canvas) happy path returns a Renderer,
|
|
537
|
+
// call `await recoverAndDraw(renderer, world)` with the World above.
|
|
538
|
+
async function recoverAndDraw(renderer: Renderer, world: World): Promise<void> {
|
|
539
|
+
const ready = await renderer.initialization;
|
|
540
|
+
if (!ready.ok) throw ready.error;
|
|
541
|
+
const attached = renderer.attach(world);
|
|
542
|
+
if (!attached.ok) throw attached.error;
|
|
543
|
+
const lease = attached.value;
|
|
544
|
+
if (renderer.state() === 'device-lost') {
|
|
545
|
+
const recovered = await renderer.recover();
|
|
546
|
+
if (!recovered.ok) throw recovered.error;
|
|
547
|
+
}
|
|
548
|
+
const retry = renderer.draw({
|
|
549
|
+
leases: [lease],
|
|
550
|
+
camera: { lease },
|
|
551
|
+
environment: { lease },
|
|
552
|
+
});
|
|
553
|
+
if (!retry.ok) throw retry.error;
|
|
554
|
+
const completed = await retry.value.completed;
|
|
555
|
+
if (!completed.ok) throw completed.error;
|
|
556
|
+
const volume = renderer.inspect().volumetricFog;
|
|
557
|
+
console.log(volume?.status, volume?.resourceStage, volume?.generation);
|
|
558
|
+
}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
The recovery call rebuilds renderer-owned resources; it is not a second volume
|
|
562
|
+
clock or registry. Pausing the simulation means withholding `world.update`.
|
|
563
|
+
Equal time and normal monotonic World ticks up to 100 ms retain eligible
|
|
564
|
+
volume history. Clock rollback, a larger jump, or changed time availability
|
|
565
|
+
invalidates it. Camera, light, medium, size and resource changes still invalidate
|
|
566
|
+
history. Integration and shadow visibility use stable cell centers, so a reset
|
|
567
|
+
does not expose render-frame random noise. The ray uses 96 base intervals;
|
|
568
|
+
segments within four interval lengths of a selected punctual source use four
|
|
569
|
+
midpoints to resolve its inverse-square peak and spot cone. This local refinement
|
|
570
|
+
keeps the density expression and packed shadow resolution unchanged. A real GPU
|
|
571
|
+
punctual-scattering oracle protects spatial accuracy alongside the continuous-frame
|
|
572
|
+
browser check; homogeneous transmittance alone cannot detect light-band artifacts.
|
|
573
|
+
History is clipped to the current
|
|
574
|
+
neighborhood and its weight decays with elapsed World time (at most 0.875 per
|
|
575
|
+
frame, about 87 ms half-life at 60 Hz or slower), limiting trails from animated
|
|
576
|
+
density. This route covers authored density and time projection only; it does
|
|
577
|
+
not add a procedural cloud API or a Voxel integration.
|
|
578
|
+
|
|
335
579
|
Point shadows use one renderer-owned cube-array `ShadowAtlas`. The public
|
|
336
580
|
`SHADOW_ATLAS_DEFAULT_FACE_SIZE` and `SHADOW_ATLAS_DEFAULT_LAYERS` constants
|
|
337
581
|
are the only capacity owner; extract assigns `shadowAtlasLayer: -1` to
|
|
@@ -368,6 +612,8 @@ rebuild or cold-cook its source, then retry the same request.
|
|
|
368
612
|
const created = await createRenderer(canvas);
|
|
369
613
|
if (!created.ok) throw created.error;
|
|
370
614
|
const renderer = created.value;
|
|
615
|
+
const ready = await renderer.initialization;
|
|
616
|
+
if (!ready.ok) throw ready.error;
|
|
371
617
|
const attached = renderer.attach(world);
|
|
372
618
|
if (!attached.ok) throw attached.error;
|
|
373
619
|
const frame = renderer.draw({
|
|
@@ -896,20 +1142,131 @@ not from inspecting shader source text for a particular output structure.
|
|
|
896
1142
|
|
|
897
1143
|
## Motion Blur temporal consumer
|
|
898
1144
|
|
|
899
|
-
The Standard camera
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1145
|
+
The Standard camera enables Motion Blur by carrying the presence-enabled
|
|
1146
|
+
component. Authoring stays bounded and validated at the component boundary:
|
|
1147
|
+
|
|
1148
|
+
```ts
|
|
1149
|
+
import { MotionBlur } from '@forgeax/engine-render';
|
|
1150
|
+
|
|
1151
|
+
const result = world.addComponent(cameraEntity, {
|
|
1152
|
+
component: MotionBlur,
|
|
1153
|
+
data: { shutterAngle: 180, maxRadiusPixels: 32, sampleCount: 8, targetFps: 60 },
|
|
1154
|
+
});
|
|
1155
|
+
if (!result.ok) throw result.error;
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
| Input / state | Contract |
|
|
1159
|
+
|:--|:--|
|
|
1160
|
+
| Parameters | `shutterAngle ∈ [0, 360]`, `maxRadiusPixels ∈ [0, 64]`, integer `sampleCount ∈ [4, 16]`, integer `targetFps ∈ [0, 240]`; zero shutter/radius is zero work. |
|
|
1161
|
+
| Temporal source | One renderer-owned `scene-data-temporal` sampled target. Motion validity and color reactivity are separate fields. |
|
|
1162
|
+
| Post order | TAA resolve → Motion Blur → auto exposure/Bloom/tone output. Motion Blur never writes TAA history. |
|
|
1163
|
+
| WebGPU/native lane | One 16×16 tile summary dispatch plus one fused reconstruction dispatch. Long vectors use an even-pixel half-resolution source lattice with depth guidance. |
|
|
1164
|
+
| Bounded work | Effective color tiers are exactly `0|4|8|16`; metadata probes are fixed and color work is linear in the tier. There is no radius-squared neighborhood, per-pixel scatter, or atomic accumulation. |
|
|
1165
|
+
| WebGL2 fallback | One directional raster gather, explicitly reported as `raster-limited`; it does not claim compute quality or timing parity. |
|
|
1166
|
+
|
|
1167
|
+
The authored integer sample count maps to one of the four execution tiers:
|
|
1168
|
+
`4..7 → 4`, `8..15 → 8`, and `16 → 16`; a zero-work bypass is `0`. The
|
|
1169
|
+
selected tier is the total color budget for every direction, including a
|
|
1170
|
+
bounded fallback when all candidates are rejected. There is no implicit
|
|
1171
|
+
center tap added to the budget.
|
|
1172
|
+
|
|
1173
|
+
For an uncovered moving support, accepted foreground coverage reconstructs a
|
|
1174
|
+
bounded portion of an opaque silhouette edge rather than averaging rejected
|
|
1175
|
+
support as black. The matching empty-background trail is attenuated by the
|
|
1176
|
+
same fixed factor, so the correction redistributes energy and does not add
|
|
1177
|
+
another sample. If no source is accepted, the receiver's center color remains
|
|
1178
|
+
the conservative fallback. A recovered midpoint anchor is still only partial
|
|
1179
|
+
evidence. Compute and raster-limited lanes share the rule without an extra
|
|
1180
|
+
pass or radius-sized neighborhood.
|
|
1181
|
+
|
|
1182
|
+
Reset and recovery remain observable through the existing temporal owner:
|
|
1183
|
+
|
|
1184
|
+
```ts
|
|
1185
|
+
const inspection = renderer.inspect();
|
|
1186
|
+
if (
|
|
1187
|
+
inspection.motionBlur?.status === 'reset' &&
|
|
1188
|
+
inspection.motionBlur.resetReason === 'time-discontinuity' &&
|
|
1189
|
+
inspection.temporal.resetReason === 'time-discontinuity'
|
|
1190
|
+
) {
|
|
1191
|
+
// The next successful draw commits the new baseline; retry the same renderer.
|
|
1192
|
+
renderer.draw(frameInput());
|
|
1193
|
+
}
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
After a rejected graph or queue submit, `motionBlur.lastFailure` is
|
|
1197
|
+
`'submit-failure'` while the previous successful temporal view remains the
|
|
1198
|
+
baseline. A `'scene-data-unavailable'` failure means the shared
|
|
1199
|
+
`standard-scene-data` producer or its `rgba16float` capability was not
|
|
1200
|
+
admitted; repair that producer/capability and retry the same frame. It never
|
|
1201
|
+
creates a private Motion Blur history target.
|
|
1202
|
+
|
|
1203
|
+
The renderer carries the raw host render sample timestamp through App/Worker
|
|
1204
|
+
and accepts it only after a successful submit. A gap greater than `100 ms`
|
|
1205
|
+
creates a `time-discontinuity` reset; a normal `30 Hz` interval remains valid,
|
|
1206
|
+
and a failed submit cannot advance the accepted temporal clock. Pause/resume
|
|
1207
|
+
also marks the next frame as a new baseline. Instance pose updates preserve
|
|
1208
|
+
the last submitted transform. Collection replacement mints new identity
|
|
1209
|
+
generations by default; a producer that keeps an instance through compaction
|
|
1210
|
+
or reorder can pass its prior generation tokens in the new order, allowing the
|
|
1211
|
+
record stage to pair each current transform with the real prior transform.
|
|
1212
|
+
|
|
1213
|
+
`renderer.inspect().motionBlur` exposes detached POD facts: status (`off`,
|
|
1214
|
+
`active`, `reset`, `limited`, or `invalid`), selected lane, effective tier,
|
|
1215
|
+
pass count, reset reason, last failure, and `historyWrites: 0`. Invalid public
|
|
1216
|
+
parameters return `MotionBlurValidationError` with code
|
|
1217
|
+
`motion-blur-invalid-params`; repair the named component field or capability
|
|
1218
|
+
and retry the same frame. The typed cause is preserved by the public draw
|
|
1219
|
+
result, so a caller can repair the named field without parsing `Error.message`:
|
|
1220
|
+
|
|
1221
|
+
```ts
|
|
1222
|
+
const result = renderer.draw(frameInput);
|
|
1223
|
+
if (!result.ok && result.error.code === 'motion-blur-invalid-params') {
|
|
1224
|
+
console.error(result.error.detail.field, result.error.detail.value);
|
|
1225
|
+
// Repair the component field, then retry the same frame.
|
|
1226
|
+
}
|
|
1227
|
+
```
|
|
1228
|
+
|
|
1229
|
+
The hello-taa carrier contains the Dawn, Browser, WebGL2, RhiNull, performance,
|
|
1230
|
+
and source-level falsifier paths.
|
|
1231
|
+
|
|
1232
|
+
## Multiscale HDR Bloom
|
|
1233
|
+
|
|
1234
|
+
Bloom is a Camera-owned Standard post feature. `bloom` selects the enabled
|
|
1235
|
+
path, while `bloomIntensity` is bounded to `[0, 8]`, `bloomThreshold` to
|
|
1236
|
+
`[0, 65504]`, `bloomSoftKnee` to `[0, 1]`, and `bloomScatter` to `[0, 0.95]`.
|
|
1237
|
+
The defaults are intensity `1`, threshold `1`, soft knee `0.5`, and scatter
|
|
1238
|
+
`0.7`; the disabled value and intensity `0` are exact zero-work cases.
|
|
1239
|
+
|
|
1240
|
+
Game authors import this contract through the public umbrella facade:
|
|
1241
|
+
|
|
1242
|
+
```ts
|
|
1243
|
+
import {
|
|
1244
|
+
BLOOM_ENABLED,
|
|
1245
|
+
Camera,
|
|
1246
|
+
TONEMAP_REINHARD_EXTENDED,
|
|
1247
|
+
} from '@forgeax/engine/render';
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
The physical `@forgeax/engine-render` package remains the repository ownership
|
|
1251
|
+
unit; it is not the recommended consumer import path.
|
|
1252
|
+
|
|
1253
|
+
An enabled frame stays in the linear HDR domain and uses one typed graph:
|
|
1254
|
+
five ceil-halved downsample levels `D0..D4`, four tent upsample levels
|
|
1255
|
+
`U3..U0`, then one HDR composite before tone mapping. `D0` applies the soft
|
|
1256
|
+
threshold per source texel and weights each texel by its exact source/target
|
|
1257
|
+
rectangle overlap, including odd extents. `D1..D4` use the shared 13-tap
|
|
1258
|
+
reduction; each upsample blends the current level with the next coarser level
|
|
1259
|
+
using the authored scatter. Composite adds only RGB bloom and preserves the
|
|
1260
|
+
scene alpha.
|
|
1261
|
+
|
|
1262
|
+
`renderer.inspect().bloom` is detached POD evidence from the compiled graph.
|
|
1263
|
+
Its level dimensions, `targetBytes`, pass topology, and
|
|
1264
|
+
`residentChildBytes` come from validated descriptors and active resource
|
|
1265
|
+
ownership, not from an expected-size calculation. A missing, forged, or
|
|
1266
|
+
invalid descriptor graph reports `invalid`; an off camera reports `empty` and
|
|
1267
|
+
zero target/resident bytes. Candidate generations remain behind the existing
|
|
1268
|
+
LKG fence and are released only after the submit fence; device recovery
|
|
1269
|
+
rehydrates the same Bloom bundle before a new receipt is accepted.
|
|
913
1270
|
|
|
914
1271
|
> [!IMPORTANT]
|
|
915
1272
|
> Render consumes the effective MaterialAsset snapshot produced by extract. Each texture slot carries its own coordinate set and transform into the built-in PBR binding layout; render records do not reinterpret authoring fields or manufacture shader artifacts. The effective `passes` are already validated.
|
|
@@ -947,7 +1304,7 @@ base-only root keeps `forward + deferred + shadow`, while declaring any
|
|
|
947
1304
|
second-stage layer or physical texture selects `forward + shadow`.
|
|
948
1305
|
|
|
949
1306
|
```ts
|
|
950
|
-
import { Materials } from '@forgeax/engine
|
|
1307
|
+
import { Materials } from '@forgeax/engine/render';
|
|
951
1308
|
|
|
952
1309
|
const material = Materials.standard({
|
|
953
1310
|
baseColor: [0.72, 0.48, 0.22, 1],
|
|
@@ -1089,10 +1446,14 @@ renderer.instances.update(collection.collectionId, {
|
|
|
1089
1446
|
transforms: patch,
|
|
1090
1447
|
});
|
|
1091
1448
|
|
|
1092
|
-
// Replacement
|
|
1449
|
+
// Replacement publishes a new revision. Pass prior generation tokens in their
|
|
1450
|
+
// new order when compaction/reorder preserves logical instance identity;
|
|
1451
|
+
// omitted tokens intentionally seed replacement rows.
|
|
1093
1452
|
renderer.instances.replace(collection.collectionId, identityMatrices(instanceCount + 1));
|
|
1094
1453
|
const current = renderer.instances.inspect(collection.collectionId).unwrap();
|
|
1095
1454
|
const detached = renderer.instances.snapshot(collection.collectionId).unwrap();
|
|
1455
|
+
// A read-only observation does not advance the renderer upload cursor.
|
|
1456
|
+
const observed = renderer.instances.peek(collection.collectionId).unwrap();
|
|
1096
1457
|
renderer.instances.release(collection.collectionId);
|
|
1097
1458
|
```
|
|
1098
1459
|
|
|
@@ -1101,7 +1462,9 @@ storage, `replace` publishes a complete new revision (and grows capacity when
|
|
|
1101
1462
|
needed), `update` validates a bounded matrix interval and records its dirty
|
|
1102
1463
|
range, `inspect` returns identity/count/capacity/revision, `snapshot` returns a
|
|
1103
1464
|
detached matrix copy plus dirty ranges for extraction, and `release` makes the
|
|
1104
|
-
collection id terminal.
|
|
1465
|
+
collection id terminal. `peek` returns the same detached matrices without
|
|
1466
|
+
advancing the renderer upload cursor, so read-only inspection cannot hide a
|
|
1467
|
+
later dirty range. A caller never provides a GPU buffer or maintains
|
|
1105
1468
|
backend-specific chunks. `detached.transforms` is an observation, not mutable
|
|
1106
1469
|
renderer state.
|
|
1107
1470
|
|
|
@@ -1393,8 +1756,8 @@ separate cache/recovery authority.
|
|
|
1393
1756
|
> Owner: render vocabulary and the extract → prepare → record frame boundary. Runtime selects concrete services and calls this package; it does not re-own these tokens.
|
|
1394
1757
|
|
|
1395
1758
|
```ts
|
|
1396
|
-
import { Camera, MeshFilter, MeshRenderer } from '@forgeax/engine
|
|
1397
|
-
import { createRenderer } from '@forgeax/engine
|
|
1759
|
+
import { Camera, MeshFilter, MeshRenderer } from '@forgeax/engine/render';
|
|
1760
|
+
import { createRenderer } from '@forgeax/engine/runtime';
|
|
1398
1761
|
|
|
1399
1762
|
const renderer = await createRenderer(canvas);
|
|
1400
1763
|
const attached = renderer.attach(world);
|
|
@@ -1908,10 +2271,10 @@ ownership is [`@forgeax/engine-render-graph`](../render-graph/README.md).
|
|
|
1908
2271
|
Dynamic consumers use the same boundary explicitly:
|
|
1909
2272
|
|
|
1910
2273
|
```ts
|
|
1911
|
-
const { Camera, MeshFilter, MeshRenderer } = await import('@forgeax/engine
|
|
2274
|
+
const { Camera, MeshFilter, MeshRenderer } = await import('@forgeax/engine/render');
|
|
1912
2275
|
```
|
|
1913
2276
|
|
|
1914
|
-
`@forgeax/engine
|
|
2277
|
+
`@forgeax/engine/runtime` remains the host assembly entry for `createRenderer` and backend policy. Import `Materials` from `@forgeax/engine/render`. Runtime is not a compatibility barrel for render components.
|
|
1915
2278
|
|
|
1916
2279
|
Optional text, tilemap, and sprite authoring is intentionally isolated from the
|
|
1917
2280
|
base vocabulary:
|
|
@@ -2290,16 +2653,66 @@ existing observable state; resource disposal is distinct from JavaScript GC.
|
|
|
2290
2653
|
|
|
2291
2654
|
### Volumetric density coordinates
|
|
2292
2655
|
|
|
2293
|
-
`VolumetricFog.density` is a linear
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
Density integration uses fixed
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2656
|
+
`VolumetricFog.density` is a producer-owned linear 3D source tile. The volume
|
|
2657
|
+
integrator samples that tile in repeated world-space coordinates, combines the
|
|
2658
|
+
same three scales used by the approved fog fixture, and uses World time for the
|
|
2659
|
+
continuous advection term. The renderer keeps the source texture resident; it
|
|
2660
|
+
does not rebuild noise data for each frame. Its source expression is mapped to a
|
|
2661
|
+
signed density and clamped only at the optical-depth boundary so Beer-Lambert
|
|
2662
|
+
extinction remains non-negative.
|
|
2663
|
+
|
|
2664
|
+
Density integration uses fixed midpoints over 96 base ray intervals, including
|
|
2665
|
+
the clipped final segment. Segments near a selected PointLight or SpotLight
|
|
2666
|
+
receive four local midpoints to resolve the inverse-square peak without making
|
|
2667
|
+
the whole ray uniformly expensive. Frame identity does not rotate density
|
|
2668
|
+
samples. Shadow visibility uses matching froxel centers without per-frame XY
|
|
2669
|
+
jitter or stochastic byte dithering; shared PCF shadow filtering remains in
|
|
2670
|
+
effect.
|
|
2671
|
+
|
|
2672
|
+
## Camera Depth of Field
|
|
2673
|
+
|
|
2674
|
+
`DepthOfField` is the built-in Standard camera effect for ordinary meshes. Add it
|
|
2675
|
+
only to the active perspective camera; component removal or
|
|
2676
|
+
`maxRadiusPixels: 0` declares the exact zero-work path. The side and quality
|
|
2677
|
+
fields use the closed numeric values exported by this package:
|
|
2678
|
+
|
|
2679
|
+
```ts
|
|
2680
|
+
import {
|
|
2681
|
+
DepthOfField,
|
|
2682
|
+
DepthOfFieldQualityValue,
|
|
2683
|
+
DepthOfFieldSideValue,
|
|
2684
|
+
} from '@forgeax/engine-render';
|
|
2685
|
+
|
|
2686
|
+
world.addComponent(camera, {
|
|
2687
|
+
component: DepthOfField,
|
|
2688
|
+
data: {
|
|
2689
|
+
focusDistance: 8,
|
|
2690
|
+
fStop: 1.4,
|
|
2691
|
+
sensorHeight: 0.024,
|
|
2692
|
+
maxRadiusPixels: 16,
|
|
2693
|
+
quality: DepthOfFieldQualityValue.medium,
|
|
2694
|
+
blurSide: DepthOfFieldSideValue.both,
|
|
2695
|
+
},
|
|
2696
|
+
});
|
|
2697
|
+
```
|
|
2698
|
+
|
|
2699
|
+
The near, far and both presets share one signed thin-lens CoC. `quality` changes
|
|
2700
|
+
sampling density only, so it does not change the optical radius. The graph uses
|
|
2701
|
+
the Standard `RenderExtent`: full-resolution CoC/composite targets follow the
|
|
2702
|
+
current internal or output domain, and half-resolution targets use
|
|
2703
|
+
`max(1, ceil(axis / 2))`. Current temporal-v1 depth validity and the resolved
|
|
2704
|
+
multisampled depth binding remain the source of truth.
|
|
2705
|
+
|
|
2706
|
+
After a real `draw()` submission, read `renderer.inspect().depthOfField` for the
|
|
2707
|
+
requested component values, effective submitted values, graph/device
|
|
2708
|
+
generations, extents, pass count and descriptor-derived bytes. `lastKnownGood`
|
|
2709
|
+
and `effective` stay bound to the accepted submission; a failed candidate keeps
|
|
2710
|
+
the prior accepted projection and reports its failure reason for recovery.
|
|
2711
|
+
When the active camera has no `DepthOfField` component, the inspection remains
|
|
2712
|
+
available with `status: 'off'` and zero DoF graph facts. Invalid fields and an
|
|
2713
|
+
orthographic camera stay on the normal camera snapshot path with structured
|
|
2714
|
+
`error.code`, `error.expected`, `error.hint`, and `error.detail`; they report
|
|
2715
|
+
`status: 'invalid'` or `status: 'unsupported'` without admitting a DoF graph.
|
|
2716
|
+
The Engine Preview/game capability lab uses this same component on its ordinary
|
|
2717
|
+
mesh camera, and its settings control toggles component presence through the
|
|
2718
|
+
normal World path.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytic-fog-registration.d.ts","sourceRoot":"","sources":["../../src/assembly/analytic-fog-registration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,wBAAgB,8BAA8B,CAC5C,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,4BAA4B,CAAC,EAC9D,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO,GACjB,IAAI,CAON"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RenderSystem } from '../render-system';
|
|
2
|
+
/** Register the built-in FXAA post-process once for the active generation. */
|
|
3
|
+
export declare function registerFxaaPostProcess(renderSystem: Pick<RenderSystem, 'registerBuiltinPostProcess'>, source: string, registered: boolean): boolean;
|
|
4
|
+
//# sourceMappingURL=fxaa-registration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fxaa-registration.d.ts","sourceRoot":"","sources":["../../src/assembly/fxaa-registration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,8EAA8E;AAC9E,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,4BAA4B,CAAC,EAC9D,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,OAAO,GAClB,OAAO,CAQT"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FrameReceipt } from '../render-contract';
|
|
2
|
+
import type { RenderSystem } from '../render-system';
|
|
3
|
+
export declare function observeLodOcclusionForReceipt(renderSystem: Pick<RenderSystem, 'observeLodOcclusion'>, receipt: Pick<FrameReceipt, 'frameId' | 'deviceGeneration'>): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=lod-observation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lod-observation.d.ts","sourceRoot":"","sources":["../../src/assembly/lod-observation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,wBAAgB,6BAA6B,CAC3C,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,qBAAqB,CAAC,EACvD,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,kBAAkB,CAAC,GAC1D,OAAO,CAAC,IAAI,CAAC,CAKf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motion-blur-registration.d.ts","sourceRoot":"","sources":["../../src/assembly/motion-blur-registration.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,wBAAgB,6BAA6B,CAC3C,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,4BAA4B,CAAC,EAC9D,MAAM,EAAE,MAAM,GACb,IAAI,CASN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer-facade.d.ts","sourceRoot":"","sources":["../../src/assembly/renderer-facade.ts"],"names":[],"mappings":"AAWA,OAAO,EAGL,KAAK,sBAAsB,EAE5B,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"renderer-facade.d.ts","sourceRoot":"","sources":["../../src/assembly/renderer-facade.ts"],"names":[],"mappings":"AAWA,OAAO,EAGL,KAAK,sBAAsB,EAE5B,MAAM,kBAAkB,CAAC;AAO1B,OAAO,KAAK,EAEV,QAAQ,EAKR,aAAa,EAEd,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAGlE,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,0BAA0B,GAAG,QAAQ,CA8M7E;AAMD,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAG7C;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,sBAAsB,CA0BjG;AA2DD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CA6ChF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,CAMzE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Result, RhiDevice, RhiError, ShaderModule } from '@forgeax/engine-rhi';
|
|
2
|
+
import type { ManifestEntry } from '@forgeax/engine-types';
|
|
3
|
+
export type AsyncShaderModuleFactory = ((device: RhiDevice, desc: {
|
|
4
|
+
readonly code: string;
|
|
5
|
+
readonly label?: string;
|
|
6
|
+
}) => Promise<Result<ShaderModule, RhiError>>) | undefined;
|
|
7
|
+
/** Create the shared readiness prewarmer for temporal/fullscreen shader entries. */
|
|
8
|
+
export declare function createTemporalShaderPrewarmer(input: {
|
|
9
|
+
readonly device: RhiDevice;
|
|
10
|
+
readonly asyncCreateShaderModule: AsyncShaderModuleFactory;
|
|
11
|
+
readonly seedShaderModule: (label: string, module: ShaderModule) => void;
|
|
12
|
+
}): (entry: ManifestEntry | undefined, id: string) => Promise<void>;
|
|
13
|
+
//# sourceMappingURL=temporal-post-process-prewarm.d.ts.map
|