@forgeax/engine-shader 0.1.6 → 0.1.19

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 (99) hide show
  1. package/README.md +61 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/ShaderRegistry.d.ts.map +1 -1
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.mjs +53 -2
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/material-schemas.d.ts.map +1 -1
  9. package/package.json +6 -3
  10. package/src/ShaderRegistry.ts +50 -0
  11. package/src/__tests__/bloom-fxaa-tonemap.unit.test.ts +86 -12
  12. package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +181 -0
  13. package/src/__tests__/lighting-punctual.unit.test.ts +1 -1
  14. package/src/__tests__/material-builtins.unit.test.ts +38 -1
  15. package/src/__tests__/material-contract.unit.test.ts +123 -0
  16. package/src/__tests__/motion-blur.unit.test.ts +44 -0
  17. package/src/__tests__/points-lines-shader.unit.test.ts +43 -0
  18. package/src/__tests__/scene-temporal.unit.test.ts +23 -5
  19. package/src/__tests__/shader-manifest-transmission.unit.test.ts +75 -0
  20. package/src/__tests__/shader.unit.test.ts +43 -3
  21. package/src/__tests__/sprite-variants.unit.test.ts +19 -0
  22. package/src/__tests__/taa-resolve.unit.test.ts +75 -3
  23. package/src/__tests__/transmission-thickness-scale.unit.test.ts +19 -0
  24. package/src/common.wgsl +30 -30
  25. package/src/default-standard-pbr-skin.wgsl +27 -9
  26. package/src/default-standard-pbr.wgsl +209 -28
  27. package/src/fxaa.wgsl +5 -6
  28. package/src/index.ts +3 -0
  29. package/src/material-schemas.ts +7 -0
  30. package/src/motion-blur.wgsl +77 -0
  31. package/src/pbr-temporal.wgsl +15 -2
  32. package/src/points-lines.wgsl +103 -0
  33. package/src/scene-temporal.wgsl +54 -0
  34. package/src/shadow_caster.wgsl +27 -2
  35. package/src/sprite-lit.wgsl +5 -4
  36. package/src/sprite.wgsl +5 -4
  37. package/src/taa-resolve.wgsl +76 -81
  38. package/src/tonemap.wgsl +7 -9
  39. package/src/unlit.wgsl +6 -5
  40. package/dist/__tests__/atmosphere-background.test.d.ts +0 -2
  41. package/dist/__tests__/atmosphere-background.test.d.ts.map +0 -1
  42. package/dist/__tests__/atmosphere-cubemap-orientation.test.d.ts +0 -2
  43. package/dist/__tests__/atmosphere-cubemap-orientation.test.d.ts.map +0 -1
  44. package/dist/__tests__/atmosphere-preetham-numeric.test.d.ts +0 -2
  45. package/dist/__tests__/atmosphere-preetham-numeric.test.d.ts.map +0 -1
  46. package/dist/__tests__/atmosphere-preetham-red.test.d.ts +0 -2
  47. package/dist/__tests__/atmosphere-preetham-red.test.d.ts.map +0 -1
  48. package/dist/__tests__/atmosphere-preetham.test.d.ts +0 -2
  49. package/dist/__tests__/atmosphere-preetham.test.d.ts.map +0 -1
  50. package/dist/__tests__/atmosphere-three-r184-reference.test.d.ts +0 -2
  51. package/dist/__tests__/atmosphere-three-r184-reference.test.d.ts.map +0 -1
  52. package/dist/__tests__/bloom-fxaa-tonemap.unit.test.d.ts +0 -2
  53. package/dist/__tests__/bloom-fxaa-tonemap.unit.test.d.ts.map +0 -1
  54. package/dist/__tests__/builtin-texture-sampling-contract.test.d.ts +0 -2
  55. package/dist/__tests__/builtin-texture-sampling-contract.test.d.ts.map +0 -1
  56. package/dist/__tests__/default-standard-pbr-alpha.unit.test.d.ts +0 -2
  57. package/dist/__tests__/default-standard-pbr-alpha.unit.test.d.ts.map +0 -1
  58. package/dist/__tests__/deferred-lighting-ssao.test.d.ts +0 -2
  59. package/dist/__tests__/deferred-lighting-ssao.test.d.ts.map +0 -1
  60. package/dist/__tests__/fog-oracle.unit.test.d.ts +0 -2
  61. package/dist/__tests__/fog-oracle.unit.test.d.ts.map +0 -1
  62. package/dist/__tests__/fog-orthographic-ray.unit.test.d.ts +0 -2
  63. package/dist/__tests__/fog-orthographic-ray.unit.test.d.ts.map +0 -1
  64. package/dist/__tests__/fog-producer-matrix.integration.test.d.ts +0 -2
  65. package/dist/__tests__/fog-producer-matrix.integration.test.d.ts.map +0 -1
  66. package/dist/__tests__/hdrp-ssao-compile.test.d.ts +0 -2
  67. package/dist/__tests__/hdrp-ssao-compile.test.d.ts.map +0 -1
  68. package/dist/__tests__/ibl-irradiance.unit.test.d.ts +0 -2
  69. package/dist/__tests__/ibl-irradiance.unit.test.d.ts.map +0 -1
  70. package/dist/__tests__/ibl-sampling.unit.test.d.ts +0 -2
  71. package/dist/__tests__/ibl-sampling.unit.test.d.ts.map +0 -1
  72. package/dist/__tests__/lighting-punctual.unit.test.d.ts +0 -2
  73. package/dist/__tests__/lighting-punctual.unit.test.d.ts.map +0 -1
  74. package/dist/__tests__/material-artifact-dedup.unit.test.d.ts +0 -2
  75. package/dist/__tests__/material-artifact-dedup.unit.test.d.ts.map +0 -1
  76. package/dist/__tests__/material-builtins.unit.test.d.ts +0 -2
  77. package/dist/__tests__/material-builtins.unit.test.d.ts.map +0 -1
  78. package/dist/__tests__/material-contract.unit.test.d.ts +0 -2
  79. package/dist/__tests__/material-contract.unit.test.d.ts.map +0 -1
  80. package/dist/__tests__/material-derived-builtins.integration.test.d.ts +0 -2
  81. package/dist/__tests__/material-derived-builtins.integration.test.d.ts.map +0 -1
  82. package/dist/__tests__/public-surface.unit.test.d.ts +0 -2
  83. package/dist/__tests__/public-surface.unit.test.d.ts.map +0 -1
  84. package/dist/__tests__/scene-temporal.unit.test.d.ts +0 -2
  85. package/dist/__tests__/scene-temporal.unit.test.d.ts.map +0 -1
  86. package/dist/__tests__/shader-registry-transaction.unit.test.d.ts +0 -2
  87. package/dist/__tests__/shader-registry-transaction.unit.test.d.ts.map +0 -1
  88. package/dist/__tests__/shader.unit.test.d.ts +0 -2
  89. package/dist/__tests__/shader.unit.test.d.ts.map +0 -1
  90. package/dist/__tests__/sprite-lit-shader.test.d.ts +0 -2
  91. package/dist/__tests__/sprite-lit-shader.test.d.ts.map +0 -1
  92. package/dist/__tests__/sprite-variants.unit.test.d.ts +0 -2
  93. package/dist/__tests__/sprite-variants.unit.test.d.ts.map +0 -1
  94. package/dist/__tests__/taa-resolve.unit.test.d.ts +0 -2
  95. package/dist/__tests__/taa-resolve.unit.test.d.ts.map +0 -1
  96. package/dist/__tests__/transparent-pbr.unit.test.d.ts +0 -2
  97. package/dist/__tests__/transparent-pbr.unit.test.d.ts.map +0 -1
  98. package/dist/__tests__/vertex-color-variant.unit.test.d.ts +0 -2
  99. package/dist/__tests__/vertex-color-variant.unit.test.d.ts.map +0 -1
