@forgeax/engine-shader 0.1.21 → 0.1.24

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 (83) hide show
  1. package/README.md +14 -5
  2. package/dist/ShaderRegistry.d.ts.map +1 -1
  3. package/dist/index.d.ts +4 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.mjs +33042 -55
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/ltc/provenance.d.ts +17 -0
  8. package/dist/ltc/provenance.d.ts.map +1 -0
  9. package/dist/ltc/tables.d.ts +22 -0
  10. package/dist/ltc/tables.d.ts.map +1 -0
  11. package/dist/material/artifact-types.d.ts +52 -0
  12. package/dist/material/artifact-types.d.ts.map +1 -1
  13. package/dist/material-schemas.d.ts +36 -0
  14. package/dist/material-schemas.d.ts.map +1 -1
  15. package/dist/register-default-sprite-lit.d.ts +3 -6
  16. package/dist/register-default-sprite-lit.d.ts.map +1 -1
  17. package/dist/types.d.ts +2 -1
  18. package/dist/types.d.ts.map +1 -1
  19. package/package.json +4 -4
  20. package/src/ShaderRegistry.ts +0 -17
  21. package/src/__tests__/default-standard-pbr-alpha.unit.test.ts +9 -3
  22. package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +67 -122
  23. package/src/__tests__/deferred-lighting-ssao.test.ts +3 -3
  24. package/src/__tests__/direct-light-layout.unit.test.ts +40 -0
  25. package/src/__tests__/ibl-irradiance.unit.test.ts +16 -4
  26. package/src/__tests__/ibl-sampling.unit.test.ts +3 -4
  27. package/src/__tests__/lighting-directional.unit.test.ts +36 -0
  28. package/src/__tests__/lighting-point-spot-volume.unit.test.ts +8 -2
  29. package/src/__tests__/lighting-punctual.unit.test.ts +40 -5
  30. package/src/__tests__/lighting-spot-volume-attenuation.unit.test.ts +1 -1
  31. package/src/__tests__/ltc-provenance.unit.test.ts +33 -0
  32. package/src/__tests__/manifest.fixture.json +33 -0
  33. package/src/__tests__/material-builtins.unit.test.ts +21 -3
  34. package/src/__tests__/material-contract.unit.test.ts +188 -14
  35. package/src/__tests__/physical-clearcoat.unit.test.ts +65 -0
  36. package/src/__tests__/probe-lighting-composition.unit.test.ts +39 -0
  37. package/src/__tests__/rect-area-brdf.unit.test.ts +36 -0
  38. package/src/__tests__/reflection-probe-sampling.unit.test.ts +24 -2
  39. package/src/__tests__/shader-manifest-transmission.unit.test.ts +8 -10
  40. package/src/__tests__/shader.unit.test.ts +54 -85
  41. package/src/__tests__/spot-cookie-composition.unit.test.ts +58 -0
  42. package/src/__tests__/spot-modifier-composition.unit.test.ts +19 -0
  43. package/src/__tests__/sprite-variants.unit.test.ts +4 -1
  44. package/src/__tests__/standard-webgl2-varying-budget.unit.test.ts +17 -0
  45. package/src/__tests__/surface-v1-negative.unit.test.ts +37 -0
  46. package/src/__tests__/surface-v1.unit.test.ts +83 -0
  47. package/src/__tests__/transmission-thickness-scale.unit.test.ts +2 -6
  48. package/src/__tests__/vertex-color-variant.unit.test.ts +16 -5
  49. package/src/brdf.wgsl +115 -1
  50. package/src/common.wgsl +53 -79
  51. package/src/default-standard-pbr-skin.wgsl +687 -144
  52. package/src/default-standard-pbr.wgsl +600 -247
  53. package/src/default_standard_surface.wgsl +88 -0
  54. package/src/hdrp-cluster-forward.wgsl +225 -0
  55. package/src/ibl-irradiance.wgsl +47 -9
  56. package/src/ibl-prefilter.wgsl +13 -3
  57. package/src/ibl-sampling.wgsl +2 -2
  58. package/src/ibl-shared.wgsl +9 -2
  59. package/src/index.ts +23 -0
  60. package/src/lighting-directional.wgsl +9 -117
  61. package/src/lighting-probe.wgsl +51 -0
  62. package/src/lighting-punctual.wgsl +21 -11
  63. package/src/lighting-rect-area.wgsl +162 -0
  64. package/src/lighting-spot-modifiers.wgsl +100 -0
  65. package/src/lighting-spot-projector.wgsl +31 -0
  66. package/src/ltc/provenance.ts +19 -0
  67. package/src/ltc/tables.ts +2831 -0
  68. package/src/material/artifact-types.ts +137 -0
  69. package/src/material/physical/anisotropy.wgsl +26 -0
  70. package/src/material/physical/clearcoat.wgsl +24 -0
  71. package/src/material/physical/iridescence.wgsl +23 -0
  72. package/src/material/physical/sheen.wgsl +17 -0
  73. package/src/material-schemas.ts +77 -30
  74. package/src/register-default-sprite-lit.ts +3 -6
  75. package/src/register-default-standard-pbr-skin.ts +2 -2
  76. package/src/sprite-lit.wgsl +3 -3
  77. package/src/sprite.wgsl +1 -1
  78. package/src/standard-cluster.wgsl +80 -78
  79. package/src/surface_v1.wgsl +26 -0
  80. package/src/types.ts +2 -1
  81. package/src/volume/volume-inject.wgsl +34 -36
  82. package/src/volume/volume-integrate.wgsl +48 -38
  83. package/dist/.tsbuildinfo +0 -1
@@ -46,7 +46,7 @@
46
46
  // layered BRDFs such as base + clearcoat)
47
47
 
48
48
  #import forgeax_view::common::{view, shadowMap, shadowSampler}
49
- #import forgeax_pbr::brdf::{f_schlick, v_smith, d_ggx}
49
+ #import forgeax_pbr::brdf::{f_schlick, v_smith, d_ggx, threeR184DirectMultiScatter}
50
50
  #import forgeax_pbr::shadow_pcf::{sample_shadow_2d}
51
51
  #ifdef DIRECTIONAL_PCSS_AVAILABLE
52
52
  #import forgeax_pbr::shadow_pcf::{shadow_biased_receiver_depth, shadow_clamp_texel_to_tile, shadow_load_raw_depth, shadow_sample_compare}
@@ -80,118 +80,9 @@ const PCSS_DISK_OFFSETS : array<vec2<f32>, 32> = array<vec2<f32>, 32>(
80
80
  );
81
81
  #endif
82
82
 
