@luma.gl/shadertools 9.3.0-alpha.6 → 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 (152) hide show
  1. package/dist/dist.dev.js +2550 -330
  2. package/dist/dist.min.js +1803 -283
  3. package/dist/index.cjs +2495 -358
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +9 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -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 +4 -3
  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 +12 -2
  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 +11 -5
  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 +7 -6
  43. package/dist/modules/engine/skin/skin.d.ts.map +1 -1
  44. package/dist/modules/engine/skin/skin.js +3 -5
  45. package/dist/modules/engine/skin/skin.js.map +1 -1
  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 -55
  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 +43 -65
  73. package/dist/modules/lighting/lights/lighting-wgsl.js.map +1 -1
  74. package/dist/modules/lighting/lights/lighting.d.ts +104 -86
  75. package/dist/modules/lighting/lights/lighting.d.ts.map +1 -1
  76. package/dist/modules/lighting/lights/lighting.js +96 -83
  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 +706 -50
  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 -61
  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 +2 -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 -1
  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 +36 -5
  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/package.json +3 -3
  122. package/src/index.ts +19 -2
  123. package/src/lib/preprocessor/preprocessor.ts +6 -3
  124. package/src/lib/shader-assembler.ts +17 -2
  125. package/src/lib/shader-assembly/assemble-shaders.ts +377 -12
  126. package/src/lib/shader-assembly/wgsl-binding-debug.ts +216 -0
  127. package/src/lib/shader-generator/glsl/generate-glsl.ts +7 -1
  128. package/src/lib/shader-generator/wgsl/generate-wgsl.ts +6 -0
  129. package/src/lib/shader-module/shader-module-uniform-layout.ts +194 -0
  130. package/src/lib/shader-module/shader-module.ts +16 -6
  131. package/src/lib/utils/uniform-types.ts +24 -9
  132. package/src/modules/engine/picking/picking.ts +3 -0
  133. package/src/modules/engine/skin/skin.ts +3 -5
  134. package/src/modules/lighting/gouraud-material/gouraud-material.ts +4 -0
  135. package/src/modules/lighting/ibl/ibl.ts +44 -0
  136. package/src/modules/lighting/lambert-material/lambert-material.ts +42 -0
  137. package/src/modules/lighting/lambert-material/lambert-shaders-glsl.ts +61 -0
  138. package/src/modules/lighting/lambert-material/lambert-shaders-wgsl.ts +73 -0
  139. package/src/modules/lighting/lights/lighting-glsl.ts +43 -55
  140. package/src/modules/lighting/lights/lighting-wgsl.ts +43 -65
  141. package/src/modules/lighting/lights/lighting.ts +186 -123
  142. package/src/modules/lighting/no-material/dirlight.ts +3 -1
  143. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +524 -28
  144. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +706 -50
  145. package/src/modules/lighting/pbr-material/pbr-material.ts +111 -18
  146. package/src/modules/lighting/pbr-material/pbr-projection.ts +2 -1
  147. package/src/modules/lighting/phong-material/phong-material.ts +5 -0
  148. package/src/modules/lighting/phong-material/phong-shaders-glsl.ts +15 -4
  149. package/src/modules/lighting/phong-material/phong-shaders-wgsl.ts +36 -5
  150. package/src/modules/math/fp64/fp64-arithmetic-glsl.ts +41 -10
  151. package/src/modules/math/fp64/fp64-arithmetic-wgsl.ts +212 -0
  152. package/src/modules/math/fp64/fp64.ts +9 -3
package/dist/index.cjs CHANGED
@@ -47,10 +47,15 @@ __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,
@@ -60,7 +65,8 @@ __export(dist_exports, {
60
65
  skin: () => skin,
61
66
  toHalfFloat: () => toHalfFloat,
62
67
  typeToChannelCount: () => typeToChannelCount,
63
- typeToChannelSuffix: () => typeToChannelSuffix
68
+ typeToChannelSuffix: () => typeToChannelSuffix,
69
+ validateShaderModuleUniformLayout: () => validateShaderModuleUniformLayout
64
70
  });
65
71
  module.exports = __toCommonJS(dist_exports);
66
72
 
@@ -379,6 +385,114 @@ function resolveModules(modules) {
379
385
  return getShaderDependencies(modules);
380
386
  }
381
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
+
382
496
  // dist/lib/shader-assembly/platform-defines.js
383
497
  function getPlatformShaderDefines(platformInfo) {
384
498
  switch (platformInfo == null ? void 0 : platformInfo.gpu.toLowerCase()) {
@@ -564,11 +678,158 @@ function getShaderVersion(source4) {
564
678
  return version;
565
679
  }
566
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
+
567
819
  // dist/lib/shader-assembly/assemble-shaders.js
568
820
  var INJECT_SHADER_DECLARATIONS = `
569
821
 
570
822
  ${DECLARATION_INJECT_MARKER}
571
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;
572
833
  var FRAGMENT_SHADER_PROLOGUE = (
573
834
  /* glsl */
574
835
  `precision highp float;
@@ -576,14 +837,17 @@ var FRAGMENT_SHADER_PROLOGUE = (
576
837
  );
577
838
  function assembleWGSLShader(options) {
578
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
+ });
579
846
  return {
580
- source: assembleShaderWGSL(options.platformInfo, {
581
- ...options,
582
- source: options.source,
583
- stage: "vertex",
584
- modules
585
- }),
586
- getUniforms: assembleGetUniforms(modules)
847
+ source: source4,
848
+ getUniforms: assembleGetUniforms(modules),
849
+ bindingAssignments,
850
+ bindingTable: getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments)
587
851
  };
588
852
  }
589
853
  function assembleGLSLShaderPair(options) {
@@ -645,11 +909,20 @@ function assembleShaderWGSL(platformInfo, options) {
645
909
  }
646
910
  }
647
911
  const modulesToInject = modules;