@@ -16,6 +16,7 @@
16
16
  // Paradigm: each block-scoped describe('<source-filename>.test.ts', ...) preserves
17
17
  // source as ancestorTitles[0]. Top-level imports merged + deduped.
18
18
 
19
+ import { readFileSync } from 'node:fs';
19
20
  import type { Result, RhiError, ShaderModule } from '@forgeax/engine-rhi';
20
21
  import { beforeAll, describe, expect, it } from 'vitest';
21
22
  import type { ShaderError } from '../errors.js';
@@ -31,6 +32,19 @@ import {
31
32
  ShaderRegistry,
32
33
  } from '../ShaderRegistry.js';
33
34
 
35
+ {
36
+ const sceneTemporalSource = readFileSync(
37
+ new URL('../scene-temporal.wgsl', import.meta.url),
38
+ 'utf8',
39
+ );
40
+ describe('scene-temporal accessor ABI', () => {
41
+ it('keeps the public shader module free of compiler and backend policy imports', () => {
42
+ expect(sceneTemporalSource).toContain('forgeax_scene_temporal');
43
+ expect(sceneTemporalSource).not.toMatch(/naga|shader-compiler|engine-rhi/i);
44
+ });
45
+ });
46
+ }
47
+
34
48
  {
35
49
  // --- from fullscreen-triangle.test.ts ---
36
50
  // @forgeax/engine-shader/__tests__/fullscreen-triangle.test.ts -
@@ -179,7 +193,7 @@ import {
179
193
  });
