@luma.gl/shadertools 9.3.0-alpha.4 → 9.3.0-alpha.8

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 (157) hide show
  1. package/dist/dist.dev.js +4657 -523
  2. package/dist/dist.min.js +1952 -301
  3. package/dist/index.cjs +2804 -406
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +10 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +4 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/preprocessor/preprocessor.d.ts.map +1 -1
  10. package/dist/lib/preprocessor/preprocessor.js +35 -8
  11. package/dist/lib/preprocessor/preprocessor.js.map +1 -1
  12. package/dist/lib/shader-assembler.d.ts +10 -0
  13. package/dist/lib/shader-assembler.d.ts.map +1 -1
  14. package/dist/lib/shader-assembler.js +20 -3
  15. package/dist/lib/shader-assembler.js.map +1 -1
  16. package/dist/lib/shader-assembly/assemble-shaders.d.ts +23 -2
  17. package/dist/lib/shader-assembly/assemble-shaders.d.ts.map +1 -1
  18. package/dist/lib/shader-assembly/assemble-shaders.js +211 -11
  19. package/dist/lib/shader-assembly/assemble-shaders.js.map +1 -1
  20. package/dist/lib/shader-assembly/wgsl-binding-debug.d.ts +37 -0
  21. package/dist/lib/shader-assembly/wgsl-binding-debug.d.ts.map +1 -0
  22. package/dist/lib/shader-assembly/wgsl-binding-debug.js +140 -0
  23. package/dist/lib/shader-assembly/wgsl-binding-debug.js.map +1 -0
  24. package/dist/lib/shader-generator/glsl/generate-glsl.js +3 -0
  25. package/dist/lib/shader-generator/glsl/generate-glsl.js.map +1 -1
  26. package/dist/lib/shader-generator/wgsl/generate-wgsl.d.ts.map +1 -1
  27. package/dist/lib/shader-generator/wgsl/generate-wgsl.js +3 -0
  28. package/dist/lib/shader-generator/wgsl/generate-wgsl.js.map +1 -1
  29. package/dist/lib/shader-module/shader-module-uniform-layout.d.ts +22 -0
  30. package/dist/lib/shader-module/shader-module-uniform-layout.d.ts.map +1 -0
  31. package/dist/lib/shader-module/shader-module-uniform-layout.js +112 -0
  32. package/dist/lib/shader-module/shader-module-uniform-layout.js.map +1 -0
  33. package/dist/lib/shader-module/shader-module.d.ts +12 -6
  34. package/dist/lib/shader-module/shader-module.d.ts.map +1 -1
  35. package/dist/lib/shader-module/shader-module.js.map +1 -1
  36. package/dist/lib/utils/uniform-types.d.ts +11 -7
  37. package/dist/lib/utils/uniform-types.d.ts.map +1 -1
  38. package/dist/modules/engine/picking/picking.d.ts +3 -0
  39. package/dist/modules/engine/picking/picking.d.ts.map +1 -1
  40. package/dist/modules/engine/picking/picking.js +3 -0
  41. package/dist/modules/engine/picking/picking.js.map +1 -1
  42. package/dist/modules/engine/skin/skin.d.ts +30 -0
  43. package/dist/modules/engine/skin/skin.d.ts.map +1 -0
  44. package/dist/modules/engine/skin/skin.js +86 -0
  45. package/dist/modules/engine/skin/skin.js.map +1 -0
  46. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts +1 -0
  47. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts.map +1 -1
  48. package/dist/modules/lighting/gouraud-material/gouraud-material.js +3 -0
  49. package/dist/modules/lighting/gouraud-material/gouraud-material.js.map +1 -1
  50. package/dist/modules/lighting/ibl/ibl.d.ts +26 -0
  51. package/dist/modules/lighting/ibl/ibl.d.ts.map +1 -0
  52. package/dist/modules/lighting/ibl/ibl.js +33 -0
  53. package/dist/modules/lighting/ibl/ibl.js.map +1 -0
  54. package/dist/modules/lighting/lambert-material/lambert-material.d.ts +10 -0
  55. package/dist/modules/lighting/lambert-material/lambert-material.d.ts.map +1 -0
  56. package/dist/modules/lighting/lambert-material/lambert-material.js +33 -0
  57. package/dist/modules/lighting/lambert-material/lambert-material.js.map +1 -0
  58. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.d.ts +3 -0
  59. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.d.ts.map +1 -0
  60. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.js +60 -0
  61. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.js.map +1 -0
  62. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.d.ts +2 -0
  63. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.d.ts.map +1 -0
  64. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.js +73 -0
  65. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.js.map +1 -0
  66. package/dist/modules/lighting/lights/lighting-glsl.d.ts +1 -1
  67. package/dist/modules/lighting/lights/lighting-glsl.d.ts.map +1 -1
  68. package/dist/modules/lighting/lights/lighting-glsl.js +43 -37
  69. package/dist/modules/lighting/lights/lighting-glsl.js.map +1 -1
  70. package/dist/modules/lighting/lights/lighting-wgsl.d.ts +1 -1
  71. package/dist/modules/lighting/lights/lighting-wgsl.d.ts.map +1 -1
  72. package/dist/modules/lighting/lights/lighting-wgsl.js +46 -18
  73. package/dist/modules/lighting/lights/lighting-wgsl.js.map +1 -1
  74. package/dist/modules/lighting/lights/lighting.d.ts +104 -62
  75. package/dist/modules/lighting/lights/lighting.d.ts.map +1 -1
  76. package/dist/modules/lighting/lights/lighting.js +107 -68
  77. package/dist/modules/lighting/lights/lighting.js.map +1 -1
  78. package/dist/modules/lighting/no-material/dirlight.d.ts +7 -2
  79. package/dist/modules/lighting/no-material/dirlight.d.ts.map +1 -1
  80. package/dist/modules/lighting/no-material/dirlight.js +3 -1
  81. package/dist/modules/lighting/no-material/dirlight.js.map +1 -1
  82. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +1 -1
  83. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  84. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +524 -28
  85. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js.map +1 -1
  86. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +2 -2
  87. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  88. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +784 -101
  89. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js.map +1 -1
  90. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +110 -45
  91. package/dist/modules/lighting/pbr-material/pbr-material.d.ts.map +1 -1
  92. package/dist/modules/lighting/pbr-material/pbr-material.js +85 -9
  93. package/dist/modules/lighting/pbr-material/pbr-material.js.map +1 -1
  94. package/dist/modules/lighting/pbr-material/pbr-projection.d.ts.map +1 -1
  95. package/dist/modules/lighting/pbr-material/pbr-projection.js +13 -1
  96. package/dist/modules/lighting/pbr-material/pbr-projection.js.map +1 -1
  97. package/dist/modules/lighting/phong-material/phong-material.d.ts +1 -0
  98. package/dist/modules/lighting/phong-material/phong-material.d.ts.map +1 -1
  99. package/dist/modules/lighting/phong-material/phong-material.js +4 -0
  100. package/dist/modules/lighting/phong-material/phong-material.js.map +1 -1
  101. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts +2 -2
  102. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts.map +1 -1
  103. package/dist/modules/lighting/phong-material/phong-shaders-glsl.js +15 -4
  104. package/dist/modules/lighting/phong-material/phong-shaders-glsl.js.map +1 -1
  105. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.d.ts +1 -40
  106. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.d.ts.map +1 -1
  107. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.js +71 -76
  108. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.js.map +1 -1
  109. package/dist/modules/math/fp64/fp64-arithmetic-glsl.d.ts +1 -1
  110. package/dist/modules/math/fp64/fp64-arithmetic-glsl.d.ts.map +1 -1
  111. package/dist/modules/math/fp64/fp64-arithmetic-glsl.js +41 -10
  112. package/dist/modules/math/fp64/fp64-arithmetic-glsl.js.map +1 -1
  113. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.d.ts +2 -0
  114. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.d.ts.map +1 -0
  115. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.js +212 -0
  116. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.js.map +1 -0
  117. package/dist/modules/math/fp64/fp64.d.ts +1 -0
  118. package/dist/modules/math/fp64/fp64.d.ts.map +1 -1
  119. package/dist/modules/math/fp64/fp64.js +8 -2
  120. package/dist/modules/math/fp64/fp64.js.map +1 -1
  121. package/dist/modules/math/random/random.d.ts +1 -1
  122. package/dist/modules/math/random/random.d.ts.map +1 -1
  123. package/dist/modules/math/random/random.js +2 -3
  124. package/dist/modules/math/random/random.js.map +1 -1
  125. package/package.json +3 -3
  126. package/src/index.ts +20 -2
  127. package/src/lib/preprocessor/preprocessor.ts +44 -8
  128. package/src/lib/shader-assembler.ts +25 -3
  129. package/src/lib/shader-assembly/assemble-shaders.ts +377 -12
  130. package/src/lib/shader-assembly/wgsl-binding-debug.ts +216 -0
  131. package/src/lib/shader-generator/glsl/generate-glsl.ts +7 -1
  132. package/src/lib/shader-generator/wgsl/generate-wgsl.ts +6 -0
  133. package/src/lib/shader-module/shader-module-uniform-layout.ts +194 -0
  134. package/src/lib/shader-module/shader-module.ts +17 -7
  135. package/src/lib/utils/uniform-types.ts +24 -9
  136. package/src/modules/engine/picking/picking.ts +3 -0
  137. package/src/modules/engine/skin/skin.ts +114 -0
  138. package/src/modules/lighting/gouraud-material/gouraud-material.ts +4 -0
  139. package/src/modules/lighting/ibl/ibl.ts +44 -0
  140. package/src/modules/lighting/lambert-material/lambert-material.ts +42 -0
  141. package/src/modules/lighting/lambert-material/lambert-shaders-glsl.ts +61 -0
  142. package/src/modules/lighting/lambert-material/lambert-shaders-wgsl.ts +73 -0
  143. package/src/modules/lighting/lights/lighting-glsl.ts +43 -37
  144. package/src/modules/lighting/lights/lighting-wgsl.ts +46 -18
  145. package/src/modules/lighting/lights/lighting.ts +198 -99
  146. package/src/modules/lighting/no-material/dirlight.ts +3 -1
  147. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +524 -28
  148. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +784 -101
  149. package/src/modules/lighting/pbr-material/pbr-material.ts +111 -18
  150. package/src/modules/lighting/pbr-material/pbr-projection.ts +14 -1
  151. package/src/modules/lighting/phong-material/phong-material.ts +5 -0
  152. package/src/modules/lighting/phong-material/phong-shaders-glsl.ts +15 -4
  153. package/src/modules/lighting/phong-material/phong-shaders-wgsl.ts +71 -77
  154. package/src/modules/math/fp64/fp64-arithmetic-glsl.ts +41 -10
  155. package/src/modules/math/fp64/fp64-arithmetic-wgsl.ts +212 -0
  156. package/src/modules/math/fp64/fp64.ts +9 -3
  157. package/src/modules/math/random/random.ts +2 -3
package/dist/index.cjs CHANGED
@@ -47,19 +47,26 @@ __export(dist_exports, {
47
47
  getShaderInfo: () => getShaderInfo,
48
48
  getShaderModuleDependencies: () => getShaderModuleDependencies,
49
49
  getShaderModuleSource: () => getShaderModuleSource,
50
+ getShaderModuleUniformBlockFields: () => getShaderModuleUniformBlockFields,
51
+ getShaderModuleUniformBlockName: () => getShaderModuleUniformBlockName,
52
+ getShaderModuleUniformLayoutValidationResult: () => getShaderModuleUniformLayoutValidationResult,
50
53
  getShaderModuleUniforms: () => getShaderModuleUniforms,
51
54
  gouraudMaterial: () => gouraudMaterial,
55
+ ibl: () => ibl,
52
56
  initializeShaderModule: () => initializeShaderModule,
53
57
  initializeShaderModules: () => initializeShaderModules,
58
+ lambertMaterial: () => lambertMaterial,
54
59
  lighting: () => lighting,
55
60
  pbrMaterial: () => pbrMaterial,
56
61
  phongMaterial: () => phongMaterial,
57
62
  picking: () => picking,
58
63
  preprocess: () => preprocess,
59
64
  random: () => random,
65
+ skin: () => skin,
60
66
  toHalfFloat: () => toHalfFloat,
61
67
  typeToChannelCount: () => typeToChannelCount,
62
- typeToChannelSuffix: () => typeToChannelSuffix
68
+ typeToChannelSuffix: () => typeToChannelSuffix,
69
+ validateShaderModuleUniformLayout: () => validateShaderModuleUniformLayout
63
70
  });
64
71
  module.exports = __toCommonJS(dist_exports);
65
72
 
@@ -204,7 +211,7 @@ function getHookStage(hook) {
204
211
  throw new Error(type);
205
212
  }
206
213
  }
207
- function injectShader(source3, stage, inject, injectStandardStubs = false) {
214
+ function injectShader(source4, stage, inject, injectStandardStubs = false) {
208
215
  const isVertex = stage === "vertex";
209
216
  for (const key in inject) {
210
217
  const fragmentData = inject[key];
@@ -218,43 +225,43 @@ function injectShader(source3, stage, inject, injectStandardStubs = false) {
218
225
  switch (key) {
219
226
  case "vs:#decl":
220
227
  if (isVertex) {
221
- source3 = source3.replace(DECLARATION_INJECT_MARKER, fragmentString);
228
+ source4 = source4.replace(DECLARATION_INJECT_MARKER, fragmentString);
222
229
  }
223
230
  break;
224
231
  case "vs:#main-start":
225
232
  if (isVertex) {
226
- source3 = source3.replace(REGEX_START_OF_MAIN, (match) => match + fragmentString);
233
+ source4 = source4.replace(REGEX_START_OF_MAIN, (match) => match + fragmentString);
227
234
  }
228
235
  break;
229
236
  case "vs:#main-end":
230
237
  if (isVertex) {
231
- source3 = source3.replace(REGEX_END_OF_MAIN, (match) => fragmentString + match);
238
+ source4 = source4.replace(REGEX_END_OF_MAIN, (match) => fragmentString + match);
232
239
  }
233
240
  break;
234
241
  case "fs:#decl":
235
242
  if (!isVertex) {
236
- source3 = source3.replace(DECLARATION_INJECT_MARKER, fragmentString);
243
+ source4 = source4.replace(DECLARATION_INJECT_MARKER, fragmentString);
237
244
  }
238
245
  break;
239
246
  case "fs:#main-start":
240
247
  if (!isVertex) {
241
- source3 = source3.replace(REGEX_START_OF_MAIN, (match) => match + fragmentString);
248
+ source4 = source4.replace(REGEX_START_OF_MAIN, (match) => match + fragmentString);
242
249
  }
243
250
  break;
244
251
  case "fs:#main-end":
245
252
  if (!isVertex) {
246
- source3 = source3.replace(REGEX_END_OF_MAIN, (match) => fragmentString + match);
253
+ source4 = source4.replace(REGEX_END_OF_MAIN, (match) => fragmentString + match);
247
254
  }
248
255
  break;
249
256
  default:
250
- source3 = source3.replace(key, (match) => match + fragmentString);
257
+ source4 = source4.replace(key, (match) => match + fragmentString);
251
258
  }
252
259
  }
253
- source3 = source3.replace(DECLARATION_INJECT_MARKER, "");
260
+ source4 = source4.replace(DECLARATION_INJECT_MARKER, "");
254
261
  if (injectStandardStubs) {
255
- source3 = source3.replace(/\}\s*$/, (match) => match + MODULE_INJECTORS[stage]);
262
+ source4 = source4.replace(/\}\s*$/, (match) => match + MODULE_INJECTORS[stage]);
256
263
  }
257
- return source3;
264
+ return source4;
258
265
  }
259
266
  function combineInjects(injects) {
260
267
  const result = {};
@@ -378,6 +385,114 @@ function resolveModules(modules) {
378
385
  return getShaderDependencies(modules);
379
386
  }
380
387
 
388
+ // dist/lib/shader-module/shader-module-uniform-layout.js
389
+ function getShaderModuleUniformBlockName(module2) {
390
+ return `${module2.name}Uniforms`;
391
+ }
392
+ function getShaderModuleUniformBlockFields(module2, stage) {
393
+ const shaderSource = stage === "wgsl" ? module2.source : stage === "vertex" ? module2.vs : module2.fs;
394
+ if (!shaderSource) {
395
+ return null;
396
+ }
397
+ const uniformBlockName = getShaderModuleUniformBlockName(module2);
398
+ return extractShaderUniformBlockFieldNames(shaderSource, stage === "wgsl" ? "wgsl" : "glsl", uniformBlockName);
399
+ }
400
+ function getShaderModuleUniformLayoutValidationResult(module2, stage) {
401
+ const expectedUniformNames = Object.keys(module2.uniformTypes || {});
402
+ if (!expectedUniformNames.length) {
403
+ return null;
404
+ }
405
+ const actualUniformNames = getShaderModuleUniformBlockFields(module2, stage);
406
+ if (!actualUniformNames) {
407
+ return null;
408
+ }
409
+ return {
410
+ moduleName: module2.name,
411
+ uniformBlockName: getShaderModuleUniformBlockName(module2),
412
+ stage,
413
+ expectedUniformNames,
414
+ actualUniformNames,
415
+ matches: areStringArraysEqual(expectedUniformNames, actualUniformNames)
416
+ };
417
+ }
418
+ function validateShaderModuleUniformLayout(module2, stage, options = {}) {
419
+ var _a, _b;
420
+ const validationResult = getShaderModuleUniformLayoutValidationResult(module2, stage);
421
+ if (!validationResult || validationResult.matches) {
422
+ return validationResult;
423
+ }
424
+ const message = formatShaderModuleUniformLayoutError(validationResult);
425
+ (_b = (_a = options.log) == null ? void 0 : _a.error) == null ? void 0 : _b.call(_a, message, validationResult)();
426
+ if (options.throwOnError !== false) {
427
+ assert(false, message);
428
+ }
429
+ return validationResult;
430
+ }
431
+ function extractShaderUniformBlockFieldNames(shaderSource, language, uniformBlockName) {
432
+ const sourceBody = language === "wgsl" ? extractWGSLStructBody(shaderSource, uniformBlockName) : extractGLSLUniformBlockBody(shaderSource, uniformBlockName);
433
+ if (!sourceBody) {
434
+ return null;
435
+ }
436
+ const fieldNames = [];
437
+ for (const sourceLine of sourceBody.split("\n")) {
438
+ const line = sourceLine.replace(/\/\/.*$/, "").trim();
439
+ if (!line || line.startsWith("#")) {
440
+ continue;
441
+ }
442
+ const fieldMatch = language === "wgsl" ? line.match(/^([A-Za-z0-9_]+)\s*:/) : line.match(/^(?:uniform\s+)?[A-Za-z0-9_]+(?:<[^>]+>)?\s+([A-Za-z0-9_]+)(?:\s*\[[^\]]+\])?\s*;/);
443
+ if (fieldMatch) {
444
+ fieldNames.push(fieldMatch[1]);
445
+ }
446
+ }
447
+ return fieldNames;
448
+ }
449
+ function extractWGSLStructBody(shaderSource, uniformBlockName) {
450
+ const structMatch = new RegExp(`\\bstruct\\s+${uniformBlockName}\\b`, "m").exec(shaderSource);
451
+ if (!structMatch) {
452
+ return null;
453
+ }
454
+ const openBraceIndex = shaderSource.indexOf("{", structMatch.index);
455
+ if (openBraceIndex < 0) {
456
+ return null;
457
+ }
458
+ let braceDepth = 0;
459
+ for (let index = openBraceIndex; index < shaderSource.length; index++) {
460
+ const character = shaderSource[index];
461
+ if (character === "{") {
462
+ braceDepth++;
463
+ continue;
464
+ }
465
+ if (character !== "}") {
466
+ continue;
467
+ }
468
+ braceDepth--;
469
+ if (braceDepth === 0) {
470
+ return shaderSource.slice(openBraceIndex + 1, index);
471
+ }
472
+ }
473
+ return null;
474
+ }
475
+ function extractGLSLUniformBlockBody(shaderSource, uniformBlockName) {
476
+ const sourceMatch = shaderSource.match(new RegExp(`uniform\\s+${uniformBlockName}\\s*\\{([\\s\\S]*?)\\}\\s*[A-Za-z0-9_]+\\s*;`, "m"));
477
+ return (sourceMatch == null ? void 0 : sourceMatch[1]) || null;
478
+ }
479
+ function areStringArraysEqual(leftValues, rightValues) {
480
+ if (leftValues.length !== rightValues.length) {
481
+ return false;
482
+ }
483
+ for (let valueIndex = 0; valueIndex < leftValues.length; valueIndex++) {
484
+ if (leftValues[valueIndex] !== rightValues[valueIndex]) {
485
+ return false;
486
+ }
487
+ }
488
+ return true;
489
+ }
490
+ function formatShaderModuleUniformLayoutError(validationResult) {
491
+ return `${validationResult.moduleName}: ${validationResult.stage} shader uniform block ${validationResult.uniformBlockName} does not match module.uniformTypes.
492
+ Expected: ${validationResult.expectedUniformNames.join(", ")}
493
+ Actual: ${validationResult.actualUniformNames.join(", ")}`;
494
+ }
495
+
381
496
  // dist/lib/shader-assembly/platform-defines.js
