@woosh/meep-engine 2.165.3 → 2.165.5

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.
Files changed (159) hide show
  1. package/editor/Editor.d.ts +65 -65
  2. package/editor/SelectionVisualizer.d.ts +19 -19
  3. package/editor/actions/concrete/ActionUpdateTexture.d.ts +11 -11
  4. package/editor/actions/concrete/ArrayCopyAction.d.ts +19 -19
  5. package/editor/actions/concrete/ComponentAddAction.d.ts +14 -14
  6. package/editor/actions/concrete/ComponentRemoveAction.d.ts +10 -10
  7. package/editor/actions/concrete/EntityCreateAction.d.ts +17 -17
  8. package/editor/actions/concrete/EntityRemoveAction.d.ts +10 -10
  9. package/editor/actions/concrete/ModifyPatchSampler2DAction.d.ts +46 -46
  10. package/editor/actions/concrete/ModifyPatchTextureArray2DAction.d.ts +37 -37
  11. package/editor/actions/concrete/PaintTerrainOverlayAction.d.ts +22 -22
  12. package/editor/actions/concrete/PatchTerrainHeightAction.d.ts +18 -18
  13. package/editor/actions/concrete/SelectionAddAction.d.ts +18 -18
  14. package/editor/actions/concrete/SelectionClearAction.d.ts +8 -8
  15. package/editor/actions/concrete/SelectionRemoveAction.d.ts +9 -9
  16. package/editor/actions/concrete/TransformModifyAction.d.ts +16 -16
  17. package/editor/actions/concrete/WriteGridValueAction.d.ts +14 -14
  18. package/editor/ecs/EditorEntity.d.ts +15 -15
  19. package/editor/ecs/EditorEntitySystem.d.ts +7 -7
  20. package/editor/ecs/component/FieldDescriptor.d.ts +26 -26
  21. package/editor/ecs/component/FieldValueAdapter.d.ts +6 -6
  22. package/editor/ecs/component/TypeEditor.d.ts +24 -24
  23. package/editor/ecs/component/createFieldEditor.d.ts +8 -8
  24. package/editor/ecs/component/createObjectEditor.d.ts +13 -13
  25. package/editor/ecs/component/editors/ColorEditor.d.ts +6 -6
  26. package/editor/ecs/component/editors/HTMLElementEditor.d.ts +6 -6
  27. package/editor/ecs/component/editors/ImagePathEditor.d.ts +6 -6
  28. package/editor/ecs/component/editors/LargeStringEditor.d.ts +15 -15
  29. package/editor/ecs/component/editors/NumericIntervalEditor.d.ts +6 -6
  30. package/editor/ecs/component/editors/ObservedBooleanEditor.d.ts +6 -6
  31. package/editor/ecs/component/editors/ObservedEnumEditor.d.ts +6 -6
  32. package/editor/ecs/component/editors/ObservedIntegerEditor.d.ts +6 -6
  33. package/editor/ecs/component/editors/ObservedStringEditor.d.ts +6 -6
  34. package/editor/ecs/component/editors/Sampler2DEditor.d.ts +6 -6
  35. package/editor/ecs/component/editors/collection/ListEditor.d.ts +7 -7
  36. package/editor/ecs/component/editors/common/BitFlagsEditor.d.ts +11 -11
  37. package/editor/ecs/component/editors/common/EnumEditor.d.ts +7 -7
  38. package/editor/ecs/component/editors/common/makeV3_editor.d.ts +14 -14
  39. package/editor/ecs/component/editors/common/noEditor.d.ts +2 -2
  40. package/editor/ecs/component/editors/ecs/GridObstacleEditor.d.ts +6 -6
  41. package/editor/ecs/component/editors/ecs/MinimapMarkerEditor.d.ts +13 -13
  42. package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.d.ts +66 -66
  43. package/editor/ecs/component/editors/ecs/ParameterTrackEditor.d.ts +11 -11
  44. package/editor/ecs/component/editors/ecs/ParameterTrackSetEditor.d.ts +13 -13
  45. package/editor/ecs/component/editors/ecs/ParticleEmitterEditor.d.ts +51 -51
  46. package/editor/ecs/component/editors/ecs/ParticleEmitterLayerEditor.d.ts +47 -47
  47. package/editor/ecs/component/editors/ecs/SimulationStepDefinitionEditor.d.ts +15 -15
  48. package/editor/ecs/component/editors/ecs/Trail2DEditor.d.ts +29 -29
  49. package/editor/ecs/component/editors/ecs/TransformEditor.d.ts +19 -19
  50. package/editor/ecs/component/editors/ecs/terrain/SplatMappingEditor.d.ts +18 -18
  51. package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.d.ts +38 -38
  52. package/editor/ecs/component/editors/ecs/terrain/TerrainLayerEditor.d.ts +15 -15
  53. package/editor/ecs/component/editors/ecs/terrain/TerrainLayersEditor.d.ts +18 -18
  54. package/editor/ecs/component/editors/ecs/terrain/TerrainOverlayEditor.d.ts +17 -17
  55. package/editor/ecs/component/editors/geom/QuaternionEditor.d.ts +5 -5
  56. package/editor/ecs/component/editors/geom/Vector1Editor.d.ts +6 -6
  57. package/editor/ecs/component/editors/geom/Vector2Editor.d.ts +5 -5
  58. package/editor/ecs/component/editors/geom/Vector3Editor.d.ts +5 -5
  59. package/editor/ecs/component/editors/geom/Vector4Editor.d.ts +5 -5
  60. package/editor/ecs/component/editors/primitive/ArrayEditor.d.ts +7 -7
  61. package/editor/ecs/component/editors/primitive/BooleanEditor.d.ts +6 -6
  62. package/editor/ecs/component/editors/primitive/FunctionEditor.d.ts +6 -6
  63. package/editor/ecs/component/editors/primitive/NumberEditor.d.ts +7 -7
  64. package/editor/ecs/component/editors/primitive/ObjectEditor.d.ts +5 -5
  65. package/editor/ecs/component/editors/primitive/StringEditor.d.ts +6 -6
  66. package/editor/ecs/component/editors/three/BufferGeometryEditor.d.ts +6 -6
  67. package/editor/ecs/component/editors/three/MaterialEditor.d.ts +6 -6
  68. package/editor/ecs/component/editors/three/MeshEditor.d.ts +6 -6
  69. package/editor/ecs/component/editors/three/TextureEditor.d.ts +6 -6
  70. package/editor/ecs/component/findNearestRegisteredType.d.ts +7 -7
  71. package/editor/enableEditor.d.ts +13 -13
  72. package/editor/library/MeshLibrary.d.ts +12 -12
  73. package/editor/process/DisableGameUIProcess.d.ts +7 -7
  74. package/editor/process/EditorProcess.d.ts +18 -18
  75. package/editor/process/ObstacleGridDisplayProcess.d.ts +12 -12
  76. package/editor/process/ProcessEngine.d.ts +29 -29
  77. package/editor/process/SymbolicDisplayProcess.d.ts +18 -18
  78. package/editor/process/symbolic/ComponentSymbolicDisplay.d.ts +10 -10
  79. package/editor/process/symbolic/SymbolicDisplayInternalAPI.d.ts +64 -64
  80. package/editor/process/symbolic/buildThreeJSHelperEntity.d.ts +8 -8
  81. package/editor/process/symbolic/make3DSymbolicDisplay.d.ts +9 -9
  82. package/editor/process/symbolic/makeCameraSymbolicDisplay.d.ts +6 -6
  83. package/editor/process/symbolic/makeGridPositionSymbolDisplay.d.ts +5 -5
  84. package/editor/process/symbolic/makeHelperBoxGeometry.d.ts +2 -2
  85. package/editor/process/symbolic/makeHelperSphereGeometry.d.ts +8 -8
  86. package/editor/process/symbolic/makeLightSymbolicDisplay.d.ts +6 -6
  87. package/editor/process/symbolic/makeParticleEmitterSymbolicDisplay.d.ts +5 -5
  88. package/editor/process/symbolic/makePathSymbolicDisplay.d.ts +6 -6
  89. package/editor/process/symbolic/makePositionedIconDisplaySymbol.d.ts +8 -8
  90. package/editor/process/symbolic/makeSocketsSymbolicDisplay.d.ts +6 -6
  91. package/editor/process/symbolic/makeSolidArrowGeometry.d.ts +25 -25
  92. package/editor/process/symbolic/makeSoundEmitterSymbolicDisplay.d.ts +5 -5
  93. package/editor/process/symbolic/synchronizeTransform.d.ts +11 -11
  94. package/editor/tools/GridPaintTool.d.ts +16 -16
  95. package/editor/tools/SelectionTool.d.ts +26 -26
  96. package/editor/tools/TopDownCameraControlTool.d.ts +12 -12
  97. package/editor/tools/engine/Tool.d.ts +63 -63
  98. package/editor/tools/engine/ToolEngine.d.ts +22 -22
  99. package/editor/tools/engine/ToolState.d.ts +6 -6
  100. package/editor/tools/paint/TerrainHeightPaintTool.d.ts +17 -17
  101. package/editor/tools/paint/TerrainPaintTool.d.ts +30 -30
  102. package/editor/tools/paint/TerrainTexturePaintTool.d.ts +17 -17
  103. package/editor/tools/v2/TransformMode.d.ts +6 -6
  104. package/editor/view/EditorView.d.ts +38 -38
  105. package/editor/view/ecs/ComponentControlFactory.d.ts +18 -18
  106. package/editor/view/ecs/ComponentControlView.d.ts +23 -23
  107. package/editor/view/ecs/EntityEditor.d.ts +29 -29
  108. package/editor/view/ecs/EntityList.d.ts +21 -21
  109. package/editor/view/ecs/HierarchicalEntityListView.d.ts +7 -7
  110. package/editor/view/ecs/components/DatGuiController.d.ts +45 -45
  111. package/editor/view/ecs/components/GridObstacleController.d.ts +13 -13
  112. package/editor/view/ecs/components/color/GaugeView.d.ts +12 -12
  113. package/editor/view/ecs/components/common/AutoCanvasView.d.ts +19 -19
  114. package/editor/view/ecs/components/common/LineView.d.ts +11 -11
  115. package/editor/view/ecs/components/common/NumberController.d.ts +17 -17
  116. package/editor/view/ecs/components/particles/ColorParameterLUTController.d.ts +13 -13
  117. package/editor/view/makeEntityDecorators.d.ts +5 -5
  118. package/editor/view/tools/ToolSettingsView.d.ts +10 -10
  119. package/editor/view/tools/ToolView.d.ts +12 -12
  120. package/editor/view/v2/SplitView.d.ts +16 -16
  121. package/package.json +1 -1
  122. package/samples/generation/grid/GridTags.d.ts +15 -15
  123. package/samples/generation/grid/MirGridLayers.d.ts +7 -7
  124. package/samples/generation/rules/matcher_tag_occupied.d.ts +2 -2
  125. package/samples/generation/rules/matcher_tag_traversable.d.ts +2 -2
  126. package/samples/generation/rules/matcher_tag_unoccupied.d.ts +2 -2
  127. package/src/core/geom/3d/sphere/harmonics/sh3_convolve_with_zh.d.ts +21 -0
  128. package/src/core/geom/3d/sphere/harmonics/sh3_convolve_with_zh.d.ts.map +1 -0
  129. package/src/core/geom/3d/sphere/harmonics/sh3_convolve_with_zh.js +53 -0
  130. package/src/core/geom/3d/sphere/harmonics/sh3_ggx_zh.d.ts +18 -0
  131. package/src/core/geom/3d/sphere/harmonics/sh3_ggx_zh.d.ts.map +1 -0
  132. package/src/core/geom/3d/sphere/harmonics/sh3_ggx_zh.js +30 -0
  133. package/src/engine/graphics/ecs/light/LightContext.d.ts +8 -0
  134. package/src/engine/graphics/ecs/light/LightContext.d.ts.map +1 -1
  135. package/src/engine/graphics/ecs/light/LightContext.js +33 -3
  136. package/src/engine/graphics/ecs/light/LightSystem.d.ts.map +1 -1
  137. package/src/engine/graphics/ecs/light/LightSystem.js +13 -6
  138. package/src/engine/graphics/ecs/light/binding/three/ThreeLightBinding.d.ts +12 -0
  139. package/src/engine/graphics/ecs/light/binding/three/ThreeLightBinding.d.ts.map +1 -1
  140. package/src/engine/graphics/ecs/light/binding/three/ThreeLightBinding.js +184 -6
  141. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.js +381 -362
  142. package/src/engine/graphics/sh3/lpg/LightProbeGridBaker.d.ts +43 -4
  143. package/src/engine/graphics/sh3/lpg/LightProbeGridBaker.d.ts.map +1 -1
  144. package/src/engine/graphics/sh3/lpg/LightProbeGridBaker.js +72 -5
  145. package/src/engine/graphics/sh3/lpg/bake_probe_sh3_resampled.d.ts +37 -0
  146. package/src/engine/graphics/sh3/lpg/bake_probe_sh3_resampled.d.ts.map +1 -0
  147. package/src/engine/graphics/sh3/lpg/bake_probe_sh3_resampled.js +298 -0
  148. package/src/engine/graphics/sh3/lpg/light_probe_grid_dilate.d.ts +18 -0
  149. package/src/engine/graphics/sh3/lpg/light_probe_grid_dilate.d.ts.map +1 -0
  150. package/src/engine/graphics/sh3/lpg/light_probe_grid_dilate.js +142 -0
  151. package/src/engine/graphics/sh3/path_tracer/PathTracer.d.ts +6 -0
  152. package/src/engine/graphics/sh3/path_tracer/PathTracer.d.ts.map +1 -1
  153. package/src/engine/graphics/sh3/path_tracer/PathTracer.js +1 -1
  154. package/src/engine/graphics/shadows/ShadowMapRenderer.d.ts +10 -0
  155. package/src/engine/graphics/shadows/ShadowMapRenderer.d.ts.map +1 -1
  156. package/src/engine/graphics/shadows/ShadowMapRenderer.js +83 -10
  157. package/src/engine/graphics/shadows/testPointShadowMapRendering.d.ts +2 -0
  158. package/src/engine/graphics/shadows/testPointShadowMapRendering.d.ts.map +1 -0
  159. package/src/engine/graphics/shadows/testPointShadowMapRendering.js +242 -0