180
194
  });
181
195
 
182
- describe('fxaa.wgsl SSOT gate (AC-05)', () => {
196
+ describe('fxaa.wgsl display-encoded SSOT gate (AC-05)', () => {
183
197
  it('does NOT define fullscreen_triangle (SSOT is in common.wgsl)', () => {
184
198
  expect(fxaaSource).not.toMatch(/fn fullscreen_triangle/);
185
199
  });
@@ -191,6 +205,17 @@ import {
191
205
  it('imports forgeax_view::common::FullscreenOutput', () => {
192
206
  expect(fxaaSource).toContain('#import forgeax_view::common::FullscreenOutput');
193
207
  });
208
+
209
+ it('declares display-encoded input/output without an OETF owner', () => {
210
+ expect(fxaaSource).toContain('display-encoded');
211
+ expect(fxaaSource).not.toContain('linearLdrColorDomain');
212
+ expect(fxaaSource).not.toContain('linearToSrgbOetf');
213
+ });
214
+
215
+ it('returns encoded colors directly from early and final paths', () => {
216
+ expect(fxaaSource).toMatch(/return vec4<f32>\(centerColor, 1\.0\);/);
217
+ expect(fxaaSource).toMatch(/return vec4<f32>\(finalColor, 1\.0\);/);
218
+ });
194
219
  });
195
220
 
196
221
  describe('fxaa.wgsl bindings', () => {
@@ -779,6 +804,7 @@ import {
779
804
  '#pragma variant_axis STORAGE_BUFFER_AVAILABLE',
780
805
  '#pragma variant_axis CLUSTER_FORWARD_AVAILABLE',
781
806
  '#pragma variant_axis VERTEX_COLOR_AVAILABLE',
807
+ '#pragma variant_axis TRANSMISSION_AVAILABLE',
782
808
  ]);
783
809
  });
784
810
 
@@ -923,12 +949,19 @@ import {
923
949
  'clearcoatRoughness',
924
950
  'specularTint',
925
951
  'normalScale',
952
+ 'transmission',
953
+ 'ior',
954
+ 'thickness',
955
+ 'attenuationColor',
956
+ 'attenuationDistance',
926
957
  'baseColorTexture',
927
958
  'metallicRoughnessTexture',
928
959
  'normalTexture',
929
960
  'specularTintTexture',
930
961
  'emissiveTexture',
931
962
  'occlusionTexture',
963
+ 'transmissionTexture',
964
+ 'thicknessTexture',
932
965
  ] as const;
933
966
 
934
967
  const STUB_WGSL = '// stub composed pbr-skin wgsl\n';
@@ -987,7 +1020,7 @@ import {
987
1020
  const registry = makeMockRegistry();
988
1021
  registerDefaultStandardPbrSkin(registry, STUB_WGSL, STORAGE_CAPS);
989
1022
  const entry = lookup(registry);
990
- expect(entry.paramSchema).toHaveLength(21);
1023
+ expect(entry.paramSchema).toHaveLength(28);
991
1024
  });
992
1025
 
993
1026
  it('paramSchema names match default-standard-pbr schema field-for-field', () => {
@@ -1020,7 +1053,12 @@ import {
1020
1053
  'f32',
1021
1054
  'f32',
1022
1055
  'f32',
1023
- // specular tint and its texture map.
1056
+ // specular tint, transmission, and attenuation controls.
1057
+ 'vec3',
1058
+ 'f32',
1059
+ 'f32',
1060
+ 'f32',
1061
+ 'f32',
1024
1062
  'vec3',
1025
1063
  'f32',
1026
1064
  'texture2d',
@@ -1029,6 +1067,8 @@ import {
1029
1067
  'texture2d',
1030
1068
  'texture2d',
1031
1069
  'texture2d',
1070
+ 'texture2d',
1071
+ 'texture2d',
1032
1072
  ]);
1033
1073
  });
1034
1074
  });
