@woosh/meep-engine 3.60.2 → 3.60.4

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 (156) hide show
  1. package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts +3 -0
  2. package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts.map +1 -1
  3. package/editor/ecs/component/editors/ecs/Trail3DEditor.js +6 -0
  4. package/package.json +1 -1
  5. package/src/engine/ecs/ik/PLAN.md +1 -1
  6. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +1 -2
  7. package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts +14 -0
  8. package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts.map +1 -0
  9. package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.js +22 -0
  10. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +19 -3
  11. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
  12. package/src/engine/graphics/ecs/trail3d/Trail3D.js +31 -4
  13. package/src/engine/graphics/ecs/trail3d/advance_trail.js +1 -1
  14. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts +5 -5
  15. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts.map +1 -1
  16. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +6 -6
  17. package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts +10 -0
  18. package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts.map +1 -1
  19. package/src/engine/graphics3/trail/trail_tube_write_geometry.js +13 -1
  20. package/src/engine/physics/cloth/ecs/cloth_geodesic.d.ts +2 -2
  21. package/src/engine/physics/cloth/ecs/cloth_geodesic.js +2 -2
  22. package/src/shade/playground/probe_clipmap/README.md +1 -0
  23. package/src/shade/playground/probe_clipmap/index.html +5 -0
  24. package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
  25. package/src/shade/playground/probe_clipmap/main.js +16 -2
  26. package/src/shade/playground/vgeo_scene/README.md +1 -1
  27. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts +4 -6
  28. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts.map +1 -1
  29. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.js +34 -36
  30. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts +1 -1
  31. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts.map +1 -1
  32. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.js +162 -163
  33. package/src/shade/renderer/atmosphere/compute_mie_absorption.d.ts +1 -1
  34. package/src/shade/renderer/atmosphere/compute_mie_absorption.js +1 -1
  35. package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_EARTH.js +1 -1
  36. package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_PARAMETERS_STRUCT.js +1 -1
  37. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.js +164 -164
  38. package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.js +1 -1
  39. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +2 -2
  40. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +2 -2
  41. package/src/shade/renderer/geometry/sdf/cards/mesh_cards_raster_pass_descriptor.js +2 -2
  42. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.d.ts +1 -1
  43. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.js +1 -1
  44. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts +4 -5
  45. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts.map +1 -1
  46. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.js +6 -7
  47. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +1 -1
  48. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +2 -4
  49. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +1 -1
  50. package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +3 -10
  51. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  52. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -0
  53. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +119 -38
  54. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +2 -2
  55. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +12 -0
  56. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
  57. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +22 -0
  58. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +4 -1
  59. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  60. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +50 -4
  61. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +6 -0
  62. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
  63. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +7 -0
  64. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts +13 -0
  65. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts.map +1 -0
  66. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.js +13 -0
  67. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +17 -0
  68. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
  69. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +20 -0
  70. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts +18 -0
  71. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts.map +1 -0
  72. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.js +42 -0
  73. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
  74. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +5 -4
  75. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +4 -0
  76. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
  77. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +13 -2
  78. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts +52 -0
  79. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts.map +1 -0
  80. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.js +250 -0
  81. package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +7 -7
  82. package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +2 -2
  83. package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts +39 -0
  84. package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts.map +1 -0
  85. package/src/shade/renderer/light/environment/environment_level_1_blur_plan.js +119 -0
  86. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts +15 -7
  87. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts.map +1 -1
  88. package/src/shade/renderer/light/environment/execute_filter_environment_map.js +66 -83
  89. package/src/shade/renderer/light/environment/graph_filter_environment_map.d.ts.map +1 -1
  90. package/src/shade/renderer/light/environment/graph_filter_environment_map.js +15 -6
  91. package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts +3 -0
  92. package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts.map +1 -0
  93. package/src/shade/renderer/light/environment/shader_blur_environment_map.js +111 -0
  94. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts +3 -0
  95. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts.map +1 -0
  96. package/src/shade/renderer/light/environment/{shader_convolve_reflection_map_from_averages.js → shader_convolve_reflection_map_from_blurred.js} +25 -66
  97. package/src/shade/renderer/light/environment/shader_downsample_environment_map.d.ts.map +1 -1
  98. package/src/shade/renderer/light/environment/shader_downsample_environment_map.js +0 -1
  99. package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +1 -4
  100. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +2 -2
  101. package/src/shade/renderer/postprocess/nss/NOTES.md +14 -21
  102. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -3
  103. package/src/shade/renderer/postprocess/nss/NSS.d.ts.map +1 -1
  104. package/src/shade/renderer/postprocess/nss/NSS.js +1 -3
  105. package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts +5 -6
  106. package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts.map +1 -1
  107. package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.js +9 -10
  108. package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.d.ts.map +1 -1
  109. package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.js +48 -49
  110. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
  111. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +12 -16
  112. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +246 -246
  113. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +1 -2
  114. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -1
  115. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +34 -35
  116. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts +2 -2
  117. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.js +2 -2
  118. package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +1 -1
  119. package/src/shade/renderer/shader/chunk/atmosphere/chunk_compute_extinction.js +1 -1
  120. package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.d.ts +1 -1
  121. package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.js +1 -1
  122. package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +4 -7
  123. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.d.ts +3 -3
  124. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.js +3 -3
  125. package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +12 -19
  126. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts +9 -10
  127. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts.map +1 -1
  128. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.js +9 -10
  129. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.d.ts +4 -4
  130. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.js +5 -5
  131. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +1 -1
  132. package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts +1 -2
  133. package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts.map +1 -1
  134. package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.js +1 -2
  135. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts +14 -1
  136. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts.map +1 -1
  137. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.js +19 -6
  138. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts +10 -1
  139. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts.map +1 -1
  140. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.js +14 -6
  141. package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.d.ts +1 -1
  142. package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.js +1 -1
  143. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +9 -14
  144. package/src/shade/renderer/shadow/contact/FINDINGS.md +5 -5
  145. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts +2 -3
  146. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts.map +1 -1
  147. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.js +7 -8
  148. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.d.ts +1 -1
  149. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.js +12 -12
  150. package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts +2 -2
  151. package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts.map +1 -1
  152. package/src/shade/renderer/shadow/vsm/VSMPageManager.js +3 -4
  153. package/src/shade/renderer/texture/mipmaps/NOTES.md +1 -2
  154. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +1 -1
  155. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts +0 -13
  156. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts.map +0 -1