382
497
  function getPlatformShaderDefines(platformInfo) {
383
498
  switch (platformInfo == null ? void 0 : platformInfo.gpu.toLowerCase()) {
@@ -434,19 +549,19 @@ function getPlatformShaderDefines(platformInfo) {
434
549
  }
435
550
 
436
551
  // dist/lib/shader-transpiler/transpile-glsl-shader.js
437
- function transpileGLSLShader(source3, stage) {
552
+ function transpileGLSLShader(source4, stage) {
438
553
  var _a;
439
- const sourceGLSLVersion = Number(((_a = source3.match(/^#version[ \t]+(\d+)/m)) == null ? void 0 : _a[1]) || 100);
554
+ const sourceGLSLVersion = Number(((_a = source4.match(/^#version[ \t]+(\d+)/m)) == null ? void 0 : _a[1]) || 100);
440
555
  if (sourceGLSLVersion !== 300) {
441
556
  throw new Error("luma.gl v9 only supports GLSL 3.00 shader sources");
442
557
  }
443
558
  switch (stage) {
444
559
  case "vertex":
445
- source3 = convertShader(source3, ES300_VERTEX_REPLACEMENTS);
446
- return source3;
560
+ source4 = convertShader(source4, ES300_VERTEX_REPLACEMENTS);
561
+ return source4;
447
562
  case "fragment":
448
- source3 = convertShader(source3, ES300_FRAGMENT_REPLACEMENTS);
449
- return source3;
563
+ source4 = convertShader(source4, ES300_FRAGMENT_REPLACEMENTS);
564
+ return source4;
450
565
  default:
451
566
  throw new Error(stage);
452
567
  }
@@ -470,11 +585,11 @@ var ES300_FRAGMENT_REPLACEMENTS = [
470
585
  // `varying` keyword replaced with `in`
471
586
  [makeVariableTextRegExp("varying"), "in $1"]
472
587
  ];
473
- function convertShader(source3, replacements) {
588
+ function convertShader(source4, replacements) {
474
589
  for (const [pattern, replacement] of replacements) {
475
- source3 = source3.replace(pattern, replacement);
590
+ source4 = source4.replace(pattern, replacement);
476
591
  }
477
- return source3;
592
+ return source4;
478
593
  }
479
594
  function makeVariableTextRegExp(qualifier) {
480
595
  return new RegExp(`\\b${qualifier}[ \\t]+(\\w+[ \\t]+\\w+(\\[\\w+\\])?;)`, "g");
@@ -536,11 +651,11 @@ function normalizeShaderHooks(hookFunctions) {
536
651
  }
537
652
 
538
653
  // dist/lib/glsl-utils/get-shader-info.js
539
- function getShaderInfo(source3, defaultName) {
654
+ function getShaderInfo(source4, defaultName) {
540
655
  return {
541
- name: getShaderName(source3, defaultName),
656
+ name: getShaderName(source4, defaultName),
542
657
  language: "glsl",
543
- version: getShaderVersion(source3)
658
+ version: getShaderVersion(source4)
544
659
  };
545
660
  }
546
661
  function getShaderName(shader, defaultName = "unnamed") {
@@ -548,9 +663,9 @@ function getShaderName(shader, defaultName = "unnamed") {
548
663
  const match = SHADER_NAME_REGEXP.exec(shader);
549
664
  return match ? match[1] : defaultName;
550
665
  }
551
- function getShaderVersion(source3) {
666
+ function getShaderVersion(source4) {
552
667
  let version = 100;
553
- const words = source3.match(/[^\s]+/g);
668
+ const words = source4.match(/[^\s]+/g);
554
669
  if (words && words.length >= 2 && words[0] === "#version") {
555
670
  const parsedVersion = parseInt(words[1], 10);
556
671
  if (Number.isFinite(parsedVersion)) {
@@ -563,11 +678,158 @@ function getShaderVersion(source3) {
563
678
  return version;
564
679
  }
565
680
 
681
+ // dist/lib/shader-assembly/wgsl-binding-debug.js
682
+ var WGSL_BINDING_DEBUG_REGEXES = [
683
+ /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g,
684
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g
685
+ ];
686
+ function getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments = []) {
687
+ var _a;
688
+ const assignmentMap = /* @__PURE__ */ new Map();
689
+ for (const bindingAssignment of bindingAssignments) {
690
+ assignmentMap.set(getBindingAssignmentKey(bindingAssignment.name, bindingAssignment.group, bindingAssignment.location), bindingAssignment.moduleName);
691
+ }
692
+ const rows = [];
693
+ for (const regex of WGSL_BINDING_DEBUG_REGEXES) {
694
+ regex.lastIndex = 0;
695
+ let match;
696
+ while (match = regex.exec(source4)) {
697
+ const isBindingFirst = regex === WGSL_BINDING_DEBUG_REGEXES[0];
698
+ const binding = Number(match[isBindingFirst ? 1 : 2]);
699
+ const group = Number(match[isBindingFirst ? 2 : 1]);
700
+ const accessDeclaration = (_a = match[3]) == null ? void 0 : _a.trim();
701
+ const name = match[4];
702
+ const resourceType = match[5].trim();
703
+ const moduleName = assignmentMap.get(getBindingAssignmentKey(name, group, binding));
704
+ rows.push(normalizeShaderBindingDebugRow({
705
+ name,
706
+ group,
707
+ binding,
708
+ owner: moduleName ? "module" : "application",
709
+ moduleName,
710
+ accessDeclaration,
711
+ resourceType
712
+ }));
713
+ }
714
+ }
715
+ return rows.sort((left, right) => {
716
+ if (left.group !== right.group) {
717
+ return left.group - right.group;
718
+ }
719
+ if (left.binding !== right.binding) {
720
+ return left.binding - right.binding;
721
+ }
722
+ return left.name.localeCompare(right.name);
723
+ });
724
+ }
725
+ function normalizeShaderBindingDebugRow(row) {
726
+ const baseRow = {
727
+ name: row.name,
728
+ group: row.group,
729
+ binding: row.binding,
730
+ owner: row.owner,
731
+ kind: "unknown",
732
+ moduleName: row.moduleName,
733
+ resourceType: row.resourceType
734
+ };
735
+ if (row.accessDeclaration) {
736
+ const access = row.accessDeclaration.split(",").map((value) => value.trim());
737
+ if (access[0] === "uniform") {
738
+ return { ...baseRow, kind: "uniform", access: "uniform" };
739
+ }
740
+ if (access[0] === "storage") {
741
+ const storageAccess = access[1] || "read_write";
742
+ return {
743
+ ...baseRow,
744
+ kind: storageAccess === "read" ? "read-only-storage" : "storage",
745
+ access: storageAccess
746
+ };
747
+ }
748
+ }
749
+ if (row.resourceType === "sampler" || row.resourceType === "sampler_comparison") {
750
+ return {
751
+ ...baseRow,
752
+ kind: "sampler",
753
+ samplerKind: row.resourceType === "sampler_comparison" ? "comparison" : "filtering"
754
+ };
755
+ }
756
+ if (row.resourceType.startsWith("texture_storage_")) {
757
+ return {
758
+ ...baseRow,
759
+ kind: "storage-texture",
760
+ access: getStorageTextureAccess(row.resourceType),
761
+ viewDimension: getTextureViewDimension(row.resourceType)
762
+ };
763
+ }
764
+ if (row.resourceType.startsWith("texture_")) {
765
+ return {
766
+ ...baseRow,
767
+ kind: "texture",
768
+ viewDimension: getTextureViewDimension(row.resourceType),
769
+ sampleType: getTextureSampleType(row.resourceType),
770
+ multisampled: row.resourceType.startsWith("texture_multisampled_")
771
+ };
772
+ }
773
+ return baseRow;
774
+ }
775
+ function getBindingAssignmentKey(name, group, binding) {
776
+ return `${group}:${binding}:${name}`;
777
+ }
778
+ function getTextureViewDimension(resourceType) {
779
+ if (resourceType.includes("cube_array")) {
780
+ return "cube-array";
781
+ }
782
+ if (resourceType.includes("2d_array")) {
783
+ return "2d-array";
784
+ }
785
+ if (resourceType.includes("cube")) {
786
+ return "cube";
787
+ }
788
+ if (resourceType.includes("3d")) {
789
+ return "3d";
790
+ }
791
+ if (resourceType.includes("2d")) {
792
+ return "2d";
793
+ }
794
+ if (resourceType.includes("1d")) {
795
+ return "1d";
796
+ }
797
+ return void 0;
798
+ }
799
+ function getTextureSampleType(resourceType) {
800
+ if (resourceType.startsWith("texture_depth_")) {
801
+ return "depth";
802
+ }
803
+ if (resourceType.includes("<i32>")) {
804
+ return "sint";
805
+ }
806
+ if (resourceType.includes("<u32>")) {
807
+ return "uint";
808
+ }
809
+ if (resourceType.includes("<f32>")) {
810
+ return "float";
811
+ }
812
+ return void 0;
813
+ }
814
+ function getStorageTextureAccess(resourceType) {
815
+ const match = /,\s*([A-Za-z_][A-Za-z0-9_]*)\s*>$/.exec(resourceType);
816
+ return match == null ? void 0 : match[1];
817
+ }
818
+
566
819
  // dist/lib/shader-assembly/assemble-shaders.js
567
820
  var INJECT_SHADER_DECLARATIONS = `
568
821
 
569
822
  ${DECLARATION_INJECT_MARKER}
570
823
  `;
824
+ var MODULE_WGSL_BINDING_DECLARATION_REGEXES = [
825
+ /@binding\(\s*(auto|\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
826
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(auto|\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
827
+ ];
828
+ var WGSL_BINDING_DECLARATION_REGEXES = [
829
+ /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
830
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
831
+ ];
832
+ var RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
571
833
  var FRAGMENT_SHADER_PROLOGUE = (
572
834
  /* glsl */
573
835
  `precision highp float;
@@ -575,30 +837,33 @@ var FRAGMENT_SHADER_PROLOGUE = (
575
837
  );
576
838
  function assembleWGSLShader(options) {
577
839
  const modules = getShaderModuleDependencies(options.modules || []);
840
+ const { source: source4, bindingAssignments } = assembleShaderWGSL(options.platformInfo, {
841
+ ...options,
842
+ source: options.source,
843
+ stage: "vertex",
844
+ modules
845
+ });
578
846
  return {
579
- source: assembleShaderWGSL(options.platformInfo, {
580
- ...options,
581
- source: options.source,
582
- stage: "vertex",
583
- modules
584
- }),
585
- getUniforms: assembleGetUniforms(modules)
847
+ source: source4,
848
+ getUniforms: assembleGetUniforms(modules),
849
+ bindingAssignments,
850
+ bindingTable: getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments)
586
851
  };
587
852
  }
588
853
  function assembleGLSLShaderPair(options) {
589
- const { vs: vs3, fs: fs4 } = options;
854
+ const { vs: vs4, fs: fs5 } = options;
590
855
  const modules = getShaderModuleDependencies(options.modules || []);
591
856
  return {
592
857
  vs: assembleShaderGLSL(options.platformInfo, {
593
858
  ...options,
594
- source: vs3,
859
+ source: vs4,
595
860
  stage: "vertex",
596
861
  modules
597
862
  }),
598
863
  fs: assembleShaderGLSL(options.platformInfo, {
599
864
  ...options,
600
865
  // @ts-expect-error
601
- source: fs4,
866
+ source: fs5,
602
867
  stage: "fragment",
603
868
  modules
604
869
  }),
@@ -609,7 +874,7 @@ function assembleShaderWGSL(platformInfo, options) {
609
874
  var _a;
610
875
  const {
611
876
  // id,
612
- source: source3,
877
+ source: source4,
613
878
  stage,
614
879
  modules,
615
880
  // defines = {},
@@ -617,8 +882,8 @@ function assembleShaderWGSL(platformInfo, options) {
617
882
  inject = {},
618
883
  log: log2
619
884
  } = options;
620
- assert(typeof source3 === "string", "shader source must be a string");
621
- const coreSource = source3;
885
+ assert(typeof source4 === "string", "shader source must be a string");
886
+ const coreSource = source4;
622
887
  let assembledSource = "";
623
888
  const hookFunctionMap = normalizeShaderHooks(hookFunctions);
624
889
  const hookInjections = {};
@@ -644,11 +909,20 @@ function assembleShaderWGSL(platformInfo, options) {
644
909
  }
645
910
  }
646
911
  const modulesToInject = modules;
912
+ const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(coreSource);
913
+ const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(modulesToInject, options._bindingRegistry, usedBindingsByGroup);
914
+ const bindingAssignments = [];
647
915
  for (const module2 of modulesToInject) {
648
916
  if (log2) {
649
917
  checkShaderModuleDeprecations(module2, coreSource, log2);
650
918
  }
651
- const moduleSource = getShaderModuleSource(module2, "wgsl");
919
+ const relocation = relocateWGSLModuleBindings(getShaderModuleSource(module2, "wgsl", log2), module2, {
920
+ usedBindingsByGroup,
921
+ bindingRegistry: options._bindingRegistry,
922
+ reservedBindingKeysByGroup
923
+ });
924
+ bindingAssignments.push(...relocation.bindingAssignments);
925
+ const moduleSource = relocation.source;
652
926
  assembledSource += moduleSource;
653
927
  const injections = ((_a = module2.injections) == null ? void 0 : _a[stage]) || {};
654
928
  for (const key in injections) {
@@ -667,18 +941,20 @@ function assembleShaderWGSL(platformInfo, options) {
667
941
  assembledSource += INJECT_SHADER_DECLARATIONS;
668
942
  assembledSource = injectShader(assembledSource, stage, declInjections);
669
943
  assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections);
944
+ assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
670
945
  assembledSource += coreSource;
671
946
  assembledSource = injectShader(assembledSource, stage, mainInjections);
672
- return assembledSource;
947
+ assertNoUnresolvedAutoBindings(assembledSource);
948
+ return { source: assembledSource, bindingAssignments };
673
949
  }
674
950
  function assembleShaderGLSL(platformInfo, options) {
675
951
  var _a;
676
- const { source: source3, stage, language = "glsl", modules, defines = {}, hookFunctions = [], inject = {}, prologue = true, log: log2 } = options;
677
- assert(typeof source3 === "string", "shader source must be a string");
678
- const sourceVersion = language === "glsl" ? getShaderInfo(source3).version : -1;
952
+ const { source: source4, stage, language = "glsl", modules, defines = {}, hookFunctions = [], inject = {}, prologue = true, log: log2 } = options;
953
+ assert(typeof source4 === "string", "shader source must be a string");
954
+ const sourceVersion = language === "glsl" ? getShaderInfo(source4).version : -1;
679
955
  const targetVersion = platformInfo.shaderLanguageVersion;
680
956
  const sourceVersionDirective = sourceVersion === 100 ? "#version 100" : "#version 300 es";
681
- const sourceLines = source3.split("\n");
957
+ const sourceLines = source4.split("\n");
682
958
  const coreSource = sourceLines.slice(1).join("\n");
683
959
  const allDefines = {};
684
960
  modules.forEach((module2) => {
@@ -733,7 +1009,7 @@ ${getApplicationDefines(allDefines)}
733
1009
  if (log2) {
734
1010
  checkShaderModuleDeprecations(module2, coreSource, log2);
735
1011
  }
736
- const moduleSource = getShaderModuleSource(module2, stage);
1012
+ const moduleSource = getShaderModuleSource(module2, stage, log2);
737
1013
  assembledSource += moduleSource;
738
1014
  const injections = ((_a = module2.instance) == null ? void 0 : _a.normalizedInjections[stage]) || {};
739
1015
  for (const key in injections) {
@@ -782,7 +1058,7 @@ function getApplicationDefines(defines = {}) {
782
1058
  }
783
1059
  return sourceText;
784
1060
  }
785
- function getShaderModuleSource(module2, stage) {
1061
+ function getShaderModuleSource(module2, stage, log2) {
786
1062
  let moduleSource;
787
1063
  switch (stage) {
788
1064
  case "vertex":
@@ -800,40 +1076,227 @@ function getShaderModuleSource(module2, stage) {
800
1076
  if (!module2.name) {
801
1077
  throw new Error("Shader module must have a name");
802
1078
  }
1079
+ validateShaderModuleUniformLayout(module2, stage, { log: log2 });
803
1080
  const moduleName = module2.name.toUpperCase().replace(/[^0-9a-z]/gi, "_");
804
- let source3 = `// ----- MODULE ${module2.name} ---------------
1081
+ let source4 = `// ----- MODULE ${module2.name} ---------------
805
1082
 
806
1083
  `;
807
1084
  if (stage !== "wgsl") {
808
- source3 += `#define MODULE_${moduleName}
1085
+ source4 += `#define MODULE_${moduleName}
809
1086
  `;
810
1087
  }
811
- source3 += `${moduleSource}
1088
+ source4 += `${moduleSource}
1089
+ `;
1090
+ return source4;
1091
+ }
1092
+ function getUsedBindingsByGroupFromApplicationWGSL(source4) {
1093
+ const usedBindingsByGroup = /* @__PURE__ */ new Map();
1094
+ for (const regex of WGSL_BINDING_DECLARATION_REGEXES) {
1095
+ regex.lastIndex = 0;
1096
+ let match;
1097
+ while (match = regex.exec(source4)) {
1098
+ const isBindingFirst = regex === WGSL_BINDING_DECLARATION_REGEXES[0];
1099
+ const location = Number(match[isBindingFirst ? 1 : 2]);
1100
+ const group = Number(match[isBindingFirst ? 2 : 1]);
1101
+ const name = match[4];
1102
+ validateApplicationWGSLBinding(group, location, name);
1103
+ registerUsedBindingLocation(usedBindingsByGroup, group, location, `application binding "${name}"`);
1104
+ }
1105
+ }
1106
+ return usedBindingsByGroup;
1107
+ }
1108
+ function relocateWGSLModuleBindings(moduleSource, module2, context) {
1109
+ const bindingAssignments = [];
1110
+ const relocationState = {
1111
+ sawSupportedBindingDeclaration: false,
1112
+ nextHintedBindingLocation: typeof module2.firstBindingSlot === "number" ? module2.firstBindingSlot : null
1113
+ };
1114
+ let relocatedSource = relocateWGSLModuleBindingsWithRegex(moduleSource, MODULE_WGSL_BINDING_DECLARATION_REGEXES[0], { isBindingFirst: true, module: module2, context, bindingAssignments, relocationState });
1115
+ relocatedSource = relocateWGSLModuleBindingsWithRegex(relocatedSource, MODULE_WGSL_BINDING_DECLARATION_REGEXES[1], { isBindingFirst: false, module: module2, context, bindingAssignments, relocationState });
1116
+ if (moduleSource.includes("@binding(auto)") && !relocationState.sawSupportedBindingDeclaration) {
1117
+ throw new Error(`Unsupported @binding(auto) declaration form in module "${module2.name}". Use "@group(N) @binding(auto) var ..." or "@binding(auto) @group(N) var ..." on a single line.`);
1118
+ }
1119
+ return { source: relocatedSource, bindingAssignments };
1120
+ }
1121
+ function relocateWGSLModuleBindingsWithRegex(source4, regex, params) {
1122
+ return source4.replace(regex, (...replaceArguments) => relocateWGSLModuleBindingMatch(replaceArguments, params));
1123
+ }
1124
+ function relocateWGSLModuleBindingMatch(replaceArguments, params) {
1125
+ var _a, _b;
1126
+ const { isBindingFirst, module: module2, context, bindingAssignments, relocationState } = params;
1127
+ relocationState.sawSupportedBindingDeclaration = true;
1128
+ const match = replaceArguments[0];
1129
+ const bindingToken = replaceArguments[isBindingFirst ? 1 : 2];
1130
+ const groupToken = replaceArguments[isBindingFirst ? 2 : 1];
1131
+ const name = replaceArguments[4];
1132
+ const group = Number(groupToken);
1133
+ if (bindingToken === "auto") {
1134
+ const registryKey = getBindingRegistryKey(group, module2.name, name);
1135
+ const registryLocation = (_a = context.bindingRegistry) == null ? void 0 : _a.get(registryKey);
1136
+ const location2 = registryLocation !== void 0 ? registryLocation : relocationState.nextHintedBindingLocation === null ? allocateAutoBindingLocation(group, context.usedBindingsByGroup) : allocateAutoBindingLocation(group, context.usedBindingsByGroup, relocationState.nextHintedBindingLocation);
1137
+ validateModuleWGSLBinding(module2.name, group, location2, name);
1138
+ if (registryLocation !== void 0 && claimReservedBindingLocation(context.reservedBindingKeysByGroup, group, location2, registryKey)) {
1139
+ bindingAssignments.push({ moduleName: module2.name, name, group, location: location2 });
1140
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location2})`);
1141
+ }
1142
+ registerUsedBindingLocation(context.usedBindingsByGroup, group, location2, `module "${module2.name}" binding "${name}"`);
1143
+ (_b = context.bindingRegistry) == null ? void 0 : _b.set(registryKey, location2);
1144
+ bindingAssignments.push({ moduleName: module2.name, name, group, location: location2 });
1145
+ if (relocationState.nextHintedBindingLocation !== null && registryLocation === void 0) {
1146
+ relocationState.nextHintedBindingLocation = location2 + 1;
1147
+ }
1148
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location2})`);
1149
+ }
1150
+ const location = Number(bindingToken);
1151
+ validateModuleWGSLBinding(module2.name, group, location, name);
1152
+ registerUsedBindingLocation(context.usedBindingsByGroup, group, location, `module "${module2.name}" binding "${name}"`);
1153
+ bindingAssignments.push({ moduleName: module2.name, name, group, location });
1154
+ return match;
1155
+ }
1156
+ function reserveRegisteredModuleBindings(modules, bindingRegistry, usedBindingsByGroup) {
1157
+ const reservedBindingKeysByGroup = /* @__PURE__ */ new Map();
1158
+ if (!bindingRegistry) {
1159
+ return reservedBindingKeysByGroup;
1160
+ }
1161
+ for (const module2 of modules) {
1162
+ for (const binding of getModuleWGSLBindingDeclarations(module2)) {
1163
+ const registryKey = getBindingRegistryKey(binding.group, module2.name, binding.name);
1164
+ const location = bindingRegistry.get(registryKey);
1165
+ if (location !== void 0) {
1166
+ const reservedBindingKeys = reservedBindingKeysByGroup.get(binding.group) || /* @__PURE__ */ new Map();
1167
+ const existingReservation = reservedBindingKeys.get(location);
1168
+ if (existingReservation && existingReservation !== registryKey) {
1169
+ throw new Error(`Duplicate WGSL binding reservation for modules "${existingReservation}" and "${registryKey}": group ${binding.group}, binding ${location}.`);
1170
+ }
1171
+ registerUsedBindingLocation(usedBindingsByGroup, binding.group, location, `registered module binding "${registryKey}"`);
1172
+ reservedBindingKeys.set(location, registryKey);
1173
+ reservedBindingKeysByGroup.set(binding.group, reservedBindingKeys);
1174
+ }
1175
+ }
1176
+ }
1177
+ return reservedBindingKeysByGroup;
1178
+ }
1179
+ function claimReservedBindingLocation(reservedBindingKeysByGroup, group, location, registryKey) {
1180
+ const reservedBindingKeys = reservedBindingKeysByGroup.get(group);
1181
+ if (!reservedBindingKeys) {
1182
+ return false;
1183
+ }
1184
+ const reservedKey = reservedBindingKeys.get(location);
1185
+ if (!reservedKey) {
1186
+ return false;
1187
+ }
1188
+ if (reservedKey !== registryKey) {
1189
+ throw new Error(`Registered module binding "${registryKey}" collided with "${reservedKey}": group ${group}, binding ${location}.`);
1190
+ }
1191
+ return true;
1192
+ }
1193
+ function getModuleWGSLBindingDeclarations(module2) {
1194
+ const declarations = [];
1195
+ const moduleSource = module2.source || "";
1196
+ for (const regex of MODULE_WGSL_BINDING_DECLARATION_REGEXES) {
1197
+ regex.lastIndex = 0;
1198
+ let match;
1199
+ while (match = regex.exec(moduleSource)) {
1200
+ const isBindingFirst = regex === MODULE_WGSL_BINDING_DECLARATION_REGEXES[0];
1201
+ declarations.push({
1202
+ name: match[4],
1203
+ group: Number(match[isBindingFirst ? 2 : 1])
1204
+ });
1205
+ }
1206
+ }
1207
+ return declarations;
1208
+ }
1209
+ function validateApplicationWGSLBinding(group, location, name) {
1210
+ if (group === 0 && location >= RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT) {
1211
+ throw new Error(`Application binding "${name}" in group 0 uses reserved binding ${location}. Application-owned explicit group-0 bindings must stay below ${RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT}.`);
1212
+ }
1213
+ }
1214
+ function validateModuleWGSLBinding(moduleName, group, location, name) {
1215
+ if (group === 0 && location < RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT) {
1216
+ throw new Error(`Module "${moduleName}" binding "${name}" in group 0 uses reserved application binding ${location}. Module-owned explicit group-0 bindings must be ${RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT} or higher.`);
1217
+ }
1218
+ }
1219
+ function registerUsedBindingLocation(usedBindingsByGroup, group, location, label) {
1220
+ const usedBindings = usedBindingsByGroup.get(group) || /* @__PURE__ */ new Set();
1221
+ if (usedBindings.has(location)) {
1222
+ throw new Error(`Duplicate WGSL binding assignment for ${label}: group ${group}, binding ${location}.`);
1223
+ }
1224
+ usedBindings.add(location);
1225
+ usedBindingsByGroup.set(group, usedBindings);
1226
+ }
1227
+ function allocateAutoBindingLocation(group, usedBindingsByGroup, preferredBindingLocation) {
1228
+ const usedBindings = usedBindingsByGroup.get(group) || /* @__PURE__ */ new Set();
1229
+ let nextBinding = preferredBindingLocation ?? (group === 0 ? RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT : usedBindings.size > 0 ? Math.max(...usedBindings) + 1 : 0);
1230
+ while (usedBindings.has(nextBinding)) {
1231
+ nextBinding++;
1232
+ }
1233
+ return nextBinding;
1234
+ }
1235
+ function assertNoUnresolvedAutoBindings(source4) {
1236
+ if (/@binding\(\s*auto\s*\)/.test(source4)) {
1237
+ throw new Error("Unresolved @binding(auto) remained in assembled WGSL source.");
1238
+ }
1239
+ }
1240
+ function formatWGSLBindingAssignmentComments(bindingAssignments) {
1241
+ if (bindingAssignments.length === 0) {
1242
+ return "";
1243
+ }
1244
+ let source4 = "// ----- MODULE WGSL BINDING ASSIGNMENTS ---------------\n";
1245
+ for (const bindingAssignment of bindingAssignments) {
1246
+ source4 += `// ${bindingAssignment.moduleName}.${bindingAssignment.name} -> @group(${bindingAssignment.group}) @binding(${bindingAssignment.location})
812
1247
  `;
813
- return source3;
1248
+ }
1249
+ source4 += "\n";
1250
+ return source4;
1251
+ }
1252
+ function getBindingRegistryKey(group, moduleName, bindingName) {
1253
+ return `${group}:${moduleName}:${bindingName}`;
814
1254
  }
815
1255
 
816
1256
  // dist/lib/preprocessor/preprocessor.js
817
- var IFDEF_REGEXP = /^\s*\#\s*ifdef\s*([a-zA-Z_]+)\s*$/;
1257
+ var DEFINE_NAME_PATTERN = "([a-zA-Z_][a-zA-Z0-9_]*)";
1258
+ var IFDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*$`);
1259
+ var IFNDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifndef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
1260
+ var ELSE_REGEXP = /^\s*\#\s*else\s*(?:\/\/.*)?$/;
818
1261
  var ENDIF_REGEXP = /^\s*\#\s*endif\s*$/;
819
- function preprocess(source3, options) {
820
- var _a;
821
- const lines = source3.split("\n");
1262
+ var IFDEF_WITH_COMMENT_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
1263
+ var ENDIF_WITH_COMMENT_REGEXP = /^\s*\#\s*endif\s*(?:\/\/.*)?$/;
1264
+ function preprocess(source4, options) {
1265
+ var _a, _b;
1266
+ const lines = source4.split("\n");
822
1267
  const output = [];
1268
+ const conditionalStack = [];
823
1269
  let conditional = true;
824
- let currentDefine = null;
825
1270
  for (const line of lines) {
826
- const matchIf = line.match(IFDEF_REGEXP);
827
- const matchEnd = line.match(ENDIF_REGEXP);
828
- if (matchIf) {
829
- currentDefine = matchIf[1];
830
- conditional = Boolean((_a = options == null ? void 0 : options.defines) == null ? void 0 : _a[currentDefine]);
1271
+ const matchIf = line.match(IFDEF_WITH_COMMENT_REGEXP) || line.match(IFDEF_REGEXP);
1272
+ const matchIfNot = line.match(IFNDEF_REGEXP);
1273
+ const matchElse = line.match(ELSE_REGEXP);
1274
+ const matchEnd = line.match(ENDIF_WITH_COMMENT_REGEXP) || line.match(ENDIF_REGEXP);
1275
+ if (matchIf || matchIfNot) {
1276
+ const defineName = (_a = matchIf || matchIfNot) == null ? void 0 : _a[1];
1277
+ const defineValue = Boolean((_b = options == null ? void 0 : options.defines) == null ? void 0 : _b[defineName]);
1278
+ const branchTaken = matchIf ? defineValue : !defineValue;
1279
+ const active = conditional && branchTaken;
1280
+ conditionalStack.push({ parentActive: conditional, branchTaken, active });
1281
+ conditional = active;
1282
+ } else if (matchElse) {
1283
+ const currentConditional = conditionalStack[conditionalStack.length - 1];
1284
+ if (!currentConditional) {
1285
+ throw new Error("Encountered #else without matching #ifdef or #ifndef");
1286
+ }
1287
+ currentConditional.active = currentConditional.parentActive && !currentConditional.branchTaken;
1288
+ currentConditional.branchTaken = true;
1289
+ conditional = currentConditional.active;
831
1290
  } else if (matchEnd) {
832
- conditional = true;
1291
+ conditionalStack.pop();
1292
+ conditional = conditionalStack.length ? conditionalStack[conditionalStack.length - 1].active : true;
833
1293
  } else if (conditional) {
834
1294
  output.push(line);
835
1295
  }
836
1296
  }
1297
+ if (conditionalStack.length > 0) {
1298
+ throw new Error("Unterminated conditional block in shader source");
1299
+ }
837
1300
  return output.join("\n");
838
1301
  }
839
1302
 
@@ -843,6 +1306,8 @@ var _ShaderAssembler = class {
843
1306
  _hookFunctions = [];
844
1307
  /** Shader modules */
845
1308
  _defaultModules = [];
1309
+ /** Stable per-run WGSL auto-binding assignments keyed by group/module/binding. */
1310
+ _wgslBindingRegistry = /* @__PURE__ */ new Map();
846
1311
  /**
847
1312
  * A default shader assembler instance - the natural place to register default modules and hooks
848
1313
  * @returns
@@ -886,15 +1351,29 @@ var _ShaderAssembler = class {
886
1351
  assembleWGSLShader(props) {
887
1352
  const modules = this._getModuleList(props.modules);
888
1353
  const hookFunctions = this._hookFunctions;
889
- const { source: source3, getUniforms: getUniforms4 } = assembleWGSLShader({
1354
+ const { source: source4, getUniforms: getUniforms4, bindingAssignments } = assembleWGSLShader({
890
1355
  ...props,
891
1356
  // @ts-expect-error
892
1357
  source: props.source,
1358
+ _bindingRegistry: this._wgslBindingRegistry,
893
1359
  modules,
894
1360
  hookFunctions
895
1361
  });
896
- const preprocessedSource = props.platformInfo.shaderLanguage === "wgsl" ? preprocess(source3) : source3;
897
- return { source: preprocessedSource, getUniforms: getUniforms4, modules };
1362
+ const defines = {
1363
+ ...modules.reduce((accumulator, module2) => {
1364
+ Object.assign(accumulator, module2.defines);
1365
+ return accumulator;
1366
+ }, {}),
1367
+ ...props.defines
1368
+ };
1369
+ const preprocessedSource = props.platformInfo.shaderLanguage === "wgsl" ? preprocess(source4, { defines }) : source4;
1370
+ return {
1371
+ source: preprocessedSource,
1372
+ getUniforms: getUniforms4,
1373
+ modules,
1374
+ bindingAssignments,
1375
+ bindingTable: getShaderBindingDebugRowsFromWGSL(preprocessedSource, bindingAssignments)
1376
+ };
898
1377
  }
899
1378
  /**
900
1379
  * Assemble a pair of shaders into a single shader program
@@ -1059,6 +1538,9 @@ function generateGLSLUniformDeclarations(module2, options) {
1059
1538
  case "uniforms":
1060
1539
  }
1061
1540
  for (const [uniformName, uniformFormat] of Object.entries(module2.uniformTypes || {})) {
1541
+ if (typeof uniformFormat !== "string") {
1542
+ throw new Error(`Composite uniform types are not supported by GLSL shader generation: ${module2.name}.${uniformName}`);
1543
+ }
1062
1544
  const glslUniformType = getGLSLUniformType(uniformFormat);
1063
1545
  switch (options.uniforms) {
1064
1546
  case "scoped-interface-blocks":
@@ -1119,6 +1601,9 @@ function generateWGSLUniformDeclarations(module2, options) {
1119
1601
  const wgsl = [];
1120
1602
  wgsl.push(`struct ${capitalize(module2.name)} {`);
1121
1603
  for (const [uniformName, uniformFormat] of Object.entries((module2 == null ? void 0 : module2.uniformTypes) || {})) {
1604
+ if (typeof uniformFormat !== "string") {
1605
+ throw new Error(`Composite uniform types are not supported by WGSL shader generation: ${module2.name}.${uniformName}`);
1606
+ }
1122
1607
  const wgslUniformType = uniformFormat;
1123
1608
  wgsl.push(` ${uniformName} : ${wgslUniformType};`);
1124
1609
  }
@@ -1248,9 +1733,8 @@ function fp64ifyMatrix4(matrix) {
1248
1733
  // dist/modules/math/random/random.js
1249
1734
  var source = (
1250
1735
  /* wgsl */
1251
- `fn random(scale: vec3f, seed: float) -> f32 {
1252
- /* use the fragment position for a different seed per-pixel */
1253
- return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);
1736
+ `fn random(scale: vec3f, seed: f32) -> f32 {
1737
+ return fract(sin(dot(scale + vec3f(seed), vec3f(12.9898, 78.233, 151.7182))) * 43758.5453 + seed);
1254
1738
  }
1255
1739
  `
1256
1740
  );
@@ -1434,6 +1918,7 @@ var fp64arithmeticShader = (
1434
1918
  `
1435
1919
  uniform fp64arithmeticUniforms {
1436
1920
  uniform float ONE;
1921
+ uniform float SPLIT;
1437
1922
  } fp64;
1438
1923
 
1439
1924
  /*
@@ -1443,6 +1928,12 @@ The purpose of this workaround is to prevent shader compilers from
1443
1928
  optimizing away necessary arithmetic operations by swapping their sequences
1444
1929
  or transform the equation to some 'equivalent' form.
1445
1930
 
1931
+ These helpers implement Dekker/Veltkamp-style error tracking. If the compiler
1932
+ folds constants or reassociates the arithmetic, the high/low split can stop
1933
+ tracking the rounding error correctly. That failure mode tends to look fine in
1934
+ simple coordinate setup, but then breaks down inside iterative arithmetic such
1935
+ as fp64 Mandelbrot loops.
1936
+
1446
1937
  The method is to multiply an artifical variable, ONE, which will be known to
1447
1938
  the compiler to be 1 only at runtime. The whole expression is then represented
1448
1939
  as a polynomial with respective to ONE. In the coefficients of all terms, only one a
@@ -1451,17 +1942,23 @@ and one b should appear
1451
1942
  err = (a + b) * ONE^6 - a * ONE^5 - (a + b) * ONE^4 + a * ONE^3 - b - (a + b) * ONE^2 + a * ONE
1452
1943
  */
1453
1944
 
1454
- // Divide float number to high and low floats to extend fraction bits
1455
- vec2 split(float a) {
1456
- const float SPLIT = 4097.0;
1457
- float t = a * SPLIT;
1945
+ float prevent_fp64_optimization(float value) {
1458
1946
  #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND)
1459
- float a_hi = t * fp64.ONE - (t - a);
1460
- float a_lo = a * fp64.ONE - a_hi;
1947
+ return value + fp64.ONE * 0.0;
1461
1948
  #else
1462
- float a_hi = t - (t - a);
1463
- float a_lo = a - a_hi;
1949
+ return value;
1464
1950
  #endif
1951
+ }
1952
+
1953
+ // Divide float number to high and low floats to extend fraction bits
1954
+ vec2 split(float a) {
1955
+ // Keep SPLIT as a runtime uniform so the compiler cannot fold the Dekker
1956
+ // split into a constant expression and reassociate the recovery steps.
1957
+ float split = prevent_fp64_optimization(fp64.SPLIT);
1958
+ float t = prevent_fp64_optimization(a * split);
1959
+ float temp = t - a;
1960
+ float a_hi = t - temp;
1961
+ float a_lo = a - a_hi;
1465
1962
  return vec2(a_hi, a_lo);
1466
1963
  }
1467
1964
 
@@ -1525,8 +2022,26 @@ vec2 twoProd(float a, float b) {
1525
2022
  float prod = a * b;
1526
2023
  vec2 a_fp64 = split(a);
1527
2024
  vec2 b_fp64 = split(b);
1528
- float err = ((a_fp64.x * b_fp64.x - prod) + a_fp64.x * b_fp64.y +
1529
- a_fp64.y * b_fp64.x) + a_fp64.y * b_fp64.y;
2025
+ // twoProd is especially sensitive because mul_fp64 and div_fp64 both depend
2026
+ // on the split terms and cross terms staying in the original evaluation
2027
+ // order. If the compiler folds or reassociates them, the low part tends to
2028
+ // collapse to zero or NaN on some drivers.
2029
+ float highProduct = prevent_fp64_optimization(a_fp64.x * b_fp64.x);
2030
+ float crossProduct1 = prevent_fp64_optimization(a_fp64.x * b_fp64.y);
2031
+ float crossProduct2 = prevent_fp64_optimization(a_fp64.y * b_fp64.x);
2032
+ float lowProduct = prevent_fp64_optimization(a_fp64.y * b_fp64.y);
2033
+ #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND)
2034
+ float err1 = (highProduct - prod) * fp64.ONE;
2035
+ float err2 = crossProduct1 * fp64.ONE * fp64.ONE;
2036
+ float err3 = crossProduct2 * fp64.ONE * fp64.ONE * fp64.ONE;
2037
+ float err4 = lowProduct * fp64.ONE * fp64.ONE * fp64.ONE * fp64.ONE;
2038
+ #else
2039
+ float err1 = highProduct - prod;
2040
+ float err2 = crossProduct1;
2041
+ float err3 = crossProduct2;
2042
+ float err4 = lowProduct;
2043
+ #endif
2044
+ float err = ((err1 + err2) + err3) + err4;
1530
2045
  return vec2(prod, err);
1531
2046
  }
1532
2047
 
@@ -1602,6 +2117,218 @@ vec2 sqrt_fp64(vec2 a) {
1602
2117
  `
1603
2118
  );
1604
2119
 
2120
+ // dist/modules/math/fp64/fp64-arithmetic-wgsl.js
2121
+ var fp64arithmeticWGSL = (
2122
+ /* wgsl */
2123
+ `struct Fp64ArithmeticUniforms {
2124
+ ONE: f32,
2125
+ SPLIT: f32,
2126
+ };
2127
+
2128
+ @group(0) @binding(auto) var<uniform> fp64arithmetic : Fp64ArithmeticUniforms;
2129
+
2130
+ fn fp64_nan(seed: f32) -> f32 {
2131
+ let nanBits = 0x7fc00000u | select(0u, 1u, seed < 0.0);
2132
+ return bitcast<f32>(nanBits);
2133
+ }
2134
+
2135
+ fn fp64_runtime_zero() -> f32 {
2136
+ return fp64arithmetic.ONE * 0.0;
2137
+ }
2138
+
2139
+ fn prevent_fp64_optimization(value: f32) -> f32 {
2140
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2141
+ return value + fp64_runtime_zero();
2142
+ #else
2143
+ return value;
2144
+ #endif
2145
+ }
2146
+
2147
+ fn split(a: f32) -> vec2f {
2148
+ let splitValue = prevent_fp64_optimization(fp64arithmetic.SPLIT + fp64_runtime_zero());
2149
+ let t = prevent_fp64_optimization(a * splitValue);
2150
+ let temp = prevent_fp64_optimization(t - a);
2151
+ let aHi = prevent_fp64_optimization(t - temp);
2152
+ let aLo = prevent_fp64_optimization(a - aHi);
2153
+ return vec2f(aHi, aLo);
2154
+ }
2155
+
2156
+ fn split2(a: vec2f) -> vec2f {
2157
+ var b = split(a.x);
2158
+ b.y = b.y + a.y;
2159
+ return b;
2160
+ }
2161
+
2162
+ fn quickTwoSum(a: f32, b: f32) -> vec2f {
2163
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2164
+ let sum = prevent_fp64_optimization((a + b) * fp64arithmetic.ONE);
2165
+ let err = prevent_fp64_optimization(b - (sum - a) * fp64arithmetic.ONE);
2166
+ #else
2167
+ let sum = prevent_fp64_optimization(a + b);
2168
+ let err = prevent_fp64_optimization(b - (sum - a));
2169
+ #endif
2170
+ return vec2f(sum, err);
2171
+ }
2172
+
2173
+ fn twoSum(a: f32, b: f32) -> vec2f {
2174
+ let s = prevent_fp64_optimization(a + b);
2175
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2176
+ let v = prevent_fp64_optimization((s * fp64arithmetic.ONE - a) * fp64arithmetic.ONE);
2177
+ let err =
2178
+ prevent_fp64_optimization((a - (s - v) * fp64arithmetic.ONE) *
2179
+ fp64arithmetic.ONE *
2180
+ fp64arithmetic.ONE *
2181
+ fp64arithmetic.ONE) +
2182
+ prevent_fp64_optimization(b - v);
2183
+ #else
2184
+ let v = prevent_fp64_optimization(s - a);
2185
+ let err = prevent_fp64_optimization(a - (s - v)) + prevent_fp64_optimization(b - v);
2186
+ #endif
2187
+ return vec2f(s, err);
2188
+ }
2189
+
2190
+ fn twoSub(a: f32, b: f32) -> vec2f {
2191
+ let s = prevent_fp64_optimization(a - b);
2192
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2193
+ let v = prevent_fp64_optimization((s * fp64arithmetic.ONE - a) * fp64arithmetic.ONE);
2194
+ let err =
2195
+ prevent_fp64_optimization((a - (s - v) * fp64arithmetic.ONE) *
2196
+ fp64arithmetic.ONE *
2197
+ fp64arithmetic.ONE *
2198
+ fp64arithmetic.ONE) -
2199
+ prevent_fp64_optimization(b + v);
2200
+ #else
2201
+ let v = prevent_fp64_optimization(s - a);
2202
+ let err = prevent_fp64_optimization(a - (s - v)) - prevent_fp64_optimization(b + v);
2203
+ #endif
2204
+ return vec2f(s, err);
2205
+ }
2206
+
2207
+ fn twoSqr(a: f32) -> vec2f {
2208
+ let prod = prevent_fp64_optimization(a * a);
2209
+ let aFp64 = split(a);
2210
+ let highProduct = prevent_fp64_optimization(aFp64.x * aFp64.x);
2211
+ let crossProduct = prevent_fp64_optimization(2.0 * aFp64.x * aFp64.y);
2212
+ let lowProduct = prevent_fp64_optimization(aFp64.y * aFp64.y);
2213
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2214
+ let err =
2215
+ (prevent_fp64_optimization(highProduct - prod) * fp64arithmetic.ONE +
2216
+ crossProduct * fp64arithmetic.ONE * fp64arithmetic.ONE) +
2217
+ lowProduct * fp64arithmetic.ONE * fp64arithmetic.ONE * fp64arithmetic.ONE;
2218
+ #else
2219
+ let err = ((prevent_fp64_optimization(highProduct - prod) + crossProduct) + lowProduct);
2220
+ #endif
2221
+ return vec2f(prod, err);
2222
+ }
2223
+
2224
+ fn twoProd(a: f32, b: f32) -> vec2f {
2225
+ let prod = prevent_fp64_optimization(a * b);
2226
+ let aFp64 = split(a);
2227
+ let bFp64 = split(b);
2228
+ let highProduct = prevent_fp64_optimization(aFp64.x * bFp64.x);
2229
+ let crossProduct1 = prevent_fp64_optimization(aFp64.x * bFp64.y);
2230
+ let crossProduct2 = prevent_fp64_optimization(aFp64.y * bFp64.x);
2231
+ let lowProduct = prevent_fp64_optimization(aFp64.y * bFp64.y);
2232
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2233
+ let err1 = (highProduct - prod) * fp64arithmetic.ONE;
2234
+ let err2 = crossProduct1 * fp64arithmetic.ONE * fp64arithmetic.ONE;
2235
+ let err3 = crossProduct2 * fp64arithmetic.ONE * fp64arithmetic.ONE * fp64arithmetic.ONE;
2236
+ let err4 =
2237
+ lowProduct *
2238
+ fp64arithmetic.ONE *
2239
+ fp64arithmetic.ONE *
2240
+ fp64arithmetic.ONE *
2241
+ fp64arithmetic.ONE;
2242
+ #else
2243
+ let err1 = highProduct - prod;
2244
+ let err2 = crossProduct1;
2245
+ let err3 = crossProduct2;
2246
+ let err4 = lowProduct;
2247
+ #endif
2248
+ let err12InputA = prevent_fp64_optimization(err1);
2249
+ let err12InputB = prevent_fp64_optimization(err2);
2250
+ let err12 = prevent_fp64_optimization(err12InputA + err12InputB);
2251
+ let err123InputA = prevent_fp64_optimization(err12);
2252
+ let err123InputB = prevent_fp64_optimization(err3);
2253
+ let err123 = prevent_fp64_optimization(err123InputA + err123InputB);
2254
+ let err1234InputA = prevent_fp64_optimization(err123);
2255
+ let err1234InputB = prevent_fp64_optimization(err4);
2256
+ let err = prevent_fp64_optimization(err1234InputA + err1234InputB);
2257
+ return vec2f(prod, err);
2258
+ }
2259
+
2260
+ fn sum_fp64(a: vec2f, b: vec2f) -> vec2f {
2261
+ var s = twoSum(a.x, b.x);
2262
+ let t = twoSum(a.y, b.y);
2263
+ s.y = prevent_fp64_optimization(s.y + t.x);
2264
+ s = quickTwoSum(s.x, s.y);
2265
+ s.y = prevent_fp64_optimization(s.y + t.y);
2266
+ s = quickTwoSum(s.x, s.y);
2267
+ return s;
2268
+ }
2269
+
2270
+ fn sub_fp64(a: vec2f, b: vec2f) -> vec2f {
2271
+ var s = twoSub(a.x, b.x);
2272
+ let t = twoSub(a.y, b.y);
2273
+ s.y = prevent_fp64_optimization(s.y + t.x);
2274
+ s = quickTwoSum(s.x, s.y);
2275
+ s.y = prevent_fp64_optimization(s.y + t.y);
2276
+ s = quickTwoSum(s.x, s.y);
2277
+ return s;
2278
+ }
2279
+
2280
+ fn mul_fp64(a: vec2f, b: vec2f) -> vec2f {
2281
+ var prod = twoProd(a.x, b.x);
2282
+ let crossProduct1 = prevent_fp64_optimization(a.x * b.y);
2283
+ prod.y = prevent_fp64_optimization(prod.y + crossProduct1);
2284
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
2285
+ prod = split2(prod);
2286
+ #endif
2287
+ prod = quickTwoSum(prod.x, prod.y);
2288
+ let crossProduct2 = prevent_fp64_optimization(a.y * b.x);
2289
+ prod.y = prevent_fp64_optimization(prod.y + crossProduct2);
2290
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
2291
+ prod = split2(prod);
2292
+ #endif
2293
+ prod = quickTwoSum(prod.x, prod.y);
2294
+ return prod;
2295
+ }
2296
+
2297
+ fn div_fp64(a: vec2f, b: vec2f) -> vec2f {
2298
+ let xn = prevent_fp64_optimization(1.0 / b.x);
2299
+ let yn = mul_fp64(a, vec2f(xn, fp64_runtime_zero()));
2300
+ let diff = prevent_fp64_optimization(sub_fp64(a, mul_fp64(b, yn)).x);
2301
+ let prod = twoProd(xn, diff);
2302
+ return sum_fp64(yn, prod);
2303
+ }
2304
+
2305
+ fn sqrt_fp64(a: vec2f) -> vec2f {
2306
+ if (a.x == 0.0 && a.y == 0.0) {
2307
+ return vec2f(0.0, 0.0);
2308
+ }
2309
+ if (a.x < 0.0) {
2310
+ let nanValue = fp64_nan(a.x);
2311
+ return vec2f(nanValue, nanValue);
2312
+ }
2313
+
2314
+ let x = prevent_fp64_optimization(1.0 / sqrt(a.x));
2315
+ let yn = prevent_fp64_optimization(a.x * x);
2316
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2317
+ let ynSqr = twoSqr(yn) * fp64arithmetic.ONE;
2318
+ #else
2319
+ let ynSqr = twoSqr(yn);
2320
+ #endif
2321
+ let diff = prevent_fp64_optimization(sub_fp64(a, ynSqr).x);
2322
+ let prod = twoProd(prevent_fp64_optimization(x * 0.5), diff);
2323
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
2324
+ return sum_fp64(split(yn), prod);
2325
+ #else
2326
+ return sum_fp64(vec2f(yn, 0.0), prod);
2327
+ #endif
2328
+ }
2329
+ `
2330
+ );
2331
+
1605
2332
  // dist/modules/math/fp64/fp64-functions-glsl.js
1606
2333
  var fp64functionShader = (
1607
2334
  /* glsl */
@@ -2280,13 +3007,18 @@ void mat4_vec4_mul_fp64(vec2 b[16], vec2 a[4], out vec2 out_val[4]) {
2280
3007
  // dist/modules/math/fp64/fp64.js
2281
3008
  var defaultUniforms = {
2282
3009
  // Used in LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2283
- ONE: 1
3010
+ ONE: 1,
3011
+ // Runtime split factor for Dekker splitting. Keeping this as a uniform helps
3012
+ // prevent aggressive constant folding in shader compilers.
3013
+ SPLIT: 4097
2284
3014
  };
2285
3015
  var fp64arithmetic = {
2286
3016
  name: "fp64arithmetic",
3017
+ source: fp64arithmeticWGSL,
3018
+ fs: fp64arithmeticShader,
2287
3019
  vs: fp64arithmeticShader,
2288
3020
  defaultUniforms,
2289
- uniformTypes: { ONE: "f32" },
3021
+ uniformTypes: { ONE: "f32", SPLIT: "f32" },
2290
3022
  // Additional Functions
2291
3023
  fp64ify,
2292
3024
  fp64LowPart,
@@ -2490,8 +3222,100 @@ function getUniforms(opts = {}, prevUniforms) {
2490
3222
  return uniforms;
2491
3223
  }
2492
3224
 
3225
+ // dist/modules/engine/skin/skin.js
3226
+ var import_core2 = require("@math.gl/core");
3227
+ var SKIN_MAX_JOINTS = 20;
3228
+ var source2 = (
3229
+ /* wgsl */
3230
+ `
3231
+ struct skinUniforms {
3232
+ jointMatrix: array<mat4x4<f32>, ${SKIN_MAX_JOINTS}>,
3233
+ };
3234
+
3235
+ @group(0) @binding(auto) var<uniform> skin: skinUniforms;
3236
+
3237
+ fn getSkinMatrix(weights: vec4f, joints: vec4u) -> mat4x4<f32> {
3238
+ return (weights.x * skin.jointMatrix[joints.x])
3239
+ + (weights.y * skin.jointMatrix[joints.y])
3240
+ + (weights.z * skin.jointMatrix[joints.z])
3241
+ + (weights.w * skin.jointMatrix[joints.w]);
3242
+ }
3243
+ `
3244
+ );
3245
+ var vs2 = (
3246
+ /* glsl */
3247
+ `
3248
+ uniform skinUniforms {
3249
+ mat4 jointMatrix[SKIN_MAX_JOINTS];
3250
+ } skin;
3251
+
3252
+ mat4 getSkinMatrix(vec4 weights, uvec4 joints) {
3253
+ return (weights.x * skin.jointMatrix[joints.x])
3254
+ + (weights.y * skin.jointMatrix[joints.y])
3255
+ + (weights.z * skin.jointMatrix[joints.z])
3256
+ + (weights.w * skin.jointMatrix[joints.w]);
3257
+ }
3258
+
3259
+ `
3260
+ );
3261
+ var fs3 = (
3262
+ /* glsl */
3263
+ ``
3264
+ );
3265
+ var skin = {
3266
+ props: {},
3267
+ uniforms: {},
3268
+ name: "skin",
3269
+ bindingLayout: [{ name: "skin", group: 0 }],
3270
+ dependencies: [],
3271
+ source: source2,
3272
+ vs: vs2,
3273
+ fs: fs3,
3274
+ defines: {
3275
+ SKIN_MAX_JOINTS
3276
+ },
3277
+ getUniforms: (props = {}, prevUniforms) => {
3278
+ var _a, _b;
3279
+ const { scenegraphsFromGLTF } = props;
3280
+ if (!((_b = (_a = scenegraphsFromGLTF == null ? void 0 : scenegraphsFromGLTF.gltf) == null ? void 0 : _a.skins) == null ? void 0 : _b[0])) {
3281
+ return { jointMatrix: [] };
3282
+ }
3283
+ const { inverseBindMatrices, joints, skeleton } = scenegraphsFromGLTF.gltf.skins[0];
3284
+ const matsib = [];
3285
+ const countib = inverseBindMatrices.value.length / 16;
3286
+ for (let i = 0; i < countib; i++) {
3287
+ const slice = inverseBindMatrices.value.subarray(i * 16, i * 16 + 16);
3288
+ matsib.push(new import_core2.Matrix4(Array.from(slice)));
3289
+ }
3290
+ const top = scenegraphsFromGLTF.gltfNodeIndexToNodeMap.get(skeleton);
3291
+ const matrices = {};
3292
+ top.preorderTraversal((node, { worldMatrix }) => {
3293
+ matrices[node.id] = worldMatrix;
3294
+ });
3295
+ const mats = new Float32Array(SKIN_MAX_JOINTS * 16);
3296
+ for (let i = 0; i < SKIN_MAX_JOINTS; ++i) {
3297
+ const nodeIndex = joints[i];
3298
+ if (nodeIndex === void 0)
3299
+ break;
3300
+ const worldMat = matrices[scenegraphsFromGLTF.gltfNodeIndexToNodeMap.get(nodeIndex).id];
3301
+ const invBindMat = matsib[i];
3302
+ const Z = new import_core2.Matrix4().copy(worldMat).multiplyRight(invBindMat);
3303
+ const off = i * 16;
3304
+ for (let j = 0; j < 16; j++) {
3305
+ mats[off + j] = Z[j];
3306
+ }
3307
+ }
3308
+ return {
3309
+ jointMatrix: mats
3310
+ };
3311
+ },
3312
+ uniformTypes: {
3313
+ jointMatrix: ["mat4x4<f32>", SKIN_MAX_JOINTS]
3314
+ }
3315
+ };
3316
+
2493
3317
  // dist/modules/lighting/lights/lighting.js
2494
- var import_core2 = require("@luma.gl/core");
3318
+ var import_core3 = require("@luma.gl/core");
2495
3319
 
2496
3320
  // dist/modules/lighting/lights/lighting-glsl.js
2497
3321
  var lightingUniformsGLSL = (
@@ -2509,59 +3333,51 @@ struct PointLight {
2509
3333
  vec3 attenuation; // 2nd order x:Constant-y:Linear-z:Exponential
2510
3334
  };
2511
3335
 
3336
+ struct SpotLight {
3337
+ vec3 color;
3338
+ vec3 position;
3339
+ vec3 direction;
3340
+ vec3 attenuation;
3341
+ vec2 coneCos;
3342
+ };
3343
+
2512
3344
  struct DirectionalLight {
2513
3345
  vec3 color;
2514
3346
  vec3 direction;
2515
3347
  };
2516
3348
 
3349
+ struct UniformLight {
3350
+ vec3 color;
3351
+ vec3 position;
3352
+ vec3 direction;
3353
+ vec3 attenuation;
3354
+ vec2 coneCos;
3355
+ };
3356
+
2517
3357
  uniform lightingUniforms {
2518
3358
  int enabled;
2519
- int lightType;
2520
-
2521
3359
  int directionalLightCount;
2522
3360
  int pointLightCount;
2523
-
3361
+ int spotLightCount;
2524
3362
  vec3 ambientColor;
2525
-
2526
- vec3 lightColor0;
2527
- vec3 lightPosition0;
2528
- vec3 lightDirection0;
2529
- vec3 lightAttenuation0;
2530
-
2531
- vec3 lightColor1;
2532
- vec3 lightPosition1;
2533
- vec3 lightDirection1;
2534
- vec3 lightAttenuation1;
2535
-
2536
- vec3 lightColor2;
2537
- vec3 lightPosition2;
2538
- vec3 lightDirection2;
2539
- vec3 lightAttenuation2;
3363
+ UniformLight lights[5];
2540
3364
  } lighting;
2541
3365
 
2542
3366
  PointLight lighting_getPointLight(int index) {
2543
- switch (index) {
2544
- case 0:
2545
- return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0);
2546
- case 1:
2547
- return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1);
2548
- case 2:
2549
- default:
2550
- return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2);
2551
- }
3367
+ UniformLight light = lighting.lights[index];
3368
+ return PointLight(light.color, light.position, light.attenuation);
3369
+ }
3370
+
3371
+ SpotLight lighting_getSpotLight(int index) {
3372
+ UniformLight light = lighting.lights[lighting.pointLightCount + index];
3373
+ return SpotLight(light.color, light.position, light.direction, light.attenuation, light.coneCos);
2552
3374
  }
2553
3375
 
2554
3376
  DirectionalLight lighting_getDirectionalLight(int index) {
2555
- switch (index) {
2556
- case 0:
2557
- return DirectionalLight(lighting.lightColor0, lighting.lightDirection0);
2558
- case 1:
2559
- return DirectionalLight(lighting.lightColor1, lighting.lightDirection1);
2560
- case 2:
2561
- default:
2562
- return DirectionalLight(lighting.lightColor2, lighting.lightDirection2);
2563
- }
2564
- }
3377
+ UniformLight light =
3378
+ lighting.lights[lighting.pointLightCount + lighting.spotLightCount + index];
3379
+ return DirectionalLight(light.color, light.direction);
3380
+ }
2565
3381
 
2566
3382
  float getPointLightAttenuation(PointLight pointLight, float distance) {
2567
3383
  return pointLight.attenuation.x
@@ -2569,6 +3385,20 @@ float getPointLightAttenuation(PointLight pointLight, float distance) {
2569
3385
  + pointLight.attenuation.z * distance * distance;
2570
3386
  }
2571
3387
 
3388
+ float getSpotLightAttenuation(SpotLight spotLight, vec3 positionWorldspace) {
3389
+ vec3 light_direction = normalize(positionWorldspace - spotLight.position);
3390
+ float coneFactor = smoothstep(
3391
+ spotLight.coneCos.y,
3392
+ spotLight.coneCos.x,
3393
+ dot(normalize(spotLight.direction), light_direction)
3394
+ );
3395
+ float distanceAttenuation = getPointLightAttenuation(
3396
+ PointLight(spotLight.color, spotLight.position, spotLight.attenuation),
3397
+ distance(spotLight.position, positionWorldspace)
3398
+ );
3399
+ return distanceAttenuation / max(coneFactor, 0.0001);
3400
+ }
3401
+
2572
3402
  // #endif
2573
3403
  `
2574
3404
  );
@@ -2577,6 +3407,8 @@ float getPointLightAttenuation(PointLight pointLight, float distance) {
2577
3407
  var lightingUniformsWGSL = (
2578
3408
  /* wgsl */
2579
3409
  `// #if (defined(SHADER_TYPE_FRAGMENT) && defined(LIGHTING_FRAGMENT)) || (defined(SHADER_TYPE_VERTEX) && defined(LIGHTING_VERTEX))
3410
+ const MAX_LIGHTS: i32 = 5;
3411
+
2580
3412
  struct AmbientLight {
2581
3413
  color: vec3<f32>,
2582
3414
  };
@@ -2587,57 +3419,85 @@ struct PointLight {
2587
3419
  attenuation: vec3<f32>, // 2nd order x:Constant-y:Linear-z:Exponential
2588
3420
  };
2589
3421
 
3422
+ struct SpotLight {
3423
+ color: vec3<f32>,
3424
+ position: vec3<f32>,
3425
+ direction: vec3<f32>,
3426
+ attenuation: vec3<f32>,
3427
+ coneCos: vec2<f32>,
3428
+ };
3429
+
2590
3430
  struct DirectionalLight {
2591
3431
  color: vec3<f32>,
2592
3432
  direction: vec3<f32>,
2593
3433
  };
2594
3434
 
3435
+ struct UniformLight {
3436
+ color: vec3<f32>,
3437
+ position: vec3<f32>,
3438
+ direction: vec3<f32>,
3439
+ attenuation: vec3<f32>,
3440
+ coneCos: vec2<f32>,
3441
+ };
3442
+
2595
3443
  struct lightingUniforms {
2596
3444
  enabled: i32,
2597
- pointLightCount: i32,
2598
3445
  directionalLightCount: i32,
2599
-
3446
+ pointLightCount: i32,
3447
+ spotLightCount: i32,
2600
3448
  ambientColor: vec3<f32>,
2601
-
2602
- // TODO - support multiple lights by uncommenting arrays below
2603
- lightType: i32,
2604
- lightColor: vec3<f32>,
2605
- lightDirection: vec3<f32>,
2606
- lightPosition: vec3<f32>,
2607
- lightAttenuation: vec3<f32>,
2608
-
2609
- // AmbientLight ambientLight;
2610
- // PointLight pointLight[MAX_LIGHTS];
2611
- // DirectionalLight directionalLight[MAX_LIGHTS];
3449
+ lights: array<UniformLight, 5>,
2612
3450
  };
2613
3451
 
2614
- // Binding 0:1 is reserved for lighting (Note: could go into separate bind group as it is stable across draw calls)
2615
- @binding(1) @group(0) var<uniform> lighting : lightingUniforms;
3452
+ @group(2) @binding(auto) var<uniform> lighting : lightingUniforms;
2616
3453
 
2617
3454
  fn lighting_getPointLight(index: i32) -> PointLight {
2618
- return PointLight(lighting.lightColor, lighting.lightPosition, lighting.lightAttenuation);
3455
+ let light = lighting.lights[index];
3456
+ return PointLight(light.color, light.position, light.attenuation);
3457
+ }
3458
+
3459
+ fn lighting_getSpotLight(index: i32) -> SpotLight {
3460
+ let light = lighting.lights[lighting.pointLightCount + index];
3461
+ return SpotLight(light.color, light.position, light.direction, light.attenuation, light.coneCos);
2619
3462
  }
2620
3463
 
2621
3464
  fn lighting_getDirectionalLight(index: i32) -> DirectionalLight {
2622
- return DirectionalLight(lighting.lightColor, lighting.lightDirection);
2623
- }
3465
+ let light = lighting.lights[lighting.pointLightCount + lighting.spotLightCount + index];
3466
+ return DirectionalLight(light.color, light.direction);
3467
+ }
2624
3468
 
2625
3469
  fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
2626
3470
  return pointLight.attenuation.x
2627
3471
  + pointLight.attenuation.y * distance
2628
3472
  + pointLight.attenuation.z * distance * distance;
2629
3473
  }
3474
+
3475
+ fn getSpotLightAttenuation(spotLight: SpotLight, positionWorldspace: vec3<f32>) -> f32 {
3476
+ let lightDirection = normalize(positionWorldspace - spotLight.position);
3477
+ let coneFactor = smoothstep(
3478
+ spotLight.coneCos.y,
3479
+ spotLight.coneCos.x,
3480
+ dot(normalize(spotLight.direction), lightDirection)
3481
+ );
3482
+ let distanceAttenuation = getPointLightAttenuation(
3483
+ PointLight(spotLight.color, spotLight.position, spotLight.attenuation),
3484
+ distance(spotLight.position, positionWorldspace)
3485
+ );
3486
+ return distanceAttenuation / max(coneFactor, 0.0001);
3487
+ }
2630
3488
  `
2631
3489
  );
2632
3490
 
2633
3491
  // dist/modules/lighting/lights/lighting.js
2634
3492
  var MAX_LIGHTS = 5;
2635
3493
  var COLOR_FACTOR = 255;
2636
- var LIGHT_TYPE;
2637
- (function(LIGHT_TYPE2) {
2638
- LIGHT_TYPE2[LIGHT_TYPE2["POINT"] = 0] = "POINT";
2639
- LIGHT_TYPE2[LIGHT_TYPE2["DIRECTIONAL"] = 1] = "DIRECTIONAL";
2640
- })(LIGHT_TYPE || (LIGHT_TYPE = {}));
3494
+ var LIGHT_UNIFORM_TYPE = {
3495
+ color: "vec3<f32>",
3496
+ position: "vec3<f32>",
3497
+ direction: "vec3<f32>",
3498
+ attenuation: "vec3<f32>",
3499
+ coneCos: "vec2<f32>"
3500
+ };
2641
3501
  var lighting = {
2642
3502
  props: {},
2643
3503
  uniforms: {},
@@ -2647,102 +3507,105 @@ var lighting = {
2647
3507
  },
2648
3508
  uniformTypes: {
2649
3509
  enabled: "i32",
2650
- lightType: "i32",
2651
3510
  directionalLightCount: "i32",
2652
3511
  pointLightCount: "i32",
3512
+ spotLightCount: "i32",
2653
3513
  ambientColor: "vec3<f32>",
2654
- // TODO define as arrays once we have appropriate uniformTypes
2655
- lightColor0: "vec3<f32>",
2656
- lightPosition0: "vec3<f32>",
2657
- // TODO - could combine direction and attenuation
2658
- lightDirection0: "vec3<f32>",
2659
- lightAttenuation0: "vec3<f32>",
2660
- lightColor1: "vec3<f32>",
2661
- lightPosition1: "vec3<f32>",
2662
- lightDirection1: "vec3<f32>",
2663
- lightAttenuation1: "vec3<f32>",
2664
- lightColor2: "vec3<f32>",
2665
- lightPosition2: "vec3<f32>",
2666
- lightDirection2: "vec3<f32>",
2667
- lightAttenuation2: "vec3<f32>"
2668
- },
2669
- defaultUniforms: {
2670
- enabled: 1,
2671
- lightType: LIGHT_TYPE.POINT,
2672
- directionalLightCount: 0,
2673
- pointLightCount: 0,
2674
- ambientColor: [0.1, 0.1, 0.1],
2675
- lightColor0: [1, 1, 1],
2676
- lightPosition0: [1, 1, 2],
2677
- // TODO - could combine direction and attenuation
2678
- lightDirection0: [1, 1, 1],
2679
- lightAttenuation0: [1, 0, 0],
2680
- lightColor1: [1, 1, 1],
2681
- lightPosition1: [1, 1, 2],
2682
- lightDirection1: [1, 1, 1],
2683
- lightAttenuation1: [1, 0, 0],
2684
- lightColor2: [1, 1, 1],
2685
- lightPosition2: [1, 1, 2],
2686
- lightDirection2: [1, 1, 1],
2687
- lightAttenuation2: [1, 0, 0]
3514
+ lights: [LIGHT_UNIFORM_TYPE, MAX_LIGHTS]
2688
3515
  },
3516
+ defaultUniforms: createDefaultLightingUniforms(),
3517
+ bindingLayout: [{ name: "lighting", group: 2 }],
3518
+ firstBindingSlot: 0,
2689
3519
  source: lightingUniformsWGSL,
2690
3520
  vs: lightingUniformsGLSL,
2691
3521
  fs: lightingUniformsGLSL,
2692
3522
  getUniforms: getUniforms2
2693
3523
  };
2694
- function getUniforms2(props, prevUniforms = {}) {
3524
+ function getUniforms2(props, _prevUniforms = {}) {
2695
3525
  props = props ? { ...props } : props;
2696
3526
  if (!props) {
2697
- return { ...lighting.defaultUniforms };
3527
+ return createDefaultLightingUniforms();
2698
3528
  }
2699
3529
  if (props.lights) {
2700
3530
  props = { ...props, ...extractLightTypes(props.lights), lights: void 0 };
2701
3531
  }
2702
- const { ambientLight, pointLights, directionalLights } = props || {};
2703
- const hasLights = ambientLight || pointLights && pointLights.length > 0 || directionalLights && directionalLights.length > 0;
3532
+ const { ambientLight, pointLights, spotLights, directionalLights } = props || {};
3533
+ const hasLights = ambientLight || pointLights && pointLights.length > 0 || spotLights && spotLights.length > 0 || directionalLights && directionalLights.length > 0;
2704
3534
  if (!hasLights) {
2705
- return { ...lighting.defaultUniforms, enabled: 0 };
3535
+ return {
3536
+ ...createDefaultLightingUniforms(),
3537
+ enabled: 0
3538
+ };
2706
3539
  }
2707
3540
  const uniforms = {
2708
- ...lighting.defaultUniforms,
2709
- ...prevUniforms,
2710
- ...getLightSourceUniforms({ ambientLight, pointLights, directionalLights })
3541
+ ...createDefaultLightingUniforms(),
3542
+ ...getLightSourceUniforms({ ambientLight, pointLights, spotLights, directionalLights })
2711
3543
  };
2712
3544
  if (props.enabled !== void 0) {
2713
3545
  uniforms.enabled = props.enabled ? 1 : 0;
2714
3546
  }
2715
3547
  return uniforms;
2716
3548
  }
2717
- function getLightSourceUniforms({ ambientLight, pointLights = [], directionalLights = [] }) {
2718
- const lightSourceUniforms = {};
2719
- lightSourceUniforms.ambientColor = convertColor(ambientLight);
3549
+ function getLightSourceUniforms({ ambientLight, pointLights = [], spotLights = [], directionalLights = [] }) {
3550
+ const lights = createDefaultLightUniforms();
2720
3551
  let currentLight = 0;
3552
+ let pointLightCount = 0;
3553
+ let spotLightCount = 0;
3554
+ let directionalLightCount = 0;
2721
3555
  for (const pointLight of pointLights) {
2722
- lightSourceUniforms.lightType = LIGHT_TYPE.POINT;
2723
- const i = currentLight;
2724
- lightSourceUniforms[`lightColor${i}`] = convertColor(pointLight);
2725
- lightSourceUniforms[`lightPosition${i}`] = pointLight.position;
2726
- lightSourceUniforms[`lightAttenuation${i}`] = pointLight.attenuation || [1, 0, 0];
3556
+ if (currentLight >= MAX_LIGHTS) {
3557
+ break;
3558
+ }
3559
+ lights[currentLight] = {
3560
+ ...lights[currentLight],
3561
+ color: convertColor(pointLight),
3562
+ position: pointLight.position,
3563
+ attenuation: pointLight.attenuation || [1, 0, 0]
3564
+ };
3565
+ currentLight++;
3566
+ pointLightCount++;
3567
+ }
3568
+ for (const spotLight of spotLights) {
3569
+ if (currentLight >= MAX_LIGHTS) {
3570
+ break;
3571
+ }
3572
+ lights[currentLight] = {
3573
+ ...lights[currentLight],
3574
+ color: convertColor(spotLight),
3575
+ position: spotLight.position,
3576
+ direction: spotLight.direction,
3577
+ attenuation: spotLight.attenuation || [1, 0, 0],
3578
+ coneCos: getSpotConeCos(spotLight)
3579
+ };
2727
3580
  currentLight++;
3581
+ spotLightCount++;
2728
3582
  }
2729
3583
  for (const directionalLight of directionalLights) {
2730
- lightSourceUniforms.lightType = LIGHT_TYPE.DIRECTIONAL;
2731
- const i = currentLight;
2732
- lightSourceUniforms[`lightColor${i}`] = convertColor(directionalLight);
2733
- lightSourceUniforms[`lightDirection${i}`] = directionalLight.direction;
3584
+ if (currentLight >= MAX_LIGHTS) {
3585
+ break;
3586
+ }
3587
+ lights[currentLight] = {
3588
+ ...lights[currentLight],
3589
+ color: convertColor(directionalLight),
3590
+ direction: directionalLight.direction
3591
+ };
2734
3592
  currentLight++;
3593
+ directionalLightCount++;
2735
3594
  }
2736
- if (currentLight > MAX_LIGHTS) {
2737
- import_core2.log.warn("MAX_LIGHTS exceeded")();
3595
+ if (pointLights.length + spotLights.length + directionalLights.length > MAX_LIGHTS) {
3596
+ import_core3.log.warn(`MAX_LIGHTS exceeded, truncating to ${MAX_LIGHTS}`)();
2738
3597
  }
2739
- lightSourceUniforms.directionalLightCount = directionalLights.length;
2740
- lightSourceUniforms.pointLightCount = pointLights.length;
2741
- return lightSourceUniforms;
3598
+ return {
3599
+ ambientColor: convertColor(ambientLight),
3600
+ directionalLightCount,
3601
+ pointLightCount,
3602
+ spotLightCount,
3603
+ lights
3604
+ };
2742
3605
  }
2743
3606
  function extractLightTypes(lights) {
2744
- var _a, _b;
2745
- const lightSources = { pointLights: [], directionalLights: [] };
3607
+ var _a, _b, _c;
3608
+ const lightSources = { pointLights: [], spotLights: [], directionalLights: [] };
2746
3609
  for (const light of lights || []) {
2747
3610
  switch (light.type) {
2748
3611
  case "ambient":
@@ -2754,6 +3617,9 @@ function extractLightTypes(lights) {
2754
3617
  case "point":
2755
3618
  (_b = lightSources.pointLights) == null ? void 0 : _b.push(light);
2756
3619
  break;
3620
+ case "spot":
3621
+ (_c = lightSources.spotLights) == null ? void 0 : _c.push(light);
3622
+ break;
2757
3623
  default:
2758
3624
  }
2759
3625
  }
@@ -2763,6 +3629,68 @@ function convertColor(colorDef = {}) {
2763
3629
  const { color = [0, 0, 0], intensity = 1 } = colorDef;
2764
3630
  return color.map((component) => component * intensity / COLOR_FACTOR);
2765
3631
  }
3632
+ function createDefaultLightingUniforms() {
3633
+ return {
3634
+ enabled: 1,
3635
+ directionalLightCount: 0,
3636
+ pointLightCount: 0,
3637
+ spotLightCount: 0,
3638
+ ambientColor: [0.1, 0.1, 0.1],
3639
+ lights: createDefaultLightUniforms()
3640
+ };
3641
+ }
3642
+ function createDefaultLightUniforms() {
3643
+ return Array.from({ length: MAX_LIGHTS }, () => createDefaultLightUniform());
3644
+ }
3645
+ function createDefaultLightUniform() {
3646
+ return {
3647
+ color: [1, 1, 1],
3648
+ position: [1, 1, 2],
3649
+ direction: [1, 1, 1],
3650
+ attenuation: [1, 0, 0],
3651
+ coneCos: [1, 0]
3652
+ };
3653
+ }
3654
+ function getSpotConeCos(spotLight) {
3655
+ const innerConeAngle = spotLight.innerConeAngle ?? 0;
3656
+ const outerConeAngle = spotLight.outerConeAngle ?? Math.PI / 4;
3657
+ return [Math.cos(innerConeAngle), Math.cos(outerConeAngle)];
3658
+ }
3659
+
3660
+ // dist/modules/lighting/ibl/ibl.js
3661
+ var iblWGSL = (
3662
+ /* wgsl */
3663
+ `#ifdef USE_IBL
3664
+ @group(2) @binding(auto) var pbr_diffuseEnvSampler: texture_cube<f32>;
3665
+ @group(2) @binding(auto) var pbr_diffuseEnvSamplerSampler: sampler;
3666
+ @group(2) @binding(auto) var pbr_specularEnvSampler: texture_cube<f32>;
3667
+ @group(2) @binding(auto) var pbr_specularEnvSamplerSampler: sampler;
3668
+ @group(2) @binding(auto) var pbr_brdfLUT: texture_2d<f32>;
3669
+ @group(2) @binding(auto) var pbr_brdfLUTSampler: sampler;
3670
+ #endif
3671
+ `
3672
+ );
3673
+ var iblGLSL = (
3674
+ /* glsl */
3675
+ `#ifdef USE_IBL
3676
+ uniform samplerCube pbr_diffuseEnvSampler;
3677
+ uniform samplerCube pbr_specularEnvSampler;
3678
+ uniform sampler2D pbr_brdfLUT;
3679
+ #endif
3680
+ `
3681
+ );
3682
+ var ibl = {
3683
+ name: "ibl",
3684
+ firstBindingSlot: 32,
3685
+ bindingLayout: [
3686
+ { name: "pbr_diffuseEnvSampler", group: 2 },
3687
+ { name: "pbr_specularEnvSampler", group: 2 },
3688
+ { name: "pbr_brdfLUT", group: 2 }
3689
+ ],
3690
+ source: iblWGSL,
3691
+ vs: iblGLSL,
3692
+ fs: iblGLSL
3693
+ };
2766
3694
 
2767
3695
  // dist/modules/lighting/no-material/dirlight.js
2768
3696
  var SOURCE_WGSL = (
@@ -2778,7 +3706,7 @@ struct DirlightInputs {
2778
3706
  normal: DirlightNormal,
2779
3707
  };
2780
3708
 
2781
- @binding(1) @group(0) var<uniform> dirlight : dirlightUniforms;
3709
+ @group(2) @binding(auto) var<uniform> dirlight : dirlightUniforms;
2782
3710
 
2783
3711
  // For vertex
2784
3712
  fn dirlight_setNormal(normal: vec3<f32>) -> DirlightNormal {
@@ -2823,6 +3751,8 @@ var dirlight = {
2823
3751
  props: {},
2824
3752
  uniforms: {},
2825
3753
  name: "dirlight",
3754
+ bindingLayout: [{ name: "dirlight", group: 2 }],
3755
+ firstBindingSlot: 16,
2826
3756
  dependencies: [],
2827
3757
  source: SOURCE_WGSL,
2828
3758
  vs: VS_GLSL,
@@ -2849,10 +3779,173 @@ function getUniforms3(opts = dirlight.defaultUniforms) {
2849
3779
  return uniforms;
2850
3780
  }
2851
3781
 
3782
+ // dist/modules/lighting/lambert-material/lambert-shaders-wgsl.js
3783
+ var LAMBERT_WGSL = (
3784
+ /* wgsl */
3785
+ `struct lambertMaterialUniforms {
3786
+ unlit: u32,
3787
+ ambient: f32,
3788
+ diffuse: f32,
3789
+ };
3790
+
3791
+ @group(3) @binding(auto) var<uniform> lambertMaterial : lambertMaterialUniforms;
3792
+
3793
+ fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, normal_worldspace: vec3<f32>, color: vec3<f32>) -> vec3<f32> {
3794
+ let lambertian: f32 = max(dot(light_direction, normal_worldspace), 0.0);
3795
+ return lambertian * lambertMaterial.diffuse * surfaceColor * color;
3796
+ }
3797
+
3798
+ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, position_worldspace: vec3<f32>, normal_worldspace: vec3<f32>) -> vec3<f32> {
3799
+ var lightColor: vec3<f32> = surfaceColor;
3800
+
3801
+ if (lambertMaterial.unlit != 0u) {
3802
+ return surfaceColor;
3803
+ }
3804
+
3805
+ if (lighting.enabled == 0) {
3806
+ return lightColor;
3807
+ }
3808
+
3809
+ lightColor = lambertMaterial.ambient * surfaceColor * lighting.ambientColor;
3810
+
3811
+ for (var i: i32 = 0; i < lighting.pointLightCount; i++) {
3812
+ let pointLight: PointLight = lighting_getPointLight(i);
3813
+ let light_position_worldspace: vec3<f32> = pointLight.position;
3814
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
3815
+ let light_attenuation = getPointLightAttenuation(
3816
+ pointLight,
3817
+ distance(light_position_worldspace, position_worldspace)
3818
+ );
3819
+ lightColor += lighting_getLightColor(
3820
+ surfaceColor,
3821
+ light_direction,
3822
+ normal_worldspace,
3823
+ pointLight.color / light_attenuation
3824
+ );
3825
+ }
3826
+
3827
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
3828
+ let spotLight: SpotLight = lighting_getSpotLight(i);
3829
+ let light_position_worldspace: vec3<f32> = spotLight.position;
3830
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
3831
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
3832
+ lightColor += lighting_getLightColor(
3833
+ surfaceColor,
3834
+ light_direction,
3835
+ normal_worldspace,
3836
+ spotLight.color / light_attenuation
3837
+ );
3838
+ }
3839
+
3840
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
3841
+ let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
3842
+ lightColor += lighting_getLightColor(
3843
+ surfaceColor,
3844
+ -directionalLight.direction,
3845
+ normal_worldspace,
3846
+ directionalLight.color
3847
+ );
3848
+ }
3849
+
3850
+ return lightColor;
3851
+ }
3852
+ `
3853
+ );
3854
+
3855
+ // dist/modules/lighting/lambert-material/lambert-shaders-glsl.js
3856
+ var LAMBERT_VS = (
3857
+ /* glsl */
3858
+ `uniform lambertMaterialUniforms {
3859
+ uniform bool unlit;
3860
+ uniform float ambient;
3861
+ uniform float diffuse;
3862
+ } material;
3863
+ `
3864
+ );
3865
+ var LAMBERT_FS = (
3866
+ /* glsl */
3867
+ `uniform lambertMaterialUniforms {
3868
+ uniform bool unlit;
3869
+ uniform float ambient;
3870
+ uniform float diffuse;
3871
+ } material;
3872
+
3873
+ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 normal_worldspace, vec3 color) {
3874
+ float lambertian = max(dot(light_direction, normal_worldspace), 0.0);
3875
+ return lambertian * material.diffuse * surfaceColor * color;
3876
+ }
3877
+
3878
+ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
3879
+ vec3 lightColor = surfaceColor;
3880
+
3881
+ if (material.unlit) {
3882
+ return surfaceColor;
3883
+ }
3884
+
3885
+ if (lighting.enabled == 0) {
3886
+ return lightColor;
3887
+ }
3888
+
3889
+ lightColor = material.ambient * surfaceColor * lighting.ambientColor;
3890
+
3891
+ for (int i = 0; i < lighting.pointLightCount; i++) {
3892
+ PointLight pointLight = lighting_getPointLight(i);
3893
+ vec3 light_position_worldspace = pointLight.position;
3894
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
3895
+ float light_attenuation = getPointLightAttenuation(pointLight, distance(light_position_worldspace, position_worldspace));
3896
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, normal_worldspace, pointLight.color / light_attenuation);
3897
+ }
3898
+
3899
+ for (int i = 0; i < lighting.spotLightCount; i++) {
3900
+ SpotLight spotLight = lighting_getSpotLight(i);
3901
+ vec3 light_position_worldspace = spotLight.position;
3902
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
3903
+ float light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
3904
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, normal_worldspace, spotLight.color / light_attenuation);
3905
+ }
3906
+
3907
+ for (int i = 0; i < lighting.directionalLightCount; i++) {
3908
+ DirectionalLight directionalLight = lighting_getDirectionalLight(i);
3909
+ lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, normal_worldspace, directionalLight.color);
3910
+ }
3911
+
3912
+ return lightColor;
3913
+ }
3914
+ `
3915
+ );
3916
+
3917
+ // dist/modules/lighting/lambert-material/lambert-material.js
3918
+ var lambertMaterial = {
3919
+ name: "lambertMaterial",
3920
+ firstBindingSlot: 0,
3921
+ bindingLayout: [{ name: "lambertMaterial", group: 3 }],
3922
+ dependencies: [lighting],
3923
+ source: LAMBERT_WGSL,
3924
+ vs: LAMBERT_VS,
3925
+ fs: LAMBERT_FS,
3926
+ defines: {
3927
+ LIGHTING_FRAGMENT: true
3928
+ },
3929
+ uniformTypes: {
3930
+ unlit: "i32",
3931
+ ambient: "f32",
3932
+ diffuse: "f32"
3933
+ },
3934
+ defaultUniforms: {
3935
+ unlit: false,
3936
+ ambient: 0.35,
3937
+ diffuse: 0.6
3938
+ },
3939
+ getUniforms(props) {
3940
+ return { ...lambertMaterial.defaultUniforms, ...props };
3941
+ }
3942
+ };
3943
+
2852
3944
  // dist/modules/lighting/phong-material/phong-shaders-glsl.js
2853
3945
  var PHONG_VS = (
2854
3946
  /* glsl */
2855
3947
  `uniform phongMaterialUniforms {
3948
+ uniform bool unlit;
2856
3949
  uniform float ambient;
2857
3950
  uniform float diffuse;
2858
3951
  uniform float shininess;
@@ -2862,9 +3955,8 @@ var PHONG_VS = (
2862
3955
  );
2863
3956
  var PHONG_FS = (
2864
3957
  /* glsl */
2865
- `#define MAX_LIGHTS 3
2866
-
2867
- uniform phongMaterialUniforms {
3958
+ `uniform phongMaterialUniforms {
3959
+ uniform bool unlit;
2868
3960
  uniform float ambient;
2869
3961
  uniform float diffuse;
2870
3962
  uniform float shininess;
@@ -2886,6 +3978,10 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 view_d
2886
3978
  vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
2887
3979
  vec3 lightColor = surfaceColor;
2888
3980
 
3981
+ if (material.unlit) {
3982
+ return surfaceColor;
3983
+ }
3984
+
2889
3985
  if (lighting.enabled == 0) {
2890
3986
  return lightColor;
2891
3987
  }
@@ -2901,8 +3997,15 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positio
2901
3997
  lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color / light_attenuation);
2902
3998
  }
2903
3999
 
2904
- int totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
2905
- for (int i = lighting.pointLightCount; i < totalLights; i++) {
4000
+ for (int i = 0; i < lighting.spotLightCount; i++) {
4001
+ SpotLight spotLight = lighting_getSpotLight(i);
4002
+ vec3 light_position_worldspace = spotLight.position;
4003
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
4004
+ float light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
4005
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, spotLight.color / light_attenuation);
4006
+ }
4007
+
4008
+ for (int i = 0; i < lighting.directionalLightCount; i++) {
2906
4009
  DirectionalLight directionalLight = lighting_getDirectionalLight(i);
2907
4010
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
2908
4011
  }
@@ -2916,13 +4019,14 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positio
2916
4019
  var PHONG_WGSL = (
2917
4020
  /* wgsl */
2918
4021
  `struct phongMaterialUniforms {
4022
+ unlit: u32,
2919
4023
  ambient: f32,
2920
4024
  diffuse: f32,
2921
4025
  shininess: f32,
2922
4026
  specularColor: vec3<f32>,
2923
4027
  };
2924
4028
 
2925
- @binding(2) @group(0) var<uniform> phongMaterial : phongMaterialUniforms;
4029
+ @group(3) @binding(auto) var<uniform> phongMaterial : phongMaterialUniforms;
2926
4030
 
2927
4031
  fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, view_direction: vec3<f32>, normal_worldspace: vec3<f32>, color: vec3<f32>) -> vec3<f32> {
2928
4032
  let halfway_direction: vec3<f32> = normalize(light_direction + view_direction);
@@ -2939,6 +4043,10 @@ fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, v
2939
4043
  fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, position_worldspace: vec3<f32>, normal_worldspace: vec3<f32>) -> vec3<f32> {
2940
4044
  var lightColor: vec3<f32> = surfaceColor;
2941
4045
 
4046
+ if (phongMaterial.unlit != 0u) {
4047
+ return surfaceColor;
4048
+ }
4049
+
2942
4050
  if (lighting.enabled == 0) {
2943
4051
  return lightColor;
2944
4052
  }
@@ -2946,56 +4054,86 @@ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, p
2946
4054
  let view_direction: vec3<f32> = normalize(cameraPosition - position_worldspace);
2947
4055
  lightColor = phongMaterial.ambient * surfaceColor * lighting.ambientColor;
2948
4056
 
2949
- if (lighting.lightType == 0) {
2950
- let pointLight: PointLight = lighting_getPointLight(0);
4057
+ for (var i: i32 = 0; i < lighting.pointLightCount; i++) {
4058
+ let pointLight: PointLight = lighting_getPointLight(i);
2951
4059
  let light_position_worldspace: vec3<f32> = pointLight.position;
2952
4060
  let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
2953
- lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color);
2954
- } else if (lighting.lightType == 1) {
2955
- var directionalLight: DirectionalLight = lighting_getDirectionalLight(0);
2956
- lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
4061
+ let light_attenuation = getPointLightAttenuation(
4062
+ pointLight,
4063
+ distance(light_position_worldspace, position_worldspace)
4064
+ );
4065
+ lightColor += lighting_getLightColor(
4066
+ surfaceColor,
4067
+ light_direction,
4068
+ view_direction,
4069
+ normal_worldspace,
4070
+ pointLight.color / light_attenuation
4071
+ );
2957
4072
  }
2958
-
2959
- return lightColor;
2960
- /*
2961
- for (int i = 0; i < MAX_LIGHTS; i++) {
2962
- if (i >= lighting.pointLightCount) {
2963
- break;
2964
- }
2965
- PointLight pointLight = lighting.pointLight[i];
2966
- vec3 light_position_worldspace = pointLight.position;
2967
- vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
2968
- lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color);
4073
+
4074
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
4075
+ let spotLight: SpotLight = lighting_getSpotLight(i);
4076
+ let light_position_worldspace: vec3<f32> = spotLight.position;
4077
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
4078
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
4079
+ lightColor += lighting_getLightColor(
4080
+ surfaceColor,
4081
+ light_direction,
4082
+ view_direction,
4083
+ normal_worldspace,
4084
+ spotLight.color / light_attenuation
4085
+ );
2969
4086
  }
2970
4087
 
2971
- for (int i = 0; i < MAX_LIGHTS; i++) {
2972
- if (i >= lighting.directionalLightCount) {
2973
- break;
2974
- }
2975
- DirectionalLight directionalLight = lighting.directionalLight[i];
4088
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
4089
+ let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
2976
4090
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
2977
- }
2978
- */
4091
+ }
4092
+
4093
+ return lightColor;
2979
4094
  }
2980
4095
 
2981
4096
  fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace: vec3<f32>, normal_worldspace: vec3<f32>) -> vec3<f32>{
2982
4097
  var lightColor = vec3<f32>(0, 0, 0);
2983
4098
  let surfaceColor = vec3<f32>(0, 0, 0);
2984
4099
 
2985
- if (lighting.enabled == 0) {
4100
+ if (lighting.enabled != 0) {
2986
4101
  let view_direction = normalize(cameraPosition - position_worldspace);
2987
4102
 
2988
- switch (lighting.lightType) {
2989
- case 0, default: {
2990
- let pointLight: PointLight = lighting_getPointLight(0);
2991
- let light_position_worldspace: vec3<f32> = pointLight.position;
2992
- let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
2993
- lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color);
2994
- }
2995
- case 1: {
2996
- let directionalLight: DirectionalLight = lighting_getDirectionalLight(0);
4103
+ for (var i: i32 = 0; i < lighting.pointLightCount; i++) {
4104
+ let pointLight: PointLight = lighting_getPointLight(i);
4105
+ let light_position_worldspace: vec3<f32> = pointLight.position;
4106
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
4107
+ let light_attenuation = getPointLightAttenuation(
4108
+ pointLight,
4109
+ distance(light_position_worldspace, position_worldspace)
4110
+ );
4111
+ lightColor += lighting_getLightColor(
4112
+ surfaceColor,
4113
+ light_direction,
4114
+ view_direction,
4115
+ normal_worldspace,
4116
+ pointLight.color / light_attenuation
4117
+ );
4118
+ }
4119
+
4120
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
4121
+ let spotLight: SpotLight = lighting_getSpotLight(i);
4122
+ let light_position_worldspace: vec3<f32> = spotLight.position;
4123
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
4124
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
4125
+ lightColor += lighting_getLightColor(
4126
+ surfaceColor,
4127
+ light_direction,
4128
+ view_direction,
4129
+ normal_worldspace,
4130
+ spotLight.color / light_attenuation
4131
+ );
4132
+ }
4133
+
4134
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
4135
+ let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
2997
4136
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
2998
- }
2999
4137
  }
3000
4138
  }
3001
4139
  return lightColor;
@@ -3007,6 +4145,7 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
3007
4145
  var gouraudMaterial = {
3008
4146
  props: {},
3009
4147
  name: "gouraudMaterial",
4148
+ bindingLayout: [{ name: "gouraudMaterial", group: 3 }],
3010
4149
  // Note these are switched between phong and gouraud
3011
4150
  vs: PHONG_FS.replace("phongMaterial", "gouraudMaterial"),
3012
4151
  fs: PHONG_VS.replace("phongMaterial", "gouraudMaterial"),
@@ -3016,12 +4155,14 @@ var gouraudMaterial = {
3016
4155
  },
3017
4156
  dependencies: [lighting],
3018
4157
  uniformTypes: {
4158
+ unlit: "i32",
3019
4159
  ambient: "f32",
3020
4160
  diffuse: "f32",
3021
4161
  shininess: "f32",
3022
4162
  specularColor: "vec3<f32>"
3023
4163
  },
3024
4164
  defaultUniforms: {
4165
+ unlit: false,
3025
4166
  ambient: 0.35,
3026
4167
  diffuse: 0.6,
3027
4168
  shininess: 32,
@@ -3039,6 +4180,8 @@ var gouraudMaterial = {
3039
4180
  // dist/modules/lighting/phong-material/phong-material.js
3040
4181
  var phongMaterial = {
3041
4182
  name: "phongMaterial",
4183
+ firstBindingSlot: 0,
4184
+ bindingLayout: [{ name: "phongMaterial", group: 3 }],
3042
4185
  dependencies: [lighting],
3043
4186
  // Note these are switched between phong and gouraud
3044
4187
  source: PHONG_WGSL,
@@ -3048,12 +4191,14 @@ var phongMaterial = {
3048
4191
  LIGHTING_FRAGMENT: true
3049
4192
  },
3050
4193
  uniformTypes: {
4194
+ unlit: "i32",
3051
4195
  ambient: "f32",
3052
4196
  diffuse: "f32",
3053
4197
  shininess: "f32",
3054
4198
  specularColor: "vec3<f32>"
3055
4199
  },
3056
4200
  defaultUniforms: {
4201
+ unlit: false,
3057
4202
  ambient: 0.35,
3058
4203
  diffuse: 0.6,
3059
4204
  shininess: 32,
@@ -3069,7 +4214,7 @@ var phongMaterial = {
3069
4214
  };
3070
4215
 
3071
4216
  // dist/modules/lighting/pbr-material/pbr-material-glsl.js
3072
- var vs2 = (
4217
+ var vs3 = (
3073
4218
  /* glsl */
3074
4219
  `out vec3 pbr_vPosition;
3075
4220
  out vec2 pbr_vUV;
@@ -3106,7 +4251,7 @@ void pbr_setPositionNormalTangentUV(vec4 position, vec4 normal, vec4 tangent, ve
3106
4251
  }
3107
4252
  `
3108
4253
  );
3109
- var fs3 = (
4254
+ var fs4 = (
3110
4255
  /* glsl */
3111
4256
  `precision highp float;
3112
4257
 
@@ -3150,10 +4295,12 @@ uniform pbrMaterialUniforms {
3150
4295
  float clearcoatFactor;
3151
4296
  float clearcoatRoughnessFactor;
3152
4297
  bool clearcoatMapEnabled;
4298
+ bool clearcoatRoughnessMapEnabled;
3153
4299
 
3154
4300
  vec3 sheenColorFactor;
3155
4301
  float sheenRoughnessFactor;
3156
4302
  bool sheenColorMapEnabled;
4303
+ bool sheenRoughnessMapEnabled;
3157
4304
 
3158
4305
  float iridescenceFactor;
3159
4306
  float iridescenceIor;
@@ -3203,26 +4350,33 @@ uniform sampler2D pbr_specularIntensitySampler;
3203
4350
  #ifdef HAS_TRANSMISSIONMAP
3204
4351
  uniform sampler2D pbr_transmissionSampler;
3205
4352
  #endif
4353
+ #ifdef HAS_THICKNESSMAP
4354
+ uniform sampler2D pbr_thicknessSampler;
4355
+ #endif
3206
4356
  #ifdef HAS_CLEARCOATMAP
3207
4357
  uniform sampler2D pbr_clearcoatSampler;
4358
+ #endif
4359
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
3208
4360
  uniform sampler2D pbr_clearcoatRoughnessSampler;
3209
4361
  #endif
4362
+ #ifdef HAS_CLEARCOATNORMALMAP
4363
+ uniform sampler2D pbr_clearcoatNormalSampler;
4364
+ #endif
3210
4365
  #ifdef HAS_SHEENCOLORMAP
3211
4366
  uniform sampler2D pbr_sheenColorSampler;
4367
+ #endif
4368
+ #ifdef HAS_SHEENROUGHNESSMAP
3212
4369
  uniform sampler2D pbr_sheenRoughnessSampler;
3213
4370
  #endif
3214
4371
  #ifdef HAS_IRIDESCENCEMAP
3215
4372
  uniform sampler2D pbr_iridescenceSampler;
3216
4373
  #endif
4374
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
4375
+ uniform sampler2D pbr_iridescenceThicknessSampler;
4376
+ #endif
3217
4377
  #ifdef HAS_ANISOTROPYMAP
3218
4378
  uniform sampler2D pbr_anisotropySampler;
3219
4379
  #endif
3220
- #ifdef USE_IBL
3221
- uniform samplerCube pbr_diffuseEnvSampler;
3222
- uniform samplerCube pbr_specularEnvSampler;
3223
- uniform sampler2D pbr_brdfLUT;
3224
- #endif
3225
-
3226
4380
  // Inputs from vertex shader
3227
4381
 
3228
4382
  in vec3 pbr_vPosition;
@@ -3259,6 +4413,8 @@ struct PBRInfo {
3259
4413
  const float M_PI = 3.141592653589793;
3260
4414
  const float c_MinRoughness = 0.04;
3261
4415
 
4416
+ vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor);
4417
+
3262
4418
  vec4 SRGBtoLINEAR(vec4 srgbIn)
3263
4419
  {
3264
4420
  #ifdef MANUAL_SRGB
@@ -3274,11 +4430,9 @@ vec4 SRGBtoLINEAR(vec4 srgbIn)
3274
4430
  #endif //MANUAL_SRGB
3275
4431
  }
3276
4432
 
3277
- // Find the normal for this fragment, pulling either from a predefined normal map
3278
- // or from the interpolated mesh normal and tangent attributes.
3279
- vec3 getNormal()
4433
+ // Build the tangent basis from interpolated attributes or screen-space derivatives.
4434
+ mat3 getTBN()
3280
4435
  {
3281
- // Retrieve the tangent space matrix
3282
4436
  #ifndef HAS_TANGENTS
3283
4437
  vec3 pos_dx = dFdx(pbr_vPosition);
3284
4438
  vec3 pos_dy = dFdy(pbr_vPosition);
@@ -3299,9 +4453,21 @@ vec3 getNormal()
3299
4453
  mat3 tbn = pbr_vTBN;
3300
4454
  #endif
3301
4455
 
4456
+ return tbn;
4457
+ }
4458
+
4459
+ // Find the normal for this fragment, pulling either from a predefined normal map
4460
+ // or from the interpolated mesh normal and tangent attributes.
4461
+ vec3 getMappedNormal(sampler2D normalSampler, mat3 tbn, float normalScale)
4462
+ {
4463
+ vec3 n = texture(normalSampler, pbr_vUV).rgb;
4464
+ return normalize(tbn * ((2.0 * n - 1.0) * vec3(normalScale, normalScale, 1.0)));
4465
+ }
4466
+
4467
+ vec3 getNormal(mat3 tbn)
4468
+ {
3302
4469
  #ifdef HAS_NORMALMAP
3303
- vec3 n = texture(pbr_normalSampler, pbr_vUV).rgb;
3304
- n = normalize(tbn * ((2.0 * n - 1.0) * vec3(pbrMaterial.normalScale, pbrMaterial.normalScale, 1.0)));
4470
+ vec3 n = getMappedNormal(pbr_normalSampler, tbn, pbrMaterial.normalScale);
3305
4471
  #else
3306
4472
  // The tbn matrix is linearly interpolated, so we need to re-normalize
3307
4473
  vec3 n = normalize(tbn[2].xyz);
@@ -3310,6 +4476,15 @@ vec3 getNormal()
3310
4476
  return n;
3311
4477
  }
3312
4478
 
4479
+ vec3 getClearcoatNormal(mat3 tbn, vec3 baseNormal)
4480
+ {
4481
+ #ifdef HAS_CLEARCOATNORMALMAP
4482
+ return getMappedNormal(pbr_clearcoatNormalSampler, tbn, 1.0);
4483
+ #else
4484
+ return baseNormal;
4485
+ #endif
4486
+ }
4487
+
3313
4488
  // Calculation of the lighting contribution from an optional Image Based Light source.
3314
4489
  // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
3315
4490
  // See our README.md on Environment Maps [3] for additional discussion.
@@ -3385,30 +4560,198 @@ float microfacetDistribution(PBRInfo pbrInfo)
3385
4560
  return roughnessSq / (M_PI * f * f);
3386
4561
  }
3387
4562
 
3388
- void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) {
3389
- pbrInfo.NdotL = 1.0;
3390
- pbrInfo.NdotH = 0.0;
3391
- pbrInfo.LdotH = 0.0;
3392
- pbrInfo.VdotH = 1.0;
4563
+ float maxComponent(vec3 value)
4564
+ {
4565
+ return max(max(value.r, value.g), value.b);
3393
4566
  }
3394
4567
 
3395
- void PBRInfo_setDirectionalLight(inout PBRInfo pbrInfo, vec3 lightDirection) {
3396
- vec3 n = pbrInfo.n;
3397
- vec3 v = pbrInfo.v;
3398
- vec3 l = normalize(lightDirection); // Vector from surface point to light
3399
- vec3 h = normalize(l+v); // Half vector between both l and v
4568
+ float getDielectricF0(float ior)
4569
+ {
4570
+ float clampedIor = max(ior, 1.0);
4571
+ float ratio = (clampedIor - 1.0) / (clampedIor + 1.0);
4572
+ return ratio * ratio;
4573
+ }
3400
4574
 
3401
- pbrInfo.NdotL = clamp(dot(n, l), 0.001, 1.0);
3402
- pbrInfo.NdotH = clamp(dot(n, h), 0.0, 1.0);
3403
- pbrInfo.LdotH = clamp(dot(l, h), 0.0, 1.0);
3404
- pbrInfo.VdotH = clamp(dot(v, h), 0.0, 1.0);
4575
+ vec2 normalizeDirection(vec2 direction)
4576
+ {
4577
+ float directionLength = length(direction);
4578
+ return directionLength > 0.0001 ? direction / directionLength : vec2(1.0, 0.0);
3405
4579
  }
3406
4580
 
3407
- void PBRInfo_setPointLight(inout PBRInfo pbrInfo, PointLight pointLight) {
4581
+ vec2 rotateDirection(vec2 direction, float rotation)
4582
+ {
4583
+ float s = sin(rotation);
4584
+ float c = cos(rotation);
4585
+ return vec2(direction.x * c - direction.y * s, direction.x * s + direction.y * c);
4586
+ }
4587
+
4588
+ vec3 getIridescenceTint(float iridescence, float thickness, float NdotV)
4589
+ {
4590
+ if (iridescence <= 0.0) {
4591
+ return vec3(1.0);
4592
+ }
4593
+
4594
+ float phase = 0.015 * thickness * pbrMaterial.iridescenceIor + (1.0 - NdotV) * 6.0;
4595
+ vec3 thinFilmTint =
4596
+ 0.5 + 0.5 * cos(vec3(phase, phase + 2.0943951, phase + 4.1887902));
4597
+ return mix(vec3(1.0), thinFilmTint, iridescence);
4598
+ }
4599
+
4600
+ vec3 getVolumeAttenuation(float thickness)
4601
+ {
4602
+ if (thickness <= 0.0) {
4603
+ return vec3(1.0);
4604
+ }
4605
+
4606
+ vec3 attenuationCoefficient =
4607
+ -log(max(pbrMaterial.attenuationColor, vec3(0.0001))) /
4608
+ max(pbrMaterial.attenuationDistance, 0.0001);
4609
+ return exp(-attenuationCoefficient * thickness);
4610
+ }
4611
+
4612
+ PBRInfo createClearcoatPBRInfo(PBRInfo basePBRInfo, vec3 clearcoatNormal, float clearcoatRoughness)
4613
+ {
4614
+ float perceptualRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
4615
+ float alphaRoughness = perceptualRoughness * perceptualRoughness;
4616
+ float NdotV = clamp(abs(dot(clearcoatNormal, basePBRInfo.v)), 0.001, 1.0);
4617
+
4618
+ return PBRInfo(
4619
+ basePBRInfo.NdotL,
4620
+ NdotV,
4621
+ basePBRInfo.NdotH,
4622
+ basePBRInfo.LdotH,
4623
+ basePBRInfo.VdotH,
4624
+ perceptualRoughness,
4625
+ 0.0,
4626
+ vec3(0.04),
4627
+ vec3(1.0),
4628
+ alphaRoughness,
4629
+ vec3(0.0),
4630
+ vec3(0.04),
4631
+ clearcoatNormal,
4632
+ basePBRInfo.v
4633
+ );
4634
+ }
4635
+
4636
+ vec3 calculateClearcoatContribution(
4637
+ PBRInfo pbrInfo,
4638
+ vec3 lightColor,
4639
+ vec3 clearcoatNormal,
4640
+ float clearcoatFactor,
4641
+ float clearcoatRoughness
4642
+ ) {
4643
+ if (clearcoatFactor <= 0.0) {
4644
+ return vec3(0.0);
4645
+ }
4646
+
4647
+ PBRInfo clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
4648
+ return calculateFinalColor(clearcoatPBRInfo, lightColor) * clearcoatFactor;
4649
+ }
4650
+
4651
+ #ifdef USE_IBL
4652
+ vec3 calculateClearcoatIBLContribution(
4653
+ PBRInfo pbrInfo,
4654
+ vec3 clearcoatNormal,
4655
+ vec3 reflection,
4656
+ float clearcoatFactor,
4657
+ float clearcoatRoughness
4658
+ ) {
4659
+ if (clearcoatFactor <= 0.0) {
4660
+ return vec3(0.0);
4661
+ }
4662
+
4663
+ PBRInfo clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
4664
+ return getIBLContribution(clearcoatPBRInfo, clearcoatNormal, reflection) * clearcoatFactor;
4665
+ }
4666
+ #endif
4667
+
4668
+ vec3 calculateSheenContribution(
4669
+ PBRInfo pbrInfo,
4670
+ vec3 lightColor,
4671
+ vec3 sheenColor,
4672
+ float sheenRoughness
4673
+ ) {
4674
+ if (maxComponent(sheenColor) <= 0.0) {
4675
+ return vec3(0.0);
4676
+ }
4677
+
4678
+ float sheenFresnel = pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0);
4679
+ float sheenVisibility = mix(1.0, pbrInfo.NdotL * pbrInfo.NdotV, sheenRoughness);
4680
+ return pbrInfo.NdotL *
4681
+ lightColor *
4682
+ sheenColor *
4683
+ (0.25 + 0.75 * sheenFresnel) *
4684
+ sheenVisibility *
4685
+ (1.0 - pbrInfo.metalness);
4686
+ }
4687
+
4688
+ float calculateAnisotropyBoost(
4689
+ PBRInfo pbrInfo,
4690
+ vec3 anisotropyTangent,
4691
+ float anisotropyStrength
4692
+ ) {
4693
+ if (anisotropyStrength <= 0.0) {
4694
+ return 1.0;
4695
+ }
4696
+
4697
+ vec3 anisotropyBitangent = normalize(cross(pbrInfo.n, anisotropyTangent));
4698
+ float bitangentViewAlignment = abs(dot(pbrInfo.v, anisotropyBitangent));
4699
+ return mix(1.0, 0.65 + 0.7 * bitangentViewAlignment, anisotropyStrength);
4700
+ }
4701
+
4702
+ vec3 calculateMaterialLightColor(
4703
+ PBRInfo pbrInfo,
4704
+ vec3 lightColor,
4705
+ vec3 clearcoatNormal,
4706
+ float clearcoatFactor,
4707
+ float clearcoatRoughness,
4708
+ vec3 sheenColor,
4709
+ float sheenRoughness,
4710
+ vec3 anisotropyTangent,
4711
+ float anisotropyStrength
4712
+ ) {
4713
+ float anisotropyBoost = calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
4714
+ vec3 color = calculateFinalColor(pbrInfo, lightColor) * anisotropyBoost;
4715
+ color += calculateClearcoatContribution(
4716
+ pbrInfo,
4717
+ lightColor,
4718
+ clearcoatNormal,
4719
+ clearcoatFactor,
4720
+ clearcoatRoughness
4721
+ );
4722
+ color += calculateSheenContribution(pbrInfo, lightColor, sheenColor, sheenRoughness);
4723
+ return color;
4724
+ }
4725
+
4726
+ void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) {
4727
+ pbrInfo.NdotL = 1.0;
4728
+ pbrInfo.NdotH = 0.0;
4729
+ pbrInfo.LdotH = 0.0;
4730
+ pbrInfo.VdotH = 1.0;
4731
+ }
4732
+
4733
+ void PBRInfo_setDirectionalLight(inout PBRInfo pbrInfo, vec3 lightDirection) {
4734
+ vec3 n = pbrInfo.n;
4735
+ vec3 v = pbrInfo.v;
4736
+ vec3 l = normalize(lightDirection); // Vector from surface point to light
4737
+ vec3 h = normalize(l+v); // Half vector between both l and v
4738
+
4739
+ pbrInfo.NdotL = clamp(dot(n, l), 0.001, 1.0);
4740
+ pbrInfo.NdotH = clamp(dot(n, h), 0.0, 1.0);
4741
+ pbrInfo.LdotH = clamp(dot(l, h), 0.0, 1.0);
4742
+ pbrInfo.VdotH = clamp(dot(v, h), 0.0, 1.0);
4743
+ }
4744
+
4745
+ void PBRInfo_setPointLight(inout PBRInfo pbrInfo, PointLight pointLight) {
3408
4746
  vec3 light_direction = normalize(pointLight.position - pbr_vPosition);
3409
4747
  PBRInfo_setDirectionalLight(pbrInfo, light_direction);
3410
4748
  }
3411
4749
 
4750
+ void PBRInfo_setSpotLight(inout PBRInfo pbrInfo, SpotLight spotLight) {
4751
+ vec3 light_direction = normalize(spotLight.position - pbr_vPosition);
4752
+ PBRInfo_setDirectionalLight(pbrInfo, light_direction);
4753
+ }
4754
+
3412
4755
  vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
3413
4756
  // Calculate the shading terms for the microfacet specular shading model
3414
4757
  vec3 F = specularReflection(pbrInfo);
@@ -3439,6 +4782,8 @@ vec4 pbr_filterColor(vec4 colorUnused)
3439
4782
 
3440
4783
  vec3 color = vec3(0, 0, 0);
3441
4784
 
4785
+ float transmission = 0.0;
4786
+
3442
4787
  if(pbrMaterial.unlit){
3443
4788
  color.rgb = baseColor.rgb;
3444
4789
  }
@@ -3457,14 +4802,252 @@ vec4 pbr_filterColor(vec4 colorUnused)
3457
4802
  #endif
3458
4803
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
3459
4804
  metallic = clamp(metallic, 0.0, 1.0);
4805
+ mat3 tbn = getTBN();
4806
+ vec3 n = getNormal(tbn); // normal at surface point
4807
+ vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
4808
+ float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
4809
+ #ifdef USE_MATERIAL_EXTENSIONS
4810
+ bool useExtendedPBR =
4811
+ pbrMaterial.specularColorMapEnabled ||
4812
+ pbrMaterial.specularIntensityMapEnabled ||
4813
+ abs(pbrMaterial.specularIntensityFactor - 1.0) > 0.0001 ||
4814
+ maxComponent(abs(pbrMaterial.specularColorFactor - vec3(1.0))) > 0.0001 ||
4815
+ abs(pbrMaterial.ior - 1.5) > 0.0001 ||
4816
+ pbrMaterial.transmissionMapEnabled ||
4817
+ pbrMaterial.transmissionFactor > 0.0001 ||
4818
+ pbrMaterial.clearcoatMapEnabled ||
4819
+ pbrMaterial.clearcoatRoughnessMapEnabled ||
4820
+ pbrMaterial.clearcoatFactor > 0.0001 ||
4821
+ pbrMaterial.clearcoatRoughnessFactor > 0.0001 ||
4822
+ pbrMaterial.sheenColorMapEnabled ||
4823
+ pbrMaterial.sheenRoughnessMapEnabled ||
4824
+ maxComponent(pbrMaterial.sheenColorFactor) > 0.0001 ||
4825
+ pbrMaterial.sheenRoughnessFactor > 0.0001 ||
4826
+ pbrMaterial.iridescenceMapEnabled ||
4827
+ pbrMaterial.iridescenceFactor > 0.0001 ||
4828
+ abs(pbrMaterial.iridescenceIor - 1.3) > 0.0001 ||
4829
+ abs(pbrMaterial.iridescenceThicknessRange.x - 100.0) > 0.0001 ||
4830
+ abs(pbrMaterial.iridescenceThicknessRange.y - 400.0) > 0.0001 ||
4831
+ pbrMaterial.anisotropyMapEnabled ||
4832
+ pbrMaterial.anisotropyStrength > 0.0001 ||
4833
+ abs(pbrMaterial.anisotropyRotation) > 0.0001 ||
4834
+ length(pbrMaterial.anisotropyDirection - vec2(1.0, 0.0)) > 0.0001;
4835
+ #else
4836
+ bool useExtendedPBR = false;
4837
+ #endif
4838
+
4839
+ if (!useExtendedPBR) {
4840
+ // Keep the baseline metallic-roughness implementation byte-for-byte equivalent in behavior.
4841
+ float alphaRoughness = perceptualRoughness * perceptualRoughness;
4842
+
4843
+ vec3 f0 = vec3(0.04);
4844
+ vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
4845
+ diffuseColor *= 1.0 - metallic;
4846
+ vec3 specularColor = mix(f0, baseColor.rgb, metallic);
4847
+
4848
+ float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
4849
+ float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
4850
+ vec3 specularEnvironmentR0 = specularColor.rgb;
4851
+ vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
4852
+ vec3 reflection = -normalize(reflect(v, n));
4853
+
4854
+ PBRInfo pbrInfo = PBRInfo(
4855
+ 0.0, // NdotL
4856
+ NdotV,
4857
+ 0.0, // NdotH
4858
+ 0.0, // LdotH
4859
+ 0.0, // VdotH
4860
+ perceptualRoughness,
4861
+ metallic,
4862
+ specularEnvironmentR0,
4863
+ specularEnvironmentR90,
4864
+ alphaRoughness,
4865
+ diffuseColor,
4866
+ specularColor,
4867
+ n,
4868
+ v
4869
+ );
4870
+
4871
+ #ifdef USE_LIGHTS
4872
+ PBRInfo_setAmbientLight(pbrInfo);
4873
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor);
4874
+
4875
+ for(int i = 0; i < lighting.directionalLightCount; i++) {
4876
+ if (i < lighting.directionalLightCount) {
4877
+ PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
4878
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
4879
+ }
4880
+ }
4881
+
4882
+ for(int i = 0; i < lighting.pointLightCount; i++) {
4883
+ if (i < lighting.pointLightCount) {
4884
+ PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
4885
+ float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
4886
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
4887
+ }
4888
+ }
4889
+
4890
+ for(int i = 0; i < lighting.spotLightCount; i++) {
4891
+ if (i < lighting.spotLightCount) {
4892
+ PBRInfo_setSpotLight(pbrInfo, lighting_getSpotLight(i));
4893
+ float attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), pbr_vPosition);
4894
+ color += calculateFinalColor(pbrInfo, lighting_getSpotLight(i).color / attenuation);
4895
+ }
4896
+ }
4897
+ #endif
4898
+
4899
+ #ifdef USE_IBL
4900
+ if (pbrMaterial.IBLenabled) {
4901
+ color += getIBLContribution(pbrInfo, n, reflection);
4902
+ }
4903
+ #endif
4904
+
4905
+ #ifdef HAS_OCCLUSIONMAP
4906
+ if (pbrMaterial.occlusionMapEnabled) {
4907
+ float ao = texture(pbr_occlusionSampler, pbr_vUV).r;
4908
+ color = mix(color, color * ao, pbrMaterial.occlusionStrength);
4909
+ }
4910
+ #endif
4911
+
4912
+ vec3 emissive = pbrMaterial.emissiveFactor;
4913
+ #ifdef HAS_EMISSIVEMAP
4914
+ if (pbrMaterial.emissiveMapEnabled) {
4915
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
4916
+ }
4917
+ #endif
4918
+ color += emissive * pbrMaterial.emissiveStrength;
4919
+
4920
+ #ifdef PBR_DEBUG
4921
+ color = mix(color, baseColor.rgb, pbrMaterial.scaleDiffBaseMR.y);
4922
+ color = mix(color, vec3(metallic), pbrMaterial.scaleDiffBaseMR.z);
4923
+ color = mix(color, vec3(perceptualRoughness), pbrMaterial.scaleDiffBaseMR.w);
4924
+ #endif
4925
+
4926
+ return vec4(pow(color, vec3(1.0 / 2.2)), baseColor.a);
4927
+ }
4928
+
4929
+ float specularIntensity = pbrMaterial.specularIntensityFactor;
4930
+ #ifdef HAS_SPECULARINTENSITYMAP
4931
+ if (pbrMaterial.specularIntensityMapEnabled) {
4932
+ specularIntensity *= texture(pbr_specularIntensitySampler, pbr_vUV).a;
4933
+ }
4934
+ #endif
4935
+
4936
+ vec3 specularFactor = pbrMaterial.specularColorFactor;
4937
+ #ifdef HAS_SPECULARCOLORMAP
4938
+ if (pbrMaterial.specularColorMapEnabled) {
4939
+ specularFactor *= SRGBtoLINEAR(texture(pbr_specularColorSampler, pbr_vUV)).rgb;
4940
+ }
4941
+ #endif
4942
+
4943
+ transmission = pbrMaterial.transmissionFactor;
4944
+ #ifdef HAS_TRANSMISSIONMAP
4945
+ if (pbrMaterial.transmissionMapEnabled) {
4946
+ transmission *= texture(pbr_transmissionSampler, pbr_vUV).r;
4947
+ }
4948
+ #endif
4949
+ transmission = clamp(transmission * (1.0 - metallic), 0.0, 1.0);
4950
+ float thickness = max(pbrMaterial.thicknessFactor, 0.0);
4951
+ #ifdef HAS_THICKNESSMAP
4952
+ thickness *= texture(pbr_thicknessSampler, pbr_vUV).g;
4953
+ #endif
4954
+
4955
+ float clearcoatFactor = pbrMaterial.clearcoatFactor;
4956
+ float clearcoatRoughness = pbrMaterial.clearcoatRoughnessFactor;
4957
+ #ifdef HAS_CLEARCOATMAP
4958
+ if (pbrMaterial.clearcoatMapEnabled) {
4959
+ clearcoatFactor *= texture(pbr_clearcoatSampler, pbr_vUV).r;
4960
+ }
4961
+ #endif
4962
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
4963
+ if (pbrMaterial.clearcoatRoughnessMapEnabled) {
4964
+ clearcoatRoughness *= texture(pbr_clearcoatRoughnessSampler, pbr_vUV).g;
4965
+ }
4966
+ #endif
4967
+ clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
4968
+ clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
4969
+ vec3 clearcoatNormal = getClearcoatNormal(tbn, n);
4970
+
4971
+ vec3 sheenColor = pbrMaterial.sheenColorFactor;
4972
+ float sheenRoughness = pbrMaterial.sheenRoughnessFactor;
4973
+ #ifdef HAS_SHEENCOLORMAP
4974
+ if (pbrMaterial.sheenColorMapEnabled) {
4975
+ sheenColor *= SRGBtoLINEAR(texture(pbr_sheenColorSampler, pbr_vUV)).rgb;
4976
+ }
4977
+ #endif
4978
+ #ifdef HAS_SHEENROUGHNESSMAP
4979
+ if (pbrMaterial.sheenRoughnessMapEnabled) {
4980
+ sheenRoughness *= texture(pbr_sheenRoughnessSampler, pbr_vUV).a;
4981
+ }
4982
+ #endif
4983
+ sheenRoughness = clamp(sheenRoughness, c_MinRoughness, 1.0);
4984
+
4985
+ float iridescence = pbrMaterial.iridescenceFactor;
4986
+ #ifdef HAS_IRIDESCENCEMAP
4987
+ if (pbrMaterial.iridescenceMapEnabled) {
4988
+ iridescence *= texture(pbr_iridescenceSampler, pbr_vUV).r;
4989
+ }
4990
+ #endif
4991
+ iridescence = clamp(iridescence, 0.0, 1.0);
4992
+ float iridescenceThickness = mix(
4993
+ pbrMaterial.iridescenceThicknessRange.x,
4994
+ pbrMaterial.iridescenceThicknessRange.y,
4995
+ 0.5
4996
+ );
4997
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
4998
+ iridescenceThickness = mix(
4999
+ pbrMaterial.iridescenceThicknessRange.x,
5000
+ pbrMaterial.iridescenceThicknessRange.y,
5001
+ texture(pbr_iridescenceThicknessSampler, pbr_vUV).g
5002
+ );
5003
+ #endif
5004
+
5005
+ float anisotropyStrength = clamp(pbrMaterial.anisotropyStrength, 0.0, 1.0);
5006
+ vec2 anisotropyDirection = normalizeDirection(pbrMaterial.anisotropyDirection);
5007
+ #ifdef HAS_ANISOTROPYMAP
5008
+ if (pbrMaterial.anisotropyMapEnabled) {
5009
+ vec3 anisotropySample = texture(pbr_anisotropySampler, pbr_vUV).rgb;
5010
+ anisotropyStrength *= anisotropySample.b;
5011
+ vec2 mappedDirection = anisotropySample.rg * 2.0 - 1.0;
5012
+ if (length(mappedDirection) > 0.0001) {
5013
+ anisotropyDirection = normalize(mappedDirection);
5014
+ }
5015
+ }
5016
+ #endif
5017
+ anisotropyDirection = rotateDirection(anisotropyDirection, pbrMaterial.anisotropyRotation);
5018
+ vec3 anisotropyTangent = normalize(tbn[0] * anisotropyDirection.x + tbn[1] * anisotropyDirection.y);
5019
+ if (length(anisotropyTangent) < 0.0001) {
5020
+ anisotropyTangent = normalize(tbn[0]);
5021
+ }
5022
+ float anisotropyViewAlignment = abs(dot(v, anisotropyTangent));
5023
+ perceptualRoughness = mix(
5024
+ perceptualRoughness,
5025
+ clamp(perceptualRoughness * (1.0 - 0.6 * anisotropyViewAlignment), c_MinRoughness, 1.0),
5026
+ anisotropyStrength
5027
+ );
5028
+
3460
5029
  // Roughness is authored as perceptual roughness; as is convention,
3461
5030
  // convert to material roughness by squaring the perceptual roughness [2].
3462
5031
  float alphaRoughness = perceptualRoughness * perceptualRoughness;
3463
5032
 
3464
- vec3 f0 = vec3(0.04);
3465
- vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
3466
- diffuseColor *= 1.0 - metallic;
3467
- vec3 specularColor = mix(f0, baseColor.rgb, metallic);
5033
+ float dielectricF0 = getDielectricF0(pbrMaterial.ior);
5034
+ vec3 dielectricSpecularF0 = min(
5035
+ vec3(dielectricF0) * specularFactor * specularIntensity,
5036
+ vec3(1.0)
5037
+ );
5038
+ vec3 iridescenceTint = getIridescenceTint(iridescence, iridescenceThickness, NdotV);
5039
+ dielectricSpecularF0 = mix(
5040
+ dielectricSpecularF0,
5041
+ dielectricSpecularF0 * iridescenceTint,
5042
+ iridescence
5043
+ );
5044
+ vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - dielectricSpecularF0);
5045
+ diffuseColor *= (1.0 - metallic) * (1.0 - transmission);
5046
+ vec3 specularColor = mix(dielectricSpecularF0, baseColor.rgb, metallic);
5047
+
5048
+ float baseLayerEnergy = 1.0 - clearcoatFactor * 0.25;
5049
+ diffuseColor *= baseLayerEnergy;
5050
+ specularColor *= baseLayerEnergy;
3468
5051
 
3469
5052
  // Compute reflectance.
3470
5053
  float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
@@ -3476,11 +5059,6 @@ vec4 pbr_filterColor(vec4 colorUnused)
3476
5059
  float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
3477
5060
  vec3 specularEnvironmentR0 = specularColor.rgb;
3478
5061
  vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
3479
-
3480
- vec3 n = getNormal(); // normal at surface point
3481
- vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
3482
-
3483
- float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
3484
5062
  vec3 reflection = -normalize(reflect(v, n));
3485
5063
 
3486
5064
  PBRInfo pbrInfo = PBRInfo(
@@ -3504,13 +5082,33 @@ vec4 pbr_filterColor(vec4 colorUnused)
3504
5082
  #ifdef USE_LIGHTS
3505
5083
  // Apply ambient light
3506
5084
  PBRInfo_setAmbientLight(pbrInfo);
3507
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
5085
+ color += calculateMaterialLightColor(
5086
+ pbrInfo,
5087
+ lighting.ambientColor,
5088
+ clearcoatNormal,
5089
+ clearcoatFactor,
5090
+ clearcoatRoughness,
5091
+ sheenColor,
5092
+ sheenRoughness,
5093
+ anisotropyTangent,
5094
+ anisotropyStrength
5095
+ );
3508
5096
 
3509
5097
  // Apply directional light
3510
5098
  for(int i = 0; i < lighting.directionalLightCount; i++) {
3511
5099
  if (i < lighting.directionalLightCount) {
3512
5100
  PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
3513
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
5101
+ color += calculateMaterialLightColor(
5102
+ pbrInfo,
5103
+ lighting_getDirectionalLight(i).color,
5104
+ clearcoatNormal,
5105
+ clearcoatFactor,
5106
+ clearcoatRoughness,
5107
+ sheenColor,
5108
+ sheenRoughness,
5109
+ anisotropyTangent,
5110
+ anisotropyStrength
5111
+ );
3514
5112
  }
3515
5113
  }
3516
5114
 
@@ -3519,7 +5117,35 @@ vec4 pbr_filterColor(vec4 colorUnused)
3519
5117
  if (i < lighting.pointLightCount) {
3520
5118
  PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
3521
5119
  float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
3522
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
5120
+ color += calculateMaterialLightColor(
5121
+ pbrInfo,
5122
+ lighting_getPointLight(i).color / attenuation,
5123
+ clearcoatNormal,
5124
+ clearcoatFactor,
5125
+ clearcoatRoughness,
5126
+ sheenColor,
5127
+ sheenRoughness,
5128
+ anisotropyTangent,
5129
+ anisotropyStrength
5130
+ );
5131
+ }
5132
+ }
5133
+
5134
+ for(int i = 0; i < lighting.spotLightCount; i++) {
5135
+ if (i < lighting.spotLightCount) {
5136
+ PBRInfo_setSpotLight(pbrInfo, lighting_getSpotLight(i));
5137
+ float attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), pbr_vPosition);
5138
+ color += calculateMaterialLightColor(
5139
+ pbrInfo,
5140
+ lighting_getSpotLight(i).color / attenuation,
5141
+ clearcoatNormal,
5142
+ clearcoatFactor,
5143
+ clearcoatRoughness,
5144
+ sheenColor,
5145
+ sheenRoughness,
5146
+ anisotropyTangent,
5147
+ anisotropyStrength
5148
+ );
3523
5149
  }
3524
5150
  }
3525
5151
  #endif
@@ -3527,7 +5153,16 @@ vec4 pbr_filterColor(vec4 colorUnused)
3527
5153
  // Calculate lighting contribution from image based lighting source (IBL)
3528
5154
  #ifdef USE_IBL
3529
5155
  if (pbrMaterial.IBLenabled) {
3530
- color += getIBLContribution(pbrInfo, n, reflection);
5156
+ color += getIBLContribution(pbrInfo, n, reflection) *
5157
+ calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
5158
+ color += calculateClearcoatIBLContribution(
5159
+ pbrInfo,
5160
+ clearcoatNormal,
5161
+ -normalize(reflect(v, clearcoatNormal)),
5162
+ clearcoatFactor,
5163
+ clearcoatRoughness
5164
+ );
5165
+ color += sheenColor * pbrMaterial.scaleIBLAmbient.x * (1.0 - sheenRoughness) * 0.25;
3531
5166
  }
3532
5167
  #endif
3533
5168
 
@@ -3539,12 +5174,17 @@ vec4 pbr_filterColor(vec4 colorUnused)
3539
5174
  }
3540
5175
  #endif
3541
5176
 
5177
+ vec3 emissive = pbrMaterial.emissiveFactor;
3542
5178
  #ifdef HAS_EMISSIVEMAP
3543
5179
  if (pbrMaterial.emissiveMapEnabled) {
3544
- vec3 emissive = SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb * pbrMaterial.emissiveFactor;
3545
- color += emissive;
5180
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
3546
5181
  }
3547
5182
  #endif
5183
+ color += emissive * pbrMaterial.emissiveStrength;
5184
+
5185
+ if (transmission > 0.0) {
5186
+ color = mix(color, color * getVolumeAttenuation(thickness), transmission);
5187
+ }
3548
5188
 
3549
5189
  // This section uses mix to override final color for reference app visualization
3550
5190
  // of various parameters in the lighting equation.
@@ -3564,68 +5204,109 @@ vec4 pbr_filterColor(vec4 colorUnused)
3564
5204
 
3565
5205
  }
3566
5206
 
3567
- return vec4(pow(color,vec3(1.0/2.2)), baseColor.a);
5207
+ float alpha = clamp(baseColor.a * (1.0 - transmission), 0.0, 1.0);
5208
+ return vec4(pow(color,vec3(1.0/2.2)), alpha);
3568
5209
  }
3569
5210
  `
3570
5211
  );
3571
5212
 
3572
5213
  // dist/modules/lighting/pbr-material/pbr-material-wgsl.js
3573
- var source2 = (
5214
+ var source3 = (
3574
5215
  /* wgsl */
3575
5216
  `struct PBRFragmentInputs {
3576
5217
  pbr_vPosition: vec3f,
3577
5218
  pbr_vUV: vec2f,
3578
- pbr_vTBN: mat3f,
5219
+ pbr_vTBN: mat3x3f,
3579
5220
  pbr_vNormal: vec3f
3580
5221
  };
3581
5222
 
3582
- var fragmentInputs: PBRFragmentInputs;
5223
+ var<private> fragmentInputs: PBRFragmentInputs;
3583
5224
 
3584
5225
  fn pbr_setPositionNormalTangentUV(position: vec4f, normal: vec4f, tangent: vec4f, uv: vec2f)
3585
5226
  {
3586
5227
  var pos: vec4f = pbrProjection.modelMatrix * position;
3587
- fragmentInputs.pbr_vPosition = vec3(pos.xyz) / pos.w;
5228
+ fragmentInputs.pbr_vPosition = pos.xyz / pos.w;
5229
+ fragmentInputs.pbr_vNormal = vec3f(0.0, 0.0, 1.0);
5230
+ fragmentInputs.pbr_vTBN = mat3x3f(
5231
+ vec3f(1.0, 0.0, 0.0),
5232
+ vec3f(0.0, 1.0, 0.0),
5233
+ vec3f(0.0, 0.0, 1.0)
5234
+ );
5235
+ fragmentInputs.pbr_vUV = vec2f(0.0, 0.0);
3588
5236
 
3589
5237
  #ifdef HAS_NORMALS
5238
+ let normalW: vec3f = normalize((pbrProjection.normalMatrix * vec4f(normal.xyz, 0.0)).xyz);
5239
+ fragmentInputs.pbr_vNormal = normalW;
3590
5240
  #ifdef HAS_TANGENTS
3591
- let normalW: vec3f = normalize(vec3(pbrProjection.normalMatrix * vec4(normal.xyz, 0.0)));
3592
- let tangentW: vec3f = normalize(vec3(pbrProjection.modelMatrix * vec4(tangent.xyz, 0.0)));
5241
+ let tangentW: vec3f = normalize((pbrProjection.modelMatrix * vec4f(tangent.xyz, 0.0)).xyz);
3593
5242
  let bitangentW: vec3f = cross(normalW, tangentW) * tangent.w;
3594
- fragmentInputs.pbr_vTBN = mat3(tangentW, bitangentW, normalW);
3595
- #else // HAS_TANGENTS != 1
3596
- fragmentInputs.pbr_vNormal = normalize(vec3(pbrProjection.modelMatrix * vec4(normal.xyz, 0.0)));
5243
+ fragmentInputs.pbr_vTBN = mat3x3f(tangentW, bitangentW, normalW);
3597
5244
  #endif
3598
5245
  #endif
3599
5246
 
3600
5247
  #ifdef HAS_UV
3601
5248
  fragmentInputs.pbr_vUV = uv;
3602
- #else
3603
- fragmentInputs.pbr_vUV = vec2(0.,0.);
3604
5249
  #endif
3605
5250
  }
3606
5251
 
3607
5252
  struct pbrMaterialUniforms {
3608
5253
  // Material is unlit
3609
- unlit: uint32,
5254
+ unlit: u32,
3610
5255
 
3611
5256
  // Base color map
3612
- baseColorMapEnabled: uint32,
5257
+ baseColorMapEnabled: u32,
3613
5258
  baseColorFactor: vec4f,
3614
5259
 
3615
- normalMapEnabled : uint32,
5260
+ normalMapEnabled : u32,
3616
5261
  normalScale: f32, // #ifdef HAS_NORMALMAP
3617
5262
 
3618
- emissiveMapEnabled: uint32,
5263
+ emissiveMapEnabled: u32,
3619
5264
  emissiveFactor: vec3f, // #ifdef HAS_EMISSIVEMAP
3620
5265
 
3621
5266
  metallicRoughnessValues: vec2f,
3622
- metallicRoughnessMapEnabled: uint32,
5267
+ metallicRoughnessMapEnabled: u32,
3623
5268
 
3624
5269
  occlusionMapEnabled: i32,
3625
5270
  occlusionStrength: f32, // #ifdef HAS_OCCLUSIONMAP
3626
5271
 
3627
5272
  alphaCutoffEnabled: i32,
3628
5273
  alphaCutoff: f32, // #ifdef ALPHA_CUTOFF
5274
+
5275
+ specularColorFactor: vec3f,
5276
+ specularIntensityFactor: f32,
5277
+ specularColorMapEnabled: i32,
5278
+ specularIntensityMapEnabled: i32,
5279
+
5280
+ ior: f32,
5281
+
5282
+ transmissionFactor: f32,
5283
+ transmissionMapEnabled: i32,
5284
+
5285
+ thicknessFactor: f32,
5286
+ attenuationDistance: f32,
5287
+ attenuationColor: vec3f,
5288
+
5289
+ clearcoatFactor: f32,
5290
+ clearcoatRoughnessFactor: f32,
5291
+ clearcoatMapEnabled: i32,
5292
+ clearcoatRoughnessMapEnabled: i32,
5293
+
5294
+ sheenColorFactor: vec3f,
5295
+ sheenRoughnessFactor: f32,
5296
+ sheenColorMapEnabled: i32,
5297
+ sheenRoughnessMapEnabled: i32,
5298
+
5299
+ iridescenceFactor: f32,
5300
+ iridescenceIor: f32,
5301
+ iridescenceThicknessRange: vec2f,
5302
+ iridescenceMapEnabled: i32,
5303
+
5304
+ anisotropyStrength: f32,
5305
+ anisotropyRotation: f32,
5306
+ anisotropyDirection: vec2f,
5307
+ anisotropyMapEnabled: i32,
5308
+
5309
+ emissiveStrength: f32,
3629
5310
 
3630
5311
  // IBL
3631
5312
  IBLenabled: i32,
@@ -3634,34 +5315,81 @@ struct pbrMaterialUniforms {
3634
5315
  // debugging flags used for shader output of intermediate PBR variables
3635
5316
  // #ifdef PBR_DEBUG
3636
5317
  scaleDiffBaseMR: vec4f,
3637
- scaleFGDSpec: vec4f
5318
+ scaleFGDSpec: vec4f,
3638
5319
  // #endif
3639
5320
  }
3640
5321
 
3641
- @binding(2) @group(0) var<uniform> pbrMaterial : pbrMaterialUniforms;
5322
+ @group(3) @binding(auto) var<uniform> pbrMaterial : pbrMaterialUniforms;
3642
5323
 
3643
5324
  // Samplers
3644
5325
  #ifdef HAS_BASECOLORMAP
3645
- uniform sampler2D pbr_baseColorSampler;
5326
+ @group(3) @binding(auto) var pbr_baseColorSampler: texture_2d<f32>;
5327
+ @group(3) @binding(auto) var pbr_baseColorSamplerSampler: sampler;
3646
5328
  #endif
3647
5329
  #ifdef HAS_NORMALMAP
3648
- uniform sampler2D pbr_normalSampler;
5330
+ @group(3) @binding(auto) var pbr_normalSampler: texture_2d<f32>;
5331
+ @group(3) @binding(auto) var pbr_normalSamplerSampler: sampler;
3649
5332
  #endif
3650
5333
  #ifdef HAS_EMISSIVEMAP
3651
- uniform sampler2D pbr_emissiveSampler;
5334
+ @group(3) @binding(auto) var pbr_emissiveSampler: texture_2d<f32>;
5335
+ @group(3) @binding(auto) var pbr_emissiveSamplerSampler: sampler;
3652
5336
  #endif
3653
5337
  #ifdef HAS_METALROUGHNESSMAP
3654
- uniform sampler2D pbr_metallicRoughnessSampler;
5338
+ @group(3) @binding(auto) var pbr_metallicRoughnessSampler: texture_2d<f32>;
5339
+ @group(3) @binding(auto) var pbr_metallicRoughnessSamplerSampler: sampler;
3655
5340
  #endif
3656
5341
  #ifdef HAS_OCCLUSIONMAP
3657
- uniform sampler2D pbr_occlusionSampler;
5342
+ @group(3) @binding(auto) var pbr_occlusionSampler: texture_2d<f32>;
5343
+ @group(3) @binding(auto) var pbr_occlusionSamplerSampler: sampler;
3658
5344
  #endif
3659
- #ifdef USE_IBL
3660
- uniform samplerCube pbr_diffuseEnvSampler;
3661
- uniform samplerCube pbr_specularEnvSampler;
3662
- uniform sampler2D pbr_brdfLUT;
5345
+ #ifdef HAS_SPECULARCOLORMAP
5346
+ @group(3) @binding(auto) var pbr_specularColorSampler: texture_2d<f32>;
5347
+ @group(3) @binding(auto) var pbr_specularColorSamplerSampler: sampler;
5348
+ #endif
5349
+ #ifdef HAS_SPECULARINTENSITYMAP
5350
+ @group(3) @binding(auto) var pbr_specularIntensitySampler: texture_2d<f32>;
5351
+ @group(3) @binding(auto) var pbr_specularIntensitySamplerSampler: sampler;
5352
+ #endif
5353
+ #ifdef HAS_TRANSMISSIONMAP
5354
+ @group(3) @binding(auto) var pbr_transmissionSampler: texture_2d<f32>;
5355
+ @group(3) @binding(auto) var pbr_transmissionSamplerSampler: sampler;
5356
+ #endif
5357
+ #ifdef HAS_THICKNESSMAP
5358
+ @group(3) @binding(auto) var pbr_thicknessSampler: texture_2d<f32>;
5359
+ @group(3) @binding(auto) var pbr_thicknessSamplerSampler: sampler;
5360
+ #endif
5361
+ #ifdef HAS_CLEARCOATMAP
5362
+ @group(3) @binding(auto) var pbr_clearcoatSampler: texture_2d<f32>;
5363
+ @group(3) @binding(auto) var pbr_clearcoatSamplerSampler: sampler;
5364
+ #endif
5365
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
5366
+ @group(3) @binding(auto) var pbr_clearcoatRoughnessSampler: texture_2d<f32>;
5367
+ @group(3) @binding(auto) var pbr_clearcoatRoughnessSamplerSampler: sampler;
5368
+ #endif
5369
+ #ifdef HAS_CLEARCOATNORMALMAP
5370
+ @group(3) @binding(auto) var pbr_clearcoatNormalSampler: texture_2d<f32>;
5371
+ @group(3) @binding(auto) var pbr_clearcoatNormalSamplerSampler: sampler;
5372
+ #endif
5373
+ #ifdef HAS_SHEENCOLORMAP
5374
+ @group(3) @binding(auto) var pbr_sheenColorSampler: texture_2d<f32>;
5375
+ @group(3) @binding(auto) var pbr_sheenColorSamplerSampler: sampler;
5376
+ #endif
5377
+ #ifdef HAS_SHEENROUGHNESSMAP
5378
+ @group(3) @binding(auto) var pbr_sheenRoughnessSampler: texture_2d<f32>;
5379
+ @group(3) @binding(auto) var pbr_sheenRoughnessSamplerSampler: sampler;
5380
+ #endif
5381
+ #ifdef HAS_IRIDESCENCEMAP
5382
+ @group(3) @binding(auto) var pbr_iridescenceSampler: texture_2d<f32>;
5383
+ @group(3) @binding(auto) var pbr_iridescenceSamplerSampler: sampler;
5384
+ #endif
5385
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
5386
+ @group(3) @binding(auto) var pbr_iridescenceThicknessSampler: texture_2d<f32>;
5387
+ @group(3) @binding(auto) var pbr_iridescenceThicknessSamplerSampler: sampler;
5388
+ #endif
5389
+ #ifdef HAS_ANISOTROPYMAP
5390
+ @group(3) @binding(auto) var pbr_anisotropySampler: texture_2d<f32>;
5391
+ @group(3) @binding(auto) var pbr_anisotropySamplerSampler: sampler;
3663
5392
  #endif
3664
-
3665
5393
  // Encapsulate the various inputs used by the various functions in the shading equation
3666
5394
  // We store values in this struct to simplify the integration of alternative implementations
3667
5395
  // of the shading terms, outlined in the Readme.MD Appendix.
@@ -3687,80 +5415,130 @@ const c_MinRoughness = 0.04;
3687
5415
 
3688
5416
  fn SRGBtoLINEAR(srgbIn: vec4f ) -> vec4f
3689
5417
  {
5418
+ var linOut: vec3f = srgbIn.xyz;
3690
5419
  #ifdef MANUAL_SRGB
5420
+ let bLess: vec3f = step(vec3f(0.04045), srgbIn.xyz);
5421
+ linOut = mix(
5422
+ srgbIn.xyz / vec3f(12.92),
5423
+ pow((srgbIn.xyz + vec3f(0.055)) / vec3f(1.055), vec3f(2.4)),
5424
+ bLess
5425
+ );
3691
5426
  #ifdef SRGB_FAST_APPROXIMATION
3692
- var linOut: vec3f = pow(srgbIn.xyz,vec3(2.2));
3693
- #else // SRGB_FAST_APPROXIMATION
3694
- var bLess: vec3f = step(vec3(0.04045),srgbIn.xyz);
3695
- var linOut: vec3f = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
3696
- #endif //SRGB_FAST_APPROXIMATION
3697
- return vec4f(linOut,srgbIn.w);;
3698
- #else //MANUAL_SRGB
3699
- return srgbIn;
3700
- #endif //MANUAL_SRGB
5427
+ linOut = pow(srgbIn.xyz, vec3f(2.2));
5428
+ #endif
5429
+ #endif
5430
+ return vec4f(linOut, srgbIn.w);
3701
5431
  }
3702
5432
 
3703
- // Find the normal for this fragment, pulling either from a predefined normal map
3704
- // or from the interpolated mesh normal and tangent attributes.
3705
- fn getNormal() -> vec3f
5433
+ // Build the tangent basis from interpolated attributes or screen-space derivatives.
5434
+ fn getTBN() -> mat3x3f
3706
5435
  {
3707
- // Retrieve the tangent space matrix
3708
- #ifndef HAS_TANGENTS
3709
- var pos_dx: vec3f = dFdx(fragmentInputs.pbr_vPosition);
3710
- var pos_dy: vec3f = dFdy(fragmentInputs.pbr_vPosition);
3711
- var tex_dx: vec3f = dFdx(vec3(fragmentInputs.pbr_vUV, 0.0));
3712
- var tex_dy: vec3f = dFdy(vec3(fragmentInputs.pbr_vUV, 0.0));
3713
- var t: vec3f = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t);
5436
+ let pos_dx: vec3f = dpdx(fragmentInputs.pbr_vPosition);
5437
+ let pos_dy: vec3f = dpdy(fragmentInputs.pbr_vPosition);
5438
+ let tex_dx: vec3f = dpdx(vec3f(fragmentInputs.pbr_vUV, 0.0));
5439
+ let tex_dy: vec3f = dpdy(vec3f(fragmentInputs.pbr_vUV, 0.0));
5440
+ var t: vec3f = (tex_dy.y * pos_dx - tex_dx.y * pos_dy) / (tex_dx.x * tex_dy.y - tex_dy.x * tex_dx.y);
3714
5441
 
3715
- #ifdef HAS_NORMALS
3716
- var ng: vec3f = normalize(fragmentInputs.pbr_vNormal);
3717
- #else
3718
5442
  var ng: vec3f = cross(pos_dx, pos_dy);
5443
+ #ifdef HAS_NORMALS
5444
+ ng = normalize(fragmentInputs.pbr_vNormal);
3719
5445
  #endif
3720
-
3721
5446
  t = normalize(t - ng * dot(ng, t));
3722
5447
  var b: vec3f = normalize(cross(ng, t));
3723
- var tbn: mat3f = mat3f(t, b, ng);
3724
- #else // HAS_TANGENTS
3725
- var tbn: mat3f = fragmentInputs.pbr_vTBN;
5448
+ var tbn: mat3x3f = mat3x3f(t, b, ng);
5449
+ #ifdef HAS_TANGENTS
5450
+ tbn = fragmentInputs.pbr_vTBN;
3726
5451
  #endif
3727
5452
 
3728
- #ifdef HAS_NORMALMAP
3729
- vec3 n = texture(pbr_normalSampler, fragmentInputs.pbr_vUV).rgb;
3730
- n = normalize(tbn * ((2.0 * n - 1.0) * vec3(pbrMaterial.normalScale, pbrMaterial.normalScale, 1.0)));
3731
- #else
5453
+ return tbn;
5454
+ }
5455
+
5456
+ // Find the normal for this fragment, pulling either from a predefined normal map
5457
+ // or from the interpolated mesh normal and tangent attributes.
5458
+ fn getMappedNormal(
5459
+ normalSampler: texture_2d<f32>,
5460
+ normalSamplerBinding: sampler,
5461
+ tbn: mat3x3f,
5462
+ normalScale: f32
5463
+ ) -> vec3f
5464
+ {
5465
+ let n = textureSample(normalSampler, normalSamplerBinding, fragmentInputs.pbr_vUV).rgb;
5466
+ return normalize(tbn * ((2.0 * n - 1.0) * vec3f(normalScale, normalScale, 1.0)));
5467
+ }
5468
+
5469
+ fn getNormal(tbn: mat3x3f) -> vec3f
5470
+ {
3732
5471
  // The tbn matrix is linearly interpolated, so we need to re-normalize
3733
- vec3 n = normalize(tbn[2].xyz);
5472
+ var n: vec3f = normalize(tbn[2].xyz);
5473
+ #ifdef HAS_NORMALMAP
5474
+ n = getMappedNormal(
5475
+ pbr_normalSampler,
5476
+ pbr_normalSamplerSampler,
5477
+ tbn,
5478
+ pbrMaterial.normalScale
5479
+ );
3734
5480
  #endif
3735
5481
 
3736
5482
  return n;
3737
5483
  }
3738
5484
 
5485
+ fn getClearcoatNormal(tbn: mat3x3f, baseNormal: vec3f) -> vec3f
5486
+ {
5487
+ #ifdef HAS_CLEARCOATNORMALMAP
5488
+ return getMappedNormal(
5489
+ pbr_clearcoatNormalSampler,
5490
+ pbr_clearcoatNormalSamplerSampler,
5491
+ tbn,
5492
+ 1.0
5493
+ );
5494
+ #else
5495
+ return baseNormal;
5496
+ #endif
5497
+ }
5498
+
3739
5499
  // Calculation of the lighting contribution from an optional Image Based Light source.
3740
5500
  // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
3741
5501
  // See our README.md on Environment Maps [3] for additional discussion.
3742
5502
  #ifdef USE_IBL
3743
- fn getIBLContribution(PBRInfo pbrInfo, vec3 n, vec3 reflection) -> vec3f
5503
+ fn getIBLContribution(pbrInfo: PBRInfo, n: vec3f, reflection: vec3f) -> vec3f
3744
5504
  {
3745
- float mipCount = 9.0; // resolution of 512x512
3746
- float lod = (pbrInfo.perceptualRoughness * mipCount);
5505
+ let mipCount: f32 = 9.0; // resolution of 512x512
5506
+ let lod: f32 = pbrInfo.perceptualRoughness * mipCount;
3747
5507
  // retrieve a scale and bias to F0. See [1], Figure 3
3748
- vec3 brdf = SRGBtoLINEAR(texture(pbr_brdfLUT,
3749
- vec2(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness))).rgb;
3750
- vec3 diffuseLight = SRGBtoLINEAR(texture(pbr_diffuseEnvSampler, n)).rgb;
3751
-
5508
+ let brdf = SRGBtoLINEAR(
5509
+ textureSampleLevel(
5510
+ pbr_brdfLUT,
5511
+ pbr_brdfLUTSampler,
5512
+ vec2f(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness),
5513
+ 0.0
5514
+ )
5515
+ ).rgb;
5516
+ let diffuseLight =
5517
+ SRGBtoLINEAR(
5518
+ textureSampleLevel(pbr_diffuseEnvSampler, pbr_diffuseEnvSamplerSampler, n, 0.0)
5519
+ ).rgb;
5520
+ var specularLight = SRGBtoLINEAR(
5521
+ textureSampleLevel(
5522
+ pbr_specularEnvSampler,
5523
+ pbr_specularEnvSamplerSampler,
5524
+ reflection,
5525
+ 0.0
5526
+ )
5527
+ ).rgb;
3752
5528
  #ifdef USE_TEX_LOD
3753
- vec3 specularLight = SRGBtoLINEAR(texture(pbr_specularEnvSampler, reflection, lod)).rgb;
3754
- #else
3755
- vec3 specularLight = SRGBtoLINEAR(texture(pbr_specularEnvSampler, reflection)).rgb;
5529
+ specularLight = SRGBtoLINEAR(
5530
+ textureSampleLevel(
5531
+ pbr_specularEnvSampler,
5532
+ pbr_specularEnvSamplerSampler,
5533
+ reflection,
5534
+ lod
5535
+ )
5536
+ ).rgb;
3756
5537
  #endif
3757
5538
 
3758
- vec3 diffuse = diffuseLight * pbrInfo.diffuseColor;
3759
- vec3 specular = specularLight * (pbrInfo.specularColor * brdf.x + brdf.y);
3760
-
3761
- // For presentation, this allows us to disable IBL terms
3762
- diffuse *= pbrMaterial.scaleIBLAmbient.x;
3763
- specular *= pbrMaterial.scaleIBLAmbient.y;
5539
+ let diffuse = diffuseLight * pbrInfo.diffuseColor * pbrMaterial.scaleIBLAmbient.x;
5540
+ let specular =
5541
+ specularLight * (pbrInfo.specularColor * brdf.x + brdf.y) * pbrMaterial.scaleIBLAmbient.y;
3764
5542
 
3765
5543
  return diffuse + specular;
3766
5544
  }
@@ -3804,7 +5582,173 @@ fn geometricOcclusion(pbrInfo: PBRInfo) -> f32 {
3804
5582
  fn microfacetDistribution(pbrInfo: PBRInfo) -> f32 {
3805
5583
  let roughnessSq = pbrInfo.alphaRoughness * pbrInfo.alphaRoughness;
3806
5584
  let f = (pbrInfo.NdotH * roughnessSq - pbrInfo.NdotH) * pbrInfo.NdotH + 1.0;
3807
- return roughnessSq / (PI * f * f);
5585
+ return roughnessSq / (M_PI * f * f);
5586
+ }
5587
+
5588
+ fn maxComponent(value: vec3f) -> f32 {
5589
+ return max(max(value.r, value.g), value.b);
5590
+ }
5591
+
5592
+ fn getDielectricF0(ior: f32) -> f32 {
5593
+ let clampedIor = max(ior, 1.0);
5594
+ let ratio = (clampedIor - 1.0) / (clampedIor + 1.0);
5595
+ return ratio * ratio;
5596
+ }
5597
+
5598
+ fn normalizeDirection(direction: vec2f) -> vec2f {
5599
+ let directionLength = length(direction);
5600
+ if (directionLength > 0.0001) {
5601
+ return direction / directionLength;
5602
+ }
5603
+
5604
+ return vec2f(1.0, 0.0);
5605
+ }
5606
+
5607
+ fn rotateDirection(direction: vec2f, rotation: f32) -> vec2f {
5608
+ let s = sin(rotation);
5609
+ let c = cos(rotation);
5610
+ return vec2f(direction.x * c - direction.y * s, direction.x * s + direction.y * c);
5611
+ }
5612
+
5613
+ fn getIridescenceTint(iridescence: f32, thickness: f32, NdotV: f32) -> vec3f {
5614
+ if (iridescence <= 0.0) {
5615
+ return vec3f(1.0);
5616
+ }
5617
+
5618
+ let phase = 0.015 * thickness * pbrMaterial.iridescenceIor + (1.0 - NdotV) * 6.0;
5619
+ let thinFilmTint =
5620
+ 0.5 +
5621
+ 0.5 *
5622
+ cos(vec3f(phase, phase + 2.0943951, phase + 4.1887902));
5623
+ return mix(vec3f(1.0), thinFilmTint, iridescence);
5624
+ }
5625
+
5626
+ fn getVolumeAttenuation(thickness: f32) -> vec3f {
5627
+ if (thickness <= 0.0) {
5628
+ return vec3f(1.0);
5629
+ }
5630
+
5631
+ let attenuationCoefficient =
5632
+ -log(max(pbrMaterial.attenuationColor, vec3f(0.0001))) /
5633
+ max(pbrMaterial.attenuationDistance, 0.0001);
5634
+ return exp(-attenuationCoefficient * thickness);
5635
+ }
5636
+
5637
+ fn createClearcoatPBRInfo(
5638
+ basePBRInfo: PBRInfo,
5639
+ clearcoatNormal: vec3f,
5640
+ clearcoatRoughness: f32
5641
+ ) -> PBRInfo {
5642
+ let perceptualRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
5643
+ let alphaRoughness = perceptualRoughness * perceptualRoughness;
5644
+ let NdotV = clamp(abs(dot(clearcoatNormal, basePBRInfo.v)), 0.001, 1.0);
5645
+
5646
+ return PBRInfo(
5647
+ basePBRInfo.NdotL,
5648
+ NdotV,
5649
+ basePBRInfo.NdotH,
5650
+ basePBRInfo.LdotH,
5651
+ basePBRInfo.VdotH,
5652
+ perceptualRoughness,
5653
+ 0.0,
5654
+ vec3f(0.04),
5655
+ vec3f(1.0),
5656
+ alphaRoughness,
5657
+ vec3f(0.0),
5658
+ vec3f(0.04),
5659
+ clearcoatNormal,
5660
+ basePBRInfo.v
5661
+ );
5662
+ }
5663
+
5664
+ fn calculateClearcoatContribution(
5665
+ pbrInfo: PBRInfo,
5666
+ lightColor: vec3f,
5667
+ clearcoatNormal: vec3f,
5668
+ clearcoatFactor: f32,
5669
+ clearcoatRoughness: f32
5670
+ ) -> vec3f {
5671
+ if (clearcoatFactor <= 0.0) {
5672
+ return vec3f(0.0);
5673
+ }
5674
+
5675
+ let clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
5676
+ return calculateFinalColor(clearcoatPBRInfo, lightColor) * clearcoatFactor;
5677
+ }
5678
+
5679
+ #ifdef USE_IBL
5680
+ fn calculateClearcoatIBLContribution(
5681
+ pbrInfo: PBRInfo,
5682
+ clearcoatNormal: vec3f,
5683
+ reflection: vec3f,
5684
+ clearcoatFactor: f32,
5685
+ clearcoatRoughness: f32
5686
+ ) -> vec3f {
5687
+ if (clearcoatFactor <= 0.0) {
5688
+ return vec3f(0.0);
5689
+ }
5690
+
5691
+ let clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
5692
+ return getIBLContribution(clearcoatPBRInfo, clearcoatNormal, reflection) * clearcoatFactor;
5693
+ }
5694
+ #endif
5695
+
5696
+ fn calculateSheenContribution(
5697
+ pbrInfo: PBRInfo,
5698
+ lightColor: vec3f,
5699
+ sheenColor: vec3f,
5700
+ sheenRoughness: f32
5701
+ ) -> vec3f {
5702
+ if (maxComponent(sheenColor) <= 0.0) {
5703
+ return vec3f(0.0);
5704
+ }
5705
+
5706
+ let sheenFresnel = pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0);
5707
+ let sheenVisibility = mix(1.0, pbrInfo.NdotL * pbrInfo.NdotV, sheenRoughness);
5708
+ return pbrInfo.NdotL *
5709
+ lightColor *
5710
+ sheenColor *
5711
+ (0.25 + 0.75 * sheenFresnel) *
5712
+ sheenVisibility *
5713
+ (1.0 - pbrInfo.metalness);
5714
+ }
5715
+
5716
+ fn calculateAnisotropyBoost(
5717
+ pbrInfo: PBRInfo,
5718
+ anisotropyTangent: vec3f,
5719
+ anisotropyStrength: f32
5720
+ ) -> f32 {
5721
+ if (anisotropyStrength <= 0.0) {
5722
+ return 1.0;
5723
+ }
5724
+
5725
+ let anisotropyBitangent = normalize(cross(pbrInfo.n, anisotropyTangent));
5726
+ let bitangentViewAlignment = abs(dot(pbrInfo.v, anisotropyBitangent));
5727
+ return mix(1.0, 0.65 + 0.7 * bitangentViewAlignment, anisotropyStrength);
5728
+ }
5729
+
5730
+ fn calculateMaterialLightColor(
5731
+ pbrInfo: PBRInfo,
5732
+ lightColor: vec3f,
5733
+ clearcoatNormal: vec3f,
5734
+ clearcoatFactor: f32,
5735
+ clearcoatRoughness: f32,
5736
+ sheenColor: vec3f,
5737
+ sheenRoughness: f32,
5738
+ anisotropyTangent: vec3f,
5739
+ anisotropyStrength: f32
5740
+ ) -> vec3f {
5741
+ let anisotropyBoost = calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
5742
+ var color = calculateFinalColor(pbrInfo, lightColor) * anisotropyBoost;
5743
+ color += calculateClearcoatContribution(
5744
+ pbrInfo,
5745
+ lightColor,
5746
+ clearcoatNormal,
5747
+ clearcoatFactor,
5748
+ clearcoatRoughness
5749
+ );
5750
+ color += calculateSheenContribution(pbrInfo, lightColor, sheenColor, sheenRoughness);
5751
+ return color;
3808
5752
  }
3809
5753
 
3810
5754
  fn PBRInfo_setAmbientLight(pbrInfo: ptr<function, PBRInfo>) {
@@ -3831,6 +5775,11 @@ fn PBRInfo_setPointLight(pbrInfo: ptr<function, PBRInfo>, pointLight: PointLight
3831
5775
  PBRInfo_setDirectionalLight(pbrInfo, light_direction);
3832
5776
  }
3833
5777
 
5778
+ fn PBRInfo_setSpotLight(pbrInfo: ptr<function, PBRInfo>, spotLight: SpotLight) {
5779
+ let light_direction = normalize(spotLight.position - fragmentInputs.pbr_vPosition);
5780
+ PBRInfo_setDirectionalLight(pbrInfo, light_direction);
5781
+ }
5782
+
3834
5783
  fn calculateFinalColor(pbrInfo: PBRInfo, lightColor: vec3<f32>) -> vec3<f32> {
3835
5784
  // Calculate the shading terms for the microfacet specular shading model
3836
5785
  let F = specularReflection(pbrInfo);
@@ -3846,11 +5795,11 @@ fn calculateFinalColor(pbrInfo: PBRInfo, lightColor: vec3<f32>) -> vec3<f32> {
3846
5795
 
3847
5796
  fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
3848
5797
  // The albedo may be defined from a base texture or a flat color
3849
- var baseColor: vec4<f32>;
5798
+ var baseColor: vec4<f32> = pbrMaterial.baseColorFactor;
3850
5799
  #ifdef HAS_BASECOLORMAP
3851
- baseColor = SRGBtoLINEAR(textureSample(pbr_baseColorSampler, pbr_baseColorSampler, fragmentInputs.pbr_vUV)) * pbrMaterial.baseColorFactor;
3852
- #else
3853
- baseColor = pbrMaterial.baseColorFactor;
5800
+ baseColor = SRGBtoLINEAR(
5801
+ textureSample(pbr_baseColorSampler, pbr_baseColorSamplerSampler, fragmentInputs.pbr_vUV)
5802
+ ) * pbrMaterial.baseColorFactor;
3854
5803
  #endif
3855
5804
 
3856
5805
  #ifdef ALPHA_CUTOFF
@@ -3860,8 +5809,9 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
3860
5809
  #endif
3861
5810
 
3862
5811
  var color = vec3<f32>(0.0, 0.0, 0.0);
5812
+ var transmission = 0.0;
3863
5813
 
3864
- if (pbrMaterial.unlit) {
5814
+ if (pbrMaterial.unlit != 0u) {
3865
5815
  color = baseColor.rgb;
3866
5816
  } else {
3867
5817
  // Metallic and Roughness material properties are packed together
@@ -3872,20 +5822,318 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
3872
5822
  #ifdef HAS_METALROUGHNESSMAP
3873
5823
  // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
3874
5824
  // This layout intentionally reserves the 'r' channel for (optional) occlusion map data
3875
- let mrSample = textureSample(pbr_metallicRoughnessSampler, pbr_metallicRoughnessSampler, fragmentInputs.pbr_vUV);
5825
+ let mrSample = textureSample(
5826
+ pbr_metallicRoughnessSampler,
5827
+ pbr_metallicRoughnessSamplerSampler,
5828
+ fragmentInputs.pbr_vUV
5829
+ );
3876
5830
  perceptualRoughness = mrSample.g * perceptualRoughness;
3877
5831
  metallic = mrSample.b * metallic;
3878
5832
  #endif
3879
5833
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
3880
5834
  metallic = clamp(metallic, 0.0, 1.0);
5835
+ let tbn = getTBN();
5836
+ let n = getNormal(tbn); // normal at surface point
5837
+ let v = normalize(pbrProjection.camera - fragmentInputs.pbr_vPosition); // Vector from surface point to camera
5838
+ let NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
5839
+ var useExtendedPBR = false;
5840
+ #ifdef USE_MATERIAL_EXTENSIONS
5841
+ useExtendedPBR =
5842
+ pbrMaterial.specularColorMapEnabled != 0 ||
5843
+ pbrMaterial.specularIntensityMapEnabled != 0 ||
5844
+ abs(pbrMaterial.specularIntensityFactor - 1.0) > 0.0001 ||
5845
+ maxComponent(abs(pbrMaterial.specularColorFactor - vec3f(1.0))) > 0.0001 ||
5846
+ abs(pbrMaterial.ior - 1.5) > 0.0001 ||
5847
+ pbrMaterial.transmissionMapEnabled != 0 ||
5848
+ pbrMaterial.transmissionFactor > 0.0001 ||
5849
+ pbrMaterial.clearcoatMapEnabled != 0 ||
5850
+ pbrMaterial.clearcoatRoughnessMapEnabled != 0 ||
5851
+ pbrMaterial.clearcoatFactor > 0.0001 ||
5852
+ pbrMaterial.clearcoatRoughnessFactor > 0.0001 ||
5853
+ pbrMaterial.sheenColorMapEnabled != 0 ||
5854
+ pbrMaterial.sheenRoughnessMapEnabled != 0 ||
5855
+ maxComponent(pbrMaterial.sheenColorFactor) > 0.0001 ||
5856
+ pbrMaterial.sheenRoughnessFactor > 0.0001 ||
5857
+ pbrMaterial.iridescenceMapEnabled != 0 ||
5858
+ pbrMaterial.iridescenceFactor > 0.0001 ||
5859
+ abs(pbrMaterial.iridescenceIor - 1.3) > 0.0001 ||
5860
+ abs(pbrMaterial.iridescenceThicknessRange.x - 100.0) > 0.0001 ||
5861
+ abs(pbrMaterial.iridescenceThicknessRange.y - 400.0) > 0.0001 ||
5862
+ pbrMaterial.anisotropyMapEnabled != 0 ||
5863
+ pbrMaterial.anisotropyStrength > 0.0001 ||
5864
+ abs(pbrMaterial.anisotropyRotation) > 0.0001 ||
5865
+ length(pbrMaterial.anisotropyDirection - vec2f(1.0, 0.0)) > 0.0001;
5866
+ #endif
5867
+
5868
+ if (!useExtendedPBR) {
5869
+ let alphaRoughness = perceptualRoughness * perceptualRoughness;
5870
+
5871
+ let f0 = vec3<f32>(0.04);
5872
+ var diffuseColor = baseColor.rgb * (vec3<f32>(1.0) - f0);
5873
+ diffuseColor *= 1.0 - metallic;
5874
+ let specularColor = mix(f0, baseColor.rgb, metallic);
5875
+
5876
+ let reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
5877
+ let reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
5878
+ let specularEnvironmentR0 = specularColor;
5879
+ let specularEnvironmentR90 = vec3<f32>(1.0, 1.0, 1.0) * reflectance90;
5880
+ let reflection = -normalize(reflect(v, n));
5881
+
5882
+ var pbrInfo = PBRInfo(
5883
+ 0.0, // NdotL
5884
+ NdotV,
5885
+ 0.0, // NdotH
5886
+ 0.0, // LdotH
5887
+ 0.0, // VdotH
5888
+ perceptualRoughness,
5889
+ metallic,
5890
+ specularEnvironmentR0,
5891
+ specularEnvironmentR90,
5892
+ alphaRoughness,
5893
+ diffuseColor,
5894
+ specularColor,
5895
+ n,
5896
+ v
5897
+ );
5898
+
5899
+ #ifdef USE_LIGHTS
5900
+ PBRInfo_setAmbientLight(&pbrInfo);
5901
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor);
5902
+
5903
+ for (var i = 0; i < lighting.directionalLightCount; i++) {
5904
+ if (i < lighting.directionalLightCount) {
5905
+ PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
5906
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
5907
+ }
5908
+ }
5909
+
5910
+ for (var i = 0; i < lighting.pointLightCount; i++) {
5911
+ if (i < lighting.pointLightCount) {
5912
+ PBRInfo_setPointLight(&pbrInfo, lighting_getPointLight(i));
5913
+ let attenuation = getPointLightAttenuation(
5914
+ lighting_getPointLight(i),
5915
+ distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
5916
+ );
5917
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
5918
+ }
5919
+ }
5920
+
5921
+ for (var i = 0; i < lighting.spotLightCount; i++) {
5922
+ if (i < lighting.spotLightCount) {
5923
+ PBRInfo_setSpotLight(&pbrInfo, lighting_getSpotLight(i));
5924
+ let attenuation = getSpotLightAttenuation(
5925
+ lighting_getSpotLight(i),
5926
+ fragmentInputs.pbr_vPosition
5927
+ );
5928
+ color += calculateFinalColor(pbrInfo, lighting_getSpotLight(i).color / attenuation);
5929
+ }
5930
+ }
5931
+ #endif
5932
+
5933
+ #ifdef USE_IBL
5934
+ if (pbrMaterial.IBLenabled != 0) {
5935
+ color += getIBLContribution(pbrInfo, n, reflection);
5936
+ }
5937
+ #endif
5938
+
5939
+ #ifdef HAS_OCCLUSIONMAP
5940
+ if (pbrMaterial.occlusionMapEnabled != 0) {
5941
+ let ao =
5942
+ textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, fragmentInputs.pbr_vUV).r;
5943
+ color = mix(color, color * ao, pbrMaterial.occlusionStrength);
5944
+ }
5945
+ #endif
5946
+
5947
+ var emissive = pbrMaterial.emissiveFactor;
5948
+ #ifdef HAS_EMISSIVEMAP
5949
+ if (pbrMaterial.emissiveMapEnabled != 0u) {
5950
+ emissive *= SRGBtoLINEAR(
5951
+ textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
5952
+ ).rgb;
5953
+ }
5954
+ #endif
5955
+ color += emissive * pbrMaterial.emissiveStrength;
5956
+
5957
+ #ifdef PBR_DEBUG
5958
+ color = mix(color, baseColor.rgb, pbrMaterial.scaleDiffBaseMR.y);
5959
+ color = mix(color, vec3<f32>(metallic), pbrMaterial.scaleDiffBaseMR.z);
5960
+ color = mix(color, vec3<f32>(perceptualRoughness), pbrMaterial.scaleDiffBaseMR.w);
5961
+ #endif
5962
+
5963
+ return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), baseColor.a);
5964
+ }
5965
+
5966
+ var specularIntensity = pbrMaterial.specularIntensityFactor;
5967
+ #ifdef HAS_SPECULARINTENSITYMAP
5968
+ if (pbrMaterial.specularIntensityMapEnabled != 0) {
5969
+ specularIntensity *= textureSample(
5970
+ pbr_specularIntensitySampler,
5971
+ pbr_specularIntensitySamplerSampler,
5972
+ fragmentInputs.pbr_vUV
5973
+ ).a;
5974
+ }
5975
+ #endif
5976
+
5977
+ var specularFactor = pbrMaterial.specularColorFactor;
5978
+ #ifdef HAS_SPECULARCOLORMAP
5979
+ if (pbrMaterial.specularColorMapEnabled != 0) {
5980
+ specularFactor *= SRGBtoLINEAR(
5981
+ textureSample(
5982
+ pbr_specularColorSampler,
5983
+ pbr_specularColorSamplerSampler,
5984
+ fragmentInputs.pbr_vUV
5985
+ )
5986
+ ).rgb;
5987
+ }
5988
+ #endif
5989
+
5990
+ transmission = pbrMaterial.transmissionFactor;
5991
+ #ifdef HAS_TRANSMISSIONMAP
5992
+ if (pbrMaterial.transmissionMapEnabled != 0) {
5993
+ transmission *= textureSample(
5994
+ pbr_transmissionSampler,
5995
+ pbr_transmissionSamplerSampler,
5996
+ fragmentInputs.pbr_vUV
5997
+ ).r;
5998
+ }
5999
+ #endif
6000
+ transmission = clamp(transmission * (1.0 - metallic), 0.0, 1.0);
6001
+ var thickness = max(pbrMaterial.thicknessFactor, 0.0);
6002
+ #ifdef HAS_THICKNESSMAP
6003
+ thickness *= textureSample(
6004
+ pbr_thicknessSampler,
6005
+ pbr_thicknessSamplerSampler,
6006
+ fragmentInputs.pbr_vUV
6007
+ ).g;
6008
+ #endif
6009
+
6010
+ var clearcoatFactor = pbrMaterial.clearcoatFactor;
6011
+ var clearcoatRoughness = pbrMaterial.clearcoatRoughnessFactor;
6012
+ #ifdef HAS_CLEARCOATMAP
6013
+ if (pbrMaterial.clearcoatMapEnabled != 0) {
6014
+ clearcoatFactor *= textureSample(
6015
+ pbr_clearcoatSampler,
6016
+ pbr_clearcoatSamplerSampler,
6017
+ fragmentInputs.pbr_vUV
6018
+ ).r;
6019
+ }
6020
+ #endif
6021
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
6022
+ if (pbrMaterial.clearcoatRoughnessMapEnabled != 0) {
6023
+ clearcoatRoughness *= textureSample(
6024
+ pbr_clearcoatRoughnessSampler,
6025
+ pbr_clearcoatRoughnessSamplerSampler,
6026
+ fragmentInputs.pbr_vUV
6027
+ ).g;
6028
+ }
6029
+ #endif
6030
+ clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
6031
+ clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
6032
+ let clearcoatNormal = getClearcoatNormal(tbn, n);
6033
+
6034
+ var sheenColor = pbrMaterial.sheenColorFactor;
6035
+ var sheenRoughness = pbrMaterial.sheenRoughnessFactor;
6036
+ #ifdef HAS_SHEENCOLORMAP
6037
+ if (pbrMaterial.sheenColorMapEnabled != 0) {
6038
+ sheenColor *= SRGBtoLINEAR(
6039
+ textureSample(
6040
+ pbr_sheenColorSampler,
6041
+ pbr_sheenColorSamplerSampler,
6042
+ fragmentInputs.pbr_vUV
6043
+ )
6044
+ ).rgb;
6045
+ }
6046
+ #endif
6047
+ #ifdef HAS_SHEENROUGHNESSMAP
6048
+ if (pbrMaterial.sheenRoughnessMapEnabled != 0) {
6049
+ sheenRoughness *= textureSample(
6050
+ pbr_sheenRoughnessSampler,
6051
+ pbr_sheenRoughnessSamplerSampler,
6052
+ fragmentInputs.pbr_vUV
6053
+ ).a;
6054
+ }
6055
+ #endif
6056
+ sheenRoughness = clamp(sheenRoughness, c_MinRoughness, 1.0);
6057
+
6058
+ var iridescence = pbrMaterial.iridescenceFactor;
6059
+ #ifdef HAS_IRIDESCENCEMAP
6060
+ if (pbrMaterial.iridescenceMapEnabled != 0) {
6061
+ iridescence *= textureSample(
6062
+ pbr_iridescenceSampler,
6063
+ pbr_iridescenceSamplerSampler,
6064
+ fragmentInputs.pbr_vUV
6065
+ ).r;
6066
+ }
6067
+ #endif
6068
+ iridescence = clamp(iridescence, 0.0, 1.0);
6069
+ var iridescenceThickness = mix(
6070
+ pbrMaterial.iridescenceThicknessRange.x,
6071
+ pbrMaterial.iridescenceThicknessRange.y,
6072
+ 0.5
6073
+ );
6074
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
6075
+ iridescenceThickness = mix(
6076
+ pbrMaterial.iridescenceThicknessRange.x,
6077
+ pbrMaterial.iridescenceThicknessRange.y,
6078
+ textureSample(
6079
+ pbr_iridescenceThicknessSampler,
6080
+ pbr_iridescenceThicknessSamplerSampler,
6081
+ fragmentInputs.pbr_vUV
6082
+ ).g
6083
+ );
6084
+ #endif
6085
+
6086
+ var anisotropyStrength = clamp(pbrMaterial.anisotropyStrength, 0.0, 1.0);
6087
+ var anisotropyDirection = normalizeDirection(pbrMaterial.anisotropyDirection);
6088
+ #ifdef HAS_ANISOTROPYMAP
6089
+ if (pbrMaterial.anisotropyMapEnabled != 0) {
6090
+ let anisotropySample = textureSample(
6091
+ pbr_anisotropySampler,
6092
+ pbr_anisotropySamplerSampler,
6093
+ fragmentInputs.pbr_vUV
6094
+ ).rgb;
6095
+ anisotropyStrength *= anisotropySample.b;
6096
+ let mappedDirection = anisotropySample.rg * 2.0 - 1.0;
6097
+ if (length(mappedDirection) > 0.0001) {
6098
+ anisotropyDirection = normalize(mappedDirection);
6099
+ }
6100
+ }
6101
+ #endif
6102
+ anisotropyDirection = rotateDirection(anisotropyDirection, pbrMaterial.anisotropyRotation);
6103
+ var anisotropyTangent =
6104
+ normalize(tbn[0] * anisotropyDirection.x + tbn[1] * anisotropyDirection.y);
6105
+ if (length(anisotropyTangent) < 0.0001) {
6106
+ anisotropyTangent = normalize(tbn[0]);
6107
+ }
6108
+ let anisotropyViewAlignment = abs(dot(v, anisotropyTangent));
6109
+ perceptualRoughness = mix(
6110
+ perceptualRoughness,
6111
+ clamp(perceptualRoughness * (1.0 - 0.6 * anisotropyViewAlignment), c_MinRoughness, 1.0),
6112
+ anisotropyStrength
6113
+ );
6114
+
3881
6115
  // Roughness is authored as perceptual roughness; as is convention,
3882
6116
  // convert to material roughness by squaring the perceptual roughness [2].
3883
6117
  let alphaRoughness = perceptualRoughness * perceptualRoughness;
3884
6118
 
3885
- let f0 = vec3<f32>(0.04);
3886
- var diffuseColor = baseColor.rgb * (vec3<f32>(1.0) - f0);
3887
- diffuseColor *= 1.0 - metallic;
3888
- let specularColor = mix(f0, baseColor.rgb, metallic);
6119
+ let dielectricF0 = getDielectricF0(pbrMaterial.ior);
6120
+ var dielectricSpecularF0 = min(
6121
+ vec3f(dielectricF0) * specularFactor * specularIntensity,
6122
+ vec3f(1.0)
6123
+ );
6124
+ let iridescenceTint = getIridescenceTint(iridescence, iridescenceThickness, NdotV);
6125
+ dielectricSpecularF0 = mix(
6126
+ dielectricSpecularF0,
6127
+ dielectricSpecularF0 * iridescenceTint,
6128
+ iridescence
6129
+ );
6130
+ var diffuseColor = baseColor.rgb * (vec3f(1.0) - dielectricSpecularF0);
6131
+ diffuseColor *= (1.0 - metallic) * (1.0 - transmission);
6132
+ var specularColor = mix(dielectricSpecularF0, baseColor.rgb, metallic);
6133
+
6134
+ let baseLayerEnergy = 1.0 - clearcoatFactor * 0.25;
6135
+ diffuseColor *= baseLayerEnergy;
6136
+ specularColor *= baseLayerEnergy;
3889
6137
 
3890
6138
  // Compute reflectance.
3891
6139
  let reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
@@ -3897,11 +6145,6 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
3897
6145
  let reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
3898
6146
  let specularEnvironmentR0 = specularColor;
3899
6147
  let specularEnvironmentR90 = vec3<f32>(1.0, 1.0, 1.0) * reflectance90;
3900
-
3901
- let n = getNormal(); // normal at surface point
3902
- let v = normalize(pbrProjection.camera - fragmentInputs.pbr_vPosition); // Vector from surface point to camera
3903
-
3904
- let NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
3905
6148
  let reflection = -normalize(reflect(v, n));
3906
6149
 
3907
6150
  var pbrInfo = PBRInfo(
@@ -3924,13 +6167,33 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
3924
6167
  #ifdef USE_LIGHTS
3925
6168
  // Apply ambient light
3926
6169
  PBRInfo_setAmbientLight(&pbrInfo);
3927
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
6170
+ color += calculateMaterialLightColor(
6171
+ pbrInfo,
6172
+ lighting.ambientColor,
6173
+ clearcoatNormal,
6174
+ clearcoatFactor,
6175
+ clearcoatRoughness,
6176
+ sheenColor,
6177
+ sheenRoughness,
6178
+ anisotropyTangent,
6179
+ anisotropyStrength
6180
+ );
3928
6181
 
3929
6182
  // Apply directional light
3930
6183
  for (var i = 0; i < lighting.directionalLightCount; i++) {
3931
6184
  if (i < lighting.directionalLightCount) {
3932
6185
  PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
3933
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
6186
+ color += calculateMaterialLightColor(
6187
+ pbrInfo,
6188
+ lighting_getDirectionalLight(i).color,
6189
+ clearcoatNormal,
6190
+ clearcoatFactor,
6191
+ clearcoatRoughness,
6192
+ sheenColor,
6193
+ sheenRoughness,
6194
+ anisotropyTangent,
6195
+ anisotropyStrength
6196
+ );
3934
6197
  }
3935
6198
  }
3936
6199
 
@@ -3942,32 +6205,77 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
3942
6205
  lighting_getPointLight(i),
3943
6206
  distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
3944
6207
  );
3945
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
6208
+ color += calculateMaterialLightColor(
6209
+ pbrInfo,
6210
+ lighting_getPointLight(i).color / attenuation,
6211
+ clearcoatNormal,
6212
+ clearcoatFactor,
6213
+ clearcoatRoughness,
6214
+ sheenColor,
6215
+ sheenRoughness,
6216
+ anisotropyTangent,
6217
+ anisotropyStrength
6218
+ );
6219
+ }
6220
+ }
6221
+
6222
+ for (var i = 0; i < lighting.spotLightCount; i++) {
6223
+ if (i < lighting.spotLightCount) {
6224
+ PBRInfo_setSpotLight(&pbrInfo, lighting_getSpotLight(i));
6225
+ let attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), fragmentInputs.pbr_vPosition);
6226
+ color += calculateMaterialLightColor(
6227
+ pbrInfo,
6228
+ lighting_getSpotLight(i).color / attenuation,
6229
+ clearcoatNormal,
6230
+ clearcoatFactor,
6231
+ clearcoatRoughness,
6232
+ sheenColor,
6233
+ sheenRoughness,
6234
+ anisotropyTangent,
6235
+ anisotropyStrength
6236
+ );
3946
6237
  }
3947
6238
  }
3948
6239
  #endif
3949
6240
 
3950
6241
  // Calculate lighting contribution from image based lighting source (IBL)
3951
6242
  #ifdef USE_IBL
3952
- if (pbrMaterial.IBLenabled) {
3953
- color += getIBLContribution(pbrInfo, n, reflection);
6243
+ if (pbrMaterial.IBLenabled != 0) {
6244
+ color += getIBLContribution(pbrInfo, n, reflection) *
6245
+ calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
6246
+ color += calculateClearcoatIBLContribution(
6247
+ pbrInfo,
6248
+ clearcoatNormal,
6249
+ -normalize(reflect(v, clearcoatNormal)),
6250
+ clearcoatFactor,
6251
+ clearcoatRoughness
6252
+ );
6253
+ color += sheenColor * pbrMaterial.scaleIBLAmbient.x * (1.0 - sheenRoughness) * 0.25;
3954
6254
  }
3955
6255
  #endif
3956
6256
 
3957
6257
  // Apply optional PBR terms for additional (optional) shading
3958
6258
  #ifdef HAS_OCCLUSIONMAP
3959
- if (pbrMaterial.occlusionMapEnabled) {
3960
- let ao = textureSample(pbr_occlusionSampler, pbr_occlusionSampler, fragmentInputs.pbr_vUV).r;
6259
+ if (pbrMaterial.occlusionMapEnabled != 0) {
6260
+ let ao =
6261
+ textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, fragmentInputs.pbr_vUV).r;
3961
6262
  color = mix(color, color * ao, pbrMaterial.occlusionStrength);
3962
6263
  }
3963
6264
  #endif
3964
6265
 
6266
+ var emissive = pbrMaterial.emissiveFactor;
3965
6267
  #ifdef HAS_EMISSIVEMAP
3966
- if (pbrMaterial.emissiveMapEnabled) {
3967
- let emissive = SRGBtoLINEAR(textureSample(pbr_emissiveSampler, pbr_emissiveSampler, fragmentInputs.pbr_vUV)).rgb * pbrMaterial.emissiveFactor;
3968
- color += emissive;
6268
+ if (pbrMaterial.emissiveMapEnabled != 0u) {
6269
+ emissive *= SRGBtoLINEAR(
6270
+ textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
6271
+ ).rgb;
3969
6272
  }
3970
6273
  #endif
6274
+ color += emissive * pbrMaterial.emissiveStrength;
6275
+
6276
+ if (transmission > 0.0) {
6277
+ color = mix(color, color * getVolumeAttenuation(thickness), transmission);
6278
+ }
3971
6279
 
3972
6280
  // This section uses mix to override final color for reference app visualization
3973
6281
  // of various parameters in the lighting equation.
@@ -3986,7 +6294,8 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
3986
6294
  #endif
3987
6295
  }
3988
6296
 
3989
- return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), baseColor.a);
6297
+ let alpha = clamp(baseColor.a * (1.0 - transmission), 0.0, 1.0);
6298
+ return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), alpha);
3990
6299
  }