@@ -117,6 +117,7 @@ describe('w6 (b) -- pbr / unlit / sprite-adjacent shaders DO NOT pick up PER_INS
117
117
  '#pragma variant_axis STORAGE_BUFFER_AVAILABLE',
118
118
  '#pragma variant_axis CLUSTER_FORWARD_AVAILABLE',
119
119
  '#pragma variant_axis VERTEX_COLOR_AVAILABLE',
120
+ '#pragma variant_axis TRANSMISSION_AVAILABLE',
120
121
  ]);
121
122
  });
122
123
 
@@ -207,3 +208,21 @@ describe('w6 (d) -- MAX_UNIFORM_INSTANCES=128 stays unchanged under the new axis
207
208
  expect(src).toMatch(/MAX_UNIFORM_INSTANCES\s*=\s*128/);
208
209
  });
209
210
  });
211
+
212
+ describe('HDR/display fragment contract', () => {
213
+ for (const file of ['sprite.wgsl', 'sprite-lit.wgsl'] as const) {
214
+ it(`${file} keeps LDR OETF in fs_main and HDR output linear`, () => {
215
+ const src = stripComments(readWgsl(file));
216
+ const ldrStart = src.indexOf('fn fs_main(');
217
+ const hdrStart = src.indexOf('fn fs_main_hdr(');
218
+ expect(ldrStart).toBeGreaterThanOrEqual(0);
219
+ expect(hdrStart).toBeGreaterThan(ldrStart);
220
+
221
+ const ldrBody = src.slice(ldrStart, hdrStart);
222
+ const hdrBody = src.slice(hdrStart);
223
+ expect(ldrBody).toContain('linear_to_srgb(');
224
+ expect(hdrBody).not.toContain('linear_to_srgb(');
225
+ expect(hdrBody).toContain('return vec4<f32>');
226
+ });
227
+ }
228
+ });
@@ -3,24 +3,29 @@ import { resolve } from 'node:path';
3
3
  import { describe, expect, it } from 'vitest';
4
4
 
5
5
  const source = readFileSync(resolve(import.meta.dirname, '../taa-resolve.wgsl'), 'utf8');
6
+ const pbrTemporal = readFileSync(resolve(import.meta.dirname, '../pbr-temporal.wgsl'), 'utf8');
7
+ const sceneTemporal = readFileSync(resolve(import.meta.dirname, '../scene-temporal.wgsl'), 'utf8');
6
8
 