912
+ const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(coreSource);
913
+ const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(modulesToInject, options._bindingRegistry, usedBindingsByGroup);
914
+ const bindingAssignments = [];
648
915
  for (const module2 of modulesToInject) {
649
916
  if (log2) {
650
917
  checkShaderModuleDeprecations(module2, coreSource, log2);
651
918
  }
652
- 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;
653
926
  assembledSource += moduleSource;
654
927
  const injections = ((_a = module2.injections) == null ? void 0 : _a[stage]) || {};
655
928
  for (const key in injections) {
@@ -668,9 +941,11 @@ function assembleShaderWGSL(platformInfo, options) {
668
941
  assembledSource += INJECT_SHADER_DECLARATIONS;
669
942
  assembledSource = injectShader(assembledSource, stage, declInjections);
670
943
  assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections);
944
+ assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
671
945
  assembledSource += coreSource;
672
946
  assembledSource = injectShader(assembledSource, stage, mainInjections);
673
- return assembledSource;
947
+ assertNoUnresolvedAutoBindings(assembledSource);
948
+ return { source: assembledSource, bindingAssignments };
674
949
  }
675
950
  function assembleShaderGLSL(platformInfo, options) {
676
951
  var _a;
@@ -734,7 +1009,7 @@ ${getApplicationDefines(allDefines)}
734
1009
  if (log2) {
735
1010
  checkShaderModuleDeprecations(module2, coreSource, log2);
736
1011
  }
737
- const moduleSource = getShaderModuleSource(module2, stage);
1012
+ const moduleSource = getShaderModuleSource(module2, stage, log2);
738
1013
  assembledSource += moduleSource;
739
1014
  const injections = ((_a = module2.instance) == null ? void 0 : _a.normalizedInjections[stage]) || {};
740
1015
  for (const key in injections) {
@@ -783,7 +1058,7 @@ function getApplicationDefines(defines = {}) {
783
1058
  }
784
1059
  return sourceText;
785
1060
  }
786
- function getShaderModuleSource(module2, stage) {
1061
+ function getShaderModuleSource(module2, stage, log2) {
787
1062
  let moduleSource;
788
1063
  switch (stage) {
789
1064
  case "vertex":
@@ -801,6 +1076,7 @@ function getShaderModuleSource(module2, stage) {
801
1076
  if (!module2.name) {
802
1077
  throw new Error("Shader module must have a name");
803
1078
  }
1079
+ validateShaderModuleUniformLayout(module2, stage, { log: log2 });
804
1080
  const moduleName = module2.name.toUpperCase().replace(/[^0-9a-z]/gi, "_");
805
1081
  let source4 = `// ----- MODULE ${module2.name} ---------------
806
1082
 
@@ -813,13 +1089,177 @@ function getShaderModuleSource(module2, stage) {
813
1089
  `;
814
1090
  return source4;
815
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})
1247
+ `;
1248
+ }
1249
+ source4 += "\n";
1250
+ return source4;
1251
+ }
1252
+ function getBindingRegistryKey(group, moduleName, bindingName) {
1253
+ return `${group}:${moduleName}:${bindingName}`;
1254
+ }
816
1255
 
817
1256
  // dist/lib/preprocessor/preprocessor.js
818
- var IFDEF_REGEXP = /^\s*\#\s*ifdef\s*([a-zA-Z_]+)\s*$/;
819
- var IFNDEF_REGEXP = /^\s*\#\s*ifndef\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*(?:\\/\\/.*)?$`);
820
1260
  var ELSE_REGEXP = /^\s*\#\s*else\s*(?:\/\/.*)?$/;
821
1261
  var ENDIF_REGEXP = /^\s*\#\s*endif\s*$/;
822
- var IFDEF_WITH_COMMENT_REGEXP = /^\s*\#\s*ifdef\s*([a-zA-Z_]+)\s*(?:\/\/.*)?$/;
1262
+ var IFDEF_WITH_COMMENT_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
823
1263
  var ENDIF_WITH_COMMENT_REGEXP = /^\s*\#\s*endif\s*(?:\/\/.*)?$/;
824
1264
  function preprocess(source4, options) {
825
1265
  var _a, _b;
@@ -866,6 +1306,8 @@ var _ShaderAssembler = class {
866
1306
  _hookFunctions = [];
867
1307
  /** Shader modules */
868
1308
  _defaultModules = [];
1309
+ /** Stable per-run WGSL auto-binding assignments keyed by group/module/binding. */
1310
+ _wgslBindingRegistry = /* @__PURE__ */ new Map();
869
1311
  /**
870
1312
  * A default shader assembler instance - the natural place to register default modules and hooks
871
1313
  * @returns
@@ -909,10 +1351,11 @@ var _ShaderAssembler = class {
909
1351
  assembleWGSLShader(props) {
910
1352
  const modules = this._getModuleList(props.modules);
911
1353
  const hookFunctions = this._hookFunctions;
912
- const { source: source4, getUniforms: getUniforms4 } = assembleWGSLShader({
1354
+ const { source: source4, getUniforms: getUniforms4, bindingAssignments } = assembleWGSLShader({
913
1355
  ...props,
914
1356
  // @ts-expect-error
915
1357
  source: props.source,
1358
+ _bindingRegistry: this._wgslBindingRegistry,
916
1359
  modules,
917
1360
  hookFunctions
918
1361
  });
@@ -924,7 +1367,13 @@ var _ShaderAssembler = class {
924
1367
  ...props.defines
925
1368
  };
926
1369
  const preprocessedSource = props.platformInfo.shaderLanguage === "wgsl" ? preprocess(source4, { defines }) : source4;
927
- return { source: preprocessedSource, getUniforms: getUniforms4, modules };
1370
+ return {
1371
+ source: preprocessedSource,
1372
+ getUniforms: getUniforms4,
1373
+ modules,
1374
+ bindingAssignments,
1375
+ bindingTable: getShaderBindingDebugRowsFromWGSL(preprocessedSource, bindingAssignments)
1376
+ };
928
1377
  }
929
1378
  /**
930
1379
  * Assemble a pair of shaders into a single shader program
@@ -1089,6 +1538,9 @@ function generateGLSLUniformDeclarations(module2, options) {
1089
1538
  case "uniforms":
1090
1539
  }
1091
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
+ }
1092
1544
  const glslUniformType = getGLSLUniformType(uniformFormat);
1093
1545
  switch (options.uniforms) {
1094
1546
  case "scoped-interface-blocks":
@@ -1149,6 +1601,9 @@ function generateWGSLUniformDeclarations(module2, options) {
1149
1601
  const wgsl = [];
1150
1602
  wgsl.push(`struct ${capitalize(module2.name)} {`);
1151
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
+ }
1152
1607
  const wgslUniformType = uniformFormat;
1153
1608
  wgsl.push(` ${uniformName} : ${wgslUniformType};`);
1154
1609
  }
@@ -1463,6 +1918,7 @@ var fp64arithmeticShader = (
1463
1918
  `
1464
1919
  uniform fp64arithmeticUniforms {
1465
1920
  uniform float ONE;
1921
+ uniform float SPLIT;
1466
1922
  } fp64;
1467
1923
 
1468
1924
  /*
@@ -1472,6 +1928,12 @@ The purpose of this workaround is to prevent shader compilers from
1472
1928
  optimizing away necessary arithmetic operations by swapping their sequences
1473
1929
  or transform the equation to some 'equivalent' form.
1474
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
+
1475
1937
  The method is to multiply an artifical variable, ONE, which will be known to
1476
1938
  the compiler to be 1 only at runtime. The whole expression is then represented
1477
1939
  as a polynomial with respective to ONE. In the coefficients of all terms, only one a
@@ -1480,17 +1942,23 @@ and one b should appear
1480
1942
  err = (a + b) * ONE^6 - a * ONE^5 - (a + b) * ONE^4 + a * ONE^3 - b - (a + b) * ONE^2 + a * ONE
1481
1943
  */
1482
1944
 
1483
- // Divide float number to high and low floats to extend fraction bits
1484
- vec2 split(float a) {
1485
- const float SPLIT = 4097.0;
1486
- float t = a * SPLIT;
1945
+ float prevent_fp64_optimization(float value) {
1487
1946
  #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND)
1488
- float a_hi = t * fp64.ONE - (t - a);
1489
- float a_lo = a * fp64.ONE - a_hi;
1947
+ return value + fp64.ONE * 0.0;
1490
1948
  #else
1491
- float a_hi = t - (t - a);
1492
- float a_lo = a - a_hi;
1949
+ return value;
1493
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;
1494
1962
  return vec2(a_hi, a_lo);
1495
1963
  }
1496
1964
 
@@ -1554,8 +2022,26 @@ vec2 twoProd(float a, float b) {
1554
2022
  float prod = a * b;
1555
2023
  vec2 a_fp64 = split(a);
1556
2024
  vec2 b_fp64 = split(b);
1557
- float err = ((a_fp64.x * b_fp64.x - prod) + a_fp64.x * b_fp64.y +
1558
- 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;
1559
2045
  return vec2(prod, err);
1560
2046
  }
1561
2047
 
@@ -1631,6 +2117,218 @@ vec2 sqrt_fp64(vec2 a) {
1631
2117
  `
1632
2118
  );
1633
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
+
1634
2332
  // dist/modules/math/fp64/fp64-functions-glsl.js
1635
2333
  var fp64functionShader = (
1636
2334
  /* glsl */
@@ -2309,13 +3007,18 @@ void mat4_vec4_mul_fp64(vec2 b[16], vec2 a[4], out vec2 out_val[4]) {
2309
3007
  // dist/modules/math/fp64/fp64.js
2310
3008
  var defaultUniforms = {
2311
3009
  // Used in LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2312
- 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
2313
3014
  };
2314
3015
  var fp64arithmetic = {
2315
3016
  name: "fp64arithmetic",
3017
+ source: fp64arithmeticWGSL,
3018
+ fs: fp64arithmeticShader,
2316
3019
  vs: fp64arithmeticShader,
2317
3020
  defaultUniforms,
2318
- uniformTypes: { ONE: "f32" },
3021
+ uniformTypes: { ONE: "f32", SPLIT: "f32" },
2319
3022
  // Additional Functions
2320
3023
  fp64ify,
2321
3024
  fp64LowPart,
@@ -2529,7 +3232,7 @@ struct skinUniforms {
2529
3232
  jointMatrix: array<mat4x4<f32>, ${SKIN_MAX_JOINTS}>,
2530
3233
  };
2531
3234
 
2532
- @binding(19) @group(0) var<uniform> skin: skinUniforms;
3235
+ @group(0) @binding(auto) var<uniform> skin: skinUniforms;
2533
3236
 
2534
3237
  fn getSkinMatrix(weights: vec4f, joints: vec4u) -> mat4x4<f32> {
2535
3238
  return (weights.x * skin.jointMatrix[joints.x])
@@ -2563,6 +3266,7 @@ var skin = {
2563
3266
  props: {},
2564
3267
  uniforms: {},
2565
3268
  name: "skin",
3269
+ bindingLayout: [{ name: "skin", group: 0 }],
2566
3270
  dependencies: [],
2567
3271
  source: source2,
2568
3272
  vs: vs2,
@@ -2606,10 +3310,7 @@ var skin = {
2606
3310
  };
2607
3311
  },
2608
3312
  uniformTypes: {
2609
- jointMatrix: "mat4x4<f32>"
2610
- },
2611
- uniformSizes: {
2612
- jointMatrix: SKIN_MAX_JOINTS
3313
+ jointMatrix: ["mat4x4<f32>", SKIN_MAX_JOINTS]
2613
3314
  }
2614
3315
  };
2615
3316
 
@@ -2632,77 +3333,51 @@ struct PointLight {
2632
3333
  vec3 attenuation; // 2nd order x:Constant-y:Linear-z:Exponential
2633
3334
  };
2634
3335
 
3336
+ struct SpotLight {
3337
+ vec3 color;
3338
+ vec3 position;
3339
+ vec3 direction;
3340
+ vec3 attenuation;
3341
+ vec2 coneCos;
3342
+ };
3343
+
2635
3344
  struct DirectionalLight {
2636
3345
  vec3 color;
2637
3346
  vec3 direction;
2638
3347
  };
2639
3348
 
3349
+ struct UniformLight {
3350
+ vec3 color;
3351
+ vec3 position;
3352
+ vec3 direction;
3353
+ vec3 attenuation;
3354
+ vec2 coneCos;
3355
+ };
3356
+
2640
3357
  uniform lightingUniforms {
2641
3358
  int enabled;
2642
- int lightType;
2643
-
2644
3359
  int directionalLightCount;
2645
3360
  int pointLightCount;
2646
-
3361
+ int spotLightCount;
2647
3362
  vec3 ambientColor;
2648
-
2649
- vec3 lightColor0;
2650
- vec3 lightPosition0;
2651
- vec3 lightDirection0;
2652
- vec3 lightAttenuation0;
2653
-
2654
- vec3 lightColor1;
2655
- vec3 lightPosition1;
2656
- vec3 lightDirection1;
2657
- vec3 lightAttenuation1;
2658
-
2659
- vec3 lightColor2;
2660
- vec3 lightPosition2;
2661
- vec3 lightDirection2;
2662
- vec3 lightAttenuation2;
2663
-
2664
- vec3 lightColor3;
2665
- vec3 lightPosition3;
2666
- vec3 lightDirection3;
2667
- vec3 lightAttenuation3;
2668
-
2669
- vec3 lightColor4;
2670
- vec3 lightPosition4;
2671
- vec3 lightDirection4;
2672
- vec3 lightAttenuation4;
3363
+ UniformLight lights[5];
2673
3364
  } lighting;
2674
3365
 
2675
3366
  PointLight lighting_getPointLight(int index) {
2676
- switch (index) {
2677
- case 0:
2678
- return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0);
2679
- case 1:
2680
- return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1);
2681
- case 2:
2682
- return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2);
2683
- case 3:
2684
- return PointLight(lighting.lightColor3, lighting.lightPosition3, lighting.lightAttenuation3);
2685
- case 4:
2686
- default:
2687
- return PointLight(lighting.lightColor4, lighting.lightPosition4, lighting.lightAttenuation4);
2688
- }
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);
2689
3374
  }
2690
3375
 
2691
3376
  DirectionalLight lighting_getDirectionalLight(int index) {
2692
- switch (index) {
2693
- case 0:
2694
- return DirectionalLight(lighting.lightColor0, lighting.lightDirection0);
2695
- case 1:
2696
- return DirectionalLight(lighting.lightColor1, lighting.lightDirection1);
2697
- case 2:
2698
- return DirectionalLight(lighting.lightColor2, lighting.lightDirection2);
2699
- case 3:
2700
- return DirectionalLight(lighting.lightColor3, lighting.lightDirection3);
2701
- case 4:
2702
- default:
2703
- return DirectionalLight(lighting.lightColor4, lighting.lightDirection4);
2704
- }
2705
- }
3377
+ UniformLight light =
3378
+ lighting.lights[lighting.pointLightCount + lighting.spotLightCount + index];
3379
+ return DirectionalLight(light.color, light.direction);
3380
+ }
2706
3381
 
2707
3382
  float getPointLightAttenuation(PointLight pointLight, float distance) {
2708
3383
  return pointLight.attenuation.x
@@ -2710,6 +3385,20 @@ float getPointLightAttenuation(PointLight pointLight, float distance) {
2710
3385
  + pointLight.attenuation.z * distance * distance;
2711
3386
  }
2712
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
+
2713
3402
  // #endif
2714
3403
  `
2715
3404
  );
@@ -2730,105 +3419,85 @@ struct PointLight {
2730
3419
  attenuation: vec3<f32>, // 2nd order x:Constant-y:Linear-z:Exponential
2731
3420
  };
2732
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
+
2733
3430
  struct DirectionalLight {
2734
3431
  color: vec3<f32>,
2735
3432
  direction: vec3<f32>,
2736
3433
  };
2737
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
+
2738
3443
  struct lightingUniforms {
2739
3444
  enabled: i32,
2740
- lightType: i32,
2741
-
2742
3445
  directionalLightCount: i32,
2743
3446
  pointLightCount: i32,
2744
-
3447
+ spotLightCount: i32,
2745
3448
  ambientColor: vec3<f32>,
2746
-
2747
- lightColor0: vec3<f32>,
2748
- lightPosition0: vec3<f32>,
2749
- lightDirection0: vec3<f32>,
2750
- lightAttenuation0: vec3<f32>,
2751
-
2752
- lightColor1: vec3<f32>,
2753
- lightPosition1: vec3<f32>,
2754
- lightDirection1: vec3<f32>,
2755
- lightAttenuation1: vec3<f32>,
2756
-
2757
- lightColor2: vec3<f32>,
2758
- lightPosition2: vec3<f32>,
2759
- lightDirection2: vec3<f32>,
2760
- lightAttenuation2: vec3<f32>,
2761
-
2762
- lightColor3: vec3<f32>,
2763
- lightPosition3: vec3<f32>,
2764
- lightDirection3: vec3<f32>,
2765
- lightAttenuation3: vec3<f32>,
2766
-
2767
- lightColor4: vec3<f32>,
2768
- lightPosition4: vec3<f32>,
2769
- lightDirection4: vec3<f32>,
2770
- lightAttenuation4: vec3<f32>,
3449
+ lights: array<UniformLight, 5>,
2771
3450
  };
2772
3451
 
2773
- // Binding 0:1 is reserved for lighting (Note: could go into separate bind group as it is stable across draw calls)
2774
- @binding(1) @group(0) var<uniform> lighting : lightingUniforms;
3452
+ @group(2) @binding(auto) var<uniform> lighting : lightingUniforms;
2775
3453
 
2776
3454
  fn lighting_getPointLight(index: i32) -> PointLight {
2777
- switch (index) {
2778
- case 0: {
2779
- return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0);
2780
- }
2781
- case 1: {
2782
- return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1);
2783
- }
2784
- case 2: {
2785
- return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2);
2786
- }
2787
- case 3: {
2788
- return PointLight(lighting.lightColor3, lighting.lightPosition3, lighting.lightAttenuation3);
2789
- }
2790
- case 4, default: {
2791
- return PointLight(lighting.lightColor4, lighting.lightPosition4, lighting.lightAttenuation4);
2792
- }
2793
- }
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);
2794
3462
  }
2795
3463
 
2796
3464
  fn lighting_getDirectionalLight(index: i32) -> DirectionalLight {
2797
- switch (index) {
2798
- case 0: {
2799
- return DirectionalLight(lighting.lightColor0, lighting.lightDirection0);
2800
- }
2801
- case 1: {
2802
- return DirectionalLight(lighting.lightColor1, lighting.lightDirection1);
2803
- }
2804
- case 2: {
2805
- return DirectionalLight(lighting.lightColor2, lighting.lightDirection2);
2806
- }
2807
- case 3: {
2808
- return DirectionalLight(lighting.lightColor3, lighting.lightDirection3);
2809
- }
2810
- case 4, default: {
2811
- return DirectionalLight(lighting.lightColor4, lighting.lightDirection4);
2812
- }
2813
- }
2814
- }
3465
+ let light = lighting.lights[lighting.pointLightCount + lighting.spotLightCount + index];
3466
+ return DirectionalLight(light.color, light.direction);
3467
+ }
2815
3468
 
2816
3469
  fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
2817
3470
  return pointLight.attenuation.x
2818
3471
  + pointLight.attenuation.y * distance
2819
3472
  + pointLight.attenuation.z * distance * distance;
2820
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
+ }
2821
3488
  `
2822
3489
  );
2823
3490
 
2824
3491
  // dist/modules/lighting/lights/lighting.js
2825
3492
  var MAX_LIGHTS = 5;
2826
3493
  var COLOR_FACTOR = 255;
2827
- var LIGHT_TYPE;
2828
- (function(LIGHT_TYPE2) {
2829
- LIGHT_TYPE2[LIGHT_TYPE2["POINT"] = 0] = "POINT";
2830
- LIGHT_TYPE2[LIGHT_TYPE2["DIRECTIONAL"] = 1] = "DIRECTIONAL";
2831
- })(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
+ };
2832
3501
  var lighting = {
2833
3502
  props: {},
2834
3503
  uniforms: {},
@@ -2838,128 +3507,105 @@ var lighting = {
2838
3507
  },
2839
3508
  uniformTypes: {
2840
3509
  enabled: "i32",
2841
- lightType: "i32",
2842
3510
  directionalLightCount: "i32",
2843
3511
  pointLightCount: "i32",
3512
+ spotLightCount: "i32",
2844
3513
  ambientColor: "vec3<f32>",
2845
- // TODO define as arrays once we have appropriate uniformTypes
2846
- lightColor0: "vec3<f32>",
2847
- lightPosition0: "vec3<f32>",
2848
- // TODO - could combine direction and attenuation
2849
- lightDirection0: "vec3<f32>",
2850
- lightAttenuation0: "vec3<f32>",
2851
- lightColor1: "vec3<f32>",
2852
- lightPosition1: "vec3<f32>",
2853
- lightDirection1: "vec3<f32>",
2854
- lightAttenuation1: "vec3<f32>",
2855
- lightColor2: "vec3<f32>",
2856
- lightPosition2: "vec3<f32>",
2857
- lightDirection2: "vec3<f32>",
2858
- lightAttenuation2: "vec3<f32>",
2859
- lightColor3: "vec3<f32>",
2860
- lightPosition3: "vec3<f32>",
2861
- lightDirection3: "vec3<f32>",
2862
- lightAttenuation3: "vec3<f32>",
2863
- lightColor4: "vec3<f32>",
2864
- lightPosition4: "vec3<f32>",
2865
- lightDirection4: "vec3<f32>",
2866
- lightAttenuation4: "vec3<f32>"
2867
- },
2868
- defaultUniforms: {
2869
- enabled: 1,
2870
- lightType: LIGHT_TYPE.POINT,
2871
- directionalLightCount: 0,
2872
- pointLightCount: 0,
2873
- ambientColor: [0.1, 0.1, 0.1],
2874
- lightColor0: [1, 1, 1],
2875
- lightPosition0: [1, 1, 2],
2876
- // TODO - could combine direction and attenuation
2877
- lightDirection0: [1, 1, 1],
2878
- lightAttenuation0: [1, 0, 0],
2879
- lightColor1: [1, 1, 1],
2880
- lightPosition1: [1, 1, 2],
2881
- lightDirection1: [1, 1, 1],
2882
- lightAttenuation1: [1, 0, 0],
2883
- lightColor2: [1, 1, 1],
2884
- lightPosition2: [1, 1, 2],
2885
- lightDirection2: [1, 1, 1],
2886
- lightAttenuation2: [1, 0, 0],
2887
- lightColor3: [1, 1, 1],
2888
- lightPosition3: [1, 1, 2],
2889
- lightDirection3: [1, 1, 1],
2890
- lightAttenuation3: [1, 0, 0],
2891
- lightColor4: [1, 1, 1],
2892
- lightPosition4: [1, 1, 2],
2893
- lightDirection4: [1, 1, 1],
2894
- lightAttenuation4: [1, 0, 0]
3514
+ lights: [LIGHT_UNIFORM_TYPE, MAX_LIGHTS]
2895
3515
  },
3516
+ defaultUniforms: createDefaultLightingUniforms(),
3517
+ bindingLayout: [{ name: "lighting", group: 2 }],
3518
+ firstBindingSlot: 0,
2896
3519
  source: lightingUniformsWGSL,
2897
3520
  vs: lightingUniformsGLSL,
2898
3521
  fs: lightingUniformsGLSL,
2899
3522
  getUniforms: getUniforms2
2900
3523
  };
2901
- function getUniforms2(props, prevUniforms = {}) {
3524
+ function getUniforms2(props, _prevUniforms = {}) {
2902
3525
  props = props ? { ...props } : props;
2903
3526
  if (!props) {
2904
- return { ...lighting.defaultUniforms };
3527
+ return createDefaultLightingUniforms();
2905
3528
  }
2906
3529
  if (props.lights) {
2907
3530
  props = { ...props, ...extractLightTypes(props.lights), lights: void 0 };
2908
3531
  }
2909
- const { ambientLight, pointLights, directionalLights } = props || {};
2910
- 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;
2911
3534
  if (!hasLights) {
2912
- return { ...lighting.defaultUniforms, enabled: 0 };
3535
+ return {
3536
+ ...createDefaultLightingUniforms(),
3537
+ enabled: 0
3538
+ };
2913
3539
  }
2914
3540
  const uniforms = {
2915
- ...lighting.defaultUniforms,
2916
- ...prevUniforms,
2917
- ...getLightSourceUniforms({ ambientLight, pointLights, directionalLights })
3541
+ ...createDefaultLightingUniforms(),
3542
+ ...getLightSourceUniforms({ ambientLight, pointLights, spotLights, directionalLights })
2918
3543
  };
2919
3544
  if (props.enabled !== void 0) {
2920
3545
  uniforms.enabled = props.enabled ? 1 : 0;
2921
3546
  }
2922
3547
  return uniforms;
2923
3548
  }
2924
- function getLightSourceUniforms({ ambientLight, pointLights = [], directionalLights = [] }) {
2925
- const lightSourceUniforms = {};
2926
- lightSourceUniforms.ambientColor = convertColor(ambientLight);
3549
+ function getLightSourceUniforms({ ambientLight, pointLights = [], spotLights = [], directionalLights = [] }) {
3550
+ const lights = createDefaultLightUniforms();
2927
3551
  let currentLight = 0;
2928
3552
  let pointLightCount = 0;
3553
+ let spotLightCount = 0;
2929
3554
  let directionalLightCount = 0;
2930
3555
  for (const pointLight of pointLights) {
2931
3556
  if (currentLight >= MAX_LIGHTS) {
2932
3557
  break;
2933
3558
  }
2934
- lightSourceUniforms.lightType = LIGHT_TYPE.POINT;
2935
- const i = currentLight;
2936
- lightSourceUniforms[`lightColor${i}`] = convertColor(pointLight);
2937
- lightSourceUniforms[`lightPosition${i}`] = pointLight.position;
2938
- lightSourceUniforms[`lightAttenuation${i}`] = pointLight.attenuation || [1, 0, 0];
3559
+ lights[currentLight] = {
3560
+ ...lights[currentLight],
3561
+ color: convertColor(pointLight),
3562
+ position: pointLight.position,
3563
+ attenuation: pointLight.attenuation || [1, 0, 0]
3564
+ };
2939
3565
  currentLight++;
2940
3566
  pointLightCount++;
2941
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
+ };
3580
+ currentLight++;
3581
+ spotLightCount++;
3582
+ }
2942
3583
  for (const directionalLight of directionalLights) {
2943
3584
  if (currentLight >= MAX_LIGHTS) {
2944
3585
  break;
2945
3586
  }
2946
- lightSourceUniforms.lightType = LIGHT_TYPE.DIRECTIONAL;
2947
- const i = currentLight;
2948
- lightSourceUniforms[`lightColor${i}`] = convertColor(directionalLight);
2949
- lightSourceUniforms[`lightDirection${i}`] = directionalLight.direction;
3587
+ lights[currentLight] = {
3588
+ ...lights[currentLight],
3589
+ color: convertColor(directionalLight),
3590
+ direction: directionalLight.direction
3591
+ };
2950
3592
  currentLight++;
2951
3593
  directionalLightCount++;
2952
3594
  }
2953
- if (pointLights.length + directionalLights.length > MAX_LIGHTS) {
3595
+ if (pointLights.length + spotLights.length + directionalLights.length > MAX_LIGHTS) {
2954
3596
  import_core3.log.warn(`MAX_LIGHTS exceeded, truncating to ${MAX_LIGHTS}`)();
2955
3597
  }
2956
- lightSourceUniforms.directionalLightCount = directionalLightCount;
2957
- lightSourceUniforms.pointLightCount = pointLightCount;
2958
- return lightSourceUniforms;
3598
+ return {
3599
+ ambientColor: convertColor(ambientLight),
3600
+ directionalLightCount,
3601
+ pointLightCount,
3602
+ spotLightCount,
3603
+ lights
3604
+ };
2959
3605
  }
2960
3606
  function extractLightTypes(lights) {
2961
- var _a, _b;
2962
- const lightSources = { pointLights: [], directionalLights: [] };
3607
+ var _a, _b, _c;
3608
+ const lightSources = { pointLights: [], spotLights: [], directionalLights: [] };
2963
3609
  for (const light of lights || []) {
2964
3610
  switch (light.type) {
2965
3611
  case "ambient":
@@ -2971,6 +3617,9 @@ function extractLightTypes(lights) {
2971
3617
  case "point":
2972
3618
  (_b = lightSources.pointLights) == null ? void 0 : _b.push(light);
2973
3619
  break;
3620
+ case "spot":
3621
+ (_c = lightSources.spotLights) == null ? void 0 : _c.push(light);
3622
+ break;
2974
3623
  default:
2975
3624
  }
2976
3625
  }
@@ -2980,6 +3629,68 @@ function convertColor(colorDef = {}) {
2980
3629
  const { color = [0, 0, 0], intensity = 1 } = colorDef;
2981
3630
  return color.map((component) => component * intensity / COLOR_FACTOR);
2982
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
+ };
2983
3694
 
2984
3695
  // dist/modules/lighting/no-material/dirlight.js
2985
3696
  var SOURCE_WGSL = (
@@ -2995,7 +3706,7 @@ struct DirlightInputs {
2995
3706
  normal: DirlightNormal,
2996
3707
  };
2997
3708
 
2998
- @binding(1) @group(0) var<uniform> dirlight : dirlightUniforms;
3709
+ @group(2) @binding(auto) var<uniform> dirlight : dirlightUniforms;
2999
3710
 
3000
3711
  // For vertex
3001
3712
  fn dirlight_setNormal(normal: vec3<f32>) -> DirlightNormal {
@@ -3040,6 +3751,8 @@ var dirlight = {
3040
3751
  props: {},
3041
3752
  uniforms: {},
3042
3753
  name: "dirlight",
3754
+ bindingLayout: [{ name: "dirlight", group: 2 }],
3755
+ firstBindingSlot: 16,
3043
3756
  dependencies: [],
3044
3757
  source: SOURCE_WGSL,
3045
3758
  vs: VS_GLSL,
@@ -3066,10 +3779,173 @@ function getUniforms3(opts = dirlight.defaultUniforms) {
3066
3779
  return uniforms;
3067
3780
  }
3068
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
+
3069
3944
  // dist/modules/lighting/phong-material/phong-shaders-glsl.js
3070
3945
  var PHONG_VS = (
3071
3946
  /* glsl */
3072
3947
  `uniform phongMaterialUniforms {
3948
+ uniform bool unlit;
3073
3949
  uniform float ambient;
3074
3950
  uniform float diffuse;
3075
3951
  uniform float shininess;
@@ -3079,9 +3955,8 @@ var PHONG_VS = (
3079
3955
  );
3080
3956
  var PHONG_FS = (
3081
3957
  /* glsl */
3082
- `#define MAX_LIGHTS 3
3083
-
3084
- uniform phongMaterialUniforms {
3958
+ `uniform phongMaterialUniforms {
3959
+ uniform bool unlit;
3085
3960
  uniform float ambient;
3086
3961
  uniform float diffuse;
3087
3962
  uniform float shininess;
@@ -3103,6 +3978,10 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 view_d
3103
3978
  vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
3104
3979
  vec3 lightColor = surfaceColor;
3105
3980
 
3981
+ if (material.unlit) {
3982
+ return surfaceColor;
3983
+ }
3984
+
3106
3985
  if (lighting.enabled == 0) {
3107
3986
  return lightColor;
3108
3987
  }
@@ -3118,8 +3997,15 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positio
3118
3997
  lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color / light_attenuation);
3119
3998
  }
3120
3999
 
3121
- int totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
3122
- 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++) {
3123
4009
  DirectionalLight directionalLight = lighting_getDirectionalLight(i);
3124
4010
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
3125
4011
  }
@@ -3133,13 +4019,14 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positio
3133
4019
  var PHONG_WGSL = (
3134
4020
  /* wgsl */
3135
4021
  `struct phongMaterialUniforms {
4022
+ unlit: u32,
3136
4023
  ambient: f32,
3137
4024
  diffuse: f32,
3138
4025
  shininess: f32,
3139
4026
  specularColor: vec3<f32>,
3140
4027
  };
3141
4028
 
3142
- @binding(2) @group(0) var<uniform> phongMaterial : phongMaterialUniforms;
4029
+ @group(3) @binding(auto) var<uniform> phongMaterial : phongMaterialUniforms;
3143
4030
 
3144
4031
  fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, view_direction: vec3<f32>, normal_worldspace: vec3<f32>, color: vec3<f32>) -> vec3<f32> {
3145
4032
  let halfway_direction: vec3<f32> = normalize(light_direction + view_direction);
@@ -3156,6 +4043,10 @@ fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, v
3156
4043
  fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, position_worldspace: vec3<f32>, normal_worldspace: vec3<f32>) -> vec3<f32> {
3157
4044
  var lightColor: vec3<f32> = surfaceColor;
3158
4045
 
4046
+ if (phongMaterial.unlit != 0u) {
4047
+ return surfaceColor;
4048
+ }
4049
+
3159
4050
  if (lighting.enabled == 0) {
3160
4051
  return lightColor;
3161
4052
  }
@@ -3180,8 +4071,21 @@ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, p
3180
4071
  );
3181
4072
  }
3182
4073
 
3183
- let totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
3184
- for (var i: i32 = lighting.pointLightCount; i < totalLights; i++) {
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
+ );
4086
+ }
4087
+
4088
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
3185
4089
  let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
3186
4090
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
3187
4091
  }
@@ -3213,8 +4117,21 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
3213
4117
  );
3214
4118
  }
3215
4119
 
3216
- let totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
3217
- for (var i: i32 = lighting.pointLightCount; i < totalLights; i++) {
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++) {
3218
4135
  let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
3219
4136
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
3220
4137
  }
@@ -3228,6 +4145,7 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
3228
4145
  var gouraudMaterial = {
3229
4146
  props: {},
3230
4147
  name: "gouraudMaterial",
4148
+ bindingLayout: [{ name: "gouraudMaterial", group: 3 }],
3231
4149
  // Note these are switched between phong and gouraud
3232
4150
  vs: PHONG_FS.replace("phongMaterial", "gouraudMaterial"),
3233
4151
  fs: PHONG_VS.replace("phongMaterial", "gouraudMaterial"),
@@ -3237,12 +4155,14 @@ var gouraudMaterial = {
3237
4155
  },
3238
4156
  dependencies: [lighting],
3239
4157
  uniformTypes: {
4158
+ unlit: "i32",
3240
4159
  ambient: "f32",
3241
4160
  diffuse: "f32",
3242
4161
  shininess: "f32",
3243
4162
  specularColor: "vec3<f32>"
3244
4163
  },
3245
4164
  defaultUniforms: {
4165
+ unlit: false,
3246
4166
  ambient: 0.35,
3247
4167
  diffuse: 0.6,
3248
4168
  shininess: 32,
@@ -3260,6 +4180,8 @@ var gouraudMaterial = {
3260
4180
  // dist/modules/lighting/phong-material/phong-material.js
3261
4181
  var phongMaterial = {
3262
4182
  name: "phongMaterial",
4183
+ firstBindingSlot: 0,
4184
+ bindingLayout: [{ name: "phongMaterial", group: 3 }],
3263
4185
  dependencies: [lighting],
3264
4186
  // Note these are switched between phong and gouraud
3265
4187
  source: PHONG_WGSL,
@@ -3269,12 +4191,14 @@ var phongMaterial = {
3269
4191
  LIGHTING_FRAGMENT: true
3270
4192
  },
3271
4193
  uniformTypes: {
4194
+ unlit: "i32",
3272
4195
  ambient: "f32",
3273
4196
  diffuse: "f32",
3274
4197
  shininess: "f32",
3275
4198
  specularColor: "vec3<f32>"
3276
4199
  },
3277
4200
  defaultUniforms: {
4201
+ unlit: false,
3278
4202
  ambient: 0.35,
3279
4203
  diffuse: 0.6,
3280
4204
  shininess: 32,
@@ -3371,10 +4295,12 @@ uniform pbrMaterialUniforms {
3371
4295
  float clearcoatFactor;
3372
4296
  float clearcoatRoughnessFactor;
3373
4297
  bool clearcoatMapEnabled;
4298
+ bool clearcoatRoughnessMapEnabled;
3374
4299
 
3375
4300
  vec3 sheenColorFactor;
3376
4301
  float sheenRoughnessFactor;
3377
4302
  bool sheenColorMapEnabled;
4303
+ bool sheenRoughnessMapEnabled;
3378
4304
 
3379
4305
  float iridescenceFactor;
3380
4306
  float iridescenceIor;
@@ -3424,26 +4350,33 @@ uniform sampler2D pbr_specularIntensitySampler;
3424
4350
  #ifdef HAS_TRANSMISSIONMAP
3425
4351
  uniform sampler2D pbr_transmissionSampler;
3426
4352
  #endif
4353
+ #ifdef HAS_THICKNESSMAP
4354
+ uniform sampler2D pbr_thicknessSampler;
4355
+ #endif
3427
4356
  #ifdef HAS_CLEARCOATMAP
3428
4357
  uniform sampler2D pbr_clearcoatSampler;
4358
+ #endif
4359
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
3429
4360
  uniform sampler2D pbr_clearcoatRoughnessSampler;
3430
4361
  #endif
4362
+ #ifdef HAS_CLEARCOATNORMALMAP
4363
+ uniform sampler2D pbr_clearcoatNormalSampler;
4364
+ #endif
3431
4365
  #ifdef HAS_SHEENCOLORMAP
3432
4366
  uniform sampler2D pbr_sheenColorSampler;
4367
+ #endif
4368
+ #ifdef HAS_SHEENROUGHNESSMAP
3433
4369
  uniform sampler2D pbr_sheenRoughnessSampler;
3434
4370
  #endif
3435
4371
  #ifdef HAS_IRIDESCENCEMAP
3436
4372
  uniform sampler2D pbr_iridescenceSampler;
3437
4373
  #endif
4374
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
4375
+ uniform sampler2D pbr_iridescenceThicknessSampler;
4376
+ #endif
3438
4377
  #ifdef HAS_ANISOTROPYMAP
3439
4378
  uniform sampler2D pbr_anisotropySampler;
3440
4379
  #endif
3441
- #ifdef USE_IBL
3442
- uniform samplerCube pbr_diffuseEnvSampler;
3443
- uniform samplerCube pbr_specularEnvSampler;
3444
- uniform sampler2D pbr_brdfLUT;
3445
- #endif
3446
-
3447
4380
  // Inputs from vertex shader
3448
4381
 
3449
4382
  in vec3 pbr_vPosition;
@@ -3480,6 +4413,8 @@ struct PBRInfo {
3480
4413
  const float M_PI = 3.141592653589793;
3481
4414
  const float c_MinRoughness = 0.04;
3482
4415
 
4416
+ vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor);
4417
+
3483
4418
  vec4 SRGBtoLINEAR(vec4 srgbIn)
3484
4419
  {
3485
4420
  #ifdef MANUAL_SRGB
@@ -3495,11 +4430,9 @@ vec4 SRGBtoLINEAR(vec4 srgbIn)
3495
4430
  #endif //MANUAL_SRGB
3496
4431
  }
3497
4432
 
3498
- // Find the normal for this fragment, pulling either from a predefined normal map
3499
- // or from the interpolated mesh normal and tangent attributes.
3500
- vec3 getNormal()
4433
+ // Build the tangent basis from interpolated attributes or screen-space derivatives.
4434
+ mat3 getTBN()
3501
4435
  {
3502
- // Retrieve the tangent space matrix
3503
4436
  #ifndef HAS_TANGENTS
3504
4437
  vec3 pos_dx = dFdx(pbr_vPosition);
3505
4438
  vec3 pos_dy = dFdy(pbr_vPosition);
@@ -3520,9 +4453,21 @@ vec3 getNormal()
3520
4453
  mat3 tbn = pbr_vTBN;
3521
4454
  #endif
3522
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
+ {
3523
4469
  #ifdef HAS_NORMALMAP
3524
- vec3 n = texture(pbr_normalSampler, pbr_vUV).rgb;
3525
- n = normalize(tbn * ((2.0 * n - 1.0) * vec3(pbrMaterial.normalScale, pbrMaterial.normalScale, 1.0)));
4470
+ vec3 n = getMappedNormal(pbr_normalSampler, tbn, pbrMaterial.normalScale);
3526
4471
  #else
3527
4472
  // The tbn matrix is linearly interpolated, so we need to re-normalize
3528
4473
  vec3 n = normalize(tbn[2].xyz);
@@ -3531,6 +4476,15 @@ vec3 getNormal()
3531
4476
  return n;
3532
4477
  }
3533
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
+
3534
4488
  // Calculation of the lighting contribution from an optional Image Based Light source.
3535
4489
  // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
3536
4490
  // See our README.md on Environment Maps [3] for additional discussion.
@@ -3606,35 +4560,203 @@ float microfacetDistribution(PBRInfo pbrInfo)
3606
4560
  return roughnessSq / (M_PI * f * f);
3607
4561
  }
3608
4562
 
3609
- void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) {
3610
- pbrInfo.NdotL = 1.0;
3611
- pbrInfo.NdotH = 0.0;
3612
- pbrInfo.LdotH = 0.0;
3613
- pbrInfo.VdotH = 1.0;
4563
+ float maxComponent(vec3 value)
4564
+ {
4565
+ return max(max(value.r, value.g), value.b);
3614
4566
  }
3615
4567
 
3616
- void PBRInfo_setDirectionalLight(inout PBRInfo pbrInfo, vec3 lightDirection) {
3617
- vec3 n = pbrInfo.n;
3618
- vec3 v = pbrInfo.v;
3619
- vec3 l = normalize(lightDirection); // Vector from surface point to light
3620
- 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
+ }
3621
4574
 
3622
- pbrInfo.NdotL = clamp(dot(n, l), 0.001, 1.0);
3623
- pbrInfo.NdotH = clamp(dot(n, h), 0.0, 1.0);
3624
- pbrInfo.LdotH = clamp(dot(l, h), 0.0, 1.0);
3625
- 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);
3626
4579
  }
3627
4580
 
3628
- void PBRInfo_setPointLight(inout PBRInfo pbrInfo, PointLight pointLight) {
3629
- vec3 light_direction = normalize(pointLight.position - pbr_vPosition);
3630
- PBRInfo_setDirectionalLight(pbrInfo, light_direction);
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);
3631
4586
  }
3632
4587
 
3633
- vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
3634
- // Calculate the shading terms for the microfacet specular shading model
3635
- vec3 F = specularReflection(pbrInfo);
3636
- float G = geometricOcclusion(pbrInfo);
3637
- float D = microfacetDistribution(pbrInfo);
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) {
4746
+ vec3 light_direction = normalize(pointLight.position - pbr_vPosition);
4747
+ PBRInfo_setDirectionalLight(pbrInfo, light_direction);
4748
+ }
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
+
4755
+ vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
4756
+ // Calculate the shading terms for the microfacet specular shading model
4757
+ vec3 F = specularReflection(pbrInfo);
4758
+ float G = geometricOcclusion(pbrInfo);
4759
+ float D = microfacetDistribution(pbrInfo);
3638
4760
 
3639
4761
  // Calculation of analytical lighting contribution
3640
4762
  vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInfo);
@@ -3660,6 +4782,8 @@ vec4 pbr_filterColor(vec4 colorUnused)
3660
4782
 
3661
4783
  vec3 color = vec3(0, 0, 0);
3662
4784
 
4785
+ float transmission = 0.0;
4786
+
3663
4787
  if(pbrMaterial.unlit){
3664
4788
  color.rgb = baseColor.rgb;
3665
4789
  }
@@ -3678,14 +4802,252 @@ vec4 pbr_filterColor(vec4 colorUnused)
3678
4802
  #endif
3679
4803
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
3680
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
+
3681
5029
  // Roughness is authored as perceptual roughness; as is convention,
3682
5030
  // convert to material roughness by squaring the perceptual roughness [2].
3683
5031
  float alphaRoughness = perceptualRoughness * perceptualRoughness;
3684
5032
 
3685
- vec3 f0 = vec3(0.04);
3686
- vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
3687
- diffuseColor *= 1.0 - metallic;
3688
- 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;
3689
5051
 
3690
5052
  // Compute reflectance.
3691
5053
  float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
@@ -3697,11 +5059,6 @@ vec4 pbr_filterColor(vec4 colorUnused)
3697
5059
  float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
3698
5060
  vec3 specularEnvironmentR0 = specularColor.rgb;
3699
5061
  vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
3700
-
3701
- vec3 n = getNormal(); // normal at surface point
3702
- vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
3703
-
3704
- float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
3705
5062
  vec3 reflection = -normalize(reflect(v, n));
3706
5063
 
3707
5064
  PBRInfo pbrInfo = PBRInfo(
@@ -3725,13 +5082,33 @@ vec4 pbr_filterColor(vec4 colorUnused)
3725
5082
  #ifdef USE_LIGHTS
3726
5083
  // Apply ambient light
3727
5084
  PBRInfo_setAmbientLight(pbrInfo);
3728
- 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
+ );
3729
5096
 
3730
5097
  // Apply directional light
3731
5098
  for(int i = 0; i < lighting.directionalLightCount; i++) {
3732
5099
  if (i < lighting.directionalLightCount) {
3733
5100
  PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
3734
- 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
+ );
3735
5112
  }
3736
5113
  }
3737
5114
 
@@ -3740,7 +5117,35 @@ vec4 pbr_filterColor(vec4 colorUnused)
3740
5117
  if (i < lighting.pointLightCount) {
3741
5118
  PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
3742
5119
  float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
3743
- 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
+ );
3744
5149
  }
3745
5150
  }
3746
5151
  #endif
@@ -3748,7 +5153,16 @@ vec4 pbr_filterColor(vec4 colorUnused)
3748
5153
  // Calculate lighting contribution from image based lighting source (IBL)
3749
5154
  #ifdef USE_IBL
3750
5155
  if (pbrMaterial.IBLenabled) {
3751
- 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;
3752
5166
  }
3753
5167
  #endif
3754
5168
 
@@ -3760,12 +5174,17 @@ vec4 pbr_filterColor(vec4 colorUnused)
3760
5174
  }
3761
5175
  #endif
3762
5176
 
5177
+ vec3 emissive = pbrMaterial.emissiveFactor;
3763
5178
  #ifdef HAS_EMISSIVEMAP
3764
5179
  if (pbrMaterial.emissiveMapEnabled) {
3765
- vec3 emissive = SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb * pbrMaterial.emissiveFactor;
3766
- color += emissive;
5180
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
3767
5181
  }
3768
5182
  #endif
5183
+ color += emissive * pbrMaterial.emissiveStrength;
5184
+
5185
+ if (transmission > 0.0) {
5186
+ color = mix(color, color * getVolumeAttenuation(thickness), transmission);
5187
+ }
3769
5188
 
3770
5189
  // This section uses mix to override final color for reference app visualization
3771
5190
  // of various parameters in the lighting equation.
@@ -3785,7 +5204,8 @@ vec4 pbr_filterColor(vec4 colorUnused)
3785
5204
 
3786
5205
  }
3787
5206
 
3788
- 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);
3789
5209
  }
3790
5210
  `
3791
5211
  );
@@ -3851,6 +5271,42 @@ struct pbrMaterialUniforms {
3851
5271
 
3852
5272
  alphaCutoffEnabled: i32,
3853
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,
3854
5310
 
3855
5311
  // IBL
3856
5312
  IBLenabled: i32,
@@ -3863,38 +5319,77 @@ struct pbrMaterialUniforms {
3863
5319
  // #endif
3864
5320
  }
3865
5321
 
3866
- @binding(2) @group(0) var<uniform> pbrMaterial : pbrMaterialUniforms;
5322
+ @group(3) @binding(auto) var<uniform> pbrMaterial : pbrMaterialUniforms;
3867
5323
 
3868
5324
  // Samplers
3869
5325
  #ifdef HAS_BASECOLORMAP
3870
- @binding(3) @group(0) var pbr_baseColorSampler: texture_2d<f32>;
3871
- @binding(4) @group(0) var pbr_baseColorSamplerSampler: sampler;
5326
+ @group(3) @binding(auto) var pbr_baseColorSampler: texture_2d<f32>;
5327
+ @group(3) @binding(auto) var pbr_baseColorSamplerSampler: sampler;
3872
5328
  #endif
3873
5329
  #ifdef HAS_NORMALMAP
3874
- @binding(5) @group(0) var pbr_normalSampler: texture_2d<f32>;
3875
- @binding(6) @group(0) var pbr_normalSamplerSampler: sampler;
5330
+ @group(3) @binding(auto) var pbr_normalSampler: texture_2d<f32>;
5331
+ @group(3) @binding(auto) var pbr_normalSamplerSampler: sampler;
3876
5332
  #endif
3877
5333
  #ifdef HAS_EMISSIVEMAP
3878
- @binding(7) @group(0) var pbr_emissiveSampler: texture_2d<f32>;
3879
- @binding(8) @group(0) var pbr_emissiveSamplerSampler: sampler;
5334
+ @group(3) @binding(auto) var pbr_emissiveSampler: texture_2d<f32>;
5335
+ @group(3) @binding(auto) var pbr_emissiveSamplerSampler: sampler;
3880
5336
  #endif
3881
5337
  #ifdef HAS_METALROUGHNESSMAP
3882
- @binding(9) @group(0) var pbr_metallicRoughnessSampler: texture_2d<f32>;
3883
- @binding(10) @group(0) var pbr_metallicRoughnessSamplerSampler: sampler;
5338
+ @group(3) @binding(auto) var pbr_metallicRoughnessSampler: texture_2d<f32>;
5339
+ @group(3) @binding(auto) var pbr_metallicRoughnessSamplerSampler: sampler;
3884
5340
  #endif
3885
5341
  #ifdef HAS_OCCLUSIONMAP
3886
- @binding(11) @group(0) var pbr_occlusionSampler: texture_2d<f32>;
3887
- @binding(12) @group(0) var pbr_occlusionSamplerSampler: sampler;
5342
+ @group(3) @binding(auto) var pbr_occlusionSampler: texture_2d<f32>;
5343
+ @group(3) @binding(auto) var pbr_occlusionSamplerSampler: sampler;
3888
5344
  #endif
3889
- #ifdef USE_IBL
3890
- @binding(13) @group(0) var pbr_diffuseEnvSampler: texture_cube<f32>;
3891
- @binding(14) @group(0) var pbr_diffuseEnvSamplerSampler: sampler;
3892
- @binding(15) @group(0) var pbr_specularEnvSampler: texture_cube<f32>;
3893
- @binding(16) @group(0) var pbr_specularEnvSamplerSampler: sampler;
3894
- @binding(17) @group(0) var pbr_BrdfLUT: texture_2d<f32>;
3895
- @binding(18) @group(0) var pbr_BrdfLUTSampler: sampler;
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;
3896
5392
  #endif
3897
-
3898
5393
  // Encapsulate the various inputs used by the various functions in the shading equation
3899
5394
  // We store values in this struct to simplify the integration of alternative implementations
3900
5395
  // of the shading terms, outlined in the Readme.MD Appendix.
@@ -3935,11 +5430,9 @@ fn SRGBtoLINEAR(srgbIn: vec4f ) -> vec4f
3935
5430
  return vec4f(linOut, srgbIn.w);
3936
5431
  }
3937
5432
 
3938
- // Find the normal for this fragment, pulling either from a predefined normal map
3939
- // or from the interpolated mesh normal and tangent attributes.
3940
- fn getNormal() -> vec3f
5433
+ // Build the tangent basis from interpolated attributes or screen-space derivatives.
5434
+ fn getTBN() -> mat3x3f
3941
5435
  {
3942
- // Retrieve the tangent space matrix
3943
5436
  let pos_dx: vec3f = dpdx(fragmentInputs.pbr_vPosition);
3944
5437
  let pos_dy: vec3f = dpdy(fragmentInputs.pbr_vPosition);
3945
5438
  let tex_dx: vec3f = dpdx(vec3f(fragmentInputs.pbr_vUV, 0.0));
@@ -3957,16 +5450,52 @@ fn getNormal() -> vec3f
3957
5450
  tbn = fragmentInputs.pbr_vTBN;
3958
5451
  #endif
3959
5452
 
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
+ {
3960
5471
  // The tbn matrix is linearly interpolated, so we need to re-normalize
3961
5472
  var n: vec3f = normalize(tbn[2].xyz);
3962
5473
  #ifdef HAS_NORMALMAP
3963
- n = textureSample(pbr_normalSampler, pbr_normalSamplerSampler, fragmentInputs.pbr_vUV).rgb;
3964
- n = normalize(tbn * ((2.0 * n - 1.0) * vec3f(pbrMaterial.normalScale, pbrMaterial.normalScale, 1.0)));
5474
+ n = getMappedNormal(
5475
+ pbr_normalSampler,
5476
+ pbr_normalSamplerSampler,
5477
+ tbn,
5478
+ pbrMaterial.normalScale
5479
+ );
3965
5480
  #endif
3966
5481
 
3967
5482
  return n;
3968
5483
  }
3969
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
+
3970
5499
  // Calculation of the lighting contribution from an optional Image Based Light source.
3971
5500
  // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
3972
5501
  // See our README.md on Environment Maps [3] for additional discussion.
@@ -3977,17 +5506,25 @@ fn getIBLContribution(pbrInfo: PBRInfo, n: vec3f, reflection: vec3f) -> vec3f
3977
5506
  let lod: f32 = pbrInfo.perceptualRoughness * mipCount;
3978
5507
  // retrieve a scale and bias to F0. See [1], Figure 3
3979
5508
  let brdf = SRGBtoLINEAR(
3980
- textureSample(
3981
- pbr_BrdfLUT,
3982
- pbr_BrdfLUTSampler,
3983
- vec2f(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness)
5509
+ textureSampleLevel(
5510
+ pbr_brdfLUT,
5511
+ pbr_brdfLUTSampler,
5512
+ vec2f(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness),
5513
+ 0.0
3984
5514
  )
3985
5515
  ).rgb;
3986
5516
  let diffuseLight =
3987
- SRGBtoLINEAR(textureSample(pbr_diffuseEnvSampler, pbr_diffuseEnvSamplerSampler, n)).rgb;
3988
- let specularLightDefault =
3989
- SRGBtoLINEAR(textureSample(pbr_specularEnvSampler, pbr_specularEnvSamplerSampler, reflection)).rgb;
3990
- var specularLight = specularLightDefault;
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;
3991
5528
  #ifdef USE_TEX_LOD
3992
5529
  specularLight = SRGBtoLINEAR(
3993
5530
  textureSampleLevel(
@@ -4048,6 +5585,172 @@ fn microfacetDistribution(pbrInfo: PBRInfo) -> f32 {
4048
5585
  return roughnessSq / (M_PI * f * f);
4049
5586
  }
4050
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;
5752
+ }
5753
+
4051
5754
  fn PBRInfo_setAmbientLight(pbrInfo: ptr<function, PBRInfo>) {
4052
5755
  (*pbrInfo).NdotL = 1.0;
4053
5756
  (*pbrInfo).NdotH = 0.0;
@@ -4072,6 +5775,11 @@ fn PBRInfo_setPointLight(pbrInfo: ptr<function, PBRInfo>, pointLight: PointLight
4072
5775
  PBRInfo_setDirectionalLight(pbrInfo, light_direction);
4073
5776
  }
4074
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
+
4075
5783
  fn calculateFinalColor(pbrInfo: PBRInfo, lightColor: vec3<f32>) -> vec3<f32> {
4076
5784
  // Calculate the shading terms for the microfacet specular shading model
4077
5785
  let F = specularReflection(pbrInfo);
@@ -4101,6 +5809,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4101
5809
  #endif
4102
5810
 
4103
5811
  var color = vec3<f32>(0.0, 0.0, 0.0);
5812
+ var transmission = 0.0;
4104
5813
 
4105
5814
  if (pbrMaterial.unlit != 0u) {
4106
5815
  color = baseColor.rgb;
@@ -4123,14 +5832,308 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4123
5832
  #endif
4124
5833
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
4125
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
+
4126
6115
  // Roughness is authored as perceptual roughness; as is convention,
4127
6116
  // convert to material roughness by squaring the perceptual roughness [2].
4128
6117
  let alphaRoughness = perceptualRoughness * perceptualRoughness;
4129
6118
 
4130
- let f0 = vec3<f32>(0.04);
4131
- var diffuseColor = baseColor.rgb * (vec3<f32>(1.0) - f0);
4132
- diffuseColor *= 1.0 - metallic;
4133
- 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;
4134
6137
 
4135
6138
  // Compute reflectance.
4136
6139
  let reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
@@ -4142,11 +6145,6 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4142
6145
  let reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
4143
6146
  let specularEnvironmentR0 = specularColor;
4144
6147
  let specularEnvironmentR90 = vec3<f32>(1.0, 1.0, 1.0) * reflectance90;
4145
-
4146
- let n = getNormal(); // normal at surface point
4147
- let v = normalize(pbrProjection.camera - fragmentInputs.pbr_vPosition); // Vector from surface point to camera
4148
-
4149
- let NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
4150
6148
  let reflection = -normalize(reflect(v, n));
4151
6149
 
4152
6150
  var pbrInfo = PBRInfo(
@@ -4169,13 +6167,33 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4169
6167
  #ifdef USE_LIGHTS
4170
6168
  // Apply ambient light
4171
6169
  PBRInfo_setAmbientLight(&pbrInfo);
4172
- 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
+ );
4173
6181
 
4174
6182
  // Apply directional light
4175
6183
  for (var i = 0; i < lighting.directionalLightCount; i++) {
4176
6184
  if (i < lighting.directionalLightCount) {
4177
6185
  PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
4178
- 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
+ );
4179
6197
  }
4180
6198
  }
4181
6199
 
@@ -4187,7 +6205,35 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4187
6205
  lighting_getPointLight(i),
4188
6206
  distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
4189
6207
  );
4190
- 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
+ );
4191
6237
  }
4192
6238
  }
4193
6239
  #endif
@@ -4195,7 +6241,16 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4195
6241
  // Calculate lighting contribution from image based lighting source (IBL)
4196
6242
  #ifdef USE_IBL
4197
6243
  if (pbrMaterial.IBLenabled != 0) {
4198
- color += getIBLContribution(pbrInfo, n, reflection);
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;
4199
6254
  }
4200
6255
  #endif
4201
6256
 
@@ -4208,14 +6263,19 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4208
6263
  }
4209
6264
  #endif
4210
6265
 
6266
+ var emissive = pbrMaterial.emissiveFactor;
4211
6267
  #ifdef HAS_EMISSIVEMAP
4212
6268
  if (pbrMaterial.emissiveMapEnabled != 0u) {
4213
- let emissive = SRGBtoLINEAR(
6269
+ emissive *= SRGBtoLINEAR(
4214
6270
  textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
4215
- ).rgb * pbrMaterial.emissiveFactor;
4216
- color += emissive;
6271
+ ).rgb;
4217
6272
  }
4218
6273
  #endif
6274
+ color += emissive * pbrMaterial.emissiveStrength;
6275
+
6276
+ if (transmission > 0.0) {
6277
+ color = mix(color, color * getVolumeAttenuation(thickness), transmission);
6278
+ }
4219
6279
 
4220
6280
  // This section uses mix to override final color for reference app visualization
4221
6281
  // of various parameters in the lighting equation.
@@ -4234,7 +6294,8 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4234
6294
  #endif
4235
6295
  }