@@ -0,0 +1,119 @@
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { cone_cosine_from_roughness } from "../../../../core/math/physics/brdf/cone_cosine_from_roughness.js";
3
+ import { BSDF_ROUGHNESS_MINIMUM } from "./DEFINES.js";
4
+ import { sphere_probe_lod_to_roughness } from "./sphere_probe_lod_to_roughness.js";
5
+
6
+ /**
7
+ * Samples per texel of level 1. They read a Gaussian-blurred copy of level 0, so the count sets how
8
+ * fine that blur can be and how much of the lobe is the lobe's own. Measured on a 1024 map against
9
+ * level 1 from 8192 point taps (sun lobe 1.315° rms): 64 → sky rms error 0.092, sun 1.345°, a bright
10
+ * texel's share 0.998 ± 0.008; 128 → 0.057, 1.342°, 0.998 ± 0.003, never above 1.
11
+ * @type {number}
12
+ */
13
+ export const LEVEL_1_SAMPLE_COUNT = 128;
14
+
15
+ /**
16
+ * Gaussian σ per spacing of the points it is sampled at. A Gaussian sampled on a lattice of spacing Δ
17
+ * sums to its integral up to its first alias, 2 exp(-2π² σ²/Δ²): 1e-4 at 0.7. Holds both for the
18
+ * blurred level's own texels and for level 1's samples reading it.
19
+ * @type {number}
20
+ */
21
+ const SIGMA_PER_SPACING = 0.7;
22
+
23
+ /**
24
+ * A Gaussian's weight past 3 σ is under 1.2% of its peak, and its share of the integral in 2D under 1.2%.
25
+ * @type {number}
26
+ */
27
+ const RADIUS_IN_SIGMA = 3;
28
+
29
+ /**
30
+ * Side of one texel of an octahedral level, as an angle. A skirted level of side `size` holds an
31
+ * n = size - 2 square over 4π steradians, but its texels are not alike: the octahedron's solid angle
32
+ * per unit area of the square is 1 / |p|^3, |p| from 1 at its vertices to 1/√3 at its face centres,
33
+ * so a texel covers 1 to 3√3 times the area-mean density π.
34
+ *
35
+ * @param {number} size
36
+ * @returns {{mean: number, min: number, max: number}}
37
+ */
38
+ function octahedral_texel_angle(size) {
39
+ const mean = Math.sqrt(4 * Math.PI) / (size - 2);
40
+
41
+ return {
42
+ mean,
43
+ min: mean * Math.sqrt(1 / Math.PI),
44
+ max: mean * Math.sqrt(3 * Math.sqrt(3) / Math.PI),
45
+ };
46
+ }
47
+
48
+ /**
49
+ * How level 1 of a map of side `map_size` is filtered.
50
+ *
51
+ * Level 1's samples are uniform in its cone, so each stands for Ω_s = Ω_cone / N. They read a copy of
52
+ * level 0 blurred by a Gaussian of σ = 0.7 · max(√Ω_s, the copy's largest texel), wide enough that
53
+ * neither the samples nor the copy's texels alias. The copy is level 0 averaged down to level 1's
54
+ * size when that leaves the lobe room for the blur, else level 0's own size.
55
+ *
56
+ * The lobe exp(2 (N·H - 1) / r^4) is a Gaussian of variance 2 r^4 an axis (N·H = cos(θ/2)), and
57
+ * Gaussians add variances, so it is narrowed by what the copy adds: the blur, and the bilinear tent
58
+ * across the copy's texels beyond the one across level 0's. It narrows to the minimum roughness when
59
+ * the map's texels are coarser than the lobe itself, and level 1 is then the blur alone.
60
+ *
61
+ * @param {number} map_size side of the map's level 0
62
+ * @returns {{
63
+ * blurred_size: number,
64
+ * average_first: boolean,
65
+ * sigma: number,
66
+ * radius: number,
67
+ * lobe_roughness: number,
68
+ * }} `average_first`: the copy is level 0 averaged to level 1's size before the blur; `sigma` the
69
+ * blur still to apply (radians), past what averaging adds; `radius` in the copy's texels
70
+ */
71
+ export function environment_level_1_blur_plan(map_size) {
72
+ assert.isNonNegativeInteger(map_size, 'map_size');
73
+ assert.greaterThanOrEqual(map_size, 6, 'a level 1 needs at least 3 texels');
74
+
75
+ const roughness = Math.max(sphere_probe_lod_to_roughness(1), BSDF_ROUGHNESS_MINIMUM);
76
+ const lobe_variance = 2 * roughness ** 4;
77
+
78
+ const cone_solid_angle = 2 * Math.PI * (1 - cone_cosine_from_roughness(roughness));
79
+ const sample_spacing = Math.sqrt(cone_solid_angle / LEVEL_1_SAMPLE_COUNT);
80
+
81
+ const level_0 = octahedral_texel_angle(map_size);
82
+
83
+ function plan(blurred_size, average_first) {
84
+ const texel = octahedral_texel_angle(blurred_size);
85
+
86
+ const total = (SIGMA_PER_SPACING * Math.max(sample_spacing, texel.max)) ** 2;
87
+
88
+ // averaging boxes a texel of the copy, s^2 / 12 an axis
89
+ const averaged = average_first ? texel.mean ** 2 / 12 : 0;
90
+ const sigma = Math.sqrt(total - averaged);
91
+
92
+ // a bilinear tent across texels of side s is s^2 / 6 an axis; level 0's own is in the lobe already
93
+ const bilinear = (texel.mean ** 2 - level_0.mean ** 2) / 6;
94
+
95
+ return {
96
+ blurred_size,
97
+ average_first,
98
+ sigma,
99
+ radius: Math.ceil(RADIUS_IN_SIGMA * sigma / texel.min),
100
+ lobe_variance: lobe_variance - total - bilinear,
101
+ };
102
+ }
103
+
104
+ let chosen = plan(map_size >> 1, true);
105
+
106
+ if (chosen.lobe_variance <= 0) {
107
+ chosen = plan(map_size, false);
108
+ }
109
+
110
+ const minimum_variance = 2 * BSDF_ROUGHNESS_MINIMUM ** 4;
111
+
112
+ return {
113
+ blurred_size: chosen.blurred_size,
114
+ average_first: chosen.average_first,
115
+ sigma: chosen.sigma,
116
+ radius: chosen.radius,
117
+ lobe_roughness: Math.sqrt(Math.sqrt(Math.max(chosen.lobe_variance, minimum_variance) / 2)),
118
+ };
119
+ }
@@ -1,3 +1,6 @@
1
+ /**
2
+ * @typedef {import("../../../device/pipeline/PipelineEnquiry.js").PipelineEnquiry} PipelineEnquiry
3
+ */
1
4
  /**
2
5
  * Offer the pipelines {@link execute_filter_environment_map} dispatches — see {@link PipelineEnquiry}.
3
6
  *
@@ -5,21 +8,26 @@
5
8
  */