@@ -1,9 +1,45 @@
1
1
  export class LightProbeGridBaker {
2
+ /**
3
+ * Directions sampled one hit deep per probe. Direct light and sky converge with this count,
4
+ * every candidate also feeds the back-face visibility statistic.
5
+ *
6
+ * The defaults match the directional noise of the previous 4096-full-path bake at roughly half
7
+ * the cost (candidates are single-segment); raise both counts together for higher quality.
8
+ * @type {number}
9
+ */
10
+ candidate_count: number;
11
+ /**
12
+ * Full multi-bounce path traces per probe, re-sampled from the candidates by first-bounce
13
+ * importance. Only the indirect part of the estimate rides on these, so far fewer are needed
14
+ * than candidates.
15
+ * @type {number}
16
+ */
17
+ indirect_sample_count: number;
18
+ /**
19
+ * Total path depth including the first hit
20
+ * @type {number}
21
+ */
22
+ max_bounce_count: number;
23
+ /**
24
+ * GGX roughness of the prefilter cone applied to the baked radiance SH, in [0, 1].
25
+ * Attenuates band 1 before the 2-band truncation, suppressing directional overshoot that would
26
+ * otherwise reconstruct as negative irradiance (light/shadow bleeding) on the far side of a
27
+ * strong light. Higher values trade directionality for stability.
28
+ * @type {number}
29
+ */
30
+ filter_ggx_alpha: number;
31
+ /**
32
+ * A probe whose fraction of candidate rays hitting geometry back-faces exceeds this threshold is
33
+ * considered to be inside geometry. Such probes are filled from their valid neighbors after the
34
+ * bake instead of contributing interior darkness to trilinear filtering.
35
+ * @type {number}
36
+ */
37
+ interior_backface_threshold: number;
2
38
  /**
3
39
  * Renderer used to bake probes. Exposed so callers (and tests) can configure the underlying scene.
4
- * @return {ProbeRenderer}
40
+ * @return {PathTracerProbeRenderer}
5
41
  */
6
- get renderer(): ProbeRenderer;
42
+ get renderer(): PathTracerProbeRenderer;
7
43
  /**
8
44
  * Bake light probes
9
45
  * @returns {TaskGroup}
@@ -13,12 +49,15 @@ export class LightProbeGridBaker {
13
49
  bake(grid: LightProbeGrid, ecd: EntityComponentDataset): TaskGroup;
14
50
  /**
15
51
  *
16
- * @param {ProbeRenderer} renderer
52
+ * @param {PathTracerProbeRenderer} renderer
17
53
  * @param {LightProbeGrid} grid
18
54
  * @param {number} probe_index
55
+ * @returns {boolean} true if the probe is valid, false if it appears to be inside geometry and
56
+ * should be filled from its neighbors
19
57
  */
20
- bake_probe(renderer: ProbeRenderer, grid: LightProbeGrid, probe_index: number): void;
58
+ bake_probe(renderer: PathTracerProbeRenderer, grid: LightProbeGrid, probe_index: number): boolean;
21
59
  #private;
22
60
  }