83
- // Source: Three.js r184 src/nodes/functions/BSDF/DFGLUT.js. The full 16x16
84
- // RG16F table is decoded to exact f32 constants here; the lookup below mirrors
85
- // the source DataTexture's linear filtering and clamp-to-edge sampling without
86
- // adding a host binding or coupling direct light to the engine IBL LUT.
87
- const THREE_R184_DFG_LUT_SIZE : u32 = 16u;
88
- const THREE_R184_DFG_LUT : array<vec2<f32>, 256> = array<vec2<f32>, 256>(
89
- vec2<f32>(0.1470947265625, 0.85205078125), vec2<f32>(0.16552734375, 0.78759765625), vec2<f32>(0.244384765625, 0.638671875), vec2<f32>(0.370849609375, 0.51953125),
90
- vec2<f32>(0.496826171875, 0.41552734375), vec2<f32>(0.60205078125, 0.326416015625), vec2<f32>(0.68408203125, 0.25390625), vec2<f32>(0.74609375, 0.197509765625),
91
- vec2<f32>(0.79052734375, 0.154296875), vec2<f32>(0.822265625, 0.12164306640625), vec2<f32>(0.84326171875, 0.09698486328125), vec2<f32>(0.8564453125, 0.07843017578125),
92
- vec2<f32>(0.86328125, 0.06439208984375), vec2<f32>(0.86572265625, 0.0537109375), vec2<f32>(0.8642578125, 0.045440673828125), vec2<f32>(0.85986328125, 0.039031982421875),
93
- vec2<f32>(0.388671875, 0.611328125), vec2<f32>(0.39306640625, 0.6005859375), vec2<f32>(0.412353515625, 0.5458984375), vec2<f32>(0.45654296875, 0.4482421875),
94
- vec2<f32>(0.52783203125, 0.3525390625), vec2<f32>(0.607421875, 0.27392578125), vec2<f32>(0.6787109375, 0.21142578125), vec2<f32>(0.7333984375, 0.16259765625),
95
- vec2<f32>(0.77099609375, 0.1253662109375), vec2<f32>(0.79345703125, 0.0972900390625), vec2<f32>(0.80322265625, 0.07623291015625), vec2<f32>(0.8037109375, 0.06036376953125),
96
- vec2<f32>(0.79638671875, 0.048431396484375), vec2<f32>(0.78564453125, 0.03936767578125), vec2<f32>(0.77197265625, 0.03240966796875), vec2<f32>(0.7548828125, 0.0269775390625),
97
- vec2<f32>(0.572265625, 0.427490234375), vec2<f32>(0.57373046875, 0.424072265625), vec2<f32>(0.57958984375, 0.403564453125), vec2<f32>(0.591796875, 0.3544921875),
98
- vec2<f32>(0.6162109375, 0.2880859375), vec2<f32>(0.6552734375, 0.224853515625), vec2<f32>(0.69873046875, 0.172607421875), vec2<f32>(0.7353515625, 0.1318359375),
99
- vec2<f32>(0.75927734375, 0.10089111328125), vec2<f32>(0.77001953125, 0.07745361328125), vec2<f32>(0.77197265625, 0.0599365234375), vec2<f32>(0.76611328125, 0.046844482421875),
100
- vec2<f32>(0.751953125, 0.0369873046875), vec2<f32>(0.732421875, 0.029541015625), vec2<f32>(0.70947265625, 0.023834228515625), vec2<f32>(0.68359375, 0.019439697265625),
101
- vec2<f32>(0.708984375, 0.291015625), vec2<f32>(0.708984375, 0.289794921875), vec2<f32>(0.7099609375, 0.28125), vec2<f32>(0.7099609375, 0.258544921875),
102
- vec2<f32>(0.71142578125, 0.220458984375), vec2<f32>(0.7197265625, 0.1768798828125), vec2<f32>(0.734375, 0.1370849609375), vec2<f32>(0.748046875, 0.10479736328125),
103
- vec2<f32>(0.755859375, 0.07989501953125), vec2<f32>(0.759765625, 0.061004638671875), vec2<f32>(0.75341796875, 0.046844482421875), vec2<f32>(0.73876953125, 0.036224365234375),
104
- vec2<f32>(0.7177734375, 0.02825927734375), vec2<f32>(0.69189453125, 0.0222625732421875), vec2<f32>(0.6611328125, 0.0177001953125), vec2<f32>(0.62841796875, 0.01419830322265625),
105
- vec2<f32>(0.80810546875, 0.1917724609375), vec2<f32>(0.8076171875, 0.1912841796875), vec2<f32>(0.80615234375, 0.18798828125), vec2<f32>(0.8017578125, 0.1781005859375),
106
- vec2<f32>(0.79296875, 0.15869140625), vec2<f32>(0.78369140625, 0.1322021484375), vec2<f32>(0.775390625, 0.1048583984375), vec2<f32>(0.7685546875, 0.08111572265625),
107
- vec2<f32>(0.7646484375, 0.061981201171875), vec2<f32>(0.75537109375, 0.047271728515625), vec2<f32>(0.740234375, 0.036163330078125), vec2<f32>(0.71875, 0.0277557373046875),
108
- vec2<f32>(0.69091796875, 0.021484375), vec2<f32>(0.658203125, 0.0167388916015625), vec2<f32>(0.6220703125, 0.01316070556640625), vec2<f32>(0.583984375, 0.01041412353515625),
109
- vec2<f32>(0.87841796875, 0.1217041015625), vec2<f32>(0.8779296875, 0.1217041015625), vec2<f32>(0.875, 0.12060546875), vec2<f32>(0.869140625, 0.116943359375),
110
- vec2<f32>(0.85693359375, 0.1080322265625), vec2<f32>(0.837890625, 0.09375), vec2<f32>(0.81494140625, 0.076904296875), vec2<f32>(0.7958984375, 0.060699462890625),
111
- vec2<f32>(0.7763671875, 0.046875), vec2<f32>(0.75634765625, 0.035919189453125), vec2<f32>(0.732421875, 0.0274505615234375), vec2<f32>(0.703125, 0.021026611328125),
112
- vec2<f32>(0.6689453125, 0.01617431640625), vec2<f32>(0.63037109375, 0.01251220703125), vec2<f32>(0.58935546875, 0.00974273681640625), vec2<f32>(0.54638671875, 0.007633209228515625),
113
- vec2<f32>(0.92626953125, 0.07379150390625), vec2<f32>(0.92578125, 0.07391357421875), vec2<f32>(0.9228515625, 0.07391357421875), vec2<f32>(0.9169921875, 0.0731201171875),
114
- vec2<f32>(0.90380859375, 0.06982421875), vec2<f32>(0.88134765625, 0.0631103515625), vec2<f32>(0.85107421875, 0.053802490234375), vec2<f32>(0.8212890625, 0.043701171875),
115
- vec2<f32>(0.791015625, 0.034393310546875), vec2<f32>(0.76123046875, 0.026611328125), vec2<f32>(0.72802734375, 0.02044677734375), vec2<f32>(0.69189453125, 0.015655517578125),
116
- vec2<f32>(0.65087890625, 0.0120086669921875), vec2<f32>(0.607421875, 0.00925445556640625), vec2<f32>(0.56103515625, 0.0071563720703125), vec2<f32>(0.51416015625, 0.005565643310546875),
117
- vec2<f32>(0.95751953125, 0.042327880859375), vec2<f32>(0.95703125, 0.042449951171875), vec2<f32>(0.95458984375, 0.042816162109375), vec2<f32>(0.94921875, 0.043182373046875),
118
- vec2<f32>(0.93701171875, 0.04266357421875), vec2<f32>(0.9130859375, 0.040252685546875), vec2<f32>(0.8818359375, 0.035797119140625), vec2<f32>(0.8447265625, 0.0301361083984375),
119
- vec2<f32>(0.80615234375, 0.0243377685546875), vec2<f32>(0.76708984375, 0.0191497802734375), vec2<f32>(0.7265625, 0.01485443115234375), vec2<f32>(0.6826171875, 0.01142120361328125),
120
- vec2<f32>(0.63623046875, 0.0087738037109375), vec2<f32>(0.5869140625, 0.006740570068359375), vec2<f32>(0.53662109375, 0.005199432373046875), vec2<f32>(0.486083984375, 0.00402069091796875),
121
- vec2<f32>(0.9775390625, 0.0226287841796875), vec2<f32>(0.97705078125, 0.0227508544921875), vec2<f32>(0.97509765625, 0.023193359375), vec2<f32>(0.9697265625, 0.0239105224609375),
122
- vec2<f32>(0.95947265625, 0.0244903564453125), vec2<f32>(0.93603515625, 0.0241241455078125), vec2<f32>(0.9052734375, 0.02252197265625), vec2<f32>(0.865234375, 0.0197601318359375),
123
- vec2<f32>(0.82177734375, 0.016510009765625), vec2<f32>(0.7744140625, 0.0132904052734375), vec2<f32>(0.7265625, 0.01045989990234375), vec2<f32>(0.67626953125, 0.0081329345703125),
124
- vec2<f32>(0.6240234375, 0.00627899169921875), vec2<f32>(0.56982421875, 0.004825592041015625), vec2<f32>(0.51513671875, 0.0037136077880859375), vec2<f32>(0.46142578125, 0.0028629302978515625),
125
- vec2<f32>(0.98876953125, 0.01107025146484375), vec2<f32>(0.98876953125, 0.01116180419921875), vec2<f32>(0.98681640625, 0.01151275634765625), vec2<f32>(0.98291015625, 0.01221466064453125),
126
- vec2<f32>(0.97314453125, 0.01299285888671875), vec2<f32>(0.953125, 0.013519287109375), vec2<f32>(0.9228515625, 0.01328277587890625), vec2<f32>(0.88232421875, 0.01226043701171875),
127
- vec2<f32>(0.8349609375, 0.01065826416015625), vec2<f32>(0.783203125, 0.00885009765625), vec2<f32>(0.728515625, 0.007114410400390625), vec2<f32>(0.671875, 0.00560760498046875),
128
- vec2<f32>(0.61376953125, 0.004360198974609375), vec2<f32>(0.5546875, 0.0033721923828125), vec2<f32>(0.496337890625, 0.0025997161865234375), vec2<f32>(0.439453125, 0.0020046234130859375),
129
- vec2<f32>(0.9951171875, 0.00479888916015625), vec2<f32>(0.9951171875, 0.004863739013671875), vec2<f32>(0.99365234375, 0.005096435546875), vec2<f32>(0.990234375, 0.005603790283203125),
130
- vec2<f32>(0.9814453125, 0.0063323974609375), vec2<f32>(0.96435546875, 0.006977081298828125), vec2<f32>(0.93603515625, 0.007297515869140625), vec2<f32>(0.896484375, 0.0071258544921875),
131
- vec2<f32>(0.8466796875, 0.00649261474609375), vec2<f32>(0.79150390625, 0.005596160888671875), vec2<f32>(0.7314453125, 0.004627227783203125), vec2<f32>(0.6689453125, 0.0037174224853515625),
132
- vec2<f32>(0.60546875, 0.0029296875), vec2<f32>(0.54150390625, 0.00228118896484375), vec2<f32>(0.479248046875, 0.001766204833984375), vec2<f32>(0.419677734375, 0.00136566162109375),
133
- vec2<f32>(0.998046875, 0.0017604827880859375), vec2<f32>(0.998046875, 0.0017938613891601562), vec2<f32>(0.99658203125, 0.0019292831420898438), vec2<f32>(0.994140625, 0.002239227294921875),
134
- vec2<f32>(0.986328125, 0.0027294158935546875), vec2<f32>(0.9716796875, 0.0032501220703125), vec2<f32>(0.94580078125, 0.00366973876953125), vec2<f32>(0.90771484375, 0.003818511962890625),
135
- vec2<f32>(0.8583984375, 0.003681182861328125), vec2<f32>(0.79931640625, 0.0033206939697265625), vec2<f32>(0.7353515625, 0.0028438568115234375), vec2<f32>(0.66748046875, 0.0023441314697265625),
136
- vec2<f32>(0.5986328125, 0.0018796920776367188), vec2<f32>(0.5302734375, 0.0014829635620117188), vec2<f32>(0.464111328125, 0.0011587142944335938), vec2<f32>(0.402099609375, 0.0008993148803710938),
137
- vec2<f32>(0.99951171875, 0.0005011558532714844), vec2<f32>(0.99951171875, 0.0005154609680175781), vec2<f32>(0.998046875, 0.000583648681640625), vec2<f32>(0.99658203125, 0.0007505416870117188),
138
- vec2<f32>(0.9892578125, 0.00101470947265625), vec2<f32>(0.97607421875, 0.001346588134765625), vec2<f32>(0.95263671875, 0.0016527175903320312), vec2<f32>(0.916015625, 0.0018558502197265625),
139
- vec2<f32>(0.8671875, 0.0019054412841796875), vec2<f32>(0.8076171875, 0.0018167495727539062), vec2<f32>(0.7392578125, 0.001621246337890625), vec2<f32>(0.6669921875, 0.0013799667358398438),
140
- vec2<f32>(0.59326171875, 0.0011358261108398438), vec2<f32>(0.52001953125, 0.0009121894836425781), vec2<f32>(0.450439453125, 0.0007219314575195312), vec2<f32>(0.385986328125, 0.0005650520324707031),
141
- vec2<f32>(1.0, 0.00009316205978393555), vec2<f32>(1.0, 0.0000978708267211914), vec2<f32>(0.9990234375, 0.00012540817260742188), vec2<f32>(0.9970703125, 0.00019216537475585938),
142
- vec2<f32>(0.99072265625, 0.0003104209899902344), vec2<f32>(0.97900390625, 0.0004699230194091797), vec2<f32>(0.95751953125, 0.0006470680236816406), vec2<f32>(0.92333984375, 0.0007915496826171875),
143
- vec2<f32>(0.87548828125, 0.0008769035339355469), vec2<f32>(0.81494140625, 0.0008869171142578125), vec2<f32>(0.744140625, 0.0008325576782226562), vec2<f32>(0.66748046875, 0.0007386207580566406),
144
- vec2<f32>(0.58837890625, 0.0006265640258789062), vec2<f32>(0.51123046875, 0.0005168914794921875), vec2<f32>(0.438232421875, 0.0004165172576904297), vec2<f32>(0.37109375, 0.0003311634063720703),
145
- vec2<f32>(1.0, 0.000007271766662597656), vec2<f32>(1.0, 0.000008165836334228516), vec2<f32>(0.9990234375, 0.000016987323760986328), vec2<f32>(0.99755859375, 0.00003790855407714844),
146
- vec2<f32>(0.9921875, 0.00007593631744384766), vec2<f32>(0.9814453125, 0.00013744831085205078), vec2<f32>(0.96142578125, 0.00020754337310791016), vec2<f32>(0.92919921875, 0.00028014183044433594),
147
- vec2<f32>(0.8828125, 0.0003345012664794922), vec2<f32>(0.82177734375, 0.00036263465881347656), vec2<f32>(0.7490234375, 0.00036215782165527344), vec2<f32>(0.66845703125, 0.0003380775451660156),
148
- vec2<f32>(0.58544921875, 0.00029969215393066406), vec2<f32>(0.50390625, 0.00025582313537597656), vec2<f32>(0.427001953125, 0.0002117156982421875), vec2<f32>(0.357666015625, 0.00017201900482177734),
149
- vec2<f32>(1.0, 0.0), vec2<f32>(1.0, 5.960464477539063e-8), vec2<f32>(0.99951171875, 0.0000012516975402832031), vec2<f32>(0.99755859375, 0.000005304813385009766),
150
- vec2<f32>(0.9931640625, 0.000015079975128173828), vec2<f32>(0.98291015625, 0.00002855062484741211), vec2<f32>(0.96435546875, 0.00004744529724121094), vec2<f32>(0.93408203125, 0.00006842613220214844),
151
- vec2<f32>(0.88916015625, 0.00008893013000488281), vec2<f32>(0.828125, 0.0001042485237121582), vec2<f32>(0.75390625, 0.00011217594146728516), vec2<f32>(0.67041015625, 0.00011241436004638672),
152
- vec2<f32>(0.5830078125, 0.00010627508163452148), vec2<f32>(0.4970703125, 0.00009584426879882812), vec2<f32>(0.4169921875, 0.0000833272933959961), vec2<f32>(0.34521484375, 0.00007051229476928711),
153
- );
154
-
155
- fn _sampleThreeR184DfgLut(roughness : f32, dotNV : f32) -> vec2<f32> {
156
- let uv = clamp(vec2<f32>(roughness, dotNV), vec2<f32>(0.0), vec2<f32>(1.0));
157
- let samplePosition = uv * f32(THREE_R184_DFG_LUT_SIZE) - vec2<f32>(0.5);
158
- let base = vec2<i32>(floor(samplePosition));
159
- let weight = fract(samplePosition);
160
- let last = i32(THREE_R184_DFG_LUT_SIZE - 1u);
161
- let lo = clamp(base, vec2<i32>(0), vec2<i32>(last));
162
- let hi = clamp(base + vec2<i32>(1), vec2<i32>(0), vec2<i32>(last));
163
- let rowLo = mix(
164
- THREE_R184_DFG_LUT[u32(lo.y) * THREE_R184_DFG_LUT_SIZE + u32(lo.x)],
165
- THREE_R184_DFG_LUT[u32(lo.y) * THREE_R184_DFG_LUT_SIZE + u32(hi.x)],
166
- weight.x,
167
- );
168
- let rowHi = mix(
169
- THREE_R184_DFG_LUT[u32(hi.y) * THREE_R184_DFG_LUT_SIZE + u32(lo.x)],
170
- THREE_R184_DFG_LUT[u32(hi.y) * THREE_R184_DFG_LUT_SIZE + u32(hi.x)],
171
- weight.x,
172
- );
173
- return mix(rowLo, rowHi, weight.y);
174
- }
175
-
176
- fn _threeR184DirectMultiScatter(
177
- roughness : f32,
178
- nDotV : f32,
179
- nDotL : f32,
180
- F0 : vec3<f32>,
181
- ) -> vec3<f32> {
182
- let dfgV = _sampleThreeR184DfgLut(roughness, nDotV);
183
- let dfgL = _sampleThreeR184DfgLut(roughness, nDotL);
184
- let fssEssV = F0 * dfgV.x + vec3<f32>(dfgV.y);
185
- let fssEssL = F0 * dfgL.x + vec3<f32>(dfgL.y);
186
- let emsV = 1.0 - dfgV.x - dfgV.y;
187
- let emsL = 1.0 - dfgL.x - dfgL.y;
188
- let favg = F0 + (vec3<f32>(1.0) - F0) * 0.047619;
189
- let energyLoss = emsV * emsL;
190
- let fms = fssEssV * fssEssL * favg
191
- / (vec3<f32>(1.0) - energyLoss * favg * favg + vec3<f32>(1e-6));
192
- return fms * energyLoss;
193
- }
194
-
83
+ // Direct multiscatter is owned by forgeax_pbr::brdf. Keep directional
84
+ // lighting as a consumer of that shared function so its LUT and energy
85
+ // compensation cannot diverge from punctual and cluster lighting.
195
86
  // Pick the cascade layer for a positive view-space depth -- walks