6
9
  export function enquire_filter_environment_map_pipelines(enquiry: PipelineEnquiry): void;
7
10
  /**
8
- * The plain averages of a map's level 0 the prefilter's level 1 samples from: level 1 down as far as
9
- * its samples reach, as a texture of their own, so the map keeps its levels 1.. for the filtered
10
- * result. Nothing reads them after the prefilter, so a frame graph holds them transiently.
11
+ * The scratch levels the prefilter's level 1 reads through: level 0 blurred by a Gaussian, and the
12
+ * plain average it is blurred from when the plan averages first. Textures of their own, so the map
13
+ * keeps its levels 1.. for the filtered result; nothing reads them after the prefilter, so a frame
14
+ * graph holds them transiently.
11
15
  *
12
16
  * @param {number} map_size side of the map's level 0
13
- * @returns {TextureResourceDescriptor}
17
+ * @returns {{averaged: TextureResourceDescriptor|null, blurred: TextureResourceDescriptor}}
14
18
  */
15
- export function environment_map_averages_descriptor(map_size: number): TextureResourceDescriptor;
19
+ export function environment_map_scratch_descriptors(map_size: number): {
20
+ averaged: TextureResourceDescriptor | null;
21
+ blurred: TextureResourceDescriptor;
22
+ };
16
23
  /**
17
24
  *
18
25
  * @param {GPUTextureContext} map
19
- * @param {GPUTextureContext} averages described by {@link environment_map_averages_descriptor}
26
+ * @param {GPUTextureContext|null} averaged described by {@link environment_map_scratch_descriptors}
27
+ * @param {GPUTextureContext} blurred described by {@link environment_map_scratch_descriptors}
20
28
  * @param {ShadeGPUCommandContext} encoder
21
29
  */