7
9
  describe('taa-resolve.wgsl', () => {
8
10
  it('keeps the fixed packed temporal and two-attachment ABI', () => {
9
11
  expect(source).toContain('struct TaaResolveParams');
10
12
  expect(source).toContain('@location(0) color');
11
13
  expect(source).toContain('@location(1) temporal');
12
- expect(source).toContain('output.temporal = temporal');
14
+ expect(source).toContain('return TaaResolveOutput(');
15
+ expect(source).toContain('temporal,');
13
16
  expect(source).toContain('current.a');
17
+ expect(source).toContain('1.0 - clamp(temporal.w');
18
+ expect(source).not.toContain('1.0 - clamp(current.a');
14
19
  });
15
20
 
16
21
  it('contains every v1 rejection and adaptive weighting term', () => {
17
- expect(source).toContain('uv + params.currentJitterUv');
22
+ expect(source).toContain('in.uv + params.currentJitterUv');
18
23
  expect(source).toContain('in.uv - temporal.xy');
19
24
  expect(source).toContain('closestCurrentTemporal(pixel, dimensions)');
20
25
  expect(source).toContain('historyInBounds');
21
26
  expect(source).toContain('depthDelta > depthThreshold');
22
27
  expect(source).toContain('neighborhoodSquareMean');
23
- expect(source).toContain('clamp(rgbToYCoCg(sampledHistory.rgb), clipMin, clipMax)');
28
+ expect(source).toContain('clamp(rgbToYCoCg(history.rgb), clipMin, clipMax)');
24
29
  expect(source).toContain('reactiveFactor');
25
30
  expect(source).toContain('velocityFactor');
26
31
  expect(source).toContain('depthFactor');
@@ -28,5 +33,72 @@ describe('taa-resolve.wgsl', () => {
28
33
  expect(source).toContain('mix(0.88, 0.97, lumaFactor)');
29
34
  expect(source).toContain('params.temporalFrameIndex < 8u');
30
35
  expect(source).toContain('progressiveWeight');
36
+ expect(source).toContain('let dimensions = vec2<i32>(textureDimensions(currentColor, 0));');
37
+ expect(source).toContain('closestCurrentTemporal(pixel, dimensions)');
38
+ expect(source).toContain('vec2<f32>(dimensions)');
39
+ });
40
+
41
+ it('compiles the PBR temporal seam with coverage-aware reactive output', async () => {
42
+ const compilerPath = resolve(import.meta.dirname, '../../../shader-compiler/dist/index.mjs');
43
+ const { compileShader } = await import(/* @vite-ignore */ compilerPath);
44
+ const common = `
45
+ fn sampleMaterialTexture(
46
+ texture : texture_2d<f32>,
47
+ textureSampler : sampler,
48
+ uv : vec2<f32>,
49
+ uvScale : vec2<f32>,
50
+ ) -> vec4<f32> {
51
+ return textureSample(texture, textureSampler, uv * uvScale);
52
+ }
53
+
54
+ `;
55
+ const sceneTemporalBody = sceneTemporal
56
+ .replace(/^#define_import_path.*$/gm, '')
57
+ .replace(/^#import.*$/gm, '');
58
+ const entry = `${common}
59
+ ${sceneTemporalBody}
60
+ ${pbrTemporal.replace(/^#define_import_path.*$/gm, '').replace(/^#import.*$/gm, '')}
61
+ @group(0) @binding(0) var colorTexture : texture_2d<f32>;
62
+ @group(0) @binding(1) var colorSampler : sampler;
63
+ @fragment
64
+ fn fs_temporal_probe() -> @location(0) vec4<f32> {
65
+ return projectPbrSceneTemporal(
66
+ 0.75, 0.5, colorTexture, colorSampler,
67
+ vec4<f32>(0.0, 0.0, 1.0, 1.0), vec4<f32>(0.0),
68
+ vec4<f32>(0.0, 0.0, 1.0, 1.0), vec4<f32>(0.0),
69
+ vec4<f32>(0.0, 0.0, 0.0, 0.0), 0.0,
70
+ vec2<f32>(0.5), vec2<f32>(0.5), vec2<f32>(0.5), vec2<f32>(0.5),
71
+ vec2<f32>(0.5), vec2<f32>(0.5), vec2<f32>(0.5), vec2<f32>(0.5),
72
+ );
73
+ }
74
+ `;
75
+ const compiled = await compileShader(entry, {
76
+ id: 'test::pbr-temporal-reactive',
77
+ defines: { PER_INSTANCE_REGION: false, STORAGE_BUFFER_AVAILABLE: true },
78
+ });
79
+ expect(compiled.ok, compiled.ok ? undefined : String(compiled.error)).toBe(true);
80
+ if (!compiled.ok) return;
81
+ expect(compiled.value.wgsl).toContain('resolvePbrTemporalReactive');
82
+ expect(compiled.value.wgsl).toContain('max(clamp(reactive');
83
+ const discardIndex = pbrTemporal.indexOf('discard;');
84
+ const reactiveIndex = pbrTemporal.indexOf('let reactiveCoverage');
85
+ expect(discardIndex).toBeGreaterThanOrEqual(0);
86
+ expect(discardIndex).toBeLessThan(reactiveIndex);
87
+ });
88
+
89
+ it('rejects a dev-only forced-reactive-zero falsifier', () => {
90
+ const reactiveCoverageLine =
91
+ 'let reactiveCoverage = resolvePbrTemporalReactive(reactive, baseColorAlpha, baseSample.a);';
92
+ const falsified = pbrTemporal.replace(reactiveCoverageLine, 'let reactiveCoverage = 0.0;');
93
+ expect(() => {
94
+ expect(falsified).toContain(reactiveCoverageLine);
95
+ expect(falsified).toContain('let reactiveCoverage = 0.0;');
96
+ }).toThrow();
97
+ });
98
+
99
+ it('keeps the reactive coverage contract explicit', () => {
100
+ expect(pbrTemporal).toContain('resolvePbrTemporalReactive');
101
+ expect(pbrTemporal).toContain('coverageReactive');
102
+ expect(pbrTemporal).not.toContain('return packSceneTemporal(currentClip, previousClip, 0.0)');
31
103
  });
32
104
  });
@@ -0,0 +1,19 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { describe, expect, it } from 'vitest';
4
+
5
+ const shader = readFileSync(resolve(import.meta.dirname, '../default-standard-pbr.wgsl'), 'utf8');
6
+
7
+ describe('standard transmission thickness scale contract', () => {
8
+ it('converts local authored thickness through the combined local-to-world basis', () => {
9
+ expect(shader).toContain('let localToWorld = entityWorld * instanceLocal;');
10
+ expect(shader).toContain(
11
+ 'let localToWorld = meshes[0].worldFromLocal * instances[in.instanceIdx].localFromInstance;',
12
+ );
13
+ expect(shader).toContain('let worldToLocal0 = vec3<f32>(');
14
+ expect(shader).toContain('let worldRefractedDirection =');
15
+ expect(shader).toMatch(
16
+ /finiteScalar\(material\.thickness, 0\.0\) \* length\(worldRefractedDirection\) \*\s+finiteScalar\(thicknessSample, 1\.0\)/,
17
+ );
18
+ });
19
+ });
package/src/common.wgsl CHANGED
@@ -24,6 +24,9 @@ fn sampleMaterialTexture(
24
24
  }
25
25
 
26
26
  // Shared linear-LDR -> display-sRGB OETF for raw swap-chain storage writes.
27
+ // The high branch deliberately locks Three.js r184's reference literal
28
+ // (`0.41666`) instead of spelling the mathematically exact `1.0 / 2.4`.
29
+ // That literal is part of the byte-level parity contract at 8-bit boundaries.
27
30
  // sRGB texture views perform this conversion on attachment writes, but the
28
31
  // native output and FXAA passes intentionally target the non-sRGB storage view.
29
32
  fn linearToSrgbOetf(color : vec3<f32>) -> vec3<f32> {
@@ -278,6 +281,33 @@ struct SpotLightsArray {
278
281
  };
279
282
 
280
283
  @group(0) @binding(0) var<uniform> view : View;
284
+
285
+ // Shared screen-space ray reconstruction for scene Fog producers. Keeping the
286
+ // inverse-view and projection branch here prevents each producer from growing
287
+ // a second Fog formula or drifting from the View ABI.
288
+ fn fogWorldPoint(ndc : vec3<f32>) -> vec3<f32> {
289
+ let homogeneous = view.inverseViewProj * vec4<f32>(ndc, 1.0);
290
+ let divisor = select(1.0, homogeneous.w, abs(homogeneous.w) > 0.000001);
291
+ return homogeneous.xyz / divisor;
292
+ }
293
+
294
+ fn fogRayFromNdc(ndc : vec3<f32>) -> FogRay {
295
+ let worldPosition = fogWorldPoint(ndc);
296
+ let nearPosition = fogWorldPoint(vec3<f32>(ndc.xy, 0.0));
297
+ let farPosition = fogWorldPoint(vec3<f32>(ndc.xy, 1.0));
298
+ let perspective = view.temporalProjection.z < 0.5;
299
+ let perspectiveVector = worldPosition - view.cameraPos;
300
+ let orthographicVector = farPosition - nearPosition;
301
+ let direction = normalize(select(orthographicVector, perspectiveVector, perspective));
302
+ let origin = select(nearPosition, view.cameraPos, perspective);
303
+ let rayDistance = select(
304
+ max(dot(worldPosition - nearPosition, direction), 0.0),
305
+ length(perspectiveVector),
306
+ perspective,
307
+ );
308
+ return FogRay(origin, direction, rayDistance);
309
+ }
310
+
281
311
  #if STORAGE_BUFFER_AVAILABLE == true
282
312
  @group(0) @binding(1) var<storage, read> pointLightsBuffer : PointLightsArray;
283
313
  @group(0) @binding(2) var<storage, read> spotLightsBuffer : SpotLightsArray;
@@ -445,36 +475,6 @@ struct InstanceData {
445
475
  #endif
446
476
  };
447
477
 
448
- fn sceneTemporalUv(clip : vec4<f32>) -> vec2<f32> {
449
- let safeW = select(1e-6, clip.w, abs(clip.w) >= 1e-6);
450
- let ndc = clip.xy / safeW;
451
- return vec2<f32>(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
452
- }
453
-
454
- fn sceneTemporalViewDepth(clip : vec4<f32>) -> f32 {
455
- let perspectiveDepth = max(clip.w, 0.0);
456
- let ndcDepth = clip.z / max(abs(clip.w), 1e-6);
457
- let orthographicDepth = view.temporalProjection.x +
458
- ndcDepth * (view.temporalProjection.y - view.temporalProjection.x);
459
- let viewDepth = select(
460
- perspectiveDepth,
461
- max(orthographicDepth, 0.0),
462
- view.temporalProjection.z >= 0.5,
463
- );
464
- return log2(1.0 + viewDepth);
465
- }
466
-
467
- fn packSceneTemporal(
468
- currentClip : vec4<f32>,
469
- previousClip : vec4<f32>,
470
- reactive : f32,
471
- ) -> vec4<f32> {
472
- return vec4<f32>(
473
- sceneTemporalUv(currentClip) - sceneTemporalUv(previousClip),
474
- sceneTemporalViewDepth(currentClip),
475
- clamp(reactive, 0.0, 1.0),
476
- );
477
- }
478
478
  #if STORAGE_BUFFER_AVAILABLE == true
479
479
  @group(3) @binding(0) var<storage, read> instances : array<InstanceData>;
480
480
  #else
@@ -88,6 +88,11 @@ struct Material {
88
88
  clearcoatRoughness : f32,
89
89
  specularTint : vec3<f32>,
90
90
  normalScale : f32,
91
+ transmission : f32,
92
+ ior : f32,
93
+ thickness : f32,
94
+ attenuationColor : vec3<f32>,
95
+ attenuationDistance : f32,
91
96
  baseColorTextureCoordinatesTransform : vec4<f32>,
92
97
  baseColorTextureCoordinatesMetadata : vec4<f32>,
93
98
  metallicRoughnessTextureCoordinatesTransform : vec4<f32>,
@@ -100,6 +105,10 @@ struct Material {
100
105
  emissiveTextureCoordinatesMetadata : vec4<f32>,
101
106
  occlusionTextureCoordinatesTransform : vec4<f32>,
102
107
  occlusionTextureCoordinatesMetadata : vec4<f32>,
108
+ transmissionTextureCoordinatesTransform : vec4<f32>,
109
+ transmissionTextureCoordinatesMetadata : vec4<f32>,
110
+ thicknessTextureCoordinatesTransform : vec4<f32>,
111
+ thicknessTextureCoordinatesMetadata : vec4<f32>,
103
112
  };
104
113
 
105
114
  @group(1) @binding(0) var<uniform> material : Material;
@@ -115,6 +124,10 @@ struct Material {
115
124
  @group(1) @binding(10) var emissiveTexture : texture_2d<f32>;
116
125
  @group(1) @binding(11) var occlusionSampler : sampler;
117
126
  @group(1) @binding(12) var occlusionTexture : texture_2d<f32>;
127
+ @group(1) @binding(13) var transmissionSampler : sampler;
128
+ @group(1) @binding(14) var transmissionTexture : texture_2d<f32>;
129
+ @group(1) @binding(15) var thicknessSampler : sampler;
130
+ @group(1) @binding(16) var thicknessTexture : texture_2d<f32>;
118
131
 
119
132
  // Preserve filtering reflection for resources passed to the shared sampler.
120
133
  fn materialTextureFilteringWitness() {
@@ -124,12 +137,16 @@ fn materialTextureFilteringWitness() {
124
137
  let specularTint = specularTintTexture;
125
138
  let emissive = emissiveTexture;
126
139
  let occlusion = occlusionTexture;
140
+ let transmission = transmissionTexture;
141
+ let thickness = thicknessTexture;
127
142
  let baseWitness = textureSample(base, baseColorSampler, vec2<f32>(0.0));
128
143
  let metallicRoughnessWitness = textureSample(metallicRoughness, metallicRoughnessSampler, vec2<f32>(0.0));
129
144
  let normalWitness = textureSample(normal, normalSampler, vec2<f32>(0.0));
130
145
  let specularTintWitness = textureSample(specularTint, specularTintSampler, vec2<f32>(0.0));
131
146
  let emissiveWitness = textureSample(emissive, emissiveSampler, vec2<f32>(0.0));
132
147
  let occlusionWitness = textureSample(occlusion, occlusionSampler, vec2<f32>(0.0));
148
+ let transmissionWitness = textureSample(transmission, transmissionSampler, vec2<f32>(0.0));
149
+ let thicknessWitness = textureSample(thickness, thicknessSampler, vec2<f32>(0.0));
133
150
  }
134
151
 
135
152
  struct SkylightUniforms {
@@ -146,13 +163,13 @@ struct SkylightUniforms {
146
163
  colorB : f32,
147
164
  rotation : vec4<f32>,
148
165
  };
149
- @group(1) @binding(13) var irradianceMap : texture_cube<f32>;
150
- @group(1) @binding(14) var irradianceSampler : sampler;
151
- @group(1) @binding(15) var prefilterMap : texture_cube<f32>;
152
- @group(1) @binding(16) var prefilterSampler : sampler;
153
- @group(1) @binding(17) var brdfLut : texture_2d<f32>;
154
- @group(1) @binding(18) var brdfLutSampler : sampler;
155
- @group(1) @binding(19) var<uniform> skylight : SkylightUniforms;
166
+ @group(1) @binding(17) var irradianceMap : texture_cube<f32>;
167
+ @group(1) @binding(18) var irradianceSampler : sampler;
168
+ @group(1) @binding(19) var prefilterMap : texture_cube<f32>;
169
+ @group(1) @binding(20) var prefilterSampler : sampler;
170
+ @group(1) @binding(21) var brdfLut : texture_2d<f32>;
171
+ @group(1) @binding(22) var brdfLutSampler : sampler;
172
+ @group(1) @binding(23) var<uniform> skylight : SkylightUniforms;
156
173
 
157
174
  #if STORAGE_BUFFER_AVAILABLE == true
158
175
  @group(2) @binding(1) var<storage, read> palette : array<mat4x4<f32>>;
@@ -469,8 +486,8 @@ struct TemporalVsOut {
469
486
  @location(5) uv5 : vec2<f32>,
470
487
  @location(6) uv6 : vec2<f32>,
471
488
  @location(7) uv7 : vec2<f32>,
472
- @location(8) @interpolate(linear) currentClip : vec4<f32>,
473
- @location(9) @interpolate(linear) previousClip : vec4<f32>,
489
+ @location(8) @interpolate(perspective) currentClip : vec4<f32>,
490
+ @location(9) @interpolate(perspective) previousClip : vec4<f32>,
474
491
  #ifdef VERTEX_COLOR_AVAILABLE
475
492
  @location(14) color : vec4<f32>,
476
493
  #endif
@@ -531,6 +548,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
531
548
  material.baseColorTextureCoordinatesMetadata,
532
549
  in.currentClip,
533
550
  in.previousClip,
551
+ view.temporalProjection,
534
552
  reactive,
535
553
  in.uv, in.uv1, in.uv2, in.uv3,
536
554
  in.uv4, in.uv5, in.uv6, in.uv7,