3991
6300
  `
3992
6301
  );
@@ -4002,8 +6311,22 @@ var uniformBlock = (
4002
6311
  } pbrProjection;
4003
6312
  `
4004
6313
  );
6314
+ var wgslUniformBlock = (
6315
+ /* wgsl */
6316
+ `struct pbrProjectionUniforms {
6317
+ modelViewProjectionMatrix: mat4x4<f32>,
6318
+ modelMatrix: mat4x4<f32>,
6319
+ normalMatrix: mat4x4<f32>,
6320
+ camera: vec3<f32>
6321
+ };
6322
+
6323
+ @group(0) @binding(auto) var<uniform> pbrProjection: pbrProjectionUniforms;
6324
+ `
6325
+ );
4005
6326
  var pbrProjection = {
4006
6327
  name: "pbrProjection",
6328
+ bindingLayout: [{ name: "pbrProjection", group: 0 }],
6329
+ source: wgslUniformBlock,
4007
6330
  vs: uniformBlock,
4008
6331
  fs: uniformBlock,
4009
6332
  // TODO why is this needed?
@@ -4012,7 +6335,7 @@ var pbrProjection = {
4012
6335
  modelViewProjectionMatrix: "mat4x4<f32>",
4013
6336
  modelMatrix: "mat4x4<f32>",
4014
6337
  normalMatrix: "mat4x4<f32>",
4015
- camera: "vec3<i32>"
6338
+ camera: "vec3<f32>"
4016
6339
  }
4017
6340
  };