22
- export function execute_filter_environment_map(map: GPUTextureContext, averages: GPUTextureContext, encoder: ShadeGPUCommandContext): void;
30
+ export function execute_filter_environment_map(map: GPUTextureContext, averaged: GPUTextureContext | null, blurred: GPUTextureContext, encoder: ShadeGPUCommandContext): void;
23
31
  export type PipelineEnquiry = import("../../../device/pipeline/PipelineEnquiry.js").PipelineEnquiry;
24
32
  import { TextureResourceDescriptor } from "../../../device/graph/TextureResourceDescriptor.js";
25
33
  //# sourceMappingURL=execute_filter_environment_map.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"execute_filter_environment_map.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/execute_filter_environment_map.js"],"names":[],"mappings":"AAuBA;;;;GAIG;AACH,kEAFW,eAAe,QAMzB;AAsBD;;;;;;;GAOG;AACH,8DAHW,MAAM,GACJ,yBAAyB,CAyBrC;AAED;;;;;GAKG;AACH,2IA0GC;8BAzLY,OAAO,6CAA6C,EAAE,eAAe;0CAZxC,oDAAoD"}
1
+ {"version":3,"file":"execute_filter_environment_map.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/execute_filter_environment_map.js"],"names":[],"mappings":"AAUA;;GAEG;AAEH;;;;GAIG;AACH,kEAFW,eAAe,QAOzB;AAcD;;;;;;;;GAQG;AACH,8DAHW,MAAM,GACJ;IAAC,QAAQ,EAAE,yBAAyB,GAAC,IAAI,CAAC;IAAC,OAAO,EAAE,yBAAyB,CAAA;CAAC,CAS1F;AAED;;;;;;GAMG;AACH,iFAJW,oBAAkB,IAAI,qEA2HhC;8BA3KY,OAAO,6CAA6C,EAAE,eAAe;0CATxC,oDAAoD"}
@@ -1,26 +1,17 @@
1
1
  import { assert } from "../../../../core/assert.js";
2
2
  import { TextureViewDescriptor } from "../../../descriptor/texture/TextureViewDescriptor.js";
3
3
  import { TextureResourceDescriptor } from "../../../device/graph/TextureResourceDescriptor.js";
4
- import { cone_cosine_from_roughness } from "../../../../core/math/physics/brdf/cone_cosine_from_roughness.js";
5
- import { BSDF_ROUGHNESS_MINIMUM, SPHERE_PROBE_MIPMAP_LEVELS } from "./DEFINES.js";
4
+ import { SPHERE_PROBE_MIPMAP_LEVELS } from "./DEFINES.js";
5
+ import { environment_level_1_blur_plan } from "./environment_level_1_blur_plan.js";
6
+ import { shader_blur_environment_map } from "./shader_blur_environment_map.js";
6
7
  import { shader_convolve_reflection_map } from "./shader_convolve_reflection_map.js";