4236
6296
 
4237
- 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);
4238
6299
  }
4239
6300
  `
4240
6301
  );
@@ -4259,11 +6320,12 @@ var wgslUniformBlock = (
4259
6320
  camera: vec3<f32>
4260
6321
  };
4261
6322
 
4262
- @binding(0) @group(0) var<uniform> pbrProjection: pbrProjectionUniforms;
6323
+ @group(0) @binding(auto) var<uniform> pbrProjection: pbrProjectionUniforms;
4263
6324
  `
4264
6325
  );
4265
6326
  var pbrProjection = {
4266
6327
  name: "pbrProjection",
6328
+ bindingLayout: [{ name: "pbrProjection", group: 0 }],
4267
6329
  source: wgslUniformBlock,
4268
6330
  vs: uniformBlock,
4269
6331
  fs: uniformBlock,
@@ -4281,8 +6343,75 @@ var pbrProjection = {
4281
6343
  var pbrMaterial = {
4282
6344
  props: {},
4283
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
+ },
4284
6392
  name: "pbrMaterial",
4285
- dependencies: [lighting, pbrProjection],
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],
4286
6415
  source: source3,
4287
6416
  vs: vs3,
4288
6417
  fs: fs4,
@@ -4296,10 +6425,16 @@ var pbrMaterial = {
4296
6425
  HAS_SPECULARCOLORMAP: false,
4297
6426
  HAS_SPECULARINTENSITYMAP: false,
4298
6427
  HAS_TRANSMISSIONMAP: false,
6428
+ HAS_THICKNESSMAP: false,
4299
6429
  HAS_CLEARCOATMAP: false,
6430
+ HAS_CLEARCOATROUGHNESSMAP: false,
6431
+ HAS_CLEARCOATNORMALMAP: false,
4300
6432
  HAS_SHEENCOLORMAP: false,
6433
+ HAS_SHEENROUGHNESSMAP: false,
4301
6434
  HAS_IRIDESCENCEMAP: false,
6435
+ HAS_IRIDESCENCETHICKNESSMAP: false,
4302
6436
  HAS_ANISOTROPYMAP: false,
6437
+ USE_MATERIAL_EXTENSIONS: false,
4303
6438
  ALPHA_CUTOFF: false,
4304
6439
  USE_IBL: false,
4305
6440
  PBR_DEBUG: false
@@ -4325,14 +6460,6 @@ var pbrMaterial = {
4325
6460
  alphaCutoffEnabled: "i32",
4326
6461
  alphaCutoff: "f32",
4327
6462
  // #ifdef ALPHA_CUTOFF
4328
- // IBL
4329
- IBLenabled: "i32",
4330
- scaleIBLAmbient: "vec2<f32>",
4331
- // #ifdef USE_IBL
4332
- // debugging flags used for shader output of intermediate PBR variables
4333
- // #ifdef PBR_DEBUG
4334
- scaleDiffBaseMR: "vec4<f32>",
4335
- scaleFGDSpec: "vec4<f32>",
4336
6463
  specularColorFactor: "vec3<f32>",
4337
6464
  specularIntensityFactor: "f32",
4338
6465
  specularColorMapEnabled: "i32",
@@ -4346,9 +6473,11 @@ var pbrMaterial = {
4346
6473
  clearcoatFactor: "f32",
4347
6474
  clearcoatRoughnessFactor: "f32",
4348
6475
  clearcoatMapEnabled: "i32",
6476
+ clearcoatRoughnessMapEnabled: "i32",
4349
6477
  sheenColorFactor: "vec3<f32>",
4350
6478
  sheenRoughnessFactor: "f32",
4351
6479
  sheenColorMapEnabled: "i32",
6480
+ sheenRoughnessMapEnabled: "i32",
4352
6481
  iridescenceFactor: "f32",
4353
6482
  iridescenceIor: "f32",
4354
6483
  iridescenceThicknessRange: "vec2<f32>",
@@ -4357,7 +6486,15 @@ var pbrMaterial = {
4357
6486
  anisotropyRotation: "f32",
4358
6487
  anisotropyDirection: "vec2<f32>",
4359
6488
  anisotropyMapEnabled: "i32",
4360
- 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>"
4361
6498
  }
4362
6499
  };
4363
6500
  //# sourceMappingURL=index.cjs.map