4018
6341
 
@@ -4020,11 +6343,78 @@ var pbrProjection = {
4020
6343
  var pbrMaterial = {
4021
6344
  props: {},
4022
6345
  uniforms: {},
6346
+ defaultUniforms: {
6347
+ unlit: false,
6348
+ baseColorMapEnabled: false,
6349
+ baseColorFactor: [1, 1, 1, 1],
6350
+ normalMapEnabled: false,
6351
+ normalScale: 1,
6352
+ emissiveMapEnabled: false,
6353
+ emissiveFactor: [0, 0, 0],
6354
+ metallicRoughnessValues: [1, 1],
6355
+ metallicRoughnessMapEnabled: false,
6356
+ occlusionMapEnabled: false,
6357
+ occlusionStrength: 1,
6358
+ alphaCutoffEnabled: false,
6359
+ alphaCutoff: 0.5,
6360
+ IBLenabled: false,
6361
+ scaleIBLAmbient: [1, 1],
6362
+ scaleDiffBaseMR: [0, 0, 0, 0],
6363
+ scaleFGDSpec: [0, 0, 0, 0],
6364
+ specularColorFactor: [1, 1, 1],
6365
+ specularIntensityFactor: 1,
6366
+ specularColorMapEnabled: false,
6367
+ specularIntensityMapEnabled: false,
6368
+ ior: 1.5,
6369
+ transmissionFactor: 0,
6370
+ transmissionMapEnabled: false,
6371
+ thicknessFactor: 0,
6372
+ attenuationDistance: 1e9,
6373
+ attenuationColor: [1, 1, 1],
6374
+ clearcoatFactor: 0,
6375
+ clearcoatRoughnessFactor: 0,
6376
+ clearcoatMapEnabled: false,
6377
+ clearcoatRoughnessMapEnabled: false,
6378
+ sheenColorFactor: [0, 0, 0],
6379
+ sheenRoughnessFactor: 0,
6380
+ sheenColorMapEnabled: false,
6381
+ sheenRoughnessMapEnabled: false,
6382
+ iridescenceFactor: 0,
6383
+ iridescenceIor: 1.3,
6384
+ iridescenceThicknessRange: [100, 400],
6385
+ iridescenceMapEnabled: false,
6386
+ anisotropyStrength: 0,
6387
+ anisotropyRotation: 0,
6388
+ anisotropyDirection: [1, 0],
6389
+ anisotropyMapEnabled: false,
6390
+ emissiveStrength: 1
6391
+ },
4023
6392
  name: "pbrMaterial",
4024
- dependencies: [lighting, pbrProjection],
4025
- source: source2,
4026
- vs: vs2,
4027
- fs: fs3,
6393
+ firstBindingSlot: 0,
6394
+ bindingLayout: [
6395
+ { name: "pbrMaterial", group: 3 },
6396
+ { name: "pbr_baseColorSampler", group: 3 },
6397
+ { name: "pbr_normalSampler", group: 3 },
6398
+ { name: "pbr_emissiveSampler", group: 3 },
6399
+ { name: "pbr_metallicRoughnessSampler", group: 3 },
6400
+ { name: "pbr_occlusionSampler", group: 3 },
6401
+ { name: "pbr_specularColorSampler", group: 3 },
6402
+ { name: "pbr_specularIntensitySampler", group: 3 },
6403
+ { name: "pbr_transmissionSampler", group: 3 },
6404
+ { name: "pbr_thicknessSampler", group: 3 },
6405
+ { name: "pbr_clearcoatSampler", group: 3 },
6406
+ { name: "pbr_clearcoatRoughnessSampler", group: 3 },
6407
+ { name: "pbr_clearcoatNormalSampler", group: 3 },
6408
+ { name: "pbr_sheenColorSampler", group: 3 },
6409
+ { name: "pbr_sheenRoughnessSampler", group: 3 },
6410
+ { name: "pbr_iridescenceSampler", group: 3 },
6411
+ { name: "pbr_iridescenceThicknessSampler", group: 3 },
6412
+ { name: "pbr_anisotropySampler", group: 3 }
6413
+ ],
6414
+ dependencies: [lighting, ibl, pbrProjection],
6415
+ source: source3,
6416
+ vs: vs3,
6417
+ fs: fs4,
4028
6418
  defines: {
4029
6419
  LIGHTING_FRAGMENT: true,
4030
6420
  HAS_NORMALMAP: false,
@@ -4035,10 +6425,16 @@ var pbrMaterial = {
4035
6425
  HAS_SPECULARCOLORMAP: false,
4036
6426
  HAS_SPECULARINTENSITYMAP: false,
4037
6427
  HAS_TRANSMISSIONMAP: false,
6428
+ HAS_THICKNESSMAP: false,
4038
6429
  HAS_CLEARCOATMAP: false,
6430
+ HAS_CLEARCOATROUGHNESSMAP: false,
6431
+ HAS_CLEARCOATNORMALMAP: false,
4039
6432
  HAS_SHEENCOLORMAP: false,
6433
+ HAS_SHEENROUGHNESSMAP: false,
4040
6434
  HAS_IRIDESCENCEMAP: false,
6435
+ HAS_IRIDESCENCETHICKNESSMAP: false,
4041
6436
  HAS_ANISOTROPYMAP: false,
6437
+ USE_MATERIAL_EXTENSIONS: false,
4042
6438
  ALPHA_CUTOFF: false,
4043
6439
  USE_IBL: false,
4044
6440
  PBR_DEBUG: false
@@ -4064,14 +6460,6 @@ var pbrMaterial = {
4064
6460
  alphaCutoffEnabled: "i32",
4065
6461
  alphaCutoff: "f32",
4066
6462
  // #ifdef ALPHA_CUTOFF
4067
- // IBL
4068
- IBLenabled: "i32",
4069
- scaleIBLAmbient: "vec2<f32>",
4070
- // #ifdef USE_IBL
4071
- // debugging flags used for shader output of intermediate PBR variables
4072
- // #ifdef PBR_DEBUG
4073
- scaleDiffBaseMR: "vec4<f32>",
4074
- scaleFGDSpec: "vec4<f32>",
4075
6463
  specularColorFactor: "vec3<f32>",
4076
6464
  specularIntensityFactor: "f32",
4077
6465
  specularColorMapEnabled: "i32",
@@ -4085,9 +6473,11 @@ var pbrMaterial = {
4085
6473
  clearcoatFactor: "f32",
4086
6474
  clearcoatRoughnessFactor: "f32",
4087
6475
  clearcoatMapEnabled: "i32",
6476
+ clearcoatRoughnessMapEnabled: "i32",
4088
6477
  sheenColorFactor: "vec3<f32>",
4089
6478
  sheenRoughnessFactor: "f32",
4090
6479
  sheenColorMapEnabled: "i32",
6480
+ sheenRoughnessMapEnabled: "i32",
4091
6481
  iridescenceFactor: "f32",
4092
6482
  iridescenceIor: "f32",
4093
6483
  iridescenceThicknessRange: "vec2<f32>",
@@ -4096,7 +6486,15 @@ var pbrMaterial = {
4096
6486
  anisotropyRotation: "f32",
4097
6487
  anisotropyDirection: "vec2<f32>",
4098
6488
  anisotropyMapEnabled: "i32",
4099
- emissiveStrength: "f32"
6489
+ emissiveStrength: "f32",
6490
+ // IBL
6491
+ IBLenabled: "i32",
6492
+ scaleIBLAmbient: "vec2<f32>",
6493
+ // #ifdef USE_IBL
6494
+ // debugging flags used for shader output of intermediate PBR variables
6495
+ // #ifdef PBR_DEBUG
6496
+ scaleDiffBaseMR: "vec4<f32>",
6497
+ scaleFGDSpec: "vec4<f32>"
4100
6498
  }
4101
6499
  };
4102
6500
  //# sourceMappingURL=index.cjs.map