7
- import {
8
- CONVOLVE_FROM_AVERAGES_SAMPLE_COUNT,
9
- shader_convolve_reflection_map_from_averages
10
- } from "./shader_convolve_reflection_map_from_averages.js";
8
+ import { shader_convolve_reflection_map_from_blurred } from "./shader_convolve_reflection_map_from_blurred.js";
11
9
  import { shader_downsample_environment_map } from "./shader_downsample_environment_map.js";
12
- import { sphere_probe_lod_to_roughness } from "./sphere_probe_lod_to_roughness.js";
13
10
 
14
11
  /**
15
12
  * @typedef {import("../../../device/pipeline/PipelineEnquiry.js").PipelineEnquiry} PipelineEnquiry
16
13
  */
17
14
 
18
- /**
19
- * The smallest side a skirted level of the averages goes down to: two interior texels.
20
- * @type {number}
21
- */
22
- const AVERAGES_MIN_SIZE = 4;
23
-
24
15
  /**
25
16
  * Offer the pipelines {@link execute_filter_environment_map} dispatches — see {@link PipelineEnquiry}.
26
17
  *
@@ -28,76 +19,56 @@ const AVERAGES_MIN_SIZE = 4;
28
19
  */
29
20
  export function enquire_filter_environment_map_pipelines(enquiry) {
30
21
  enquiry.compute.push(shader_downsample_environment_map.pipeline);
31
- enquiry.compute.push(shader_convolve_reflection_map_from_averages.pipeline);
22
+ enquiry.compute.push(shader_blur_environment_map.pipeline);
23
+ enquiry.compute.push(shader_convolve_reflection_map_from_blurred.pipeline);
32
24
  enquiry.compute.push(shader_convolve_reflection_map.pipeline);
33
25
  }
34
26
 
35
27
  /**
36
- * How many averages level 1's samples reach. A sample reads the level whose texel is the solid angle
37
- * it stands for, and the coarsest it asks for is where the octahedron's texels are smallest
38
- * (|d|_1 = 1): the lod `shader_convolve_reflection_map_from_averages` takes, at its largest. 3 for a
39
- * 1024 map, one more for each doubling.
40
- *
41
- * @param {number} map_size side of the map's level 0
42
- * @returns {number}
43
- */
44
- function averages_read_by_level_1(map_size) {
45
- const roughness = Math.max(sphere_probe_lod_to_roughness(1), BSDF_ROUGHNESS_MINIMUM);
46
- const cone_solid_angle = 2 * Math.PI * (1 - cone_cosine_from_roughness(roughness));
47
- const sample_solid_angle = cone_solid_angle / CONVOLVE_FROM_AVERAGES_SAMPLE_COUNT;
48
-
49
- const interior = map_size - 2;
50
- const smallest_texel_solid_angle = 4 / (interior * interior);
51
-
52
- return Math.max(1, Math.ceil(0.5 * Math.log2(sample_solid_angle / smallest_texel_solid_angle)));
53
- }
54
-
55
- /**
56
- * The plain averages of a map's level 0 the prefilter's level 1 samples from: level 1 down as far as
57
- * its samples reach, as a texture of their own, so the map keeps its levels 1.. for the filtered
58
- * result. Nothing reads them after the prefilter, so a frame graph holds them transiently.
59
- *
60
- * @param {number} map_size side of the map's level 0
28
+ * @param {number} size
61
29
  * @returns {TextureResourceDescriptor}
62
30
  */