61
+ import { PathTracerProbeRenderer } from "../lpv/PathTracerProbeRenderer.js";
23
62
  import TaskGroup from "../../../../core/process/task/TaskGroup.js";
24
63
  //# sourceMappingURL=LightProbeGridBaker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LightProbeGridBaker.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/sh3/lpg/LightProbeGridBaker.js"],"names":[],"mappings":"AAWA;IAII;;;OAGG;IACH,8BAEC;IAED;;;;;OAKG;IACH,yDAJa,SAAS,CA2CrB;IAED;;;;;OAKG;IACH,uEAFW,MAAM,QAYhB;;CACJ;sBArFqB,4CAA4C"}
1
+ {"version":3,"file":"LightProbeGridBaker.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/sh3/lpg/LightProbeGridBaker.js"],"names":[],"mappings":"AAgBA;IAII;;;;;;;OAOG;IACH,iBAFU,MAAM,CAEO;IAEvB;;;;;OAKG;IACH,uBAFU,MAAM,CAEY;IAE5B;;;OAGG;IACH,kBAFU,MAAM,CAEK;IAErB;;;;;;OAMG;IACH,kBAFU,MAAM,CAEO;IAEvB;;;;;OAKG;IACH,6BAFU,MAAM,CAEmB;IAEnC;;;OAGG;IACH,wCAEC;IAED;;;;;OAKG;IACH,yDAJa,SAAS,CAoDrB;IAED;;;;;;;OAOG;IACH,qBANW,uBAAuB,qCAEvB,MAAM,GACJ,OAAO,CAuBnB;;CACJ;wCAlJuC,mCAAmC;sBAJrD,4CAA4C"}
@@ -1,21 +1,67 @@
1
1
  import { assert } from "../../../../core/assert.js";
