@woosh/meep-engine 3.18.0 → 3.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/editor/view/particles/effect/ParticleCurveEditorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleCurveEditorView.js +303 -120
- package/editor/view/particles/effect/ParticleGradientEditorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleGradientEditorView.js +108 -63
- package/editor/view/particles/effect/ParticleGraphEditorView.js +2 -2
- package/editor/view/particles/effect/ParticleNodeParametersView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleNodeParametersView.js +7 -46
- package/editor/view/particles/effect/particle-editor.css +60 -30
- package/package.json +1 -2
- package/samples/engine/README.md +1 -1
- package/src/core/binary/compression/decompress_bytes.d.ts +13 -0
- package/src/core/binary/compression/decompress_bytes.d.ts.map +1 -0
- package/src/core/binary/compression/decompress_bytes.js +28 -0
- package/src/core/collection/array/array_group_by.d.ts.map +1 -1
- package/src/core/collection/array/array_group_by.js +2 -11
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/HeightMapShape3D.js +4 -9
- package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.js +67 -22
- package/src/core/model/object/optional_clone.d.ts +7 -0
- package/src/core/model/object/optional_clone.d.ts.map +1 -0
- package/src/core/model/object/optional_equality.d.ts +10 -0
- package/src/core/model/object/optional_equality.d.ts.map +1 -0
- package/src/{shade/descriptor/util → core/model/object}/optional_equality.js +2 -2
- package/src/core/model/object/optional_hash.d.ts +9 -0
- package/src/core/model/object/optional_hash.d.ts.map +1 -0
- package/src/{shade/descriptor/util → core/model/object}/optional_hash.js +3 -3
- package/src/engine/asset/Asset.d.ts +1 -1
- package/src/engine/asset/Asset.d.ts.map +1 -1
- package/src/engine/asset/Asset.js +3 -1
- package/src/engine/asset/loaders/image/ImageDecoderWorker.js +12 -27
- package/src/engine/asset/loaders/image/prototypePNG.js +8 -7
- package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.d.ts.map +1 -1
- package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.js +9 -10
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +4 -0
- package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts +266 -0
- package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts.map +1 -0
- package/src/engine/graphics/ecs/particles/ParticleEffect.js +439 -0
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.d.ts +58 -0
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.d.ts.map +1 -0
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.js +219 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +178 -25
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/GPUParticleEmitterSystem.js +809 -310
- package/src/format/image/png/PNGReader.d.ts +7 -6
- package/src/format/image/png/PNGReader.d.ts.map +1 -1
- package/src/format/image/png/PNGReader.js +13 -12
- package/src/format/image/png/chunk/png_chunk_decode_iTXt.d.ts +3 -2
- package/src/format/image/png/chunk/png_chunk_decode_iTXt.d.ts.map +1 -1
- package/src/format/image/png/chunk/png_chunk_decode_iTXt.js +5 -4
- package/src/format/image/png/chunk/png_chunk_decode_zTXt.d.ts +3 -2
- package/src/format/image/png/chunk/png_chunk_decode_zTXt.d.ts.map +1 -1
- package/src/format/image/png/chunk/png_chunk_decode_zTXt.js +5 -4
- package/src/format/image/png/png_inflate.d.ts +3 -3
- package/src/format/image/png/png_inflate.d.ts.map +1 -1
- package/src/format/image/png/png_inflate.js +29 -39
- package/src/format/texture/ktx2/ktx2_read.d.ts +4 -4
- package/src/format/texture/ktx2/ktx2_read.d.ts.map +1 -1
- package/src/format/texture/ktx2/ktx2_read.js +18 -21
- package/src/shade/descriptor/binding/BindGroupLayoutDescriptor.js +1 -1
- package/src/shade/descriptor/binding/BindGroupLayoutEntry.js +3 -3
- package/src/shade/descriptor/pipeline/PipelineDescriptorBase.js +1 -1
- package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.js +1 -1
- package/src/shade/descriptor/pipeline/render/ColorTargetState.js +3 -3
- package/src/shade/descriptor/pipeline/render/FragmentState.js +1 -1
- package/src/shade/descriptor/pipeline/render/RenderPipelineDescriptor.js +3 -3
- package/src/shade/playground/particle_ecs/README.md +203 -0
- package/src/shade/playground/particle_ecs/bonfire_editor.d.ts +39 -0
- package/src/shade/playground/particle_ecs/bonfire_editor.d.ts.map +1 -0
- package/src/shade/playground/particle_ecs/bonfire_editor.js +315 -0
- package/src/shade/playground/particle_ecs/bonfire_effects.d.ts +145 -0
- package/src/shade/playground/particle_ecs/bonfire_effects.d.ts.map +1 -0
- package/src/shade/playground/particle_ecs/bonfire_effects.js +202 -0
- package/src/shade/playground/particle_ecs/bonfire_sprites.d.ts +23 -0
- package/src/shade/playground/particle_ecs/bonfire_sprites.d.ts.map +1 -0
- package/src/shade/playground/particle_ecs/bonfire_sprites.js +315 -0
- package/src/shade/playground/particle_ecs/bonfire_world.d.ts +86 -0
- package/src/shade/playground/particle_ecs/bonfire_world.d.ts.map +1 -0
- package/src/shade/playground/particle_ecs/bonfire_world.js +303 -0
- package/src/shade/playground/particle_ecs/effects/embers.json +1634 -0
- package/src/shade/playground/particle_ecs/effects/flame.json +1882 -0
- package/src/shade/playground/particle_ecs/effects/smoke.json +1860 -0
- package/src/shade/playground/particle_ecs/effects/soot.json +1606 -0
- package/src/shade/playground/particle_ecs/index.html +330 -0
- package/src/shade/playground/particle_ecs/main.d.ts +2 -0
- package/src/shade/playground/particle_ecs/main.d.ts.map +1 -0
- package/src/shade/playground/particle_ecs/main.js +566 -0
- package/src/shade/playground/particle_ecs/moonlit_environment.d.ts +16 -0
- package/src/shade/playground/particle_ecs/moonlit_environment.d.ts.map +1 -0
- package/src/shade/playground/particle_ecs/moonlit_environment.js +144 -0
- package/src/shade/playground/particle_editor/README.md +5 -4
- package/src/shade/playground/particle_system/particle_prototype.d.ts +7 -9
- package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_prototype.js +7 -9
- package/src/shade/playground/profile_hotkey.d.ts +58 -0
- package/src/shade/playground/profile_hotkey.d.ts.map +1 -0
- package/src/shade/playground/profile_hotkey.js +325 -0
- package/src/shade/playground/ssr_motion/README.md +22 -0
- package/src/shade/playground/ssr_motion/index.html +18 -0
- package/src/shade/playground/ssr_motion/main.d.ts +2 -0
- package/src/shade/playground/ssr_motion/main.d.ts.map +1 -0
- package/src/shade/playground/ssr_motion/main.js +149 -0
- package/src/shade/playground/ssr_variance/README.md +105 -0
- package/src/shade/playground/ssr_variance/capture.d.ts +16 -0
- package/src/shade/playground/ssr_variance/capture.d.ts.map +1 -0
- package/src/shade/playground/ssr_variance/capture.js +96 -0
- package/src/shade/playground/ssr_variance/index.html +23 -0
- package/src/shade/playground/ssr_variance/main.d.ts +2 -0
- package/src/shade/playground/ssr_variance/main.d.ts.map +1 -0
- package/src/shade/playground/ssr_variance/main.js +246 -0
- package/src/shade/playground/ssr_variance/reference.d.ts +18 -0
- package/src/shade/playground/ssr_variance/reference.d.ts.map +1 -0
- package/src/shade/playground/ssr_variance/reference.js +78 -0
- package/src/shade/playground/ssr_variance/scene.d.ts +11 -0
- package/src/shade/playground/ssr_variance/scene.d.ts.map +1 -0
- package/src/shade/playground/ssr_variance/scene.js +61 -0
- package/src/shade/playground/ssr_variance/statistics.d.ts +44 -0
- package/src/shade/playground/ssr_variance/statistics.d.ts.map +1 -0
- package/src/shade/playground/ssr_variance/statistics.js +51 -0
- package/src/shade/renderer/geometry/Geometry.d.ts.map +1 -1
- package/src/shade/renderer/geometry/Geometry.js +4 -7
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts.map +1 -1
- package/src/shade/renderer/loader/gltf/tiny-gltf.js +9 -3
- package/src/shade/renderer/loader/usd/usd_decode_image.d.ts +4 -4
- package/src/shade/renderer/loader/usd/usd_decode_image.d.ts.map +1 -1
- package/src/shade/renderer/loader/usd/usd_decode_image.js +4 -4
- package/src/shade/renderer/material/StandardShadeMaterial.js +2 -2
- package/src/shade/renderer/particles/DESIGN.md +745 -702
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +3 -1
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts +1 -1
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.js +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +9 -15
- package/src/shade/renderer/particles/graph/particle_group_library.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_group_library.js +8 -3
- package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts +0 -2
- package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_node_parameters.js +0 -9
- package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_node_presentation.js +0 -10
- package/src/shade/renderer/particles/graph/particle_ramp.d.ts +2 -8
- package/src/shade/renderer/particles/graph/particle_ramp.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_ramp.js +2 -8
- package/src/shade/renderer/particles/graph/validate_particle_graph.js +0 -6
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles_avboit.js +13 -4
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts +1 -12
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/InstructionStream.js +1 -12
- package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts +54 -60
- package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleVMISA.js +9 -17
- package/src/shade/renderer/particles/isa/particle_assembly.d.ts +0 -1
- package/src/shade/renderer/particles/isa/particle_assembly.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/particle_assembly.js +2 -16
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.d.ts +1 -1
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.js +1 -1
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.js +0 -14
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.js +3 -17
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.js +15 -36
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.js +1 -5
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.js +1 -8
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts +2 -11
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +2 -11
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +11 -4
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ParticleEmitter.js +431 -424
- package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.js +13 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts +3 -6
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +49 -17
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.js +0 -2
- package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.js +0 -2
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +1 -5
- package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.js +0 -2
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +1 -3
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.js +4 -20
- package/src/shade/renderer/particles/vm/chunk_particle_vm.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/chunk_particle_vm.js +2 -5
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.d.ts.map +1 -1
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.js +0 -2
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.d.ts.map +1 -1
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.js +0 -2
- package/src/shade/renderer/postprocess/ssr/SSR.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/SSR.js +2 -1
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.js +20 -5
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.js +19 -5
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.d.ts +4 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.js +137 -0
- package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts +0 -8
- package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.js +18 -114
- package/src/shade/renderer/scene/Mesh.js +1 -1
- package/src/shade/renderer/texture/ShadeTexture.js +1 -1
- package/src/shade/renderer/texture/source/texel_data_from_ktx2.d.ts +2 -2
- package/src/shade/renderer/texture/source/texel_data_from_ktx2.d.ts.map +1 -1
- package/src/shade/renderer/texture/source/texel_data_from_ktx2.js +3 -3
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.d.ts +0 -19
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.d.ts.map +0 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.js +0 -30
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.d.ts +0 -11
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.d.ts.map +0 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.js +0 -13
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_warmup.d.ts +0 -20
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_warmup.d.ts.map +0 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.d.ts +0 -14
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.d.ts.map +0 -1
- package/src/shade/renderer/particles/shaders/shader_particle_reclaim.d.ts +0 -50
- package/src/shade/renderer/particles/shaders/shader_particle_reclaim.d.ts.map +0 -1
- package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.d.ts +0 -17
- package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.d.ts.map +0 -1
- /package/src/{shade/descriptor/util → core/model/object}/optional_clone.js +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
3
|
+
<title>SSR cross · camera motion</title><style>
|
|
4
|
+
body{margin:24px;background:#14161a;color:#e7e9ee;font:14px/1.5 system-ui}h1{font-size:24px;margin-bottom:8px}p{color:#acb5c5;max-width:900px}main{display:grid;grid-template-columns:minmax(0,800px) 260px;gap:24px}canvas{display:block;width:100%;height:auto;background:#08090a;touch-action:none;outline:none}canvas:focus{outline:1px solid #749ac4}label{display:block;margin:0 0 14px}input[type=range],select{width:100%}button,select{font:inherit;padding:8px;background:#29313e;color:inherit;border:1px solid #4b586d;border-radius:4px}button{margin:0 4px 8px 0}output{float:right;font-variant-numeric:tabular-nums}a{color:#9ac9ff}#status{white-space:pre-wrap}.error{color:#ff9a9a}@media(max-width:900px){main{grid-template-columns:1fr}}
|
|
5
|
+
</style></head><body tabindex="0">
|
|
6
|
+
<h1>SSR cross · camera motion</h1>
|
|
7
|
+
<p>The same glowing cross and colored markers as the variance lab. Fixed exposure; final-image TAA defaults off. Drag to orbit, right-drag to pan, scroll to zoom, or focus the scene and use WASD.</p>
|
|
8
|
+
<main><section><canvas id="view" width="800" height="500" tabindex="0" aria-label="Interactive reflection scene"></canvas><p id="status" role="status">Initializing…</p><p><a href="../ssr_variance/">Exact variance and convergence measurements</a></p></section><aside>
|
|
9
|
+
<label>Floor roughness <output id="roughness-value">0.30</output><input id="roughness" aria-label="Floor roughness" type="range" min="0.01" max="1" step="0.01" value="0.3"></label>
|
|
10
|
+
<label>Camera <select id="motion"><option value="free">Free camera</option><option value="lateral">Lateral movement</option><option value="forward">Forward movement</option><option value="orbit">Orbit movement</option><option value="still">Still camera</option></select></label>
|
|
11
|
+
<label>Motion speed <output id="speed-value">1.0×</output><input id="speed" aria-label="Motion speed" type="range" min="0.1" max="3" step="0.1" value="1"></label>
|
|
12
|
+
<label>SSR history <select id="temporal"><option value="0">Parallax</option><option value="1">Surface only</option><option value="2">No temporal history</option></select></label>
|
|
13
|
+
<label><input id="spatial" type="checkbox" checked> Spatial denoising</label>
|
|
14
|
+
<label><input id="taa" type="checkbox"> Final-image TAA</label>
|
|
15
|
+
<label><input id="jitter" type="checkbox"> Raster jitter</label>
|
|
16
|
+
<button id="motion-pause">Pause motion</button><button id="render-pause">Pause rendering</button><button id="step">Step frame</button><button id="restart">Reset camera</button><button id="reset">Reset SSR history</button>
|
|
17
|
+
<p>Pausing motion keeps sampling. Pausing rendering freezes the displayed frame. Ordinary camera movement preserves history; resets and material changes clear it.</p>
|
|
18
|
+
</aside></main><script type="module" src="./main.js"></script></body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/ssr_motion/main.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import Vector3 from "../../../core/geom/Vector3.js";
|
|
2
|
+
import { PerspectiveCamera } from "../../renderer/camera/PerspectiveCamera.js";
|
|
3
|
+
import { OrbitalCameraController } from "../../renderer/camera/OrbitalCameraController.js";
|
|
4
|
+
import { Renderer } from "../../renderer/Renderer.js";
|
|
5
|
+
import { ShadeIndirectLightingMode } from "../../renderer/ShadeIndirectLightingMode.js";
|
|
6
|
+
import { FixedProjection } from "../ssr_variance/capture.js";
|
|
7
|
+
import { create_scene } from "../ssr_variance/scene.js";
|
|
8
|
+
|
|
9
|
+
const element = id => document.getElementById(id);
|
|
10
|
+
const params = new URLSearchParams(location.search);
|
|
11
|
+
const number = (name, fallback, min, max) => {
|
|
12
|
+
const value = Number(params.get(name) ?? fallback);
|
|
13
|
+
return Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : fallback;
|
|
14
|
+
};
|
|
15
|
+
element('roughness').value = number('roughness', 0.3, 0.01, 1);
|
|
16
|
+
element('speed').value = number('speed', 1, 0.1, 3);
|
|
17
|
+
if (['free', 'lateral', 'forward', 'orbit', 'still'].includes(params.get('motion'))) { element('motion').value = params.get('motion'); }
|
|
18
|
+
if (['0', '1', '2'].includes(params.get('temporal'))) { element('temporal').value = params.get('temporal'); }
|
|
19
|
+
element('spatial').checked = params.get('spatial') !== 'off';
|
|
20
|
+
for (const name of ['taa', 'jitter']) { element(name).checked = params.get(name) === 'on'; }
|
|
21
|
+
|
|
22
|
+
async function start() {
|
|
23
|
+
const canvas = element('view'), renderer = new Renderer();
|
|
24
|
+
await renderer.initialize({ context: canvas.getContext('webgpu'), pixelRatio: 1 });
|
|
25
|
+
renderer.feature_ssr_enabled = true;
|
|
26
|
+
renderer.indirect_lighting_mode = ShadeIndirectLightingMode.IBL;
|
|
27
|
+
for (const name of ['ssao', 'bloom', 'sharpening', 'motion_blur', 'automatic_exposure']) { renderer[`feature_${name}_enabled`] = false; }
|
|
28
|
+
renderer.exposure_compensation = 1;
|
|
29
|
+
renderer.internal_resolution_scale = 1;
|
|
30
|
+
renderer.feature_taa_enabled = element('taa').checked;
|
|
31
|
+
renderer.ssr.spatial_denoising_enabled = element('spatial').checked;
|
|
32
|
+
renderer.ssr.reprojection_mode = Number(element('temporal').value);
|
|
33
|
+
const world = await create_scene();
|
|
34
|
+
world.ground_material.roughness_factor = Number(element('roughness').value);
|
|
35
|
+
const floor_materials = new Map([[world.ground_material.roughness_factor, world.ground_material]]);
|
|
36
|
+
const camera = new PerspectiveCamera();
|
|
37
|
+
camera.near = 0.1; camera.far = 50; camera.fov = 50 * Math.PI / 180;
|
|
38
|
+
camera.aspect = canvas.width / canvas.height;
|
|
39
|
+
const controller = new OrbitalCameraController(camera, canvas);
|
|
40
|
+
controller.movement_speed_scale = 2;
|
|
41
|
+
renderer.resize(canvas.width, canvas.height);
|
|
42
|
+
class ProjectionControl extends FixedProjection {
|
|
43
|
+
record(frame) { if (!element('jitter').checked) { super.record(frame); } }
|
|
44
|
+
}
|
|
45
|
+
renderer.add_extension(new ProjectionControl());
|
|
46
|
+
let phase = 0, frame_count = 0, motion_paused = false;
|
|
47
|
+
let rendering_paused = params.has('manual'), stopped = false, request, last_time, last_status = 0;
|
|
48
|
+
const pose = (position, target) => {
|
|
49
|
+
camera.transform.setTranslation(...position);
|
|
50
|
+
camera.transform.lookAt(new Vector3(...target));
|
|
51
|
+
camera.transform.updateMatrix();
|
|
52
|
+
controller.look(new Vector3(...position), new Vector3(...target));
|
|
53
|
+
};
|
|
54
|
+
function status() {
|
|
55
|
+
element('status').textContent = `Frame ${frame_count} · roughness ${world.ground_material.roughness_factor.toFixed(2)}\n${rendering_paused ? 'Rendering paused' : motion_paused ? 'Motion paused · sampling continues' : 'Running'} · final TAA ${renderer.feature_taa_enabled ? 'on' : 'off'}`;
|
|
56
|
+
element('render-pause').textContent = rendering_paused ? 'Resume rendering' : 'Pause rendering';
|
|
57
|
+
}
|
|
58
|
+
function fail(error) {
|
|
59
|
+
stopped = true; cancelAnimationFrame(request);
|
|
60
|
+
element('status').textContent = error.message ?? String(error);
|
|
61
|
+
element('status').classList.add('error'); console.error(error);
|
|
62
|
+
}
|
|
63
|
+
function reset_camera() {
|
|
64
|
+
phase = 0; pose([0, 3.2, 7], [0, 0.2, -2]);
|
|
65
|
+
renderer.indicate_view_change(); status();
|
|
66
|
+
}
|
|
67
|
+
function set_roughness() {
|
|
68
|
+
const roughness = Number(element('roughness').value);
|
|
69
|
+
// GPU material contexts are immutable after upload. Reuse at most one
|
|
70
|
+
// material per slider value, and refresh the scene's material membership.
|
|
71
|
+
let material = floor_materials.get(roughness);
|
|
72
|
+
if (!material) {
|
|
73
|
+
material = world.ground_material.clone();
|
|
74
|
+
material.roughness_factor = roughness;
|
|
75
|
+
floor_materials.set(roughness, material);
|
|
76
|
+
}
|
|
77
|
+
if (material !== world.ground_material) {
|
|
78
|
+
world.scene.remove(world.ground);
|
|
79
|
+
world.ground.material = world.ground_material = material;
|
|
80
|
+
world.ground.updateMatrices();
|
|
81
|
+
renderer.graphics.materials.obtain(material);
|
|
82
|
+
world.scene.add(world.ground);
|
|
83
|
+
}
|
|
84
|
+
element('roughness-value').textContent = world.ground_material.roughness_factor.toFixed(2);
|
|
85
|
+
renderer.indicate_view_change(); status();
|
|
86
|
+
}
|
|
87
|
+
function frame(dt = 1 / 60, advance = true) {
|
|
88
|
+
if (stopped) { throw new Error('Renderer stopped after an error'); }
|
|
89
|
+
const mode = element('motion').value;
|
|
90
|
+
if (advance && !motion_paused) {
|
|
91
|
+
phase += dt * Number(element('speed').value) * 0.7;
|
|
92
|
+
const t = Math.sin(phase);
|
|
93
|
+
if (mode === 'free') { controller.update(dt); }
|
|
94
|
+
else if (mode === 'lateral') { pose([2 * t, 3.2, 7], [2 * t, 0.2, -2]); }
|
|
95
|
+
else if (mode === 'forward') { pose([0, 3.2, 7 + 1.6 * t], [0, 0.2, -2 + 1.6 * t]); }
|
|
96
|
+
else if (mode === 'orbit') { pose([4 * t, 3.2, 7 - 0.7 * t * t], [0, 0.2, -2]); }
|
|
97
|
+
}
|
|
98
|
+
if (renderer.render(camera, world.scene, dt) === false) { throw new Error('Renderer did not submit the frame'); }
|
|
99
|
+
frame_count++;
|
|
100
|
+
if (performance.now() - last_status > 250 || rendering_paused) { status(); last_status = performance.now(); }
|
|
101
|
+
return frame_count;
|
|
102
|
+
}
|
|
103
|
+
function tick(time) {
|
|
104
|
+
if (stopped) { return; }
|
|
105
|
+
const dt = last_time === undefined ? 1 / 60 : Math.min((time - last_time) / 1000, 0.05);
|
|
106
|
+
last_time = time;
|
|
107
|
+
try { if (!rendering_paused) { frame(dt); } }
|
|
108
|
+
catch (error) { fail(error); return; }
|
|
109
|
+
request = requestAnimationFrame(tick);
|
|
110
|
+
}
|
|
111
|
+
element('roughness').oninput = set_roughness;
|
|
112
|
+
element('speed').oninput = () => { element('speed-value').textContent = `${Number(element('speed').value).toFixed(1)}×`; };
|
|
113
|
+
element('speed').oninput();
|
|
114
|
+
element('motion').onchange = () => {
|
|
115
|
+
if (element('motion').value !== 'free') { reset_camera(); }
|
|
116
|
+
};
|
|
117
|
+
canvas.addEventListener('pointerdown', () => { canvas.focus(); element('motion').value = 'free'; motion_paused = false; element('motion-pause').textContent = 'Pause motion'; });
|
|
118
|
+
canvas.addEventListener('contextmenu', event => event.preventDefault());
|
|
119
|
+
element('temporal').onchange = () => { renderer.ssr.reprojection_mode = Number(element('temporal').value); };
|
|
120
|
+
element('spatial').onchange = () => { renderer.ssr.spatial_denoising_enabled = element('spatial').checked; };
|
|
121
|
+
element('taa').onchange = () => { renderer.feature_taa_enabled = element('taa').checked; renderer.indicate_view_change(); status(); };
|
|
122
|
+
element('jitter').onchange = () => renderer.indicate_view_change();
|
|
123
|
+
element('motion-pause').onclick = () => { motion_paused = !motion_paused; element('motion-pause').textContent = motion_paused ? 'Resume motion' : 'Pause motion'; status(); };
|
|
124
|
+
element('render-pause').onclick = () => { rendering_paused = !rendering_paused; status(); };
|
|
125
|
+
element('step').onclick = () => { rendering_paused = true; try { frame(); } catch (error) { fail(error); } };
|
|
126
|
+
element('restart').onclick = reset_camera;
|
|
127
|
+
element('reset').onclick = () => { renderer.ssr.reset_history = true; };
|
|
128
|
+
const device = renderer.graphics.device;
|
|
129
|
+
device.addEventListener('uncapturederror', event => fail(event.error));
|
|
130
|
+
device.lost.then(info => { if (!stopped) { fail(new Error(`GPU device lost: ${info.message}`)); } });
|
|
131
|
+
addEventListener('pagehide', () => { stopped = true; cancelAnimationFrame(request); controller.pointer.stop(); controller.keyboard.stop(); renderer.destroy(); device.destroy(); });
|
|
132
|
+
addEventListener('pageshow', event => { if (event.persisted) { location.reload(); } });
|
|
133
|
+
reset_camera(); set_roughness();
|
|
134
|
+
const lab = globalThis.ssr_motion_lab = {
|
|
135
|
+
renderer, camera, world, frame, pose,
|
|
136
|
+
step(n = 1, advance = true) {
|
|
137
|
+
if (!rendering_paused) { throw new Error('Pause rendering before exact stepping'); }
|
|
138
|
+
if (!Number.isSafeInteger(n) || n < 1) { throw new Error('Step count must be a positive integer'); }
|
|
139
|
+
for (let i = 0; i < n; i++) { frame(1 / 60, advance); }
|
|
140
|
+
return frame_count;
|
|
141
|
+
},
|
|
142
|
+
get frame_count() { return frame_count; },
|
|
143
|
+
};
|
|
144
|
+
request = requestAnimationFrame(tick);
|
|
145
|
+
return lab;
|
|
146
|
+
}
|
|
147
|
+
globalThis.ssr_motion_ready = start().catch(error => {
|
|
148
|
+
element('status').textContent = error.message ?? String(error); console.error(error); throw error;
|
|
149
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# SSR variance playground
|
|
2
|
+
|
|
3
|
+
Run `npm run dev` from the workspace, then open
|
|
4
|
+
<http://localhost:5173/src/shade/playground/ssr_variance/>.
|
|
5
|
+
|
|
6
|
+
The scene contains exactly two quads: a metallic ground plane at y=0 with roughness
|
|
7
|
+
0.30, and a vertical card facing the camera. The card carries an emissive cross
|
|
8
|
+
texture with two coloured details. The pattern does not add geometry or normal
|
|
9
|
+
edges. A constant dim environment supplies the SSR fallback.
|
|
10
|
+
|
|
11
|
+
The camera, scene, exposure (compensation 1), and 800×500 resolution are fixed.
|
|
12
|
+
Automatic exposure, SSAO, bloom, sharpening, and motion blur are disabled.
|
|
13
|
+
Main TAA defaults off and can be enabled independently.
|
|
14
|
+
Raster jitter defaults off separately: the production renderer still applies its
|
|
15
|
+
jitter sequence with TAA off, so a playground-only `FrameStart` extension freezes
|
|
16
|
+
the view's projection and jitter uniforms before rasterization. The jitter control
|
|
17
|
+
can restore production behaviour. SSR's stochastic frame index still advances.
|
|
18
|
+
|
|
19
|
+
## Exact capture
|
|
20
|
+
|
|
21
|
+
The page starts at frame **0** without submitting a frame or scheduling an animation
|
|
22
|
+
loop. **Capture frames 100–115** submits exactly 115 frames, each with dt=1/60, and
|
|
23
|
+
records frame 100 and every subsequent frame through 115. GPU completion and readback
|
|
24
|
+
are awaited between captures. Readback does not render extra frames. The renderer's
|
|
25
|
+
own count is checked against the experiment count; its zero-based SSR indices are
|
|
26
|
+
99–114 for these captures.
|
|
27
|
+
|
|
28
|
+
From the console:
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
const lab = await ssr_variance_ready;
|
|
32
|
+
const measurements = await lab.run(); // requires frame zero; finishes at frame 115
|
|
33
|
+
lab.frame_count; // 115, stays there indefinitely
|
|
34
|
+
lab.frames; // 16 PNG byte arrays and linear float32 stage arrays
|
|
35
|
+
lab.files; // Map<filename, Uint8Array | Float32Array>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For manual control on a fresh page:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
await ssr_variance_ready;
|
|
42
|
+
await ssr_variance_lab.step(99);
|
|
43
|
+
await ssr_variance_lab.step(1, true); // submit frame 100 and capture it
|
|
44
|
+
await ssr_variance_lab.step(15, true); // capture every frame from 101 through 115
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`step(n, true)` records each of its n frames. The standard `run()` additionally
|
|
48
|
+
calculates statistics and fills the page's results. Operations are serialized;
|
|
49
|
+
overlapping calls are rejected. Restarting or changing a control reloads the page,
|
|
50
|
+
giving every comparison a fresh renderer and the same frame-index sequence.
|
|
51
|
+
Appending `?run` runs the standard capture automatically. `spatial=off`,
|
|
52
|
+
`temporal=off`, `jitter=on`, and `taa=on` select controls in the query string.
|
|
53
|
+
Use `?run&jitter=on&taa=on` to measure the full production jitter/TAA path.
|
|
54
|
+
|
|
55
|
+
## Convergence
|
|
56
|
+
|
|
57
|
+
**Measure convergence**, `?convergence`, or `await lab.convergence()` on a fresh page
|
|
58
|
+
records frames 4, 8, 16, 32, 64, 100, 128, 256, and 512. It then averages the
|
|
59
|
+
pre-temporal input from frames 513–1536 into a separate float32 GPU texture. This
|
|
60
|
+
reference is never fed into the renderer. The table compares each checkpoint's
|
|
61
|
+
temporal and post-spatial output with that 1,024-sample mean, using linear luminance
|
|
62
|
+
RMSE and signed bias over the same reflection rectangle.
|
|
63
|
+
|
|
64
|
+
This catches a darkened or frozen output that scores well on temporal variance.
|
|
65
|
+
The reference estimates the prefilter's expected output; it is not ray-traced
|
|
66
|
+
ground truth and does not validate SSR visibility, ray reuse, or the prefilter's
|
|
67
|
+
own bias. Finite reference noise also sets an error floor. This pixel-aligned
|
|
68
|
+
comparison requires raster jitter off; the playground rejects it otherwise before
|
|
69
|
+
submitting any frames. Production history is bounded at 128 frames to
|
|
70
|
+
retain responsiveness, so convergence approaches a noise floor rather than an
|
|
71
|
+
unbounded average. `convergence.json`, `reference.f32`, checkpoint PNGs, and stage
|
|
72
|
+
arrays are available in `lab.files` after the run stops at frame 1536.
|
|
73
|
+
|
|
74
|
+
## Outputs and measurement
|
|
75
|
+
|
|
76
|
+
The playground copies resolve, spatial step 1, temporal reprojection, spatial step 2,
|
|
77
|
+
spatial step 4, pre-TAA scene colour, and final linear scene colour after TAA using
|
|
78
|
+
integer `textureLoad` calls before presentation.
|
|
79
|
+
Copies run only on recorded frames. Disabling spatial filtering makes those stage
|
|
80
|
+
captures refer to their bypass inputs. Resolve still performs its own ray reuse
|
|
81
|
+
and roughness-dependent source mip sampling.
|
|
82
|
+
|
|
83
|
+
Each displayed image is the actual canvas output copied immediately after render,
|
|
84
|
+
before the browser can discard it. Select a thumbnail to download its PNG. The JSON
|
|
85
|
+
download includes frame indices, controls, camera settings, and statistics. Full raw
|
|
86
|
+
captures and per-pixel mean/variance maps are available as typed arrays in `lab.files`.
|
|
87
|
+
An external driver can write each array directly, for example as a `fetch` request
|
|
88
|
+
body. Captures stay in CPU memory without duplicating HDR arrays into Blob storage.
|
|
89
|
+
RGBA captures are little-endian float32, row-major, top-left origin. SSR radiance
|
|
90
|
+
alpha carries the shader's internal variance estimate; scene alpha is opacity.
|
|
91
|
+
The mask uses 1 for upward-facing ground pixels and 0 elsewhere.
|
|
92
|
+
|
|
93
|
+
Temporal variance is computed **per pixel** over all 16 linear Rec.709 luminance
|
|
94
|
+
samples (0.2126 R + 0.7152 G + 0.0722 B), then averaged spatially. Population variance
|
|
95
|
+
divides by 16; sample variance divides by 15. RMS temporal sigma is the square root
|
|
96
|
+
of mean population variance. This is distinct from image contrast or the shader's
|
|
97
|
+
alpha-channel estimate. Non-finite readback values fail the measurement.
|
|
98
|
+
|
|
99
|
+
The JSON reports full-image, whole-ground, and reflection-region statistics. The
|
|
100
|
+
reflection region is the fixed rectangle x=200…599, y=250…459 intersected with the
|
|
101
|
+
frame-100 ground mask. It covers the main visible reflection, not its full rough
|
|
102
|
+
tail. The heatmap uses a fixed 0…0.10 linear-luminance sigma scale. With raster jitter
|
|
103
|
+
enabled, silhouette coverage can vary; the fixed-projection default avoids that
|
|
104
|
+
confound. A lower variance alone does not prove better image quality: blurring can
|
|
105
|
+
reduce variance while destroying reflected detail.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** TAA-off currently leaves raster jitter active. Freeze only this lab's view before rasterization. */
|
|
2
|
+
export class FixedProjection extends RenderExtension {
|
|
3
|
+
record(frame: any): void;
|
|
4
|
+
}
|
|
5
|
+
/** Exact textureLoad copies of production SSR stages, only on requested frames. */
|
|
6
|
+
export class CaptureSSR extends RenderExtension {
|
|
7
|
+
constructor(device: any);
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
textures: Map<any, any>;
|
|
10
|
+
device: any;
|
|
11
|
+
record(frame: any): void;
|
|
12
|
+
jitter: any[];
|
|
13
|
+
read(): Promise<{}>;
|
|
14
|
+
}
|
|
15
|
+
import { RenderExtension } from "../../renderer/extension/RenderExtension.js";
|
|
16
|
+
//# sourceMappingURL=capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/ssr_variance/capture.js"],"names":[],"mappings":"AA6BA,uGAAuG;AACvG;IAGI,yBAKC;CACJ;AAED,mFAAmF;AACnF;IAKI,yBAAsD;IAFtD,iBAAgB;IAChB,wBAAqB;IACU,YAAoB;IACnD,yBAuCC;IArCG,cAA2C;IAsC/C,oBAMC;CACJ;gCA/F+B,6CAA6C"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { RenderExtension } from "../../renderer/extension/RenderExtension.js";
|
|
2
|
+
import { FramePhase } from "../../renderer/extension/FramePhase.js";
|
|
3
|
+
import { SceneColor } from "../../renderer/extension/SceneColor.js";
|
|
4
|
+
import { ImageShader } from "../../renderer/shader/ImageShader.js";
|
|
5
|
+
import { CodeChunk } from "../../renderer/shader/compiler/CodeChunk.js";
|
|
6
|
+
import { chunk_decode_g_buffer_normal } from "../../renderer/shader/chunk/geometry/normal/chunk_decode_g_buffer_normal.js";
|
|
7
|
+
import { graph_image_pass } from "../../renderer/shader/graph/graph_image_pass.js";
|
|
8
|
+
import { graph_import_texture } from "../../renderer/shader/graph/graph_import_texture.js";
|
|
9
|
+
import { ShaderResourceSetDescriptor } from "../../renderer/shader/resource/ShaderResourceSetDescriptor.js";
|
|
10
|
+
import { ShaderDescriptor } from "../../renderer/shader/ShaderDescriptor.js";
|
|
11
|
+
import { GPUTextureContext } from "../../renderer/texture/GPUTextureContext.js";
|
|
12
|
+
import { download_texture_data } from "../../renderer/texture/download_texture_data.js";
|
|
13
|
+
|
|
14
|
+
const resources = new ShaderResourceSetDescriptor();
|
|
15
|
+
resources.createGroup().addTexture('tSource', 'unfilterable-float').addTexture('tNormal', 'uint');
|
|
16
|
+
const shader = new ImageShader({
|
|
17
|
+
descriptor: ShaderDescriptor.from({ label: 'SSR variance / capture', resources,
|
|
18
|
+
body: CodeChunk.from(/* language=WGSL */ `
|
|
19
|
+
struct Capture { @location(0) radiance: vec4f, @location(1) mask: vec4f }
|
|
20
|
+
@fragment fn main(@builtin(position) coord: vec4f) -> Capture {
|
|
21
|
+
let p = vec2i(coord.xy);
|
|
22
|
+
var output: Capture;
|
|
23
|
+
output.radiance = textureLoad(tSource, p, 0);
|
|
24
|
+
output.mask = vec4f(select(0.0, 1.0, decode_g_buffer_normal(textureLoad(tNormal, p, 0).xy).y > 0.99));
|
|
25
|
+
return output;
|
|
26
|
+
}`, [chunk_decode_g_buffer_normal]) }),
|
|
27
|
+
targets: [{ format: 'rgba32float' }, { format: 'rgba32float' }],
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/** TAA-off currently leaves raster jitter active. Freeze only this lab's view before rasterization. */
|
|
31
|
+
export class FixedProjection extends RenderExtension {
|
|
32
|
+
name = 'SSR variance / fixed projection';
|
|
33
|
+
phase = FramePhase.FrameStart;
|
|
34
|
+
record(frame) {
|
|
35
|
+
frame.view.setJitter(0, 0);
|
|
36
|
+
frame.view.camera.setViewportOffset(0, 0);
|
|
37
|
+
frame.view.camera.update();
|
|
38
|
+
frame.view.update_uniforms();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Exact textureLoad copies of production SSR stages, only on requested frames. */
|
|
43
|
+
export class CaptureSSR extends RenderExtension {
|
|
44
|
+
name = 'SSR variance / capture';
|
|
45
|
+
phase = FramePhase.BeforePresent;
|
|
46
|
+
enabled = false;
|
|
47
|
+
textures = new Map();
|
|
48
|
+
constructor(device) { super(); this.device = device; }
|
|
49
|
+
record(frame) {
|
|
50
|
+
if (!this.enabled) { return; }
|
|
51
|
+
this.jitter = Array.from(frame.view.jitter);
|
|
52
|
+
const passes = frame.graph.__pass_nodes;
|
|
53
|
+
const resolve = passes.find(p => p.name === 'SSR resolve / IBL');
|
|
54
|
+
const spatial1 = passes.find(p => p.name === 'SSR / spatial denoise with metadata');
|
|
55
|
+
const temporal = passes.find(p => p.name === 'SSR Reproject');
|
|
56
|
+
const spatial = passes.filter(p => p.name === 'SSR / spatial denoise');
|
|
57
|
+
const taa = passes.find(p => p.name === 'TAA');
|
|
58
|
+
if (!resolve || !temporal) { throw new Error('SSR stages missing from the frame graph'); }
|
|
59
|
+
const resolved = resolve.data['@output'][0];
|
|
60
|
+
const reprojected = temporal.data['@output'][0];
|
|
61
|
+
const stages = {
|
|
62
|
+
resolve: resolved,
|
|
63
|
+
spatial1: spatial1?.data['@output'][0] ?? resolved,
|
|
64
|
+
temporal: reprojected,
|
|
65
|
+
spatial2: spatial[0]?.data['@output'][0] ?? reprojected,
|
|
66
|
+
spatial4: spatial[1]?.data['@output'][0] ?? reprojected,
|
|
67
|
+
'pre-taa': taa?.data.ColourTexture ?? frame.get(SceneColor).color,
|
|
68
|
+
scene: frame.get(SceneColor).color,
|
|
69
|
+
};
|
|
70
|
+
const [width, height] = frame.resolution;
|
|
71
|
+
const texture = name => {
|
|
72
|
+
let t = this.textures.get(name);
|
|
73
|
+
if (!t || t.width !== width || t.height !== height) {
|
|
74
|
+
t?.destroy();
|
|
75
|
+
t = GPUTextureContext.fromTexture(this.device.createTexture({
|
|
76
|
+
label: `SSR variance / ${name}`, size: [width, height, 1], format: 'rgba32float',
|
|
77
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
|
|
78
|
+
}), this.device);
|
|
79
|
+
this.textures.set(name, t);
|
|
80
|
+
}
|
|
81
|
+
return graph_import_texture(frame.graph, t, `SSR variance / ${name}`);
|
|
82
|
+
};
|
|
83
|
+
let mask = texture('mask');
|
|
84
|
+
for (const [name, handle] of Object.entries(stages)) {
|
|
85
|
+
[, mask] = graph_image_pass({ graph: frame.graph, shader,
|
|
86
|
+
inputs: { tSource: handle, tNormal: resolve.data.tNormal }, output: [texture(name), mask] });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async read() {
|
|
90
|
+
const result = {};
|
|
91
|
+
for (const [name, texture] of this.textures) {
|
|
92
|
+
result[name] = await download_texture_data({ device: this.device, source: texture.gpu_texture });
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
3
|
+
<title>SSR variance lab</title><style>
|
|
4
|
+
body{margin:24px;background:#14161a;color:#e7e9ee;font:14px system-ui}h1{font-size:24px;margin-bottom:8px}p{color:#acb5c5;max-width:900px}button,select,a{font:inherit}button,select{padding:8px;background:#29313e;color:inherit;border:1px solid #4b586d;border-radius:4px}button:disabled{opacity:.45}a{color:#9ac9ff}#view{display:block;width:800px;max-width:100%;height:auto;background:#08090a;margin:18px 0}#status{white-space:pre-wrap}table{border-collapse:collapse;font-variant-numeric:tabular-nums}td,th{padding:8px 14px;border-bottom:1px solid #394150;text-align:right}td:first-child,th:first-child{text-align:left}.frames{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;max-width:1200px}.frames img{width:100%}figure{margin:0}figcaption{padding:5px}#heatmap{width:800px;max-width:100%}.error{color:#ff9a9a}
|
|
5
|
+
</style></head><body>
|
|
6
|
+
<h1>SSR variance lab</h1>
|
|
7
|
+
<p>One ground plane · roughness 0.30 · metallic 1 · textured emissive card · fixed camera and exposure. Starts at frame zero. Rendering advances only when requested.</p>
|
|
8
|
+
<p><a href="../ssr_motion/">Move the camera and adjust floor roughness</a></p>
|
|
9
|
+
<label>Spatial <select id="spatial"><option value="on">On</option><option value="off">Off</option></select></label>
|
|
10
|
+
<label>Temporal <select id="temporal"><option value="on">On</option><option value="off">Off</option></select></label>
|
|
11
|
+
<label>Raster jitter <select id="jitter"><option value="off">Off</option><option value="on">On</option></select></label>
|
|
12
|
+
<label>Main TAA <select id="taa"><option value="off">Off</option><option value="on">On</option></select></label>
|
|
13
|
+
<button id="step" disabled>Step one frame</button> <button id="run" disabled>Capture frames 100–115</button>
|
|
14
|
+
<button id="restart">Restart at frame zero</button>
|
|
15
|
+
<button id="converge" disabled>Measure convergence</button>
|
|
16
|
+
<canvas id="view" width="800" height="500"></canvas><div id="status">Initializing…</div>
|
|
17
|
+
<div id="results" hidden>
|
|
18
|
+
<p>Per-pixel temporal luminance variance, measured in linear HDR across 16 samples. Table: fixed reflection rectangle intersected with upward-facing ground pixels. Population variance uses ÷16; the JSON also includes sample variance (÷15), whole-ground and full-image measurements.</p>
|
|
19
|
+
<table><thead><tr><th>Stage</th><th>Mean variance</th><th>RMS σ</th><th>95th percentile σ</th><th>Mean peak-to-peak</th></tr></thead><tbody id="metrics"></tbody></table>
|
|
20
|
+
<p><a id="download">Download measurements JSON</a></p>
|
|
21
|
+
<p>Final scene temporal standard deviation. Fixed scale: black = 0, yellow = 0.10 linear luminance. Red outline = measurement rectangle.</p><canvas id="heatmap" width="800" height="500"></canvas>
|
|
22
|
+
<p>Recorded canvas output. Select a frame to download its full-resolution PNG.</p><div id="frames" class="frames"></div>
|
|
23
|
+
</div><div id="convergence-results" hidden><p>SSR error against a separate float32 average of 1,024 later pre-temporal samples (frames 513–1536). The reference is never fed into the rendered image. This measures convergence to the prefilter's mean, not physical ground truth.</p><table><thead><tr><th>Frame</th><th>Input RMSE</th><th>Temporal RMSE</th><th>After spatial RMSE</th><th>Temporal bias</th></tr></thead><tbody id="convergence-metrics"></tbody></table><p><a id="convergence-download">Download convergence JSON</a></p></div><script type="module" src="./main.js"></script></body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/ssr_variance/main.js"],"names":[],"mappings":""}
|