63
- export function environment_map_averages_descriptor(map_size) {
64
- assert.isNonNegativeInteger(map_size, 'map_size');
65
-
66
- const size = map_size >> 1;
67
-
68
- let available = 0;
69
- for (let s = size; s >= AVERAGES_MIN_SIZE; s >>= 1) {
70
- available++;
71
- }
72
-
73
- assert.greaterThanOrEqual(available, 1, 'environment map too small to average');
74
-
75
- const mipLevelCount = Math.min(available, averages_read_by_level_1(map_size));
76
-
77
- const descriptor = TextureResourceDescriptor.from({
31
+ function scratch_level_descriptor(size) {
32
+ return TextureResourceDescriptor.from({
78
33
  resolution: [size, size],
79
34
  format: "rgba16float",
80
35
  usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING,
81
36
  });
37
+ }
82
38
 
83
- descriptor.mipLevelCount = mipLevelCount;
39
+ /**
40
+ * The scratch levels the prefilter's level 1 reads through: level 0 blurred by a Gaussian, and the
41
+ * plain average it is blurred from when the plan averages first. Textures of their own, so the map
42
+ * keeps its levels 1.. for the filtered result; nothing reads them after the prefilter, so a frame
43
+ * graph holds them transiently.
44
+ *
45
+ * @param {number} map_size side of the map's level 0
46
+ * @returns {{averaged: TextureResourceDescriptor|null, blurred: TextureResourceDescriptor}}
47
+ */
48
+ export function environment_map_scratch_descriptors(map_size) {
49
+ const plan = environment_level_1_blur_plan(map_size);
84
50
 
85
- return descriptor;
51
+ return {
52
+ averaged: plan.average_first ? scratch_level_descriptor(plan.blurred_size) : null,
53
+ blurred: scratch_level_descriptor(plan.blurred_size),
54
+ };
86
55
  }
87
56
 
88
57
  /**
89
58
  *
90
59
  * @param {GPUTextureContext} map
91
- * @param {GPUTextureContext} averages described by {@link environment_map_averages_descriptor}
60
+ * @param {GPUTextureContext|null} averaged described by {@link environment_map_scratch_descriptors}
61
+ * @param {GPUTextureContext} blurred described by {@link environment_map_scratch_descriptors}
92
62
  * @param {ShadeGPUCommandContext} encoder
93
63
  */
94
64
  export function execute_filter_environment_map(
95
65
  map,
96
- averages,
66
+ averaged,
67
+ blurred,
97
68
  encoder
98
69
  ) {
99
70
  assert.defined(map);
100
- assert.defined(averages);
71
+ assert.defined(blurred);
101
72
  assert.defined(encoder);
102
73
 
103
74
  /*
@@ -121,35 +92,50 @@ export function execute_filter_environment_map(
121
92
  return;
122
93
  }
123
94
 
124
- const level_0 = map.obtainView(TextureViewDescriptor.from({
95
+ const plan = environment_level_1_blur_plan(map.size[0]);
96
+
97
+ assert.equal(blurred.size[0], plan.blurred_size, 'blurred level is not the size the plan blurs at');
98
+
99
+ const single_level = TextureViewDescriptor.from({
125
100
  baseMipLevel: 0,
126
101
  mipLevelCount: 1,
127
- }));
102
+ });
128
103
 
129
- // the averages, each from the one above it, the first from level 0
130
- const average_levels = averages.descriptor.mipLevelCount;
104
+ const level_0 = map.obtainView(single_level);
105
+ const blurred_group_count = Math.max(1, Math.ceil(plan.blurred_size / 16));
131
106
 
132
- for (let i = 0; i < average_levels; i++) {
133
- const target_size = averages.size[0] >> i;
134
- const group_count = Math.max(1, Math.ceil(target_size / 16));
107
+ let unblurred = level_0;
108
+
109
+ if (plan.average_first) {
110
+ assert.defined(averaged, 'averaged');
135
111
 
136
112
  shader_downsample_environment_map.dispatch({
137
113
  encoder,
138
- group_count_x: group_count,
139
- group_count_y: group_count,
114
+ group_count_x: blurred_group_count,
115
+ group_count_y: blurred_group_count,
140
116
  bindings: {
141
- input: i === 0 ? level_0 : averages.obtainView(TextureViewDescriptor.from({
142
- baseMipLevel: i - 1,
143
- mipLevelCount: 1,
144
- })),
145
- output: averages.obtainView(TextureViewDescriptor.from({
146
- baseMipLevel: i,
147
- mipLevelCount: 1
148
- }))
117
+ input: level_0,
118
+ output: averaged.obtainView(single_level)
149
119
  }
150
120
  });
121
+
122
+ unblurred = averaged.obtainView(single_level);
151
123
  }
152
124
 
125
+ shader_blur_environment_map.dispatch({
126
+ encoder,
127
+ group_count_x: blurred_group_count,
128
+ group_count_y: blurred_group_count,
129
+ bindings: {
130
+ settings: {
131
+ inverse_two_sigma_squared: 1 / (2 * plan.sigma * plan.sigma),
132
+ radius: plan.radius,
133
+ },
134
+ input: unblurred,
135
+ output: blurred.obtainView(single_level)
136
+ }
137
+ });
138
+
153
139
  for (let i = 1; i < mips; i++) {
154
140
  const target_width = map.size[0] >> i;
155
141
  const target_height = map.size[1] >> i;
@@ -165,17 +151,14 @@ export function execute_filter_environment_map(
165
151
  }));
166
152
 
167
153
  if (i === 1) {
168
- // from level 0 and its averages: level 0 is too fine for the taps that cover level 1's cone
169
- shader_convolve_reflection_map_from_averages.dispatch({
154
+ // level 0 is too fine for the taps that cover level 1's cone; its blurred copy is not
155
+ shader_convolve_reflection_map_from_blurred.dispatch({
170
156
  encoder,
171
157
  group_count_x: dispatch_x,
172
158
  group_count_y: dispatch_y,
173
159
  bindings: {
174
- level_0,
175
- averages: averages.obtainView(TextureViewDescriptor.from({
176
- baseMipLevel: 0,
177
- mipLevelCount: average_levels,
178
- })),
160
+ lobe_roughness: plan.lobe_roughness,
161
+ blurred: blurred.obtainView(single_level),
179
162
  output
180
163
  }
181
164
  });
@@ -1 +1 @@
1
- {"version":3,"file":"graph_filter_environment_map.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/graph_filter_environment_map.js"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH;IAJ+B,KAAK,EAAzB,UAAU;IACM,GAAG,EAAnB,MAAM;IACJ,MAAM,CAmClB;AAED;;;;;;GAMG;AACH,6GAYC;2BApE0B,8DAA8D"}
1
+ {"version":3,"file":"graph_filter_environment_map.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/graph_filter_environment_map.js"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH;IAJ+B,KAAK,EAAzB,UAAU;IACM,GAAG,EAAnB,MAAM;IACJ,MAAM,CA4ClB;AAED;;;;;;GAMG;AACH,6GAYC;2BA7E0B,8DAA8D"}
@@ -1,7 +1,7 @@
1
1
  import { FrameGraph } from "../../../../engine/graphics/render/frame_graph/FrameGraph.js";
2
2
  import { TextureResourceDescriptor } from "../../../device/graph/TextureResourceDescriptor.js";
3
3
  import {
4
- environment_map_averages_descriptor,
4
+ environment_map_scratch_descriptors,
5
5
  execute_filter_environment_map
6
6
  } from "./execute_filter_environment_map.js";
7
7
 
@@ -32,17 +32,26 @@ export function graph_filter_environment_map({
32
32
  */
33
33
  const map = resources.get(data.map);
34
34
 
35
- execute_filter_environment_map(map, resources.get(data.averages), context.encoder);
35
+ execute_filter_environment_map(
36
+ map,
37
+ data.averaged === undefined ? null : resources.get(data.averaged),
38
+ resources.get(data.blurred),
39
+ context.encoder
40
+ );
36
41
 
37
42
  }
38
43
 
39
44
  const builder = graph.add("filter env map", data, execute);
40
45
 
41
46
  data.map = builder.read(map);
42
- data.averages = builder.create(
43
- "env map averages",
44
- environment_map_averages_descriptor(graph.getDescriptor(map).resolution[0])
45
- );
47
+
48
+ const scratch = environment_map_scratch_descriptors(graph.getDescriptor(map).resolution[0]);
49
+
50
+ if (scratch.averaged !== null) {
51
+ data.averaged = builder.create("env map averaged", scratch.averaged);
52
+ }
53
+
54
+ data.blurred = builder.create("env map blurred", scratch.blurred);
46
55
 
47
56
  return builder.write(map);
48
57
  }
@@ -0,0 +1,3 @@
1
+ export const shader_blur_environment_map: ComputeShader;
2
+ import { ComputeShader } from "../../shader/ComputeShader.js";
3
+ //# sourceMappingURL=shader_blur_environment_map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shader_blur_environment_map.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/shader_blur_environment_map.js"],"names":[],"mappings":"AA0GA,wDAII;8BAvG0B,+BAA+B"}
@@ -0,0 +1,111 @@
1
+ import {
2
+ chunk_uv_octahedral_unit_decode
3
+ } from "../../shader/chunk/geometry/octahedron/chunk_uv_octahedral_unit_decode.js";
4
+ import {
5
+ chunk_texture_octahedral_wrap_texel_coordinates
6
+ } from "../../shader/chunk/texture/octahedral/chunk_texture_octahedral_wrap_texel_coordinates.js";
7
+ import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
8
+ import { ComputeShader } from "../../shader/ComputeShader.js";
9
+ import { ShaderResourceSetDescriptor } from "../../shader/resource/ShaderResourceSetDescriptor.js";
10
+ import { ShaderDescriptor } from "../../shader/ShaderDescriptor.js";
11
+ import { f32, u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
12
+ import { WebGPUStruct } from "../../shader/type/WebGPUStruct.js";
13
+
14
+ const BLUR_SETTINGS = WebGPUStruct.from({
15
+ // 1 / (2 σ²), σ in radians
16
+ inverse_two_sigma_squared: f32,
17
+ // texels either side, enough for 3 σ where the octahedron's texels are smallest
18
+ radius: u32,
19
+ }, 'EnvironmentBlurSettings');
20
+
21
+ const resources = new ShaderResourceSetDescriptor();
22
+
23
+ resources.setImmediate("settings", BLUR_SETTINGS);
24
+
25
+ resources.createGroup()
26
+ .addTexture("input", "float")
27
+ .addStorageTexture("output", "rgba16float")
28
+ ;
29
+
30
+ /**
31
+ * A skirted octahedral level blurred by a Gaussian on the sphere, into a level of the same size:
32
+ * each texel the mean of the texels within `radius`, weighted by exp(-θ² / 2σ²) of the angle θ
33
+ * between them and by their solid angle. The octahedral square wraps at its edges, so the kernel is
34
+ * the same on either side of a fold.
35
+ *
36
+ * A Gaussian, not a box, because level 1 reads this through a lobe that is itself a Gaussian, and
37
+ * the two compose into a Gaussian whose variance is the sum: the lobe narrows by exactly what the
38
+ * blur adds (see environment_level_1_blur_plan).
39
+ *
40
+ * @type {CodeChunk}
41
+ */
42
+ const body = CodeChunk.from(
43
+ //language=WGSL
44
+ `
45
+ @compute @workgroup_size(16,16,1)
46
+ fn main(
47
+ @builtin(global_invocation_id) global_invocation_id : vec3<u32>,
48
+ ){
49
+ let output_size = textureDimensions(output);
50
+
51
+ let output_texel_i = global_invocation_id.xy;
52
+
53
+ if(any(output_texel_i >= output_size)){
54
+ return;
55
+ }
56
+
57
+ let interior = i32(output_size.x) - 2;
58
+
59
+ // the skirt too: a skirt texel takes its interior twin, so it is that texel, computed again
60
+ let centre = texture_octahedral_wrap_texel_coordinates(vec2<i32>(output_texel_i) - 1, interior);
61
+ let centre_direction = uv_octahedral_unit_decode((vec2<f32>(centre) + 0.5) / f32(interior));
62
+
63
+ let radius = i32(settings.radius);
64
+
65
+ var sum = vec4(0.0);
66
+ var weight_sum = 0.0;
67
+
68
+ for (var y = -radius; y <= radius; y++) {
69
+ for (var x = -radius; x <= radius; x++) {
70
+ let unwrapped = vec2<i32>(centre) + vec2(x, y);
71
+ let texel = texture_octahedral_wrap_texel_coordinates(unwrapped, interior);
72
+
73
+ // near the middle of the square's edge the fold maps the window onto itself, so a tap that
74
+ // wraps can land on a texel the window reaches directly: count every texel once
75
+ let wrapped = any(unwrapped < vec2(0)) || any(unwrapped >= vec2(interior));
76
+ let in_window = all(abs(vec2<i32>(texel) - vec2<i32>(centre)) <= vec2(radius));
77
+
78
+ if (wrapped && in_window) {
79
+ continue;
80
+ }
81
+
82
+ let direction = uv_octahedral_unit_decode((vec2<f32>(texel) + 0.5) / f32(interior));
83
+
84
+ // θ² from the chord c: θ = 2 asin(c / 2), so θ² = c² + c⁴ / 12 + ...
85
+ let chord = direction - centre_direction;
86
+ let chord_squared = dot(chord, chord);
87
+ let theta_squared = chord_squared * (1.0 + chord_squared / 12.0);
88
+
89
+ // the octahedron's solid angle per unit area of the square is |d|_1^3 along unit d
90
+ let l1 = dot(abs(direction), vec3(1.0));
91
+
92
+ let weight = exp(-theta_squared * settings.inverse_two_sigma_squared) * l1 * l1 * l1;
93
+
94
+ sum += textureLoad(input, texel + 1u, 0) * weight;
95
+ weight_sum += weight;
96
+ }
97
+ }
98
+
99
+ textureStore(output, output_texel_i, sum / weight_sum);
100
+ }
101
+ `, [
102
+ chunk_texture_octahedral_wrap_texel_coordinates,
103
+ chunk_uv_octahedral_unit_decode,
104
+ ]
105
+ );
106
+
107
+ export const shader_blur_environment_map = new ComputeShader(ShaderDescriptor.from({
108
+ label: 'Blur environment map',
109
+ body,
110
+ resources
111
+ }));
@@ -0,0 +1,3 @@
1
+ export const shader_convolve_reflection_map_from_blurred: ComputeShader;
2
+ import { ComputeShader } from "../../shader/ComputeShader.js";
3
+ //# sourceMappingURL=shader_convolve_reflection_map_from_blurred.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shader_convolve_reflection_map_from_blurred.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.js"],"names":[],"mappings":"AA2HA,wEAII;8BA7G0B,+BAA+B"}