@forgeax/engine-shader 0.1.21 → 0.1.23

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 (59) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +4 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.mjs +33019 -8
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/ltc/provenance.d.ts +17 -0
  7. package/dist/ltc/provenance.d.ts.map +1 -0
  8. package/dist/ltc/tables.d.ts +22 -0
  9. package/dist/ltc/tables.d.ts.map +1 -0
  10. package/dist/material/artifact-types.d.ts +52 -0
  11. package/dist/material/artifact-types.d.ts.map +1 -1
  12. package/dist/material-schemas.d.ts +5 -0
  13. package/dist/material-schemas.d.ts.map +1 -1
  14. package/dist/register-default-sprite-lit.d.ts +3 -6
  15. package/dist/register-default-sprite-lit.d.ts.map +1 -1
  16. package/dist/types.d.ts +2 -1
  17. package/dist/types.d.ts.map +1 -1
  18. package/package.json +4 -4
  19. package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +121 -58
  20. package/src/__tests__/direct-light-layout.unit.test.ts +40 -0
  21. package/src/__tests__/ibl-irradiance.unit.test.ts +7 -4
  22. package/src/__tests__/ibl-sampling.unit.test.ts +3 -4
  23. package/src/__tests__/lighting-point-spot-volume.unit.test.ts +8 -2
  24. package/src/__tests__/lighting-punctual.unit.test.ts +29 -2
  25. package/src/__tests__/lighting-spot-volume-attenuation.unit.test.ts +1 -1
  26. package/src/__tests__/ltc-provenance.unit.test.ts +33 -0
  27. package/src/__tests__/manifest.fixture.json +33 -0
  28. package/src/__tests__/material-contract.unit.test.ts +61 -1
  29. package/src/__tests__/probe-lighting-composition.unit.test.ts +38 -0
  30. package/src/__tests__/rect-area-brdf.unit.test.ts +36 -0
  31. package/src/__tests__/reflection-probe-sampling.unit.test.ts +24 -2
  32. package/src/__tests__/shader.unit.test.ts +6 -2
  33. package/src/__tests__/spot-cookie-composition.unit.test.ts +57 -0
  34. package/src/__tests__/spot-modifier-composition.unit.test.ts +19 -0
  35. package/src/__tests__/sprite-variants.unit.test.ts +4 -1
  36. package/src/brdf.wgsl +115 -1
  37. package/src/common.wgsl +53 -79
  38. package/src/default-standard-pbr-skin.wgsl +80 -23
  39. package/src/default-standard-pbr.wgsl +119 -50
  40. package/src/ibl-irradiance.wgsl +4 -1
  41. package/src/ibl-sampling.wgsl +2 -2
  42. package/src/index.ts +17 -0
  43. package/src/lighting-directional.wgsl +6 -116
  44. package/src/lighting-probe.wgsl +51 -0
  45. package/src/lighting-punctual.wgsl +21 -11
  46. package/src/lighting-rect-area.wgsl +162 -0
  47. package/src/lighting-spot-modifiers.wgsl +93 -0
  48. package/src/ltc/provenance.ts +19 -0
  49. package/src/ltc/tables.ts +2831 -0
  50. package/src/material/artifact-types.ts +137 -0
  51. package/src/material-schemas.ts +12 -0
  52. package/src/register-default-sprite-lit.ts +3 -6
  53. package/src/sprite-lit.wgsl +3 -3
  54. package/src/sprite.wgsl +1 -1
  55. package/src/standard-cluster.wgsl +94 -57
  56. package/src/types.ts +2 -1
  57. package/src/volume/volume-inject.wgsl +34 -36
  58. package/src/volume/volume-integrate.wgsl +48 -38
  59. package/dist/.tsbuildinfo +0 -1
@@ -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,93 @@
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
+ if (metadata.w != 0xffffffffu && (metadata.y & PROJECTOR_FLAG) == 0u) {
71
+ if (depth <= 0.0) {
72
+ cookie = vec3<f32>(0.0);
73
+ } else {
74
+ let sinOuter = sqrt(max(1.0 - cosOuter * cosOuter, 0.0));
75
+ let tanOuter = sinOuter / max(cosOuter, 0.0001);
76
+ let matrixPlane = cookieMatrices[metadata.w] * vec4<f32>(rolledX / depth, rolledY / depth, 0.0, 1.0);
77
+ let cookieUv = vec2<f32>(0.5) + matrixPlane.xy / tanOuter * 0.5;
78
+ if (
79
+ cookieUv.x < 0.0 || cookieUv.x > 1.0 ||
80
+ cookieUv.y < 0.0 || cookieUv.y > 1.0
81
+ ) {
82
+ cookie = vec3<f32>(0.0);
83
+ } else {
84
+ let cookieSample = textureSampleLevel(cookieTexture, spotModifierSampler, cookieUv, metadata.w, 0.0);
85
+ cookie = max(cookieSample.rgb * cookieSample.a, vec3<f32>(0.0));
86
+ }
87
+ }
88
+ }
89
+ return vec4<f32>(ies, cookie);
90
+ #else
91
+ return vec4<f32>(1.0);
92
+ #endif
93
+ }
@@ -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;