196
87
  // splitPlanes in order, returns the first split the depth falls below. Last
197
88
  // layer (count - 1) catches everything beyond splits[count-2]. cascadeCount=1
@@ -517,15 +408,16 @@ fn evalDirectionalNoShadow(
517
408
  F0 : vec3<f32>,
518
409
  ) -> vec3<f32> {
519
410
  let l = normalize(-view.lightDir);
520
- let h = normalize(viewDir + l);
411
+ let halfVector = viewDir + l;
412
+ let halfVectorLengthSquared = max(dot(halfVector, halfVector), 1e-8);
413
+ let h = halfVector * inverseSqrt(halfVectorLengthSquared);
521
414
  let nDotL = max(dot(normal, l), 0.0);
522
415
  let nDotV = max(dot(normal, viewDir), 1e-5);
523
416
  let nDotH = max(dot(normal, h), 0.0);
524
417
  let vDotH = max(dot(viewDir, h), 0.0);
525
- let fresnel = exp2((-5.55473 * vDotH - 6.98316) * vDotH);
526
- let f = F0 * (vec3<f32>(1.0) - fresnel) + vec3<f32>(fresnel);
418
+ let f = f_schlick(vDotH, F0);
527
419
  let roughness = sqrt(max(alphaSq, 0.0));
528
- let multiScatter = _threeR184DirectMultiScatter(roughness, nDotV, nDotL, F0);
420
+ let multiScatter = threeR184DirectMultiScatter(roughness, nDotV, nDotL, F0);
529
421
  let specular = d_ggx(nDotH, alphaSq) * v_smith(nDotV, nDotL, alphaSq) * f + multiScatter;
530
422
  let diffuse = (1.0 - metallic) * baseColor / 3.14159265;
531
423
  return (diffuse + specular) * view.lightColor * nDotL;
@@ -0,0 +1,51 @@
1
+ // @forgeax/engine-shader - lighting-probe.wgsl
2
+ // Local diffuse LightProbe composition. The record is supplied by the
3
+ // renderer's per-object retained projection; Skylight remains the sole Sky
4
+ // and specular owner.
5
+
6
+ #define_import_path forgeax_pbr::lighting_probe
7
+
8
+ const PROBE_INV_PI : f32 = 0.3183098861837907;
9
+
10
+ fn probe_sh9(shPreblend : array<vec4<f32>, 9>, normal : vec3<f32>) -> vec3<f32> {
11
+ let x = normal.x;
12
+ let y = normal.y;
13
+ let z = normal.z;
14
+ let basis = array<f32, 9>(
15
+ 0.2820947918,
16
+ 0.4886025119 * y,
17
+ 0.4886025119 * z,
18
+ 0.4886025119 * x,
19
+ 1.0925484306 * x * y,
20
+ 1.0925484306 * y * z,
21
+ 0.3153915653 * (3.0 * z * z - 1.0),
22
+ 1.0925484306 * x * z,
23
+ 0.5462742153 * (x * x - y * y),
24
+ );
25
+ var result = vec3<f32>(0.0);
26
+ for (var band = 0u; band < 9u; band = band + 1u) {
27
+ result += shPreblend[band].xyz * basis[band];
28
+ }
29
+ return result;
30
+ }
31
+
32
+ // One SH9 evaluation, then the real Skylight residual. No Sky q, probe loop,
33
+ // hidden gain, smoothstep, or specular probe path is present here.
34
+ fn evaluateProbeDiffuse(
35
+ shPreblend : array<vec4<f32>, 9>,
36
+ localBlendFraction : f32,
37
+ normal : vec3<f32>,
38
+ e_sky : vec3<f32>,
39
+ k_d : vec3<f32>,
40
+ albedo : vec3<f32>,
41
+ metallic : f32,
42
+ ) -> vec3<f32> {
43
+ // `shPreblend` stores cosine-convolved irradiance. Match the existing
44
+ // Standard-PBR Skylight path and Three's BRDF_Lambert: irradiance becomes
45
+ // reflected diffuse radiance only after the single 1/pi factor. The Sky
46
+ // argument is already the Skylight path's E/pi * color * intensity value,
47
+ // so it must not be divided a second time below.
48
+ let local = max(probe_sh9(shPreblend, normal), vec3<f32>(0.0)) * PROBE_INV_PI;
49
+ let skyResidualFraction = 1.0 - localBlendFraction;
50
+ return (local + skyResidualFraction * e_sky) * k_d * albedo * (1.0 - metallic);
51
+ }
@@ -31,7 +31,11 @@
31
31
  // - evalSpot(lightPos, lightDir, colorTimesIntensity, cosInner, cosOuter,
32
32
  // invRangeSquared, ...) -> vec3<f32>
33
33
 
34
- #import forgeax_pbr::brdf::{f_schlick, v_smith, d_ggx}
34
+ #import forgeax_pbr::brdf::{f_schlick, v_smith, d_ggx, threeR184DirectMultiScatter}
35
+ #import forgeax_pbr::lighting_spot_modifiers::{spotModifierProduct}
36
+
37
+ // extendedLighting is one closed resource topology for IES, Cookie, and
38
+ // probe factors. The ordinary path supplies identity factors.
35
39
  #import forgeax_pbr::lighting_attenuation::{evalDistanceAttenuation, evalSpotAttenuation, projectSpotUv}
36
40
  // feat-20260625-spot-light-shadow-mapping M3 / w15 (plan-strategy D-3 + D-5):
37
41
  // spot shadow sampling reuses the shared 2D 9-tap PCF core (sample_shadow_2d)
@@ -102,9 +106,13 @@ fn evalPunctualBody(
102
106
  let nDotH = max(dot(normal, h), 0.0);
103
107
  let vDotH = max(dot(viewDir, h), 0.0);
104
108
  let f = f_schlick(vDotH, F0);
105
- let specular = d_ggx(nDotH, alphaSq) * v_smith(nDotV, nDotL, alphaSq) * f;
106
- let kd = (vec3<f32>(1.0) - f) * (1.0 - metallic);
107
- let diffuse = kd * baseColor / 3.14159265;
109
+ let roughness = sqrt(max(alphaSq, 0.0));
110
+ let multiScatter = threeR184DirectMultiScatter(roughness, nDotV, nDotL, F0);
111
+ let specular = d_ggx(nDotH, alphaSq) * v_smith(nDotV, nDotL, alphaSq) * f + multiScatter;
112
+ // Three r184 direct-light parity keeps diffuse independent of the
113
+ // view-dependent Schlick term; the metallic factor is the only diffuse
114
+ // energy gate in this punctual path.
115
+ let diffuse = (1.0 - metallic) * baseColor / 3.14159265;
108
116
  let attenuation = evalDistanceAttenuation(dSquared, invRangeSquared);
109
117
  return (diffuse + specular) * colorTimesIntensity * nDotL * attenuation;
110
118
  }
@@ -128,14 +136,14 @@ fn evalPoint(
128
136
  );
129
137
  }