2
2
  import { array_copy } from "../../../../core/collection/array/array_copy.js";
3
+ import { sh3_convolve_with_zh } from "../../../../core/geom/3d/sphere/harmonics/sh3_convolve_with_zh.js";
4
+ import { sh3_ggx_zh } from "../../../../core/geom/3d/sphere/harmonics/sh3_ggx_zh.js";
3
5
  import TaskGroup from "../../../../core/process/task/TaskGroup.js";
4
6
  import { actionTask } from "../../../../core/process/task/util/actionTask.js";
5
7
  import { countTask } from "../../../../core/process/task/util/countTask.js";
6
8
  import { promiseTask } from "../../../../core/process/task/util/promiseTask.js";
7
9
  import { PathTracerProbeRenderer } from "../lpv/PathTracerProbeRenderer.js";
10
+ import { bake_probe_sh3_resampled } from "./bake_probe_sh3_resampled.js";
11
+ import { light_probe_grid_dilate } from "./light_probe_grid_dilate.js";
8
12
 
9
13
  const __position = new Float32Array(3);
10
14
  const __sh3_rgb = new Float64Array(27);
15
+ const __zh = new Float64Array(3);
11
16
 
12
17
  export class LightProbeGridBaker {
13
18
 
14
19
  #ren = new PathTracerProbeRenderer();
15
20
 
21
+ /**
22
+ * Directions sampled one hit deep per probe. Direct light and sky converge with this count,
23
+ * every candidate also feeds the back-face visibility statistic.
24
+ *
25
+ * The defaults match the directional noise of the previous 4096-full-path bake at roughly half
26
+ * the cost (candidates are single-segment); raise both counts together for higher quality.
27
+ * @type {number}
28
+ */
29
+ candidate_count = 2048;
30
+
31
+ /**
32
+ * Full multi-bounce path traces per probe, re-sampled from the candidates by first-bounce
33
+ * importance. Only the indirect part of the estimate rides on these, so far fewer are needed
34
+ * than candidates.
35
+ * @type {number}
36
+ */
37
+ indirect_sample_count = 512;
38
+
39
+ /**
40
+ * Total path depth including the first hit
41
+ * @type {number}
42
+ */
43
+ max_bounce_count = 5;
44
+
45
+ /**
46
+ * GGX roughness of the prefilter cone applied to the baked radiance SH, in [0, 1].
47
+ * Attenuates band 1 before the 2-band truncation, suppressing directional overshoot that would
48
+ * otherwise reconstruct as negative irradiance (light/shadow bleeding) on the far side of a
49
+ * strong light. Higher values trade directionality for stability.
50
+ * @type {number}
51
+ */
52
+ filter_ggx_alpha = 0.5;
53
+
54
+ /**
55
+ * A probe whose fraction of candidate rays hitting geometry back-faces exceeds this threshold is
56
+ * considered to be inside geometry. Such probes are filled from their valid neighbors after the
57
+ * bake instead of contributing interior darkness to trilinear filtering.
58
+ * @type {number}
59
+ */
60
+ interior_backface_threshold = 0.25;
61
+
16
62
  /**
17
63
  * Renderer used to bake probes. Exposed so callers (and tests) can configure the underlying scene.
18
- * @return {ProbeRenderer}
64
+ * @return {PathTracerProbeRenderer}
19
65
  */
20
66
  get renderer() {
21
67
  return this.#ren;
@@ -40,8 +86,10 @@ export class LightProbeGridBaker {
40
86
 
41
87
  const probe_count = grid.count;
42
88
 
89
+ const validity = new Uint8Array(probe_count);
90
+
43
91
  const tBakeIrradiance = countTask(0, probe_count, (probe_index) => {
44
- this.bake_probe(renderer, grid, probe_index);
92
+ validity[probe_index] = this.bake_probe(renderer, grid, probe_index) ? 1 : 0;
45
93
 
46
94
  });
47
95
  tBakeIrradiance.estimatedDuration = probe_count;
@@ -57,6 +105,13 @@ export class LightProbeGridBaker {
57
105
  const tFinish = actionTask(() => {
58
106
  renderer.bake_end();
59
107
 
108
+ // probes inside geometry take on the average of their valid neighbors, so trilinear
109
+ // sampling near walls does not blend in interior darkness
110
+ light_probe_grid_dilate(
111
+ grid.harmonics, validity,
112
+ grid.resolution_x, grid.resolution_y, grid.resolution_z
113
+ );
114
+
60
115
  grid.incrementVersion();
61
116
  });
62
117
 
@@ -70,19 +125,31 @@ export class LightProbeGridBaker {
70
125
 
71
126
  /**
72
127
  *
73
- * @param {ProbeRenderer} renderer
128
+ * @param {PathTracerProbeRenderer} renderer
74
129
  * @param {LightProbeGrid} grid
75
130
  * @param {number} probe_index
131
+ * @returns {boolean} true if the probe is valid, false if it appears to be inside geometry and
132
+ * should be filled from its neighbors
76
133
  */
77
134
  bake_probe(renderer, grid, probe_index) {
78
135
  assert.isNonNegativeInteger(probe_index, 'probe_index');
79
136
 
80
137
  grid.probe_position(__position, 0, probe_index);
81
138
 
82
- // render a full SH3 probe with the shared facilities
83
- renderer.bake(__position, 0, __sh3_rgb, 0);
139
+ const backface_fraction = bake_probe_sh3_resampled(
140
+ __sh3_rgb, 0,
141
+ renderer.scene, renderer.tracer, renderer.random,
142
+ __position, 0,
143
+ this.candidate_count, this.indirect_sample_count, this.max_bounce_count
144
+ );
145
+
146
+ // prefilter the radiance with a GGX cone before truncation, taming band-1 overshoot
147
+ sh3_ggx_zh(__zh, 0, this.filter_ggx_alpha);
148
+ sh3_convolve_with_zh(__sh3_rgb, 0, __sh3_rgb, 0, __zh[0], __zh[1], __zh[2], 3);
84
149
 
85
150
  // keep only bands 0 and 1: the coefficient-major layout puts them in the first 12 values
86
151
  array_copy(__sh3_rgb, 0, grid.harmonics, probe_index * 12, 12);
152
+
153
+ return backface_fraction <= this.interior_backface_threshold;
87
154
  }
88
155
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Bake a single SH3 radiance probe with importance re-sampling and back-face visibility rejection.
3
+ *
4
+ * Two-phase estimator, split by path length:
5
+ *
6
+ * 1. Candidate phase: `candidate_count` uniform sphere directions are traced one hit deep. Misses
7
+ * take the background radiance; front-face hits take `emissive + albedo * direct_light`, both of
8
+ * which are cheap and low-noise, and every such sample is projected onto the SH. Directions whose
9
+ * first hit is a back face see the inside of geometry — they are rejected and the estimate is
10
+ * renormalized over the remaining directions, so interior darkness does not bleed into the probe.
11
+ *
12
+ * 2. Indirect phase: `indirect_sample_count` continuations are picked from the hit candidates with
13
+ * probability proportional to their first-bounce luminance (resampled importance sampling — the
14
+ * selection a weighted reservoir would produce, drawn from the materialized candidate table), and
15
+ * only these few directions pay for a full multi-bounce path trace. The 1/p_hat re-sampling weight
16
+ * keeps the combined estimator unbiased.
17
+ *
18
+ * The two phases sum to the same integral as tracing every direction to full depth, but direct light
19
+ * converges with the full candidate count while the expensive multi-bounce work is spent where the
20
+ * first bounce found energy.
21
+ *
22
+ * @param {number[]|Float32Array|Float64Array} out 27 values: SH3 radiance, coefficient-major RGB triplets
23
+ * @param {number} out_offset
24
+ * @param {PathTracedScene} scene
25
+ * @param {PathTracer} tracer
26
+ * @param {function():number} random
27
+ * @param {number[]|Float32Array} position probe position
28
+ * @param {number} position_offset
29
+ * @param {number} candidate_count directions sampled one hit deep, at least 1
30
+ * @param {number} indirect_sample_count full path-traced continuations re-sampled from the candidates
31
+ * @param {number} max_bounce_count total path depth including the first hit, at least 1
32
+ * @returns {number} fraction of candidate directions whose first hit was a back face, in [0, 1]
33
+ */
34
+ export function bake_probe_sh3_resampled(out: number[] | Float32Array | Float64Array, out_offset: number, scene: PathTracedScene, tracer: PathTracer, random: () => number, position: number[] | Float32Array, position_offset: number, candidate_count: number, indirect_sample_count: number, max_bounce_count: number): number;
35
+ import { PathTracedScene } from "../path_tracer/PathTracedScene.js";
36
+ import { PathTracer } from "../path_tracer/PathTracer.js";
37
+ //# sourceMappingURL=bake_probe_sh3_resampled.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bake_probe_sh3_resampled.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/sh3/lpg/bake_probe_sh3_resampled.js"],"names":[],"mappings":"AAyEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,8CAZW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,cAClC,MAAM,SACN,eAAe,UACf,UAAU,gBACC,MAAM,YACjB,MAAM,EAAE,GAAC,YAAY,mBACrB,MAAM,mBACN,MAAM,yBACN,MAAM,oBACN,MAAM,GACJ,MAAM,CAiMlB;gCAlS+B,mCAAmC;2BACjB,8BAA8B"}
@@ -0,0 +1,298 @@
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { rgb_to_luminance } from "../../../../core/color/rgb_to_luminance.js";
3
+ import { Ray3 } from "../../../../core/geom/3d/ray/Ray3.js";
4
+ import { sh3_basis_at } from "../../../../core/geom/3d/sphere/harmonics/sh3_basis_at.js";
5
+ import { sh3_vector_accumulate } from "../../../../core/geom/3d/sphere/harmonics/sh3_vector_accumulate.js";
6
+ import { random_point_on_sphere } from "../../../../core/geom/random/random_point_on_sphere.js";
7
+ import { v3_dot } from "../../../../core/geom/vec3/v3_dot.js";
8
+ import { PathTracedScene } from "../path_tracer/PathTracedScene.js";
9
+ import { PathTracer, SELF_OCCLUSION_OFFSET } from "../path_tracer/PathTracer.js";
10
+ import { getBiasedNormalSample } from "../path_tracer/sampling/getBiasedNormalSample.js";
11
+
12
+ /**
13
+ * Per-candidate record: direction(3), offset surface position(3), shading normal(3), albedo(3)
14
+ * @type {number}
15
+ */
16
+ const CANDIDATE_STRIDE = 12;
17
+
18
+ /**
19
+ * Fraction of the mean candidate luminance added to every re-sampling weight. Keeps surfaces that
20
+ * receive no direct light selectable — they may still return indirect radiance — so the estimator
21
+ * stays unbiased.
22
+ * @type {number}
23
+ */
24
+ const RESAMPLING_WEIGHT_FLOOR = 0.1;
25
+
26
+ const __ray = new Ray3();
27
+ const __hit = [];
28
+ const __surface = new Float32Array(9);
29
+ const __direct = new Float32Array(3);
30
+ const __radiance = new Float32Array(3);
31
+ const __basis = new Float64Array(9);
32
+ const __sh_direct = new Float64Array(27);
33
+ const __sh_indirect = new Float64Array(27);
34
+
35
+ let __candidates = new Float32Array(0);
36
+ let __p_hat = new Float64Array(0);
37
+ let __cumulative_p_hat = new Float64Array(0);
38
+
39
+ /**
40
+ *
41
+ * @param {number} candidate_count
42
+ */
43
+ function ensure_pool_capacity(candidate_count) {
44
+ if (__p_hat.length < candidate_count) {
45
+ __candidates = new Float32Array(candidate_count * CANDIDATE_STRIDE);
46
+ __p_hat = new Float64Array(candidate_count);
47
+ __cumulative_p_hat = new Float64Array(candidate_count);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Index of the candidate owning the cumulative-weight interval that contains `value`
53
+ * @param {number} value in [0, cumulative[count - 1])
54
+ * @param {number} count
55
+ * @returns {number}
56
+ */
57
+ function pick_candidate(value, count) {
58
+ let low = 0;
59
+ let high = count - 1;
60
+
61
+ while (low < high) {
62
+ const mid = (low + high) >> 1;
63
+
64
+ if (__cumulative_p_hat[mid] > value) {
65
+ high = mid;
66
+ } else {
67
+ low = mid + 1;
68
+ }
69
+ }
70
+
71
+ return low;
72
+ }
73
+
74
+ /**
75
+ * Bake a single SH3 radiance probe with importance re-sampling and back-face visibility rejection.
76
+ *
77
+ * Two-phase estimator, split by path length:
78
+ *
79
+ * 1. Candidate phase: `candidate_count` uniform sphere directions are traced one hit deep. Misses
80
+ * take the background radiance; front-face hits take `emissive + albedo * direct_light`, both of
81
+ * which are cheap and low-noise, and every such sample is projected onto the SH. Directions whose
82
+ * first hit is a back face see the inside of geometry — they are rejected and the estimate is
83
+ * renormalized over the remaining directions, so interior darkness does not bleed into the probe.
84
+ *
85
+ * 2. Indirect phase: `indirect_sample_count` continuations are picked from the hit candidates with
86
+ * probability proportional to their first-bounce luminance (resampled importance sampling — the
87
+ * selection a weighted reservoir would produce, drawn from the materialized candidate table), and
88
+ * only these few directions pay for a full multi-bounce path trace. The 1/p_hat re-sampling weight
89
+ * keeps the combined estimator unbiased.
90
+ *
91
+ * The two phases sum to the same integral as tracing every direction to full depth, but direct light
92
+ * converges with the full candidate count while the expensive multi-bounce work is spent where the
93
+ * first bounce found energy.
94
+ *
95
+ * @param {number[]|Float32Array|Float64Array} out 27 values: SH3 radiance, coefficient-major RGB triplets
96
+ * @param {number} out_offset
97
+ * @param {PathTracedScene} scene
98
+ * @param {PathTracer} tracer
99
+ * @param {function():number} random
100
+ * @param {number[]|Float32Array} position probe position
101
+ * @param {number} position_offset
102
+ * @param {number} candidate_count directions sampled one hit deep, at least 1
103
+ * @param {number} indirect_sample_count full path-traced continuations re-sampled from the candidates
104
+ * @param {number} max_bounce_count total path depth including the first hit, at least 1
105
+ * @returns {number} fraction of candidate directions whose first hit was a back face, in [0, 1]
106
+ */
107
+ export function bake_probe_sh3_resampled(
108
+ out, out_offset,
109
+ scene, tracer, random,
110
+ position, position_offset,
111
+ candidate_count, indirect_sample_count, max_bounce_count
112
+ ) {
113
+ assert.isArrayLike(out, 'out');
114
+ assert.isNonNegativeInteger(out_offset, 'out_offset');
115
+ assert.isInstanceOf(scene, PathTracedScene, 'scene');
116
+ assert.isInstanceOf(tracer, PathTracer, 'tracer');
117
+ assert.isFunction(random, 'random');
118
+ assert.isArrayLike(position, 'position');
119
+ assert.isNonNegativeInteger(position_offset, 'position_offset');
120
+ assert.isNonNegativeInteger(candidate_count, 'candidate_count');
121
+ assert.greaterThan(candidate_count, 0, 'candidate_count');
122
+ assert.isNonNegativeInteger(indirect_sample_count, 'indirect_sample_count');
123
+ assert.isNonNegativeInteger(max_bounce_count, 'max_bounce_count');
124
+ assert.greaterThan(max_bounce_count, 0, 'max_bounce_count');
125
+
126
+ ensure_pool_capacity(candidate_count);
127
+
128
+ __sh_direct.fill(0);
129
+ __sh_indirect.fill(0);
130
+
131
+ const position_x = position[position_offset];
132
+ const position_y = position[position_offset + 1];
133
+ const position_z = position[position_offset + 2];
134
+
135
+ let hit_count = 0;
136
+ let valid_count = 0;
137
+ let backface_count = 0;
138
+ let luminance_sum = 0;
139
+
140
+ for (let i = 0; i < candidate_count; i++) {
141
+
142
+ __ray[0] = position_x;
143
+ __ray[1] = position_y;
144
+ __ray[2] = position_z;
145
+
146
+ random_point_on_sphere(random, __ray, 3);
147
+
148
+ __ray.tMax = Infinity;
149
+
150
+ const direction_x = __ray[3];
151
+ const direction_y = __ray[4];
152
+ const direction_z = __ray[5];
153
+
154
+ const hit_distance = scene.trace(__hit, __ray);
155
+
156
+ if (hit_distance < 0) {
157
+ // sky
158
+ scene.sample_background(__radiance, 0, __ray, 3);
159
+ } else if (v3_dot(
160
+ __hit[3], __hit[4], __hit[5],
161
+ direction_x, direction_y, direction_z
162
+ ) > 0) {
163
+ // the first hit is a back face: this direction sees the inside of geometry
164
+ backface_count++;
165
+ continue;
166
+ } else {
167
+ scene.sample_material(__surface, __hit, __ray);
168
+
169
+ // stage the surface state in the ray: origin at the hit, direction holding the shading
170
+ // normal, then step off the surface along the normal — mirrors PathTracer exactly, so
171
+ // shadow rays and continuations start from the same offset point the tracer would use
172
+ __ray[0] = __hit[0];
173
+ __ray[1] = __hit[1];
174
+ __ray[2] = __hit[2];
175
+
176
+ __ray[3] = __surface[3];
177
+ __ray[4] = __surface[4];
178
+ __ray[5] = __surface[5];
179
+
180
+ const origin_magnitude = Math.max(Math.abs(__ray[0]), Math.abs(__ray[1]), Math.abs(__ray[2]));
181
+ __ray.shiftForward(SELF_OCCLUSION_OFFSET * Math.max(1, origin_magnitude));
182
+
183
+ scene.sample_lights(__direct, 0, __ray);
184
+
185
+ // first-bounce radiance: emission plus albedo-modulated direct light
186
+ __radiance[0] = __surface[6] + __surface[0] * __direct[0];
187
+ __radiance[1] = __surface[7] + __surface[1] * __direct[1];
188
+ __radiance[2] = __surface[8] + __surface[2] * __direct[2];
189
+
190
+ // record the candidate for indirect re-sampling
191
+ const candidate_address = hit_count * CANDIDATE_STRIDE;
192
+
193
+ __candidates[candidate_address] = direction_x;
194
+ __candidates[candidate_address + 1] = direction_y;
195
+ __candidates[candidate_address + 2] = direction_z;
196
+
197
+ __candidates[candidate_address + 3] = __ray[0];
198
+ __candidates[candidate_address + 4] = __ray[1];
199
+ __candidates[candidate_address + 5] = __ray[2];
200
+
201
+ __candidates[candidate_address + 6] = __surface[3];
202
+ __candidates[candidate_address + 7] = __surface[4];
203
+ __candidates[candidate_address + 8] = __surface[5];
204
+
205
+ __candidates[candidate_address + 9] = __surface[0];
206
+ __candidates[candidate_address + 10] = __surface[1];
207
+ __candidates[candidate_address + 11] = __surface[2];
208
+
209
+ const luminance = rgb_to_luminance(__radiance[0], __radiance[1], __radiance[2]);
210
+
211
+ __p_hat[hit_count] = luminance;
212
+ luminance_sum += luminance;
213
+
214
+ hit_count++;
215
+ }
216
+
217
+ valid_count++;
218
+
219
+ sh3_basis_at(direction_x, direction_y, direction_z, __basis);
220
+ sh3_vector_accumulate(__sh_direct, 0, __basis, 0, __radiance, 0, 3);
221
+ }
222
+
223
+ const backface_fraction = backface_count / candidate_count;
224
+
225
+ if (valid_count === 0) {
226
+ // probe is fully embedded in geometry, there is no usable radiance
227
+ for (let i = 0; i < 27; i++) {
228
+ out[out_offset + i] = 0;
229
+ }
230
+
231
+ return backface_fraction;
232
+ }
233
+
234
+ let total_p_hat = 0;
235
+
236
+ if (max_bounce_count > 1 && indirect_sample_count > 0 && hit_count > 0) {
237
+
238
+ // floor the weights so indirectly-lit surfaces stay selectable; when nothing received any
239
+ // direct light the selection degenerates to uniform (selection is scale-invariant in p_hat)
240
+ const mean_luminance = luminance_sum / hit_count;
241
+ const weight_floor = mean_luminance > 0 ? mean_luminance * RESAMPLING_WEIGHT_FLOOR : 1;
242
+
243
+ for (let i = 0; i < hit_count; i++) {
244
+ const p_hat = __p_hat[i] + weight_floor;
245
+
246
+ __p_hat[i] = p_hat;
247
+ total_p_hat += p_hat;
248
+
249
+ __cumulative_p_hat[i] = total_p_hat;
250
+ }
251
+
252
+ for (let k = 0; k < indirect_sample_count; k++) {
253
+ const pick = pick_candidate(random() * total_p_hat, hit_count);
254
+
255
+ const candidate_address = pick * CANDIDATE_STRIDE;
256
+
257
+ __ray[0] = __candidates[candidate_address + 3];
258
+ __ray[1] = __candidates[candidate_address + 4];
259
+ __ray[2] = __candidates[candidate_address + 5];
260
+
261
+ // cosine-weighted continuation about the shading normal, matching the tracer's own
262
+ // bounce sampling; the albedo/pi BRDF and the cosine/pi pdf cancel to a bare albedo scale
263
+ getBiasedNormalSample(__ray, 3, __candidates, candidate_address + 6, 1, random);
264
+
265
+ __ray.tMax = Infinity;
266
+
267
+ tracer.path_trace(__radiance, __ray, 1, max_bounce_count - 1, random, scene);
268
+
269
+ const resampling_weight = 1 / __p_hat[pick];
270
+
271
+ __radiance[0] *= __candidates[candidate_address + 9] * resampling_weight;
272
+ __radiance[1] *= __candidates[candidate_address + 10] * resampling_weight;
273
+ __radiance[2] *= __candidates[candidate_address + 11] * resampling_weight;
274
+
275
+ sh3_basis_at(
276
+ __candidates[candidate_address],
277
+ __candidates[candidate_address + 1],
278
+ __candidates[candidate_address + 2],
279
+ __basis
280
+ );
281
+ sh3_vector_accumulate(__sh_indirect, 0, __basis, 0, __radiance, 0, 3);
282
+ }
283
+ }
284
+
285
+ // back-face directions are excluded from the domain: normalizing by the valid count spreads the
286
+ // radiance of the visible directions over the full sphere, so partially embedded probes represent
287
+ // the open side instead of averaging in interior darkness
288
+ const direct_scale = (4 * Math.PI) / valid_count;
289
+ const indirect_scale = indirect_sample_count > 0
290
+ ? direct_scale * (total_p_hat / indirect_sample_count)
291
+ : 0;
292
+
293
+ for (let i = 0; i < 27; i++) {
294
+ out[out_offset + i] = __sh_direct[i] * direct_scale + __sh_indirect[i] * indirect_scale;
295
+ }
296
+
297
+ return backface_fraction;
298
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Fill invalid probes with the average of their valid 6-connected neighbors, expanding in waves until
3
+ * every reachable probe is filled.
4
+ *
5
+ * Probes marked invalid (typically because they sit inside geometry) would otherwise contribute their
6
+ * meaningless data to hardware trilinear filtering and bleed darkness across walls. Each pass reads
7
+ * only probes that were valid before the pass started, so the result is independent of traversal
8
+ * order. Probes with no valid probe anywhere in their connected region are left untouched.
9
+ *
10
+ * @param {Float32Array} harmonics probe data, {@link PROBE_VALUE_COUNT} values per probe; filled probes are written in place
11
+ * @param {Uint8Array} validity one flag per probe, non-zero = valid; filled probes are marked valid in place
12
+ * @param {number} resolution_x
13
+ * @param {number} resolution_y
14
+ * @param {number} resolution_z
15
+ * @returns {number} number of probes that remained invalid
16
+ */
17
+ export function light_probe_grid_dilate(harmonics: Float32Array, validity: Uint8Array, resolution_x: number, resolution_y: number, resolution_z: number): number;
18
+ //# sourceMappingURL=light_probe_grid_dilate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"light_probe_grid_dilate.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/graphics/sh3/lpg/light_probe_grid_dilate.js"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;GAeG;AACH,mDAPW,YAAY,YACZ,UAAU,gBACV,MAAM,gBACN,MAAM,gBACN,MAAM,GACJ,MAAM,CAsGlB"}