@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.
- package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts +3 -0
- package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts.map +1 -1
- package/editor/ecs/component/editors/ecs/Trail3DEditor.js +6 -0
- package/package.json +1 -1
- package/src/engine/ecs/ik/PLAN.md +1 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +1 -2
- package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts +14 -0
- package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts.map +1 -0
- package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.js +22 -0
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +19 -3
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.js +31 -4
- package/src/engine/graphics/ecs/trail3d/advance_trail.js +1 -1
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts +5 -5
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts.map +1 -1
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +6 -6
- package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts +10 -0
- package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts.map +1 -1
- package/src/engine/graphics3/trail/trail_tube_write_geometry.js +13 -1
- package/src/engine/physics/cloth/ecs/cloth_geodesic.d.ts +2 -2
- package/src/engine/physics/cloth/ecs/cloth_geodesic.js +2 -2
- package/src/shade/playground/probe_clipmap/README.md +1 -0
- package/src/shade/playground/probe_clipmap/index.html +5 -0
- package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
- package/src/shade/playground/probe_clipmap/main.js +16 -2
- package/src/shade/playground/vgeo_scene/README.md +1 -1
- package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts +4 -6
- package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.js +34 -36
- package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts +1 -1
- package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.js +162 -163
- package/src/shade/renderer/atmosphere/compute_mie_absorption.d.ts +1 -1
- package/src/shade/renderer/atmosphere/compute_mie_absorption.js +1 -1
- package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_EARTH.js +1 -1
- package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_PARAMETERS_STRUCT.js +1 -1
- package/src/shade/renderer/atmosphere/shader_multiscatter_lut.js +164 -164
- package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.js +1 -1
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +2 -2
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +2 -2
- package/src/shade/renderer/geometry/sdf/cards/mesh_cards_raster_pass_descriptor.js +2 -2
- package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.d.ts +1 -1
- package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.js +1 -1
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts +4 -5
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.js +6 -7
- package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +1 -1
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +2 -4
- package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +3 -10
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +119 -38
- package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +2 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +12 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +22 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +4 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +50 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +6 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +7 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts +13 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.js +13 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +17 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +20 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts +18 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.js +42 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +5 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +4 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +13 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts +52 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.js +250 -0
- package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +7 -7
- package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +2 -2
- package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts +39 -0
- package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/environment_level_1_blur_plan.js +119 -0
- package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts +15 -7
- package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/execute_filter_environment_map.js +66 -83
- package/src/shade/renderer/light/environment/graph_filter_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/graph_filter_environment_map.js +15 -6
- package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts +3 -0
- package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/shader_blur_environment_map.js +111 -0
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts +3 -0
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/{shader_convolve_reflection_map_from_averages.js → shader_convolve_reflection_map_from_blurred.js} +25 -66
- package/src/shade/renderer/light/environment/shader_downsample_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/shader_downsample_environment_map.js +0 -1
- package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +1 -4
- package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +2 -2
- package/src/shade/renderer/postprocess/nss/NOTES.md +14 -21
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -3
- package/src/shade/renderer/postprocess/nss/NSS.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.js +1 -3
- package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts +5 -6
- package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.js +9 -10
- package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.js +48 -49
- package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +12 -16
- package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +246 -246
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +1 -2
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +34 -35
- package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts +2 -2
- package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.js +2 -2
- package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +1 -1
- package/src/shade/renderer/shader/chunk/atmosphere/chunk_compute_extinction.js +1 -1
- package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.d.ts +1 -1
- package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.js +1 -1
- package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +4 -7
- package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.d.ts +3 -3
- package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.js +3 -3
- package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +12 -19
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts +9 -10
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.js +9 -10
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.d.ts +4 -4
- package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.js +5 -5
- package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +1 -1
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts +1 -2
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.js +1 -2
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts +14 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.js +19 -6
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts +10 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.js +14 -6
- package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.d.ts +1 -1
- package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.js +1 -1
- package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +9 -14
- package/src/shade/renderer/shadow/contact/FINDINGS.md +5 -5
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts +2 -3
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.js +7 -8
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.d.ts +1 -1
- package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.js +12 -12
- package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts +2 -2
- package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/VSMPageManager.js +3 -4
- package/src/shade/renderer/texture/mipmaps/NOTES.md +1 -2
- package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +1 -1
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts +0 -13
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts.map +0 -1
|
@@ -16,7 +16,7 @@ fn compute_extinction(densities: vec3<f32>) -> vec3<f32>{
|
|
|
16
16
|
let mie_absorption = mie_density * C_MIE_ABSORPTION;
|
|
17
17
|
let mie_scattering = mie_density * C_MIE_SCATTERING;
|
|
18
18
|
|
|
19
|
-
//
|
|
19
|
+
// Rayleigh does not absorb
|
|
20
20
|
let rayleigh_absorption = 0.0;
|
|
21
21
|
let rayleigh_scattering = densities.x * C_RAYLEIGH;
|
|
22
22
|
|
package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* An atmosphere layer of width 'width', and whose density is defined as
|
|
3
3
|
* 'exp_term' * exp('exp_scale' * h) + 'linear_term' * h + 'constant_term',
|
|
4
4
|
* clamped to [0,1], and where h is the altitude.
|
|
5
|
-
* @see
|
|
5
|
+
* @see "Precomputed Atmospheric Scattering", Bruneton & Neyret (2008) / Bruneton (2017)
|
|
6
6
|
*/
|
|
7
7
|
export const DENSITY_PROFILE_LAYER_STRUCT: WebGPUStruct;
|
|
8
8
|
/**
|
|
@@ -6,7 +6,7 @@ import { WebGPUStruct } from "../../../type/WebGPUStruct.js";
|
|
|
6
6
|
* An atmosphere layer of width 'width', and whose density is defined as
|
|
7
7
|
* 'exp_term' * exp('exp_scale' * h) + 'linear_term' * h + 'constant_term',
|
|
8
8
|
* clamped to [0,1], and where h is the altitude.
|
|
9
|
-
* @see
|
|
9
|
+
* @see "Precomputed Atmospheric Scattering", Bruneton & Neyret (2008) / Bruneton (2017)
|
|
10
10
|
*/
|
|
11
11
|
export const DENSITY_PROFILE_LAYER_STRUCT = WebGPUStruct.from({
|
|
12
12
|
width: f32,
|
|
@@ -198,8 +198,8 @@ Notes on the sources (the literature summaries are with the scripts, §8):
|
|
|
198
198
|
- **Hoffman 2019 and 2023, Kutz et al. 2021/2023 (Adobe Standard Material), OpenPBR 1.1.1:**
|
|
199
199
|
`F = Schlick(μ) − b μ (1 − μ)⁶`, with `b = Schlick(1/7)(1 − tint)/(μ̄(1 − μ̄)⁶)` and μ̄ = 1/7, so that F(82°) =
|
|
200
200
|
tint · Schlick(82°). It is linear in F0, F90 and b, so a split-sum LUT needs one more channel, `∫ μ(1 − μ)⁶`.
|
|
201
|
-
The OpenPBR companion paper gives its hemispherical average as `F0 + (1 − F0)/21 − b/126`. Unreal 5.4 has
|
|
202
|
-
|
|
201
|
+
The OpenPBR companion paper gives its hemispherical average as `F0 + (1 − F0)/21 − b/126`. Unreal 5.4 has the
|
|
202
|
+
Adobe F82 tint only in its Substrate path tracer (disabled by default); its rasterizer runs Schlick.
|
|
203
203
|
- **Filament and Frostbite** use Schlick with a shadowed F90. Belcour et al. 2020 needs a tabulated basis.
|
|
204
204
|
Vernooij 2024 fits the Lazányi form per metal. No F0-only metal approximation newer than Hauber's was found.
|
|
205
205
|
|
|
@@ -219,7 +219,7 @@ interpolate. DXIL is counted as above.
|
|
|
219
219
|
| Narkowicz 2014, polynomial | uncorrelated Smith, α = (1 − gloss)⁴ | 36 (1 sqrt) | 0.221 (−28%) / 0.019 | 0.080 |
|
|
220
220
|
| Narkowicz 2014, Lazarov form | same | — | 0.315 (−39%) / 0.041 | 0.080 |
|
|
221
221
|
| Pesce & Iwanicki 2015 | not stated; roughness = √r, the best of three readings | 15 (1 sqrt, 1 exp) | 0.126 (−15%) / 0.028 | 0.135 |
|
|
222
|
-
| UE 5.4 energy fit (
|
|
222
|
+
| UE 5.4 analytic energy fit ("WIP") | height-correlated table | 26 (2 div, 3 log, 3 exp) | 0.058 (+6.5%) / 0.0064 | 0.015 |
|
|
223
223
|
| MaterialX `mx_ggx_dir_albedo_analytic` (2022) | height-correlated, Schlick | 89 (2 div) | 0.084 (−8.4%) / 0.0060 | 0.025 |
|
|
224
224
|
| Kulla & Conty 2017, E only | height-correlated | 38 (div, sqrt, log, exp) | 0.011 (3.5%) / 0.0022 | — |
|
|
225
225
|
| Sforza & Pellacini 2022, E only | uncorrelated Smith | — | 0.226 (−28%) / 0.016 | — |
|
|
@@ -331,10 +331,7 @@ images are with the scripts (§8).
|
|
|
331
331
|
|
|
332
332
|
## 7. As built (dbdb1cdb1)
|
|
333
333
|
|
|
334
|
-
**The form is
|
|
335
|
-
calls, computes `Fc = Pow5(1 − VoH)` by multiplies, then returns `F90 Fc + (1 − Fc) F0`. Its `EnvBRDF(F0, F90)` reads
|
|
336
|
-
a LUT integrated with the same weight. `chunk_F_Schlick` now has that form; its `pow(x, 5.0)` had compiled to a log
|
|
337
|
-
and an exp.
|
|
334
|
+
**The form is standard Schlick.** `F_Schlick(F0, F90, VoH)` computes `Fc = (1 − VoH)^5` by multiplies, then returns `F90 Fc + (1 − Fc) F0`, and the split-sum lookup reads a LUT integrated with the same weight. `chunk_F_Schlick` now has that form; its `pow(x, 5.0)` had compiled to a log and an exp.
|
|
338
335
|
|
|
339
336
|
**Every `F_Hauber` caller moved to it,** and `chunk_F_Hauber` is deleted:
|
|
340
337
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Schlick's approximation of the Fresnel reflectance (Schlick 1994)
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Schlick's approximation of the Fresnel reflectance (Schlick 1994): `(1 - VoH)^5` by
|
|
3
|
+
* multiplies, then `F90 Fc + (1 - Fc) F0`.
|
|
4
|
+
* A `pow(x, 5.0)` compiles to a log and an exp under DXC.
|
|
5
5
|
*
|
|
6
6
|
* It is linear in F0 and F90 and reflects all of a perfect conductor (F0 = F90 = 1) at every angle, so the split-sum
|
|
7
7
|
* LUT holds both its split, `f0 · A + f90 · B` (`chunk_integrate_brdf` integrates this weight), and in `A + B` the
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { CodeChunk } from "../../../compiler/CodeChunk.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Schlick's approximation of the Fresnel reflectance (Schlick 1994)
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Schlick's approximation of the Fresnel reflectance (Schlick 1994): `(1 - VoH)^5` by
|
|
5
|
+
* multiplies, then `F90 Fc + (1 - Fc) F0`.
|
|
6
|
+
* A `pow(x, 5.0)` compiles to a log and an exp under DXC.
|
|
7
7
|
*
|
|
8
8
|
* It is linear in F0 and F90 and reflects all of a perfect conductor (F0 = F90 = 1) at every angle, so the split-sum
|
|
9
9
|
* LUT holds both its split, `f0 · A + f90 · B` (`chunk_integrate_brdf` integrates this weight), and in `A + B` the
|
|
@@ -6,15 +6,12 @@ as fixed-function tone mapping: no settings and no LUTs.
|
|
|
6
6
|
|
|
7
7
|
## 1. What it is
|
|
8
8
|
|
|
9
|
-
- **SDR is Unreal's "Filmic" tone mapper at its default settings.** It
|
|
10
|
-
`CombineLUTsCommon` (PostProcessCombineLUTs.usf, the sRGB output device) and `FilmToneMap` (TonemapCommon.ush), in
|
|
11
|
-
`chunk_tonemap_ue_filmic`, evaluated per pixel where UE bakes it into a 32³ LUT:
|
|
9
|
+
- **SDR is Unreal's "Filmic" tone mapper at its default settings.** It evaluates UE 5.4's sRGB LUT colour-grading and filmic tone-mapping pipeline in `chunk_tonemap_ue_filmic` per pixel where UE bakes it into a 32³ LUT:
|
|
12
10
|
1. ACEScg from UE's default sRGB working space;
|
|
13
11
|
2. Expand Gamut at 1.0;
|
|
14
12
|
3. the colour corrections at their defaults, which only clamp at zero;
|
|
15
13
|
4. Blue Correction at 0.6;
|
|
16
|
-
5. the curve at Slope 0.88, Toe 0.55, Shoulder 0.26, Black Clip 0 and White Clip 0.04 (
|
|
17
|
-
Scene.cpp);
|
|
14
|
+
5. the curve at Slope 0.88, Toe 0.55, Shoulder 0.26, Black Clip 0 and White Clip 0.04 (the default post-process settings);
|
|
18
15
|
6. the inverse blue correction, then Rec.709 clipped to [0, 1].
|
|
19
16
|
- **HDR is what Unreal 5.8 does with Filmic on a display past 100 cd/m²:** the SDR picture, taken back to the scene
|
|
20
17
|
through the inverse ACES 2.0 output transform for a 100 cd/m² display, and rendered again with the ACES 2.0 output
|
|
@@ -35,27 +32,24 @@ display, plus 14% of that for every doubling of the peak. A display with no head
|
|
|
35
32
|
|
|
36
33
|
## 2. Sources
|
|
37
34
|
|
|
38
|
-
- **UE 5.4
|
|
39
|
-
ACES.ush, GammaCorrectionCommon.ush, PostProcessCommon.ush, PostProcessTonemap.usf; PostProcessCombineLUTs.cpp,
|
|
40
|
-
PostProcessTonemap.cpp, RenderCore.cpp, HDRHelper.h, Scene.cpp. Every default above was read there.
|
|
35
|
+
- **UE 5.4 documentation:** "Color Grading and the Filmic Tonemapper in Unreal Engine", Epic Games.
|
|
41
36
|
- **UE after 5.4, release notes** (Epic, verified):
|
|
42
|
-
- 5.6 adds ACES 2.0 for HDR as an option
|
|
37
|
+
- 5.6 adds ACES 2.0 for HDR as an option.
|
|
43
38
|
- 5.8 adds "support for ACES 2.0 in SDR", moves ACES into a secondary LUT, and drops the HDR UI paper white from 300
|
|
44
39
|
to 203 cd/m² to match BT.2408.
|
|
45
|
-
- 5.8
|
|
40
|
+
- 5.8 offers Filmic (default) and Standard ACES tone-mapping methods.
|
|
46
41
|
- **UE 5.8's behaviour, Epic staff** (Yutaro Sawada, forums.unrealengine.com/t/tonemappingmethod/2747218, 2026-07-30,
|
|
47
42
|
in Japanese):
|
|
48
|
-
-
|
|
43
|
+
- ACES 2.0 is now the default HDR version.
|
|
49
44
|
- Filmic, the default, aims at one look on SDR and HDR. Past 100 cd/m² it is "applied, converted back to linear
|
|
50
45
|
through the inverse ACES 2.0, then output again through ACES 2.0 at the target luminance".
|
|
51
|
-
-
|
|
46
|
+
- The scene colour multiplier applies to Standard ACES only.
|
|
52
47
|
- The peak comes from the hardware, and mid and minimum luminance derive from it.
|
|
53
48
|
- **ACES 2.0:**
|
|
54
49
|
- ampas/aces-core, Lib.Academy.OutputTransform.ctl and Lib.Academy.Tonescale.ctl;
|
|
55
50
|
- OpenColorIO v2.6.0, ops/fixedfunction/ACES2/Transform.cpp, FixedFunctionOpCPU.cpp and transforms/builtins/ACES.cpp,
|
|
56
51
|
which this ports.
|
|
57
|
-
-
|
|
58
|
-
description, not read.
|
|
52
|
+
- The glue between Filmic and ACES 2.0 (§3, D2) is inferred from the staff description.
|
|
59
53
|
|
|
60
54
|
The state of the art elsewhere, for context:
|
|
61
55
|
|
|
@@ -77,11 +71,10 @@ The state of the art elsewhere, for context:
|
|
|
77
71
|
- Rec.709 rather than the canvas's Display P3, because the review measured what P3 does: just past the paper white
|
|
78
72
|
(203.01 cd/m²) saturated colours jumped up to 67 codes from the SDR picture, 213 of 512 by more than 2. Limited
|
|
79
73
|
to the SDR picture's own gamut, the HDR picture becomes the SDR one as the headroom shrinks to none. HDR gives up
|
|
80
|
-
colours outside Rec.709, which the SDR picture never had either.
|
|
81
|
-
source would say.
|
|
74
|
+
colours outside Rec.709, which the SDR picture never had either.
|
|
82
75
|
- A display at or under the paper white shows the SDR picture, Unreal's "past 100 cd/m²" rule in this engine's
|
|
83
76
|
units.
|
|
84
|
-
- Unreal sets the HDR level
|
|
77
|
+
- Unreal sets the HDR paper-white level in nits and runs the forward transform at the display's absolute
|
|
85
78
|
peak. If its paper white scales the SDR picture onto the paper white, which its default of 203, BT.2408 and ours
|
|
86
79
|
suggest, the two agree.
|
|
87
80
|
- **D3. No scene colour multiplier.** UE applies its 1.5 to Standard ACES only.
|
|
@@ -111,7 +104,7 @@ The state of the art elsewhere, for context:
|
|
|
111
104
|
|
|
112
105
|
**Against the references**, in `shader_tonemap_aces.spec.js`, under the WGSL emulator, in Display P3:
|
|
113
106
|
|
|
114
|
-
- SDR is within the dither of a float64 Python
|
|
107
|
+
- SDR is within the dither of a float64 Python reference of the Filmic curve and grading pipeline, at 29 colours from black to 1000.
|
|
115
108
|
- HDR is within 1e-4 (encoded) of OpenColorIO 2.6.0 at four display peaks: its builtin inverse at 100 cd/m², then its
|
|
116
109
|
ACES 2.0 fixed function limited to Rec.709 between its builtins' clamps.
|
|
117
110
|
- The exceptions are three bright saturated colours whose SDR picture is near white, where D5's limits apply, and
|
|
@@ -157,4 +150,4 @@ It has not been looked at on an HDR display.
|
|
|
157
150
|
- **The HDR pass costs three to four times GT7's**: two ACES 2.0 transforms a pixel, 55–102 µs at 0.8 MP on a 4090.
|
|
158
151
|
Unreal pays this once into a LUT, which was ruled out here.
|
|
159
152
|
- **The glue to Filmic is inferred** (D2, D5). Unreal evaluates this composite into a LUT, whose interpolation smooths
|
|
160
|
-
what D5 limits
|
|
153
|
+
what D5 limits.
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The colour matrices of Unreal Engine 5.4's default tone mapping, each the product
|
|
3
|
-
*
|
|
2
|
+
* The colour matrices of Unreal Engine 5.4's default tone mapping, each the product of the standard colour-space
|
|
3
|
+
* transforms, taken in float64 and written out.
|
|
4
4
|
*
|
|
5
5
|
* - `UE_REC709_TO_AP1`, `UE_AP1_TO_REC709`: UE's default working space (sRGB primaries, D65) to ACEScg and back,
|
|
6
|
-
* through XYZ with
|
|
7
|
-
* - `UE_AP1_TO_AP0`, `UE_AP0_TO_AP1`:
|
|
8
|
-
* - `UE_EXPAND_GAMUT`:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* AP0, conjugated into AP1 (`AP0_2_AP1 · BlueCorrect · AP1_2_AP0`).
|
|
6
|
+
* through XYZ with Bradford D65/D60 adaptation.
|
|
7
|
+
* - `UE_AP1_TO_AP0`, `UE_AP0_TO_AP1`: ACEScg (AP1) to ACES2065-1 (AP0) and back.
|
|
8
|
+
* - `UE_EXPAND_GAMUT`: gamut expansion matrix from sRGB toward wide primaries,
|
|
9
|
+
* in AP1 without chromatic adaptation on the wide side.
|
|
10
|
+
* - `UE_BLUE_CORRECT`, `UE_BLUE_CORRECT_INVERSE`: the blue-correction matrix and its inverse, both defined on
|
|
11
|
+
* AP0, conjugated into AP1.
|
|
13
12
|
*
|
|
14
|
-
* `UE_AP1_LUMINANCE` is
|
|
13
|
+
* `UE_AP1_LUMINANCE` is the Y row of the AP1-to-XYZ matrix.
|
|
15
14
|
*
|
|
16
15
|
* @see chunk_tonemap_ue_filmic
|
|
17
16
|
* @see chunk_aces2_output_transform
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk_aces_ue_matrices.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.js"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"chunk_aces_ue_matrices.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,qCAFU,SAAS,CAgDjB;0BAlEwB,mCAAmC"}
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { CodeChunk } from "../../../../compiler/CodeChunk.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* The colour matrices of Unreal Engine 5.4's default tone mapping, each the product
|
|
5
|
-
*
|
|
4
|
+
* The colour matrices of Unreal Engine 5.4's default tone mapping, each the product of the standard colour-space
|
|
5
|
+
* transforms, taken in float64 and written out.
|
|
6
6
|
*
|
|
7
7
|
* - `UE_REC709_TO_AP1`, `UE_AP1_TO_REC709`: UE's default working space (sRGB primaries, D65) to ACEScg and back,
|
|
8
|
-
* through XYZ with
|
|
9
|
-
* - `UE_AP1_TO_AP0`, `UE_AP0_TO_AP1`:
|
|
10
|
-
* - `UE_EXPAND_GAMUT`:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* AP0, conjugated into AP1 (`AP0_2_AP1 · BlueCorrect · AP1_2_AP0`).
|
|
8
|
+
* through XYZ with Bradford D65/D60 adaptation.
|
|
9
|
+
* - `UE_AP1_TO_AP0`, `UE_AP0_TO_AP1`: ACEScg (AP1) to ACES2065-1 (AP0) and back.
|
|
10
|
+
* - `UE_EXPAND_GAMUT`: gamut expansion matrix from sRGB toward wide primaries,
|
|
11
|
+
* in AP1 without chromatic adaptation on the wide side.
|
|
12
|
+
* - `UE_BLUE_CORRECT`, `UE_BLUE_CORRECT_INVERSE`: the blue-correction matrix and its inverse, both defined on
|
|
13
|
+
* AP0, conjugated into AP1.
|
|
15
14
|
*
|
|
16
|
-
* `UE_AP1_LUMINANCE` is
|
|
15
|
+
* `UE_AP1_LUMINANCE` is the Y row of the AP1-to-XYZ matrix.
|
|
17
16
|
*
|
|
18
17
|
* @see chunk_tonemap_ue_filmic
|
|
19
18
|
* @see chunk_aces2_output_transform
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unreal Engine's default SDR tone mapping, "Filmic", with every setting at its default.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Evaluates UE 5.4's sRGB LUT colour-grading and filmic tone-mapping pipeline,
|
|
5
|
+
* as UE evaluates them into its colour grading LUT, here evaluated per pixel:
|
|
6
6
|
*
|
|
7
7
|
* 1. into ACEScg (AP1) from UE's default sRGB working space;
|
|
8
8
|
* 2. Expand Gamut at its default 1.0, which pushes bright saturated colours toward wider primaries;
|
|
9
9
|
* 3. the colour corrections at their defaults, which only clamp AP1 at zero;
|
|
10
10
|
* 4. Blue Correction at its default 0.6, a matrix that turns bright blue toward cyan rather than violet, before the
|
|
11
11
|
* curve, and its inverse after;
|
|
12
|
-
* 5.
|
|
13
|
-
*
|
|
12
|
+
* 5. The filmic curve with Slope 0.88, Toe 0.55, Shoulder 0.26, Black Clip 0 and White Clip 0.04 (the default
|
|
13
|
+
* post-process settings). It is the ACES reference rendering's glow and red modifier and its 0.96 desaturation, then a
|
|
14
14
|
* parametric toe-straight-shoulder curve on log10 of each AP1 channel, solved so that 0.18 comes out at 0.18, then a
|
|
15
15
|
* 0.93 desaturation;
|
|
16
16
|
* 6. back to the working space, clamped at zero, and clipped at 1 as the sRGB target clips it.
|
|
@@ -4,16 +4,16 @@ import { chunk_aces_ue_matrices } from "./chunk_aces_ue_matrices.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Unreal Engine's default SDR tone mapping, "Filmic", with every setting at its default.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Evaluates UE 5.4's sRGB LUT colour-grading and filmic tone-mapping pipeline,
|
|
8
|
+
* as UE evaluates them into its colour grading LUT, here evaluated per pixel:
|
|
9
9
|
*
|
|
10
10
|
* 1. into ACEScg (AP1) from UE's default sRGB working space;
|
|
11
11
|
* 2. Expand Gamut at its default 1.0, which pushes bright saturated colours toward wider primaries;
|
|
12
12
|
* 3. the colour corrections at their defaults, which only clamp AP1 at zero;
|
|
13
13
|
* 4. Blue Correction at its default 0.6, a matrix that turns bright blue toward cyan rather than violet, before the
|
|
14
14
|
* curve, and its inverse after;
|
|
15
|
-
* 5.
|
|
16
|
-
*
|
|
15
|
+
* 5. The filmic curve with Slope 0.88, Toe 0.55, Shoulder 0.26, Black Clip 0 and White Clip 0.04 (the default
|
|
16
|
+
* post-process settings). It is the ACES reference rendering's glow and red modifier and its 0.96 desaturation, then a
|
|
17
17
|
* parametric toe-straight-shoulder curve on log10 of each AP1 channel, solved so that 0.18 comes out at 0.18, then a
|
|
18
18
|
* 0.93 desaturation;
|
|
19
19
|
* 6. back to the working space, clamped at zero, and clipped at 1 as the sRGB target clips it.
|
|
@@ -80,7 +80,7 @@ fn ue_film_hue(rgb: vec3<f32>) -> f32 {
|
|
|
80
80
|
return clamp(hue, 0.0, 360.0);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
//
|
|
83
|
+
// Filmic curve: AP1 in, AP1 out
|
|
84
84
|
fn ue_film_tone_map(color_ap1: vec3<f32>) -> vec3<f32> {
|
|
85
85
|
var aces = UE_AP1_TO_AP0 * color_ap1;
|
|
86
86
|
|
|
@@ -63,7 +63,7 @@ the diffuse as well: under a uniform sky a white dielectric returned 1.01 – 1.
|
|
|
63
63
|
most at low roughness and grazing view. `re_direct_multiscatter` now scales the accumulated direct diffuse by
|
|
64
64
|
1 − the dielectric's specular albedo at N·V: `compute_multiscattering_specular` at `specularF0_dielectric`, on the
|
|
65
65
|
`f_ab` it already fetched. That is the indirect path's factor, "albedo scaling"; UE 5.4 does the same for direct
|
|
66
|
-
light (
|
|
66
|
+
light (by luminance: always under Substrate, in legacy shading only with the project's
|
|
67
67
|
energy-conservation setting on), as do EEVEE Next (by the RGB maximum) and OpenPBR / MaterialX
|
|
68
68
|
(`throughput = 1 − dirAlbedo`). Filament leaves direct diffuse uncoupled (a TODO in
|
|
69
69
|
`surface_shading_model_standard.fs`); three.js since #33985 scales it per light by 1 − F(V·H).
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
* it stops short of `x` by up to a ULP/α: half of that rounded to nearest, a whole one rounded toward zero, and
|
|
14
14
|
* one-sided. WGSL leaves rounding an f32 to a half to the implementation. An RTX 4090 and an RDNA2 iGPU were
|
|
15
15
|
* measured to round toward zero on every path: `pack2x16float`, `textureStore` and a render target's write. The
|
|
16
|
-
* result here is a half already, which any store keeps exactly.
|
|
17
|
-
* `QuantizeForFloatRenderTarget` for the same reason.
|
|
16
|
+
* result here is a half already, which any store keeps exactly.
|
|
18
17
|
*
|
|
19
18
|
* Within the half's finite range: past 65504 the half above is not finite.
|
|
20
19
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk_f16_round_stochastic.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.js"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"chunk_f16_round_stochastic.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,yCAFU,SAAS,CAoBhB;0BAzCuB,6BAA6B"}
|
|
@@ -15,8 +15,7 @@ import { CodeChunk } from "../../compiler/CodeChunk.js";
|
|
|
15
15
|
* it stops short of `x` by up to a ULP/α: half of that rounded to nearest, a whole one rounded toward zero, and
|
|
16
16
|
* one-sided. WGSL leaves rounding an f32 to a half to the implementation. An RTX 4090 and an RDNA2 iGPU were
|
|
17
17
|
* measured to round toward zero on every path: `pack2x16float`, `textureStore` and a render target's write. The
|
|
18
|
-
* result here is a half already, which any store keeps exactly.
|
|
19
|
-
* `QuantizeForFloatRenderTarget` for the same reason.
|
|
18
|
+
* result here is a half already, which any store keeps exactly.
|
|
20
19
|
*
|
|
21
20
|
* Within the half's finite range: past 65504 the half above is not finite.
|
|
22
21
|
*
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Two numbers in (0, 1) from the spatio-temporal blue noise texture (NVIDIA, "Spatiotemporal Blue Noise Masks",
|
|
3
|
+
* EGSR 2022), tiled 128 × 128 over the first two axes and 64 slices over the third.
|
|
4
|
+
*
|
|
5
|
+
* ```wgsl
|
|
6
|
+
* fn stbn_sample_vec2(index: vec3<u32>) -> vec2<f32>
|
|
7
|
+
* ```
|
|
8
|
+
*
|
|
9
|
+
* The generator quantises a value x in [0, 1) as `byte = floor(x · 256)` (NVIDIA-RTX/STBN,
|
|
10
|
+
* BlueNoiseTexturesND.cpp and RGBAExporters.cpp), so byte k stands for x in [k/256, (k+1)/256). A `unorm` texture
|
|
11
|
+
* hands it over as k/255, the inverse of a `round(x · 255)` encoding, which puts two of the 256 values on 0 and 1
|
|
12
|
+
* exactly. The value is read as its bin's centre, (k + ½)/256, the mean of x over the bin: the 256 values sit
|
|
13
|
+
* evenly inside (0, 1), symmetric about ½, and a draw that needs u in [0, 1) never meets either end (at u = 0 a
|
|
14
|
+
* weighted draw takes the last item of any weight, however small; at u = 1 none).
|
|
15
|
+
*
|
|
3
16
|
* @type {CodeChunk}
|
|
4
17
|
*/
|
|
5
18
|
export const chunk_stbn_sample_vec2: CodeChunk;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk_stbn_sample_vec2.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.js"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"chunk_stbn_sample_vec2.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;GAgBG;AACH,qCAFU,SAAS,CAYlB;0BA9ByB,6BAA6B"}
|
|
@@ -2,19 +2,32 @@ import { CodeChunk } from "../../compiler/CodeChunk.js";
|
|
|
2
2
|
import { STBN_VEC2_RESOURCE } from "../../../STATIC_GRAPHICS_ENGINE_ASSET_RESOURCES.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Two numbers in (0, 1) from the spatio-temporal blue noise texture (NVIDIA, "Spatiotemporal Blue Noise Masks",
|
|
6
|
+
* EGSR 2022), tiled 128 × 128 over the first two axes and 64 slices over the third.
|
|
7
|
+
*
|
|
8
|
+
* ```wgsl
|
|
9
|
+
* fn stbn_sample_vec2(index: vec3<u32>) -> vec2<f32>
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* The generator quantises a value x in [0, 1) as `byte = floor(x · 256)` (NVIDIA-RTX/STBN,
|
|
13
|
+
* BlueNoiseTexturesND.cpp and RGBAExporters.cpp), so byte k stands for x in [k/256, (k+1)/256). A `unorm` texture
|
|
14
|
+
* hands it over as k/255, the inverse of a `round(x · 255)` encoding, which puts two of the 256 values on 0 and 1
|
|
15
|
+
* exactly. The value is read as its bin's centre, (k + ½)/256, the mean of x over the bin: the 256 values sit
|
|
16
|
+
* evenly inside (0, 1), symmetric about ½, and a draw that needs u in [0, 1) never meets either end (at u = 0 a
|
|
17
|
+
* weighted draw takes the last item of any weight, however small; at u = 1 none).
|
|
18
|
+
*
|
|
6
19
|
* @type {CodeChunk}
|
|
7
20
|
*/
|
|
8
21
|
export const chunk_stbn_sample_vec2 = CodeChunk.from(
|
|
9
22
|
//language=WGSL
|
|
10
23
|
`
|
|
11
|
-
fn stbn_sample_vec2(index: vec3<u32>
|
|
24
|
+
fn stbn_sample_vec2(index: vec3<u32>) -> vec2<f32> {
|
|
25
|
+
let noise_coord = index % vec3(128, 128, 64);
|
|
12
26
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return textureLoad(tSTBN_vec2, noise_coord, 0).rg;
|
|
27
|
+
// k/255 to (k + 1/2)/256
|
|
28
|
+
return fma(textureLoad(tSTBN_vec2, noise_coord, 0).rg, vec2(255.0 / 256.0), vec2(0.5 / 256.0));
|
|
16
29
|
}
|
|
17
30
|
`
|
|
18
31
|
)
|
|
19
32
|
|
|
20
|
-
chunk_stbn_sample_vec2.requireResource(STBN_VEC2_RESOURCE);
|
|
33
|
+
chunk_stbn_sample_vec2.requireResource(STBN_VEC2_RESOURCE);
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Three numbers in (0, 1) from the spatio-temporal blue noise texture (NVIDIA, "Spatiotemporal Blue Noise Masks",
|
|
3
|
+
* EGSR 2022), tiled 128 × 128 over the first two axes and 64 slices over the third.
|
|
4
|
+
*
|
|
5
|
+
* ```wgsl
|
|
6
|
+
* fn stbn_sample_vec3(index: vec3<u32>) -> vec3<f32>
|
|
7
|
+
* ```
|
|
8
|
+
*
|
|
9
|
+
* Each byte is read as the centre of the bin the generator quantised it from, (k + ½)/256, rather than the
|
|
10
|
+
* `unorm` k/255; see {@link chunk_stbn_sample_vec2}.
|
|
11
|
+
*
|
|
3
12
|
* @type {CodeChunk}
|
|
4
13
|
*/
|
|
5
14
|
export const chunk_stbn_sample_vec3: CodeChunk;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk_stbn_sample_vec3.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.js"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"chunk_stbn_sample_vec3.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AACH,qCAFU,SAAS,CAYlB;0BA1ByB,6BAA6B"}
|
|
@@ -2,20 +2,28 @@ import { CodeChunk } from "../../compiler/CodeChunk.js";
|
|
|
2
2
|
import { STBN_VEC3_RESOURCE } from "../../../STATIC_GRAPHICS_ENGINE_ASSET_RESOURCES.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Three numbers in (0, 1) from the spatio-temporal blue noise texture (NVIDIA, "Spatiotemporal Blue Noise Masks",
|
|
6
|
+
* EGSR 2022), tiled 128 × 128 over the first two axes and 64 slices over the third.
|
|
7
|
+
*
|
|
8
|
+
* ```wgsl
|
|
9
|
+
* fn stbn_sample_vec3(index: vec3<u32>) -> vec3<f32>
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Each byte is read as the centre of the bin the generator quantised it from, (k + ½)/256, rather than the
|
|
13
|
+
* `unorm` k/255; see {@link chunk_stbn_sample_vec2}.
|
|
14
|
+
*
|
|
6
15
|
* @type {CodeChunk}
|
|
7
16
|
*/
|
|
8
17
|
export const chunk_stbn_sample_vec3 = CodeChunk.from(
|
|
9
18
|
//language=WGSL
|
|
10
19
|
`
|
|
11
|
-
fn stbn_sample_vec3(index: vec3<u32>
|
|
20
|
+
fn stbn_sample_vec3(index: vec3<u32>) -> vec3<f32> {
|
|
21
|
+
let noise_coord = index % vec3(128, 128, 64);
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return textureLoad(tSTBN_vec3, noise_coord, 0).rgb;
|
|
23
|
+
// k/255 to (k + 1/2)/256
|
|
24
|
+
return fma(textureLoad(tSTBN_vec3, noise_coord, 0).rgb, vec3(255.0 / 256.0), vec3(0.5 / 256.0));
|
|
16
25
|
}
|
|
17
26
|
`
|
|
18
27
|
)
|
|
19
28
|
|
|
20
29
|
chunk_stbn_sample_vec3.requireResource(STBN_VEC3_RESOURCE);
|
|
21
|
-
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* MurmurHash3 3D integer hash (Jarzynski & Olano 2020, "Hash Functions for GPU Rendering", JCGT).
|
|
3
3
|
* @type {CodeChunk}
|
|
4
4
|
*/
|
|
5
5
|
export const chunk_murmur3_vec3u: CodeChunk;
|
|
@@ -3,7 +3,7 @@ import { chunk_u32_rotate_left } from "../chunk_u32_rotate_left.js";
|
|
|
3
3
|
import { chunk_murmur3_fmix } from "./chunk_murmur3_fmix.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* MurmurHash3 3D integer hash (Jarzynski & Olano 2020, "Hash Functions for GPU Rendering", JCGT).
|
|
7
7
|
* @type {CodeChunk}
|
|
8
8
|
*/
|
|
9
9
|
export const chunk_murmur3_vec3u = CodeChunk.from(
|
|
@@ -135,29 +135,24 @@ Neither replaces the other.
|
|
|
135
135
|
|
|
136
136
|
## 3. What other engines do
|
|
137
137
|
|
|
138
|
-
Read from
|
|
138
|
+
Read from primary docs and references. The rest comes from the research brief
|
|
139
139
|
`shader-switch-prior-art.md` in the side-notes folder of this session group, which has the URLs and identifiers; its
|
|
140
140
|
Bevy and gpuweb claims were re-checked against the pages.
|
|
141
141
|
|
|
142
|
-
**Unreal: permutation domains.**
|
|
143
|
-
`SHADER_PERMUTATION_BOOL` / `_INT` / `_RANGE_INT` / `_SPARSE_INT` / `_ENUM_CLASS`, each carrying a `DefineName`.
|
|
144
|
-
`TShaderPermutationDomain<…>` is their product, and its permutation id is mixed-radix over the dimensions
|
|
145
|
-
(`ToDimensionValueId`). A shader class lists its dimensions. `FDeferredLightPS` (`Renderer/Private/LightRendering.cpp:816`)
|
|
146
|
-
has 14 of them, including `FVirtualShadowMapMask`.
|
|
142
|
+
**Unreal: permutation domains.** A dimension is a C++ type (bool, int, range, sparse int, or enum), each carrying a preprocessor define name. Their product is a permutation domain whose permutation id is mixed-radix over the dimensions. A shader class lists its dimensions (for example, the deferred light pixel shader has 14 of them, including the virtual shadow map mask).
|
|
147
143
|
|
|
148
|
-
-
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
- The draw site sets every dimension by hand
|
|
144
|
+
- Unused combinations are culled before compilation (e.g., a directional light × the VSM mask is never compiled).
|
|
145
|
+
- Redundant combinations are remapped together (e.g., visualize-culling forces the others off).
|
|
146
|
+
- Conditional defines (such as VSM defines) are added to the compilation environment only when that dimension is on.
|
|
147
|
+
- The draw site sets every dimension by hand, then asks the shader map for
|
|
152
148
|
`(shader type, permutation id)`. **The owner knows every dimension.** Nothing derives the domain: the shader class lists it
|
|
153
149
|
and the draw site sets it.
|
|
154
150
|
- Dimensions select `#if` blocks. Dependencies are `#include`s, which can sit inside an `#if`, but nothing declares
|
|
155
151
|
what each value pulls in.
|
|
156
152
|
|
|
157
153
|
**Unreal: vertex factories and materials.** These are the other half of Unreal's answer, and they are an *interface
|
|
158
|
-
slot*.
|
|
159
|
-
|
|
160
|
-
`/Engine/Generated/Material.ush` is mapped to the generated material code (`Engine/Private/Materials/MaterialShared.cpp:3357`).
|
|
154
|
+
slot*. The vertex factory compilation environment maps a virtual include path to the concrete implementation's source file.
|
|
155
|
+
|
|
161
156
|
Every shader that includes the path gets the implementation the compile supplies, and nothing in between is twinned.
|
|
162
157
|
The shader map is keyed by material × vertex factory × shader type × permutation.
|
|
163
158
|
|
|
@@ -587,7 +582,7 @@ Risks:
|
|
|
587
582
|
| piece | reuses / generalises |
|
|
588
583
|
|---|---|
|
|
589
584
|
| specialization | `CodeChunk`'s own graph and private fields, with the same identity-based dedup the linker relies on. No second linker; `compile()` is untouched. |
|
|
590
|
-
| values | engine enums as they are (`ShadeIndirectLightingMode` unchanged), or a bool.
|
|
585
|
+
| values | engine enums as they are (`ShadeIndirectLightingMode` unchanged), or a bool. Like Unreal's enum/bool/sparse-int permutation dimensions, an enum need not be contiguous. |
|
|
591
586
|
| variant objects | `ShaderDescriptor`, `ImageShader`, `ComputeShader` and `TemplatePassDescriptor` themselves. A variant is one of them, so draw, dispatch, validation and the profiler need nothing new. |
|
|
592
587
|
| GPU pipeline cache | `RenderPipelineManager` / `ComputePipelineManager` unchanged. Text equality separates and shares variants. |
|
|
593
588
|
| preparation | `PipelineEnquiry` and `prepare()` at start-up, offering the selected variants. A flip uses `obtain` as anything made on first use does. |
|
|
@@ -60,20 +60,20 @@ summed over the ten scenes, with less false shadow on fine detail, at 3.4× its
|
|
|
60
60
|
|
|
61
61
|
## 1. The failure modes, and why they happen
|
|
62
62
|
|
|
63
|
-
From the primary sources.
|
|
63
|
+
From the primary sources. Bend is
|
|
64
64
|
`bend_sss_gpu.h` / `bend_sss_cpu.h` (Sony, Apache 2.0) and the SIGGRAPH 2023 talk's slide text.
|
|
65
65
|
|
|
66
66
|
| # | Failure mode | Cause | What shipping code does about it |
|
|
67
67
|
|---|---|---|---|
|
|
68
|
-
| 1 | **Heightfield thickness**: thin casters smear, thick ones leak | The depth buffer holds each caster's front and nothing behind it, so every method assumes a slab thickness | Bend: 0.5% of the sample's depth to the far plane (here: in front of the ray by up to 1% of its view depth). UE desktop: ≈ L/2, set by ray length and 8 steps
|
|
68
|
+
| 1 | **Heightfield thickness**: thin casters smear, thick ones leak | The depth buffer holds each caster's front and nothing behind it, so every method assumes a slab thickness | Bend: 0.5% of the sample's depth to the far plane (here: in front of the ray by up to 1% of its view depth). UE desktop: ≈ L/2, set by ray length and 8 steps. Filament ≈ 7.5 cm, three.js 1 cm, Bevy 10 cm, all world space. Nobody reconstructs back faces |
|
|
69
69
|
| 2 | **Self-shadowing** where the sun or the view grazes a surface, and on normal-mapped surfaces | A ray leaving a surface at a grazing angle runs along it, so the surface's own neighbouring depths sit just in front of it; depth is quantised per pixel; the shading normal and the depth disagree | Bend: an edge-aware bilinear read across the ray's minor axis, pushing the casting depth back by the step across the pixel; a start bias "suppressed legitimate detail" (talk). UE: skip samples bit-equal to the start depth. Bevy: an open draft (#25355) bends the ray toward the normal. Normal maps are invisible to all of them; Days Gone wrote a ±8 cm material displacement into depth to get that detail |
|
|
70
70
|
| 3 | **View dependence** | Only casters that are on screen and in front cast; the reach is counted in pixels (Bend, Godot, UE screen mode) or the screen reach shrinks with distance (world-space methods) | Bend: "they cannot replace shadow maps, only complement them" (talk). Nothing fixes it |
|
|
71
71
|
| 4 | **Noise and TAA instability** | Jittered starts and few steps (UE 8 steps + IGN over 8 frames, Bevy, three.js, Filament) need TAA to converge; the jittered depth buffer changes what is in it | Bend is deterministic: 4 interleaved min-reductions averaged, 4 hard near samples, an 8-sample fade. Every other source leans on the scene's TAA |
|
|
72
72
|
| 5 | **Disocclusion trails** | Temporal accumulation of a term that changes with the view | No source keeps contact-shadow history; trails are whatever TAA does |
|
|
73
73
|
| 6 | **Screen edges** | Off-screen casters are not in the depth buffer | Bend reads off-screen as the far plane (clamp-to-border); UE treats an off-screen hit as a miss; Filament fades by the ray's end. A fade only hides the pop |
|
|
74
|
-
| 7 | **Scale with distance** | A pixel reach means a world reach growing with depth; a world reach shrinks below a pixel | Bend 60 px fixed; UE screen mode L ∝ depth
|
|
75
|
-
| 8 | **Alpha-tested foliage** | Cut-outs alias per texel, cards are millimetres thick, two-sided leaves are their own receivers | Bend's `IgnoreEdgePixels` "can also thin out otherwise valid shadows, especially on foliage edges". UE attenuates subsurface/foliage receivers by 1 − exp(−density·distance)
|
|
76
|
-
| 9 | **Double darkening** against the shadow map | Multiplying two partial visibilities (a PCF penumbra and a partial contact) is darker than either | UE desktop multiplies
|
|
74
|
+
| 7 | **Scale with distance** | A pixel reach means a world reach growing with depth; a world reach shrinks below a pixel | Bend 60 px fixed; UE screen mode L ∝ depth; world-space methods vanish far away |
|
|
75
|
+
| 8 | **Alpha-tested foliage** | Cut-outs alias per texel, cards are millimetres thick, two-sided leaves are their own receivers | Bend's `IgnoreEdgePixels` "can also thin out otherwise valid shadows, especially on foliage edges". UE attenuates subsurface/foliage receivers by 1 − exp(−density·distance) |
|
|
76
|
+
| 9 | **Double darkening** against the shadow map | Multiplying two partial visibilities (a PCF penumbra and a partial contact) is darker than either | UE desktop multiplies; UE mobile and Godot take the min; Filament and Bevy multiply but skip pixels the map already shadows fully |
|
|
77
77
|
| 10 | **The light's point on screen** | Rays converge on it, so near it every ray is a few pixels long; Bend widens its window there | Bend: `min(sample_distance + direction, 1/thickness)` in the window scale |
|
|
78
78
|
|
|
79
79
|
## 2. Measured
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Default ceiling on the dynamic LOD bias a virtual shadow map may add under pool pressure (
|
|
3
|
-
*
|
|
4
|
-
* disables dynamic resolution throttling.
|
|
2
|
+
* Default ceiling on the dynamic LOD bias a virtual shadow map may add under pool pressure (up to a
|
|
3
|
+
* 4× linear / 16× area reduction). Setting `0` disables dynamic resolution throttling.
|
|
5
4
|
*
|
|
6
5
|
* @type {number}
|
|
7
6
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.js"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,+CAFU,MAAM,CAEkC"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default ceiling on the dynamic LOD bias a virtual shadow map may add under pool pressure (
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS = 2;
|
|
1
|
+
/**
|
|
2
|
+
* Default ceiling on the dynamic LOD bias a virtual shadow map may add under pool pressure (up to a
|
|
3
|
+
* 4× linear / 16× area reduction). Setting `0` disables dynamic resolution throttling.
|
|
4
|
+
*
|
|
5
|
+
* @type {number}
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS = 2;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Default high-watermark pool demand ratio `(wants + requests_dropped) / slots.capacity` above
|
|
3
3
|
* which a virtual shadow map steps its dynamic LOD bias up to coarsen requested pages and prevent
|
|
4
|
-
* hard page-pool overflow
|
|
4
|
+
* hard page-pool overflow.
|
|
5
5
|
*
|
|
6
6
|
* Paired with {@link DEFAULT_VSM_LOW_LOAD_FACTOR} to form a hysteresis deadband so demand hovering
|
|
7
7
|
* near capacity does not oscillate between adjacent clipmap levels. Setting `0` disables dynamic
|