130
138
 
131
- // Flat 2D punctual evaluators share the range/cone math with the compatibility
132
- // path and the clustered path, but intentionally skip the 3D BRDF normal term.
139
+ // Flat 2D punctual evaluators share the range/cone math with the clustered
140
+ // path, but intentionally skip the 3D BRDF normal term.
133
141
  // Sprite-lit treats every quad as an omnidirectional receiver; keeping this
134
142
  // owner here prevents URP and Cluster from drifting when the light lies in the
135
143
  // sprite plane (the common 2D flashlight setup).
136
144
  fn evalFlatRangeAttenuation(dSquared : f32, invRangeSquared : f32) -> f32 {
137
145
  let factor = max(min(1.0 - (dSquared * invRangeSquared) * (dSquared * invRangeSquared), 1.0), 0.0);
138
- return factor / dSquared;
146
+ return factor * factor / dSquared;
139
147
  }
140
148
 
141
149
  fn evalPointFlat(
@@ -182,7 +190,8 @@ fn evalSpotFlat(
182
190
  // projection (research L0.5 Bevy pattern); the caller passes `near` / `far`
183
191
  // directly so both pipelines route the same constants without owning the
184
192
  // upstream binding (URP reads them from `shadowParams[layer]` at @group(0)
185
- // binding 6; HDRP unpacks them off `LightSlot.kind_and_pad.zw` per
193
+ // binding 6; the shared DirectLightSlot metadata remains the identity owner
194
+ // while this shadow-parameter buffer carries the reconstruction constants per
186
195
  // plan-strategy §D-8).
187
196
  //
188
197
  // Caller responsibility: gate this on `shadowAtlasLayer >= 0` so the
@@ -258,9 +267,10 @@ fn evalSpot(
258
267
  lightPos, colorTimesIntensity, invRangeSquared,
259
268
  worldPos, normal, viewDir, baseColor, metallic, alphaSq, F0,
260
269
  );
261
- return body * evalSpotAttenuation(
262
- lightPos, lightDir, worldPos, cosInner, cosOuter, invRangeSquared,
263
- ) / max(evalDistanceAttenuation(dot(lightPos - worldPos, lightPos - worldPos), invRangeSquared), 1e-4);
270
+ let toLight = lightPos - worldPos;
271
+ let l = normalize(toLight);
272
+ let cone = smoothstep(cosOuter, cosInner, dot(l, -lightDir));
273
+ return body * spotModifierProduct(1.0, 1.0, cone, 1.0, 1.0, 1.0);
264
274
  }
265
275
 
266
276
  // feat-20260625-spot-light-shadow-mapping M3 / w15 (plan-strategy D-3 + D-4 +
@@ -0,0 +1,162 @@
1
+ #define_import_path forgeax_pbr::lighting_rect_area
2
+ #ifdef EXTENDED_LIGHTING_AVAILABLE
3
+ #import forgeax_view::common::{ltcLambertTexture, ltcGgxTexture, spotModifierSampler}
4
+ #endif
5
+
6
+ // One-sided finite-area direct lighting using the same Heitz LTC formulation
7
+ // and tracked 64x64 tables as Three.js r184. The rectangle corners, rather
8
+ // than a punctual center direction, are transformed into the fitted cosine
9
+ // space before the spherical edge integral is evaluated.
10
+
11
+ fn ltcUv(n : vec3<f32>, v : vec3<f32>, roughness : f32) -> vec2<f32> {
12
+ let lutScale = 63.0 / 64.0;
13
+ let lutBias = 0.5 / 64.0;
14
+ let nDotV = clamp(dot(n, v), 0.0, 1.0);
15
+ return vec2<f32>(clamp(roughness, 0.0, 1.0), sqrt(1.0 - nDotV)) * lutScale + lutBias;
16
+ }
17
+
18
+ fn ltcClippedSphereFormFactor(f : vec3<f32>) -> f32 {
19
+ let magnitude = length(f);
20
+ return max((magnitude * magnitude + f.z) / (magnitude + 1.0), 0.0);
21
+ }
22
+
23
+ fn ltcEdgeVectorFormFactor(v1 : vec3<f32>, v2 : vec3<f32>) -> vec3<f32> {
24
+ let x = clamp(dot(v1, v2), -1.0, 1.0);
25
+ let y = abs(x);
26
+ let a = 0.8543985 + (0.4965155 + 0.0145206 * y) * y;
27
+ let b = 3.4175940 + (4.1616724 + y) * y;
28
+ let rational = a / b;
29
+ let thetaOverSinTheta = select(
30
+ 0.5 * inverseSqrt(max(1.0 - x * x, 1e-7)) - rational,
31
+ rational,
32
+ x > 0.0,
33
+ );
34
+ return cross(v1, v2) * thetaOverSinTheta;
35
+ }
36
+
37
+ fn ltcEvaluate(
38
+ n : vec3<f32>,
39
+ v : vec3<f32>,
40
+ worldPos : vec3<f32>,
41
+ mInv : mat3x3<f32>,
42
+ lightPos : vec3<f32>,
43
+ axisX : vec3<f32>,
44
+ axisY : vec3<f32>,
45
+ halfWidth : f32,
46
+ halfHeight : f32,
47
+ ) -> f32 {
48
+ let halfX = axisX * halfWidth;
49
+ let halfY = axisY * halfHeight;
50
+ // Counter-clockwise from the authored front (+cross(axisX, axisY)).
51
+ let rect0 = lightPos - halfX - halfY;
52
+ let rect1 = lightPos + halfX - halfY;
53
+ let rect2 = lightPos + halfX + halfY;
54
+ let rect3 = lightPos - halfX + halfY;
55
+ let lightNormal = cross(rect1 - rect0, rect3 - rect0);
56
+ if (dot(lightNormal, worldPos - rect0) <= 0.0) {
57
+ return 0.0;
58
+ }
59
+
60
+ let tangentCandidate = v - n * dot(v, n);
61
+ let tangentLengthSquared = dot(tangentCandidate, tangentCandidate);
62
+ var tangent = vec3<f32>(1.0, 0.0, 0.0);
63
+ if (tangentLengthSquared > 1e-7) {
64
+ tangent = tangentCandidate * inverseSqrt(tangentLengthSquared);
65
+ } else {
66
+ let fallback = select(vec3<f32>(0.0, 1.0, 0.0), vec3<f32>(1.0, 0.0, 0.0), abs(n.x) < 0.9);
67
+ tangent = normalize(cross(fallback, n));
68
+ }
69
+ let bitangent = -cross(n, tangent);
70
+ let transform = mInv * transpose(mat3x3<f32>(tangent, bitangent, n));
71
+
72
+ let c0 = normalize(transform * (rect0 - worldPos));
73
+ let c1 = normalize(transform * (rect1 - worldPos));
74
+ let c2 = normalize(transform * (rect2 - worldPos));
75
+ let c3 = normalize(transform * (rect3 - worldPos));
76
+ let vectorFormFactor =
77
+ ltcEdgeVectorFormFactor(c0, c1) +
78
+ ltcEdgeVectorFormFactor(c1, c2) +
79
+ ltcEdgeVectorFormFactor(c2, c3) +
80
+ ltcEdgeVectorFormFactor(c3, c0);
81
+ return ltcClippedSphereFormFactor(vectorFormFactor);
82
+ }
83
+
84
+ fn rectAreaRangeFactor(lightPos : vec3<f32>, worldPos : vec3<f32>, invRangeSquared : f32) -> f32 {
85
+ let toLight = lightPos - worldPos;
86
+ let distanceSquared = dot(toLight, toLight);
87
+ let rangeTerm = distanceSquared * invRangeSquared;
88
+ let factor = clamp(1.0 - rangeTerm * rangeTerm, 0.0, 1.0);
89
+ return factor * factor;
90
+ }
91
+
92
+ fn evalRectAreaLtcDiffuse(
93
+ lightPos : vec3<f32>, lightColor : vec3<f32>, axisX : vec3<f32>, axisY : vec3<f32>,
94
+ halfWidth : f32, halfHeight : f32, invRangeSquared : f32, worldPos : vec3<f32>,
95
+ n : vec3<f32>, v : vec3<f32>, baseColor : vec3<f32>, metallic : f32, alphaSq : f32, F0 : vec3<f32>,
96
+ ) -> vec3<f32> {
97
+ let identity = mat3x3<f32>(
98
+ vec3<f32>(1.0, 0.0, 0.0),
99
+ vec3<f32>(0.0, 1.0, 0.0),
100
+ vec3<f32>(0.0, 0.0, 1.0),
101
+ );
102
+ let formFactor = ltcEvaluate(
103
+ n, v, worldPos, identity, lightPos, axisX, axisY, halfWidth, halfHeight,
104
+ );
105
+ // The LTC edge fit is already normalized by 1/(2*pi); unlike punctual
106
+ // irradiance this form factor must not receive another Lambert 1/pi.
107
+ let diffuse = baseColor * (1.0 - metallic);
108
+ return lightColor * diffuse * formFactor * rectAreaRangeFactor(lightPos, worldPos, invRangeSquared);
109
+ }
110
+
111
+ fn evalRectAreaLtcStandard(
112
+ lightPos : vec3<f32>, lightColor : vec3<f32>, axisX : vec3<f32>, axisY : vec3<f32>,
113
+ halfWidth : f32, halfHeight : f32, invRangeSquared : f32, worldPos : vec3<f32>,
114
+ n : vec3<f32>, v : vec3<f32>, baseColor : vec3<f32>, metallic : f32, alphaSq : f32, F0 : vec3<f32>,
115
+ ) -> vec3<f32> {
116
+ #ifdef EXTENDED_LIGHTING_AVAILABLE
117
+ let uv = ltcUv(n, v, sqrt(max(alphaSq, 0.0)));
118
+ let matrixSample = textureSampleLevel(ltcLambertTexture, spotModifierSampler, uv, 0.0);
119
+ let fresnelSample = textureSampleLevel(ltcGgxTexture, spotModifierSampler, uv, 0.0);
120
+ let mInv = mat3x3<f32>(
121
+ vec3<f32>(matrixSample.x, 0.0, matrixSample.y),
122
+ vec3<f32>(0.0, 1.0, 0.0),
123
+ vec3<f32>(matrixSample.z, 0.0, matrixSample.w),
124
+ );
125
+ let fresnel = F0 * fresnelSample.x + (vec3<f32>(1.0) - F0) * fresnelSample.y;
126
+ let specularFormFactor = ltcEvaluate(
127
+ n, v, worldPos, mInv, lightPos, axisX, axisY, halfWidth, halfHeight,
128
+ );
129
+ let diffuse = evalRectAreaLtcDiffuse(
130
+ lightPos, lightColor, axisX, axisY, halfWidth, halfHeight, invRangeSquared,
131
+ worldPos, n, v, baseColor, metallic, alphaSq, F0,
132
+ );
133
+ return diffuse + lightColor * fresnel * specularFormFactor * rectAreaRangeFactor(lightPos, worldPos, invRangeSquared);
134
+ #else
135
+ return vec3<f32>(0.0);
136
+ #endif
137
+ }
138
+
139
+ // Both renderer routes consume one complete Standard-PBR Rect contribution.
140
+ // The public names retain the direct/cluster shader import contract while the
141
+ // Lambert and fitted-GGX terms stay inseparable in one owner implementation.
142
+ fn evalRectAreaLtcLambert(
143
+ lightPos : vec3<f32>, lightColor : vec3<f32>, axisX : vec3<f32>, axisY : vec3<f32>,
144
+ halfWidth : f32, halfHeight : f32, invRangeSquared : f32, worldPos : vec3<f32>,
145
+ n : vec3<f32>, v : vec3<f32>, baseColor : vec3<f32>, metallic : f32, alphaSq : f32, F0 : vec3<f32>,
146
+ ) -> vec3<f32> {
147
+ return evalRectAreaLtcStandard(
148
+ lightPos, lightColor, axisX, axisY, halfWidth, halfHeight, invRangeSquared,
149
+ worldPos, n, v, baseColor, metallic, alphaSq, F0,
150
+ );
151
+ }
152
+
153
+ fn evalRectAreaLtcGgx(
154
+ lightPos : vec3<f32>, lightColor : vec3<f32>, axisX : vec3<f32>, axisY : vec3<f32>,
155
+ halfWidth : f32, halfHeight : f32, invRangeSquared : f32, worldPos : vec3<f32>,
156
+ n : vec3<f32>, v : vec3<f32>, baseColor : vec3<f32>, metallic : f32, alphaSq : f32, F0 : vec3<f32>,
157
+ ) -> vec3<f32> {
158
+ return evalRectAreaLtcStandard(
159
+ lightPos, lightColor, axisX, axisY, halfWidth, halfHeight, invRangeSquared,
160
+ worldPos, n, v, baseColor, metallic, alphaSq, F0,
161
+ );
162
+ }
@@ -0,0 +1,100 @@
1
+ #define_import_path forgeax_pbr::lighting_spot_modifiers
2
+
3
+ #ifdef EXTENDED_LIGHTING_AVAILABLE
4
+ #import forgeax_view::common::{spotModifierSampler, iesProfileTexture, cookieTexture, cookieMatrices}
5
+ #endif
6
+
7
+ // DirectLightSlot.metadata.y carries this marker for the SpotLight projector
8
+ // path. A marked spot samples the shared array with its lightViewProj in the
9
+ // punctual owner, so the Cookie projection
10
+ // below must not modulate it a second time.
11
+ const PROJECTOR_FLAG: u32 = 0x40000000u;
12
+
13
+ // Shared Spot modifier composition. Identity defaults preserve the ordinary
14
+ // punctual path when an extension resource is absent.
15
+ fn spotModifierProduct(
16
+ brdf : f32,
17
+ range : f32,
18
+ cone : f32,
19
+ ies : f32,
20
+ cookie : f32,
21
+ shadow : f32,
22
+ ) -> f32 {
23
+ return brdf * range * cone * ies * cookie * shadow;
24
+ }
25
+
26
+ // Shared IES/Cookie projection owner. The light direction is the authored
27
+ // outgoing axis (light -> scene), matching evalSpot's `dot(L, -lightDir)`.
28
+ // Cookie matrices carry only source-texture aspect correction; keeping the
29
+ // cone term here allows a Cookie slice to be shared by Spots with different
30
+ // outer cones.
31
+ fn spotModifierFactors(
32
+ lightPos : vec3<f32>,
33
+ lightDir : vec3<f32>,
34
+ cosOuter : f32,
35
+ worldPos : vec3<f32>,
36
+ rollDeg : f32,
37
+ metadata : vec4<u32>,
38
+ ) -> vec4<f32> {
39
+ #ifdef EXTENDED_LIGHTING_AVAILABLE
40
+ let toSurface = worldPos - lightPos;
41
+ let distanceToSurface = length(toSurface);
42
+ var ies = 1.0;
43
+ var cookie = vec3<f32>(1.0);
44
+ if (distanceToSurface <= 0.000001) {
45
+ return vec4<f32>(ies, cookie);
46
+ }
47
+
48
+ let outgoing = toSurface / distanceToSurface;
49
+ let forward = normalize(lightDir);
50
+ var referenceUp = vec3<f32>(0.0, 1.0, 0.0);
51
+ if (abs(forward.y) > 0.999) {
52
+ referenceUp = vec3<f32>(0.0, 0.0, 1.0);
53
+ }
54
+ let right = normalize(cross(forward, referenceUp));
55
+ let up = normalize(cross(right, forward));
56
+ let localX = dot(outgoing, right);
57
+ let localY = dot(outgoing, up);
58
+ let depth = dot(outgoing, forward);
59
+ let roll = rollDeg * 0.01745329252;
60
+ let rolledX = localX * cos(roll) - localY * sin(roll);
61
+ let rolledY = localX * sin(roll) + localY * cos(roll);
62
+
63
+ if (metadata.z != 0xffffffffu) {
64
+ let azimuth = (atan2(rolledY, rolledX) + 6.283185307) % 6.283185307;
65
+ let elevation = acos(clamp(depth, -1.0, 1.0)) / 3.14159265;
66
+ let iesUv = vec2<f32>(azimuth / 6.283185307, elevation);
67
+ ies = max(textureSampleLevel(iesProfileTexture, spotModifierSampler, iesUv, metadata.z, 0.0).r, 0.0);
68
+ }
69
+
70
+ // The shadow lane uses 0xffffffff as its no-shadow sentinel. That value
71
+ // contains PROJECTOR_FLAG bits, so test the sentinel before interpreting
72
+ // the lane as a projector marker; otherwise every non-shadow Cookie spot
73
+ // silently takes the projector exclusion path.
74
+ if (
75
+ metadata.w != 0xffffffffu &&
76
+ (metadata.y == 0xffffffffu || (metadata.y & PROJECTOR_FLAG) == 0u)
77
+ ) {
78
+ if (depth <= 0.0) {
79
+ cookie = vec3<f32>(0.0);
80
+ } else {
81
+ let sinOuter = sqrt(max(1.0 - cosOuter * cosOuter, 0.0));
82
+ let tanOuter = sinOuter / max(cosOuter, 0.0001);
83
+ let matrixPlane = cookieMatrices[metadata.w] * vec4<f32>(rolledX / depth, rolledY / depth, 0.0, 1.0);
84
+ let cookieUv = vec2<f32>(0.5) + matrixPlane.xy / tanOuter * 0.5;
85
+ if (
86
+ cookieUv.x < 0.0 || cookieUv.x > 1.0 ||
87
+ cookieUv.y < 0.0 || cookieUv.y > 1.0
88
+ ) {
89
+ cookie = vec3<f32>(0.0);
90
+ } else {
91
+ let cookieSample = textureSampleLevel(cookieTexture, spotModifierSampler, cookieUv, metadata.w, 0.0);
92
+ cookie = max(cookieSample.rgb * cookieSample.a, vec3<f32>(0.0));
93
+ }
94
+ }
95
+ }
96
+ return vec4<f32>(ies, cookie);
97
+ #else
98
+ return vec4<f32>(1.0);
99
+ #endif
100
+ }
@@ -0,0 +1,31 @@
1
+ #define_import_path forgeax_pbr::lighting_spot_projector
2
+
3
+ // Clustered Standard owns the light membership/decode, while this helper
4
+ // keeps projector projection and resource sampling in one module. Keeping the
5
+ // projection/sample pair out of standard-cluster avoids a naga-oil scope edge
6
+ // when the low-resource projector path is composed with the shared cluster.
7
+ #import forgeax_pbr::lighting_attenuation::{projectSpotUv}
8
+ #ifdef EXTENDED_LIGHTING_AVAILABLE
9
+ #import forgeax_view::common::{spotModifierSampler, cookieTexture}
10
+ #else
11
+ #import forgeax_view::common::{projectorTexture, projectorSampler}
12
+ #endif
13
+
14
+ fn sampleStandardSpotProjector(
15
+ lightViewProj : mat4x4<f32>,
16
+ world_pos : vec3<f32>,
17
+ metadata : vec4<u32>,
18
+ ) -> vec3<f32> {
19
+ let uv = projectSpotUv(lightViewProj, world_pos);
20
+ if (!(uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0)) {
21
+ return vec3<f32>(1.0);
22
+ }
23
+ #ifdef EXTENDED_LIGHTING_AVAILABLE
24
+ if (metadata.w == 0xffffffffu) {
25
+ return vec3<f32>(1.0);
26
+ }
27
+ return textureSampleLevel(cookieTexture, spotModifierSampler, uv, metadata.w, 0.0).rgb;
28
+ #else
29
+ return textureSampleLevel(projectorTexture, projectorSampler, uv, 0.0).rgb;
30
+ #endif
31
+ }
@@ -0,0 +1,19 @@
1
+ // @forgeax/engine-shader - provenance for the tracked RectArea LTC tables.
2
+
3
+ /**
4
+ * The table source is the Three.js r184 RectAreaLight implementation. The
5
+ * source arrays are converted to IEEE-754 binary16 offline and checked into
6
+ * `tables.ts`; the runtime never fetches or parses a source file.
7
+ */
8
+ export const LTC_SOURCE_PROVENANCE = {
9
+ source: 'Three.js',
10
+ revision: 'r184',
11
+ sourceUrl:
12
+ 'https://github.com/mrdoob/three.js/blob/r184/examples/jsm/lights/RectAreaLightTexturesLib.js',
13
+ upstreamData: 'selfshadow/ltc_code fit/results/ltc.js',
14
+ generator: 'deterministic Three.js source-array to rgba16float conversion',
15
+ inputSha256: {
16
+ lambert: 'cf5cf21e5c112d2095c7e2418cb0a1ac54636e275d73e42f3453646c67f26814',
17
+ ggx: '3b1b09080b26104498db277c14fc1733786465c6958e7a8403d688b1e24c2ff5',
18
+ },
19
+ } as const;