@luma.gl/shadertools 9.3.0-alpha.6 → 9.3.0-alpha.9

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 -331
  2. package/dist/dist.min.js +1803 -283
  3. package/dist/index.cjs +2496 -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 +113 -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 +195 -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/dist.dev.js CHANGED
@@ -75,10 +75,15 @@ var __exports__ = (() => {
75
75
  getShaderInfo: () => getShaderInfo,
76
76
  getShaderModuleDependencies: () => getShaderModuleDependencies,
77
77
  getShaderModuleSource: () => getShaderModuleSource,
78
+ getShaderModuleUniformBlockFields: () => getShaderModuleUniformBlockFields,
79
+ getShaderModuleUniformBlockName: () => getShaderModuleUniformBlockName,
80
+ getShaderModuleUniformLayoutValidationResult: () => getShaderModuleUniformLayoutValidationResult,
78
81
  getShaderModuleUniforms: () => getShaderModuleUniforms,
79
82
  gouraudMaterial: () => gouraudMaterial,
83
+ ibl: () => ibl,
80
84
  initializeShaderModule: () => initializeShaderModule,
81
85
  initializeShaderModules: () => initializeShaderModules,
86
+ lambertMaterial: () => lambertMaterial,
82
87
  lighting: () => lighting,
83
88
  pbrMaterial: () => pbrMaterial,
84
89
  phongMaterial: () => phongMaterial,
@@ -88,7 +93,8 @@ var __exports__ = (() => {
88
93
  skin: () => skin,
89
94
  toHalfFloat: () => toHalfFloat,
90
95
  typeToChannelCount: () => typeToChannelCount,
91
- typeToChannelSuffix: () => typeToChannelSuffix
96
+ typeToChannelSuffix: () => typeToChannelSuffix,
97
+ validateShaderModuleUniformLayout: () => validateShaderModuleUniformLayout
92
98
  });
93
99
  __reExport(bundle_exports, __toESM(require_core(), 1));
94
100
 
@@ -409,6 +415,122 @@ ${inject[key]}` : inject[key];
409
415
  return getShaderDependencies(modules);
410
416
  }
411
417
 
418
+ // src/lib/shader-module/shader-module-uniform-layout.ts
419
+ var GLSL_UNIFORM_BLOCK_FIELD_REGEXP = /^(?:uniform\s+)?(?:(?:lowp|mediump|highp)\s+)?[A-Za-z0-9_]+(?:<[^>]+>)?\s+([A-Za-z0-9_]+)(?:\s*\[[^\]]+\])?\s*;/;
420
+ function getShaderModuleUniformBlockName(module) {
421
+ return `${module.name}Uniforms`;
422
+ }
423
+ function getShaderModuleUniformBlockFields(module, stage) {
424
+ const shaderSource = stage === "wgsl" ? module.source : stage === "vertex" ? module.vs : module.fs;
425
+ if (!shaderSource) {
426
+ return null;
427
+ }
428
+ const uniformBlockName = getShaderModuleUniformBlockName(module);
429
+ return extractShaderUniformBlockFieldNames(
430
+ shaderSource,
431
+ stage === "wgsl" ? "wgsl" : "glsl",
432
+ uniformBlockName
433
+ );
434
+ }
435
+ function getShaderModuleUniformLayoutValidationResult(module, stage) {
436
+ const expectedUniformNames = Object.keys(module.uniformTypes || {});
437
+ if (!expectedUniformNames.length) {
438
+ return null;
439
+ }
440
+ const actualUniformNames = getShaderModuleUniformBlockFields(module, stage);
441
+ if (!actualUniformNames) {
442
+ return null;
443
+ }
444
+ return {
445
+ moduleName: module.name,
446
+ uniformBlockName: getShaderModuleUniformBlockName(module),
447
+ stage,
448
+ expectedUniformNames,
449
+ actualUniformNames,
450
+ matches: areStringArraysEqual(expectedUniformNames, actualUniformNames)
451
+ };
452
+ }
453
+ function validateShaderModuleUniformLayout(module, stage, options = {}) {
454
+ const validationResult = getShaderModuleUniformLayoutValidationResult(module, stage);
455
+ if (!validationResult || validationResult.matches) {
456
+ return validationResult;
457
+ }
458
+ const message = formatShaderModuleUniformLayoutError(validationResult);
459
+ options.log?.error?.(message, validationResult)();
460
+ if (options.throwOnError !== false) {
461
+ assert(false, message);
462
+ }
463
+ return validationResult;
464
+ }
465
+ function extractShaderUniformBlockFieldNames(shaderSource, language, uniformBlockName) {
466
+ const sourceBody = language === "wgsl" ? extractWGSLStructBody(shaderSource, uniformBlockName) : extractGLSLUniformBlockBody(shaderSource, uniformBlockName);
467
+ if (!sourceBody) {
468
+ return null;
469
+ }
470
+ const fieldNames = [];
471
+ for (const sourceLine of sourceBody.split("\n")) {
472
+ const line = sourceLine.replace(/\/\/.*$/, "").trim();
473
+ if (!line || line.startsWith("#")) {
474
+ continue;
475
+ }
476
+ const fieldMatch = language === "wgsl" ? line.match(/^([A-Za-z0-9_]+)\s*:/) : line.match(GLSL_UNIFORM_BLOCK_FIELD_REGEXP);
477
+ if (fieldMatch) {
478
+ fieldNames.push(fieldMatch[1]);
479
+ }
480
+ }
481
+ return fieldNames;
482
+ }
483
+ function extractWGSLStructBody(shaderSource, uniformBlockName) {
484
+ const structMatch = new RegExp(`\\bstruct\\s+${uniformBlockName}\\b`, "m").exec(shaderSource);
485
+ if (!structMatch) {
486
+ return null;
487
+ }
488
+ const openBraceIndex = shaderSource.indexOf("{", structMatch.index);
489
+ if (openBraceIndex < 0) {
490
+ return null;
491
+ }
492
+ let braceDepth = 0;
493
+ for (let index = openBraceIndex; index < shaderSource.length; index++) {
494
+ const character = shaderSource[index];
495
+ if (character === "{") {
496
+ braceDepth++;
497
+ continue;
498
+ }
499
+ if (character !== "}") {
500
+ continue;
501
+ }
502
+ braceDepth--;
503
+ if (braceDepth === 0) {
504
+ return shaderSource.slice(openBraceIndex + 1, index);
505
+ }
506
+ }
507
+ return null;
508
+ }
509
+ function extractGLSLUniformBlockBody(shaderSource, uniformBlockName) {
510
+ const sourceMatch = shaderSource.match(
511
+ new RegExp(`uniform\\s+${uniformBlockName}\\s*\\{([\\s\\S]*?)\\}\\s*[A-Za-z0-9_]+\\s*;`, "m")
512
+ );
513
+ return sourceMatch?.[1] || null;
514
+ }
515
+ function areStringArraysEqual(leftValues, rightValues) {
516
+ if (leftValues.length !== rightValues.length) {
517
+ return false;
518
+ }
519
+ for (let valueIndex = 0; valueIndex < leftValues.length; valueIndex++) {
520
+ if (leftValues[valueIndex] !== rightValues[valueIndex]) {
521
+ return false;
522
+ }
523
+ }
524
+ return true;
525
+ }
526
+ function formatShaderModuleUniformLayoutError(validationResult) {
527
+ return `${validationResult.moduleName}: ${validationResult.stage} shader uniform block ${validationResult.uniformBlockName} does not match module.uniformTypes.
528
+ Expected: ${validationResult.expectedUniformNames.join(
529
+ ", "
530
+ )}
531
+ Actual: ${validationResult.actualUniformNames.join(", ")}`;
532
+ }
533
+
412
534
  // src/lib/shader-assembly/platform-defines.ts
413
535
  function getPlatformShaderDefines(platformInfo) {
414
536
  switch (platformInfo?.gpu.toLowerCase()) {
@@ -593,11 +715,166 @@ ${inject[key]}` : inject[key];
593
715
  return version;
594
716
  }
595
717
 
718
+ // src/lib/shader-assembly/wgsl-binding-debug.ts
719
+ var WGSL_BINDING_DEBUG_REGEXES = [
720
+ /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g,
721
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g
722
+ ];
723
+ function getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments = []) {
724
+ const assignmentMap = /* @__PURE__ */ new Map();
725
+ for (const bindingAssignment of bindingAssignments) {
726
+ assignmentMap.set(
727
+ getBindingAssignmentKey(
728
+ bindingAssignment.name,
729
+ bindingAssignment.group,
730
+ bindingAssignment.location
731
+ ),
732
+ bindingAssignment.moduleName
733
+ );
734
+ }
735
+ const rows = [];
736
+ for (const regex of WGSL_BINDING_DEBUG_REGEXES) {
737
+ regex.lastIndex = 0;
738
+ let match;
739
+ while (match = regex.exec(source4)) {
740
+ const isBindingFirst = regex === WGSL_BINDING_DEBUG_REGEXES[0];
741
+ const binding = Number(match[isBindingFirst ? 1 : 2]);
742
+ const group = Number(match[isBindingFirst ? 2 : 1]);
743
+ const accessDeclaration = match[3]?.trim();
744
+ const name = match[4];
745
+ const resourceType = match[5].trim();
746
+ const moduleName = assignmentMap.get(getBindingAssignmentKey(name, group, binding));
747
+ rows.push(
748
+ normalizeShaderBindingDebugRow({
749
+ name,
750
+ group,
751
+ binding,
752
+ owner: moduleName ? "module" : "application",
753
+ moduleName,
754
+ accessDeclaration,
755
+ resourceType
756
+ })
757
+ );
758
+ }
759
+ }
760
+ return rows.sort((left, right) => {
761
+ if (left.group !== right.group) {
762
+ return left.group - right.group;
763
+ }
764
+ if (left.binding !== right.binding) {
765
+ return left.binding - right.binding;
766
+ }
767
+ return left.name.localeCompare(right.name);
768
+ });
769
+ }
770
+ function normalizeShaderBindingDebugRow(row) {
771
+ const baseRow = {
772
+ name: row.name,
773
+ group: row.group,
774
+ binding: row.binding,
775
+ owner: row.owner,
776
+ kind: "unknown",
777
+ moduleName: row.moduleName,
778
+ resourceType: row.resourceType
779
+ };
780
+ if (row.accessDeclaration) {
781
+ const access = row.accessDeclaration.split(",").map((value) => value.trim());
782
+ if (access[0] === "uniform") {
783
+ return { ...baseRow, kind: "uniform", access: "uniform" };
784
+ }
785
+ if (access[0] === "storage") {
786
+ const storageAccess = access[1] || "read_write";
787
+ return {
788
+ ...baseRow,
789
+ kind: storageAccess === "read" ? "read-only-storage" : "storage",
790
+ access: storageAccess
791
+ };
792
+ }
793
+ }
794
+ if (row.resourceType === "sampler" || row.resourceType === "sampler_comparison") {
795
+ return {
796
+ ...baseRow,
797
+ kind: "sampler",
798
+ samplerKind: row.resourceType === "sampler_comparison" ? "comparison" : "filtering"
799
+ };
800
+ }
801
+ if (row.resourceType.startsWith("texture_storage_")) {
802
+ return {
803
+ ...baseRow,
804
+ kind: "storage-texture",
805
+ access: getStorageTextureAccess(row.resourceType),
806
+ viewDimension: getTextureViewDimension(row.resourceType)
807
+ };
808
+ }
809
+ if (row.resourceType.startsWith("texture_")) {
810
+ return {
811
+ ...baseRow,
812
+ kind: "texture",
813
+ viewDimension: getTextureViewDimension(row.resourceType),
814
+ sampleType: getTextureSampleType(row.resourceType),
815
+ multisampled: row.resourceType.startsWith("texture_multisampled_")
816
+ };
817
+ }
818
+ return baseRow;
819
+ }
820
+ function getBindingAssignmentKey(name, group, binding) {
821
+ return `${group}:${binding}:${name}`;
822
+ }
823
+ function getTextureViewDimension(resourceType) {
824
+ if (resourceType.includes("cube_array")) {
825
+ return "cube-array";
826
+ }
827
+ if (resourceType.includes("2d_array")) {
828
+ return "2d-array";
829
+ }
830
+ if (resourceType.includes("cube")) {
831
+ return "cube";
832
+ }
833
+ if (resourceType.includes("3d")) {
834
+ return "3d";
835
+ }
836
+ if (resourceType.includes("2d")) {
837
+ return "2d";
838
+ }
839
+ if (resourceType.includes("1d")) {
840
+ return "1d";
841
+ }
842
+ return void 0;
843
+ }
844
+ function getTextureSampleType(resourceType) {
845
+ if (resourceType.startsWith("texture_depth_")) {
846
+ return "depth";
847
+ }
848
+ if (resourceType.includes("<i32>")) {
849
+ return "sint";
850
+ }
851
+ if (resourceType.includes("<u32>")) {
852
+ return "uint";
853
+ }
854
+ if (resourceType.includes("<f32>")) {
855
+ return "float";
856
+ }
857
+ return void 0;
858
+ }
859
+ function getStorageTextureAccess(resourceType) {
860
+ const match = /,\s*([A-Za-z_][A-Za-z0-9_]*)\s*>$/.exec(resourceType);
861
+ return match?.[1];
862
+ }
863
+
596
864
  // src/lib/shader-assembly/assemble-shaders.ts
597
865
  var INJECT_SHADER_DECLARATIONS = `
598
866
 
599
867
  ${DECLARATION_INJECT_MARKER}
600
868
  `;
869
+ var MODULE_WGSL_BINDING_DECLARATION_REGEXES = [
870
+ /@binding\(\s*(auto|\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
871
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(auto|\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
872
+ ];
873
+ var WGSL_BINDING_DECLARATION_REGEXES = [
874
+ /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
875
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
876
+ ];
877
+ var RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
601
878
  var FRAGMENT_SHADER_PROLOGUE = (
602
879
  /* glsl */
603
880
  `precision highp float;
@@ -605,14 +882,17 @@ ${DECLARATION_INJECT_MARKER}
605
882
  );
606
883
  function assembleWGSLShader(options) {
607
884
  const modules = getShaderModuleDependencies(options.modules || []);
885
+ const { source: source4, bindingAssignments } = assembleShaderWGSL(options.platformInfo, {
886
+ ...options,
887
+ source: options.source,
888
+ stage: "vertex",
889
+ modules
890
+ });
608
891
  return {
609
- source: assembleShaderWGSL(options.platformInfo, {
610
- ...options,
611
- source: options.source,
612
- stage: "vertex",
613
- modules
614
- }),
615
- getUniforms: assembleGetUniforms(modules)
892
+ source: source4,
893
+ getUniforms: assembleGetUniforms(modules),
894
+ bindingAssignments,
895
+ bindingTable: getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments)
616
896
  };
617
897
  }
618
898
  function assembleGLSLShaderPair(options) {
@@ -673,11 +953,28 @@ ${DECLARATION_INJECT_MARKER}
673
953
  }
674
954
  }
675
955
  const modulesToInject = modules;
956
+ const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(coreSource);
957
+ const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(
958
+ modulesToInject,
959
+ options._bindingRegistry,
960
+ usedBindingsByGroup
961
+ );
962
+ const bindingAssignments = [];
676
963
  for (const module of modulesToInject) {
677
964
  if (log2) {
678
965
  checkShaderModuleDeprecations(module, coreSource, log2);
679
966
  }
680
- const moduleSource = getShaderModuleSource(module, "wgsl");
967
+ const relocation = relocateWGSLModuleBindings(
968
+ getShaderModuleSource(module, "wgsl", log2),
969
+ module,
970
+ {
971
+ usedBindingsByGroup,
972
+ bindingRegistry: options._bindingRegistry,
973
+ reservedBindingKeysByGroup
974
+ }
975
+ );
976
+ bindingAssignments.push(...relocation.bindingAssignments);
977
+ const moduleSource = relocation.source;
681
978
  assembledSource += moduleSource;
682
979
  const injections = module.injections?.[stage] || {};
683
980
  for (const key in injections) {
@@ -696,9 +993,11 @@ ${DECLARATION_INJECT_MARKER}
696
993
  assembledSource += INJECT_SHADER_DECLARATIONS;
697
994
  assembledSource = injectShader(assembledSource, stage, declInjections);
698
995
  assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections);
996
+ assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
699
997
  assembledSource += coreSource;
700
998
  assembledSource = injectShader(assembledSource, stage, mainInjections);
701
- return assembledSource;
999
+ assertNoUnresolvedAutoBindings(assembledSource);
1000
+ return { source: assembledSource, bindingAssignments };
702
1001
  }
703
1002
  function assembleShaderGLSL(platformInfo, options) {
704
1003
  const {
@@ -771,7 +1070,7 @@ ${getApplicationDefines(allDefines)}
771
1070
  if (log2) {
772
1071
  checkShaderModuleDeprecations(module, coreSource, log2);
773
1072
  }
774
- const moduleSource = getShaderModuleSource(module, stage);
1073
+ const moduleSource = getShaderModuleSource(module, stage, log2);
775
1074
  assembledSource += moduleSource;
776
1075
  const injections = module.instance?.normalizedInjections[stage] || {};
777
1076
  for (const key in injections) {
@@ -819,7 +1118,7 @@ ${getApplicationDefines(allDefines)}
819
1118
  }
820
1119
  return sourceText;
821
1120
  }
822
- function getShaderModuleSource(module, stage) {
1121
+ function getShaderModuleSource(module, stage, log2) {
823
1122
  let moduleSource;
824
1123
  switch (stage) {
825
1124
  case "vertex":
@@ -837,6 +1136,7 @@ ${getApplicationDefines(allDefines)}
837
1136
  if (!module.name) {
838
1137
  throw new Error("Shader module must have a name");
839
1138
  }
1139
+ validateShaderModuleUniformLayout(module, stage, { log: log2 });
840
1140
  const moduleName = module.name.toUpperCase().replace(/[^0-9a-z]/gi, "_");
841
1141
  let source4 = `// ----- MODULE ${module.name} ---------------
842
1142
 
@@ -849,13 +1149,225 @@ ${getApplicationDefines(allDefines)}
849
1149
  `;
850
1150
  return source4;
851
1151
  }
1152
+ function getUsedBindingsByGroupFromApplicationWGSL(source4) {
1153
+ const usedBindingsByGroup = /* @__PURE__ */ new Map();
1154
+ for (const regex of WGSL_BINDING_DECLARATION_REGEXES) {
1155
+ regex.lastIndex = 0;
1156
+ let match;
1157
+ while (match = regex.exec(source4)) {
1158
+ const isBindingFirst = regex === WGSL_BINDING_DECLARATION_REGEXES[0];
1159
+ const location = Number(match[isBindingFirst ? 1 : 2]);
1160
+ const group = Number(match[isBindingFirst ? 2 : 1]);
1161
+ const name = match[4];
1162
+ validateApplicationWGSLBinding(group, location, name);
1163
+ registerUsedBindingLocation(
1164
+ usedBindingsByGroup,
1165
+ group,
1166
+ location,
1167
+ `application binding "${name}"`
1168
+ );
1169
+ }
1170
+ }
1171
+ return usedBindingsByGroup;
1172
+ }
1173
+ function relocateWGSLModuleBindings(moduleSource, module, context) {
1174
+ const bindingAssignments = [];
1175
+ const relocationState = {
1176
+ sawSupportedBindingDeclaration: false,
1177
+ nextHintedBindingLocation: typeof module.firstBindingSlot === "number" ? module.firstBindingSlot : null
1178
+ };
1179
+ let relocatedSource = relocateWGSLModuleBindingsWithRegex(
1180
+ moduleSource,
1181
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES[0],
1182
+ { isBindingFirst: true, module, context, bindingAssignments, relocationState }
1183
+ );
1184
+ relocatedSource = relocateWGSLModuleBindingsWithRegex(
1185
+ relocatedSource,
1186
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES[1],
1187
+ { isBindingFirst: false, module, context, bindingAssignments, relocationState }
1188
+ );
1189
+ if (moduleSource.includes("@binding(auto)") && !relocationState.sawSupportedBindingDeclaration) {
1190
+ throw new Error(
1191
+ `Unsupported @binding(auto) declaration form in module "${module.name}". Use "@group(N) @binding(auto) var ..." or "@binding(auto) @group(N) var ..." on a single line.`
1192
+ );
1193
+ }
1194
+ return { source: relocatedSource, bindingAssignments };
1195
+ }
1196
+ function relocateWGSLModuleBindingsWithRegex(source4, regex, params) {
1197
+ return source4.replace(
1198
+ regex,
1199
+ (...replaceArguments) => relocateWGSLModuleBindingMatch(replaceArguments, params)
1200
+ );
1201
+ }
1202
+ function relocateWGSLModuleBindingMatch(replaceArguments, params) {
1203
+ const { isBindingFirst, module, context, bindingAssignments, relocationState } = params;
1204
+ relocationState.sawSupportedBindingDeclaration = true;
1205
+ const match = replaceArguments[0];
1206
+ const bindingToken = replaceArguments[isBindingFirst ? 1 : 2];
1207
+ const groupToken = replaceArguments[isBindingFirst ? 2 : 1];
1208
+ const name = replaceArguments[4];
1209
+ const group = Number(groupToken);
1210
+ if (bindingToken === "auto") {
1211
+ const registryKey = getBindingRegistryKey(group, module.name, name);
1212
+ const registryLocation = context.bindingRegistry?.get(registryKey);
1213
+ const location2 = registryLocation !== void 0 ? registryLocation : relocationState.nextHintedBindingLocation === null ? allocateAutoBindingLocation(group, context.usedBindingsByGroup) : allocateAutoBindingLocation(
1214
+ group,
1215
+ context.usedBindingsByGroup,
1216
+ relocationState.nextHintedBindingLocation
1217
+ );
1218
+ validateModuleWGSLBinding(module.name, group, location2, name);
1219
+ if (registryLocation !== void 0 && claimReservedBindingLocation(context.reservedBindingKeysByGroup, group, location2, registryKey)) {
1220
+ bindingAssignments.push({ moduleName: module.name, name, group, location: location2 });
1221
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location2})`);
1222
+ }
1223
+ registerUsedBindingLocation(
1224
+ context.usedBindingsByGroup,
1225
+ group,
1226
+ location2,
1227
+ `module "${module.name}" binding "${name}"`
1228
+ );
1229
+ context.bindingRegistry?.set(registryKey, location2);
1230
+ bindingAssignments.push({ moduleName: module.name, name, group, location: location2 });
1231
+ if (relocationState.nextHintedBindingLocation !== null && registryLocation === void 0) {
1232
+ relocationState.nextHintedBindingLocation = location2 + 1;
1233
+ }
1234
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location2})`);
1235
+ }
1236
+ const location = Number(bindingToken);
1237
+ validateModuleWGSLBinding(module.name, group, location, name);
1238
+ registerUsedBindingLocation(
1239
+ context.usedBindingsByGroup,
1240
+ group,
1241
+ location,
1242
+ `module "${module.name}" binding "${name}"`
1243
+ );
1244
+ bindingAssignments.push({ moduleName: module.name, name, group, location });
1245
+ return match;
1246
+ }
1247
+ function reserveRegisteredModuleBindings(modules, bindingRegistry, usedBindingsByGroup) {
1248
+ const reservedBindingKeysByGroup = /* @__PURE__ */ new Map();
1249
+ if (!bindingRegistry) {
1250
+ return reservedBindingKeysByGroup;
1251
+ }
1252
+ for (const module of modules) {
1253
+ for (const binding of getModuleWGSLBindingDeclarations(module)) {
1254
+ const registryKey = getBindingRegistryKey(binding.group, module.name, binding.name);
1255
+ const location = bindingRegistry.get(registryKey);
1256
+ if (location !== void 0) {
1257
+ const reservedBindingKeys = reservedBindingKeysByGroup.get(binding.group) || /* @__PURE__ */ new Map();
1258
+ const existingReservation = reservedBindingKeys.get(location);
1259
+ if (existingReservation && existingReservation !== registryKey) {
1260
+ throw new Error(
1261
+ `Duplicate WGSL binding reservation for modules "${existingReservation}" and "${registryKey}": group ${binding.group}, binding ${location}.`
1262
+ );
1263
+ }
1264
+ registerUsedBindingLocation(
1265
+ usedBindingsByGroup,
1266
+ binding.group,
1267
+ location,
1268
+ `registered module binding "${registryKey}"`
1269
+ );
1270
+ reservedBindingKeys.set(location, registryKey);
1271
+ reservedBindingKeysByGroup.set(binding.group, reservedBindingKeys);
1272
+ }
1273
+ }
1274
+ }
1275
+ return reservedBindingKeysByGroup;
1276
+ }
1277
+ function claimReservedBindingLocation(reservedBindingKeysByGroup, group, location, registryKey) {
1278
+ const reservedBindingKeys = reservedBindingKeysByGroup.get(group);
1279
+ if (!reservedBindingKeys) {
1280
+ return false;
1281
+ }
1282
+ const reservedKey = reservedBindingKeys.get(location);
1283
+ if (!reservedKey) {
1284
+ return false;
1285
+ }
1286
+ if (reservedKey !== registryKey) {
1287
+ throw new Error(
1288
+ `Registered module binding "${registryKey}" collided with "${reservedKey}": group ${group}, binding ${location}.`
1289
+ );
1290
+ }
1291
+ return true;
1292
+ }
1293
+ function getModuleWGSLBindingDeclarations(module) {
1294
+ const declarations = [];
1295
+ const moduleSource = module.source || "";
1296
+ for (const regex of MODULE_WGSL_BINDING_DECLARATION_REGEXES) {
1297
+ regex.lastIndex = 0;
1298
+ let match;
1299
+ while (match = regex.exec(moduleSource)) {
1300
+ const isBindingFirst = regex === MODULE_WGSL_BINDING_DECLARATION_REGEXES[0];
1301
+ declarations.push({
1302
+ name: match[4],
1303
+ group: Number(match[isBindingFirst ? 2 : 1])
1304
+ });
1305
+ }
1306
+ }
1307
+ return declarations;
1308
+ }
1309
+ function validateApplicationWGSLBinding(group, location, name) {
1310
+ if (group === 0 && location >= RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT) {
1311
+ throw new Error(
1312
+ `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}.`
1313
+ );
1314
+ }
1315
+ }
1316
+ function validateModuleWGSLBinding(moduleName, group, location, name) {
1317
+ if (group === 0 && location < RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT) {
1318
+ throw new Error(
1319
+ `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.`
1320
+ );
1321
+ }
1322
+ }
1323
+ function registerUsedBindingLocation(usedBindingsByGroup, group, location, label) {
1324
+ const usedBindings = usedBindingsByGroup.get(group) || /* @__PURE__ */ new Set();
1325
+ if (usedBindings.has(location)) {
1326
+ throw new Error(
1327
+ `Duplicate WGSL binding assignment for ${label}: group ${group}, binding ${location}.`
1328
+ );
1329
+ }
1330
+ usedBindings.add(location);
1331
+ usedBindingsByGroup.set(group, usedBindings);
1332
+ }
1333
+ function allocateAutoBindingLocation(group, usedBindingsByGroup, preferredBindingLocation) {
1334
+ const usedBindings = usedBindingsByGroup.get(group) || /* @__PURE__ */ new Set();
1335
+ let nextBinding = preferredBindingLocation ?? (group === 0 ? RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT : usedBindings.size > 0 ? Math.max(...usedBindings) + 1 : 0);
1336
+ while (usedBindings.has(nextBinding)) {
1337
+ nextBinding++;
1338
+ }
1339
+ return nextBinding;
1340
+ }
1341
+ function assertNoUnresolvedAutoBindings(source4) {
1342
+ if (/@binding\(\s*auto\s*\)/.test(source4)) {
1343
+ throw new Error("Unresolved @binding(auto) remained in assembled WGSL source.");
1344
+ }
1345
+ }
1346
+ function formatWGSLBindingAssignmentComments(bindingAssignments) {
1347
+ if (bindingAssignments.length === 0) {
1348
+ return "";
1349
+ }
1350
+ let source4 = "// ----- MODULE WGSL BINDING ASSIGNMENTS ---------------\n";
1351
+ for (const bindingAssignment of bindingAssignments) {
1352
+ source4 += `// ${bindingAssignment.moduleName}.${bindingAssignment.name} -> @group(${bindingAssignment.group}) @binding(${bindingAssignment.location})
1353
+ `;
1354
+ }
1355
+ source4 += "\n";
1356
+ return source4;
1357
+ }
1358
+ function getBindingRegistryKey(group, moduleName, bindingName) {
1359
+ return `${group}:${moduleName}:${bindingName}`;
1360
+ }
852
1361
 
853
1362
  // src/lib/preprocessor/preprocessor.ts
854
- var IFDEF_REGEXP = /^\s*\#\s*ifdef\s*([a-zA-Z_]+)\s*$/;
855
- var IFNDEF_REGEXP = /^\s*\#\s*ifndef\s*([a-zA-Z_]+)\s*(?:\/\/.*)?$/;
1363
+ var DEFINE_NAME_PATTERN = "([a-zA-Z_][a-zA-Z0-9_]*)";
1364
+ var IFDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*$`);
1365
+ var IFNDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifndef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
856
1366
  var ELSE_REGEXP = /^\s*\#\s*else\s*(?:\/\/.*)?$/;
857
1367
  var ENDIF_REGEXP = /^\s*\#\s*endif\s*$/;
858
- var IFDEF_WITH_COMMENT_REGEXP = /^\s*\#\s*ifdef\s*([a-zA-Z_]+)\s*(?:\/\/.*)?$/;
1368
+ var IFDEF_WITH_COMMENT_REGEXP = new RegExp(
1369
+ `^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`
1370
+ );
859
1371
  var ENDIF_WITH_COMMENT_REGEXP = /^\s*\#\s*endif\s*(?:\/\/.*)?$/;
860
1372
  function preprocess(source4, options) {
861
1373
  const lines = source4.split("\n");
@@ -901,6 +1413,8 @@ ${getApplicationDefines(allDefines)}
901
1413
  _hookFunctions = [];
902
1414
  /** Shader modules */
903
1415
  _defaultModules = [];
1416
+ /** Stable per-run WGSL auto-binding assignments keyed by group/module/binding. */
1417
+ _wgslBindingRegistry = /* @__PURE__ */ new Map();
904
1418
  /**
905
1419
  * A default shader assembler instance - the natural place to register default modules and hooks
906
1420
  * @returns
@@ -946,10 +1460,11 @@ ${getApplicationDefines(allDefines)}
946
1460
  assembleWGSLShader(props) {
947
1461
  const modules = this._getModuleList(props.modules);
948
1462
  const hookFunctions = this._hookFunctions;
949
- const { source: source4, getUniforms: getUniforms4 } = assembleWGSLShader({
1463
+ const { source: source4, getUniforms: getUniforms4, bindingAssignments } = assembleWGSLShader({
950
1464
  ...props,
951
1465
  // @ts-expect-error
952
1466
  source: props.source,
1467
+ _bindingRegistry: this._wgslBindingRegistry,
953
1468
  modules,
954
1469
  hookFunctions
955
1470
  });
@@ -961,7 +1476,13 @@ ${getApplicationDefines(allDefines)}
961
1476
  ...props.defines
962
1477
  };
963
1478
  const preprocessedSource = props.platformInfo.shaderLanguage === "wgsl" ? preprocess(source4, { defines }) : source4;
964
- return { source: preprocessedSource, getUniforms: getUniforms4, modules };
1479
+ return {
1480
+ source: preprocessedSource,
1481
+ getUniforms: getUniforms4,
1482
+ modules,
1483
+ bindingAssignments,
1484
+ bindingTable: getShaderBindingDebugRowsFromWGSL(preprocessedSource, bindingAssignments)
1485
+ };
965
1486
  }
966
1487
  /**
967
1488
  * Assemble a pair of shaders into a single shader program
@@ -1126,6 +1647,11 @@ void main() {
1126
1647
  case "uniforms":
1127
1648
  }
1128
1649
  for (const [uniformName, uniformFormat] of Object.entries(module.uniformTypes || {})) {
1650
+ if (typeof uniformFormat !== "string") {
1651
+ throw new Error(
1652
+ `Composite uniform types are not supported by GLSL shader generation: ${module.name}.${uniformName}`
1653
+ );
1654
+ }
1129
1655
  const glslUniformType = getGLSLUniformType(uniformFormat);
1130
1656
  switch (options.uniforms) {
1131
1657
  case "scoped-interface-blocks":
@@ -1186,6 +1712,11 @@ void main() {
1186
1712
  const wgsl = [];
1187
1713
  wgsl.push(`struct ${capitalize(module.name)} {`);
1188
1714
  for (const [uniformName, uniformFormat] of Object.entries(module?.uniformTypes || {})) {
1715
+ if (typeof uniformFormat !== "string") {
1716
+ throw new Error(
1717
+ `Composite uniform types are not supported by WGSL shader generation: ${module.name}.${uniformName}`
1718
+ );
1719
+ }
1189
1720
  const wgslUniformType = uniformFormat;
1190
1721
  wgsl.push(` ${uniformName} : ${wgslUniformType};`);
1191
1722
  }
@@ -3190,6 +3721,7 @@ float tan_fp32(float a) {
3190
3721
  `
3191
3722
  uniform fp64arithmeticUniforms {
3192
3723
  uniform float ONE;
3724
+ uniform float SPLIT;
3193
3725
  } fp64;
3194
3726
 
3195
3727
  /*
@@ -3199,6 +3731,12 @@ The purpose of this workaround is to prevent shader compilers from
3199
3731
  optimizing away necessary arithmetic operations by swapping their sequences
3200
3732
  or transform the equation to some 'equivalent' form.
3201
3733
 
3734
+ These helpers implement Dekker/Veltkamp-style error tracking. If the compiler
3735
+ folds constants or reassociates the arithmetic, the high/low split can stop
3736
+ tracking the rounding error correctly. That failure mode tends to look fine in
3737
+ simple coordinate setup, but then breaks down inside iterative arithmetic such
3738
+ as fp64 Mandelbrot loops.
3739
+
3202
3740
  The method is to multiply an artifical variable, ONE, which will be known to
3203
3741
  the compiler to be 1 only at runtime. The whole expression is then represented
3204
3742
  as a polynomial with respective to ONE. In the coefficients of all terms, only one a
@@ -3207,17 +3745,23 @@ and one b should appear
3207
3745
  err = (a + b) * ONE^6 - a * ONE^5 - (a + b) * ONE^4 + a * ONE^3 - b - (a + b) * ONE^2 + a * ONE
3208
3746
  */
3209
3747
 
3210
- // Divide float number to high and low floats to extend fraction bits
3211
- vec2 split(float a) {
3212
- const float SPLIT = 4097.0;
3213
- float t = a * SPLIT;
3748
+ float prevent_fp64_optimization(float value) {
3214
3749
  #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND)
3215
- float a_hi = t * fp64.ONE - (t - a);
3216
- float a_lo = a * fp64.ONE - a_hi;
3750
+ return value + fp64.ONE * 0.0;
3217
3751
  #else
3218
- float a_hi = t - (t - a);
3219
- float a_lo = a - a_hi;
3752
+ return value;
3220
3753
  #endif
3754
+ }
3755
+
3756
+ // Divide float number to high and low floats to extend fraction bits
3757
+ vec2 split(float a) {
3758
+ // Keep SPLIT as a runtime uniform so the compiler cannot fold the Dekker
3759
+ // split into a constant expression and reassociate the recovery steps.
3760
+ float split = prevent_fp64_optimization(fp64.SPLIT);
3761
+ float t = prevent_fp64_optimization(a * split);
3762
+ float temp = t - a;
3763
+ float a_hi = t - temp;
3764
+ float a_lo = a - a_hi;
3221
3765
  return vec2(a_hi, a_lo);
3222
3766
  }
3223
3767
 
@@ -3281,8 +3825,26 @@ vec2 twoProd(float a, float b) {
3281
3825
  float prod = a * b;
3282
3826
  vec2 a_fp64 = split(a);
3283
3827
  vec2 b_fp64 = split(b);
3284
- float err = ((a_fp64.x * b_fp64.x - prod) + a_fp64.x * b_fp64.y +
3285
- a_fp64.y * b_fp64.x) + a_fp64.y * b_fp64.y;
3828
+ // twoProd is especially sensitive because mul_fp64 and div_fp64 both depend
3829
+ // on the split terms and cross terms staying in the original evaluation
3830
+ // order. If the compiler folds or reassociates them, the low part tends to
3831
+ // collapse to zero or NaN on some drivers.
3832
+ float highProduct = prevent_fp64_optimization(a_fp64.x * b_fp64.x);
3833
+ float crossProduct1 = prevent_fp64_optimization(a_fp64.x * b_fp64.y);
3834
+ float crossProduct2 = prevent_fp64_optimization(a_fp64.y * b_fp64.x);
3835
+ float lowProduct = prevent_fp64_optimization(a_fp64.y * b_fp64.y);
3836
+ #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND)
3837
+ float err1 = (highProduct - prod) * fp64.ONE;
3838
+ float err2 = crossProduct1 * fp64.ONE * fp64.ONE;
3839
+ float err3 = crossProduct2 * fp64.ONE * fp64.ONE * fp64.ONE;
3840
+ float err4 = lowProduct * fp64.ONE * fp64.ONE * fp64.ONE * fp64.ONE;
3841
+ #else
3842
+ float err1 = highProduct - prod;
3843
+ float err2 = crossProduct1;
3844
+ float err3 = crossProduct2;
3845
+ float err4 = lowProduct;
3846
+ #endif
3847
+ float err = ((err1 + err2) + err3) + err4;
3286
3848
  return vec2(prod, err);
3287
3849
  }
3288
3850
 
@@ -3358,6 +3920,218 @@ vec2 sqrt_fp64(vec2 a) {
3358
3920
  `
3359
3921
  );
3360
3922
 
3923
+ // src/modules/math/fp64/fp64-arithmetic-wgsl.ts
3924
+ var fp64arithmeticWGSL = (
3925
+ /* wgsl */
3926
+ `struct Fp64ArithmeticUniforms {
3927
+ ONE: f32,
3928
+ SPLIT: f32,
3929
+ };
3930
+
3931
+ @group(0) @binding(auto) var<uniform> fp64arithmetic : Fp64ArithmeticUniforms;
3932
+
3933
+ fn fp64_nan(seed: f32) -> f32 {
3934
+ let nanBits = 0x7fc00000u | select(0u, 1u, seed < 0.0);
3935
+ return bitcast<f32>(nanBits);
3936
+ }
3937
+
3938
+ fn fp64_runtime_zero() -> f32 {
3939
+ return fp64arithmetic.ONE * 0.0;
3940
+ }
3941
+
3942
+ fn prevent_fp64_optimization(value: f32) -> f32 {
3943
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
3944
+ return value + fp64_runtime_zero();
3945
+ #else
3946
+ return value;
3947
+ #endif
3948
+ }
3949
+
3950
+ fn split(a: f32) -> vec2f {
3951
+ let splitValue = prevent_fp64_optimization(fp64arithmetic.SPLIT + fp64_runtime_zero());
3952
+ let t = prevent_fp64_optimization(a * splitValue);
3953
+ let temp = prevent_fp64_optimization(t - a);
3954
+ let aHi = prevent_fp64_optimization(t - temp);
3955
+ let aLo = prevent_fp64_optimization(a - aHi);
3956
+ return vec2f(aHi, aLo);
3957
+ }
3958
+
3959
+ fn split2(a: vec2f) -> vec2f {
3960
+ var b = split(a.x);
3961
+ b.y = b.y + a.y;
3962
+ return b;
3963
+ }
3964
+
3965
+ fn quickTwoSum(a: f32, b: f32) -> vec2f {
3966
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
3967
+ let sum = prevent_fp64_optimization((a + b) * fp64arithmetic.ONE);
3968
+ let err = prevent_fp64_optimization(b - (sum - a) * fp64arithmetic.ONE);
3969
+ #else
3970
+ let sum = prevent_fp64_optimization(a + b);
3971
+ let err = prevent_fp64_optimization(b - (sum - a));
3972
+ #endif
3973
+ return vec2f(sum, err);
3974
+ }
3975
+
3976
+ fn twoSum(a: f32, b: f32) -> vec2f {
3977
+ let s = prevent_fp64_optimization(a + b);
3978
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
3979
+ let v = prevent_fp64_optimization((s * fp64arithmetic.ONE - a) * fp64arithmetic.ONE);
3980
+ let err =
3981
+ prevent_fp64_optimization((a - (s - v) * fp64arithmetic.ONE) *
3982
+ fp64arithmetic.ONE *
3983
+ fp64arithmetic.ONE *
3984
+ fp64arithmetic.ONE) +
3985
+ prevent_fp64_optimization(b - v);
3986
+ #else
3987
+ let v = prevent_fp64_optimization(s - a);
3988
+ let err = prevent_fp64_optimization(a - (s - v)) + prevent_fp64_optimization(b - v);
3989
+ #endif
3990
+ return vec2f(s, err);
3991
+ }
3992
+
3993
+ fn twoSub(a: f32, b: f32) -> vec2f {
3994
+ let s = prevent_fp64_optimization(a - b);
3995
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
3996
+ let v = prevent_fp64_optimization((s * fp64arithmetic.ONE - a) * fp64arithmetic.ONE);
3997
+ let err =
3998
+ prevent_fp64_optimization((a - (s - v) * fp64arithmetic.ONE) *
3999
+ fp64arithmetic.ONE *
4000
+ fp64arithmetic.ONE *
4001
+ fp64arithmetic.ONE) -
4002
+ prevent_fp64_optimization(b + v);
4003
+ #else
4004
+ let v = prevent_fp64_optimization(s - a);
4005
+ let err = prevent_fp64_optimization(a - (s - v)) - prevent_fp64_optimization(b + v);
4006
+ #endif
4007
+ return vec2f(s, err);
4008
+ }
4009
+
4010
+ fn twoSqr(a: f32) -> vec2f {
4011
+ let prod = prevent_fp64_optimization(a * a);
4012
+ let aFp64 = split(a);
4013
+ let highProduct = prevent_fp64_optimization(aFp64.x * aFp64.x);
4014
+ let crossProduct = prevent_fp64_optimization(2.0 * aFp64.x * aFp64.y);
4015
+ let lowProduct = prevent_fp64_optimization(aFp64.y * aFp64.y);
4016
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
4017
+ let err =
4018
+ (prevent_fp64_optimization(highProduct - prod) * fp64arithmetic.ONE +
4019
+ crossProduct * fp64arithmetic.ONE * fp64arithmetic.ONE) +
4020
+ lowProduct * fp64arithmetic.ONE * fp64arithmetic.ONE * fp64arithmetic.ONE;
4021
+ #else
4022
+ let err = ((prevent_fp64_optimization(highProduct - prod) + crossProduct) + lowProduct);
4023
+ #endif
4024
+ return vec2f(prod, err);
4025
+ }
4026
+
4027
+ fn twoProd(a: f32, b: f32) -> vec2f {
4028
+ let prod = prevent_fp64_optimization(a * b);
4029
+ let aFp64 = split(a);
4030
+ let bFp64 = split(b);
4031
+ let highProduct = prevent_fp64_optimization(aFp64.x * bFp64.x);
4032
+ let crossProduct1 = prevent_fp64_optimization(aFp64.x * bFp64.y);
4033
+ let crossProduct2 = prevent_fp64_optimization(aFp64.y * bFp64.x);
4034
+ let lowProduct = prevent_fp64_optimization(aFp64.y * bFp64.y);
4035
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
4036
+ let err1 = (highProduct - prod) * fp64arithmetic.ONE;
4037
+ let err2 = crossProduct1 * fp64arithmetic.ONE * fp64arithmetic.ONE;
4038
+ let err3 = crossProduct2 * fp64arithmetic.ONE * fp64arithmetic.ONE * fp64arithmetic.ONE;
4039
+ let err4 =
4040
+ lowProduct *
4041
+ fp64arithmetic.ONE *
4042
+ fp64arithmetic.ONE *
4043
+ fp64arithmetic.ONE *
4044
+ fp64arithmetic.ONE;
4045
+ #else
4046
+ let err1 = highProduct - prod;
4047
+ let err2 = crossProduct1;
4048
+ let err3 = crossProduct2;
4049
+ let err4 = lowProduct;
4050
+ #endif
4051
+ let err12InputA = prevent_fp64_optimization(err1);
4052
+ let err12InputB = prevent_fp64_optimization(err2);
4053
+ let err12 = prevent_fp64_optimization(err12InputA + err12InputB);
4054
+ let err123InputA = prevent_fp64_optimization(err12);
4055
+ let err123InputB = prevent_fp64_optimization(err3);
4056
+ let err123 = prevent_fp64_optimization(err123InputA + err123InputB);
4057
+ let err1234InputA = prevent_fp64_optimization(err123);
4058
+ let err1234InputB = prevent_fp64_optimization(err4);
4059
+ let err = prevent_fp64_optimization(err1234InputA + err1234InputB);
4060
+ return vec2f(prod, err);
4061
+ }
4062
+
4063
+ fn sum_fp64(a: vec2f, b: vec2f) -> vec2f {
4064
+ var s = twoSum(a.x, b.x);
4065
+ let t = twoSum(a.y, b.y);
4066
+ s.y = prevent_fp64_optimization(s.y + t.x);
4067
+ s = quickTwoSum(s.x, s.y);
4068
+ s.y = prevent_fp64_optimization(s.y + t.y);
4069
+ s = quickTwoSum(s.x, s.y);
4070
+ return s;
4071
+ }
4072
+
4073
+ fn sub_fp64(a: vec2f, b: vec2f) -> vec2f {
4074
+ var s = twoSub(a.x, b.x);
4075
+ let t = twoSub(a.y, b.y);
4076
+ s.y = prevent_fp64_optimization(s.y + t.x);
4077
+ s = quickTwoSum(s.x, s.y);
4078
+ s.y = prevent_fp64_optimization(s.y + t.y);
4079
+ s = quickTwoSum(s.x, s.y);
4080
+ return s;
4081
+ }
4082
+
4083
+ fn mul_fp64(a: vec2f, b: vec2f) -> vec2f {
4084
+ var prod = twoProd(a.x, b.x);
4085
+ let crossProduct1 = prevent_fp64_optimization(a.x * b.y);
4086
+ prod.y = prevent_fp64_optimization(prod.y + crossProduct1);
4087
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
4088
+ prod = split2(prod);
4089
+ #endif
4090
+ prod = quickTwoSum(prod.x, prod.y);
4091
+ let crossProduct2 = prevent_fp64_optimization(a.y * b.x);
4092
+ prod.y = prevent_fp64_optimization(prod.y + crossProduct2);
4093
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
4094
+ prod = split2(prod);
4095
+ #endif
4096
+ prod = quickTwoSum(prod.x, prod.y);
4097
+ return prod;
4098
+ }
4099
+
4100
+ fn div_fp64(a: vec2f, b: vec2f) -> vec2f {
4101
+ let xn = prevent_fp64_optimization(1.0 / b.x);
4102
+ let yn = mul_fp64(a, vec2f(xn, fp64_runtime_zero()));
4103
+ let diff = prevent_fp64_optimization(sub_fp64(a, mul_fp64(b, yn)).x);
4104
+ let prod = twoProd(xn, diff);
4105
+ return sum_fp64(yn, prod);
4106
+ }
4107
+
4108
+ fn sqrt_fp64(a: vec2f) -> vec2f {
4109
+ if (a.x == 0.0 && a.y == 0.0) {
4110
+ return vec2f(0.0, 0.0);
4111
+ }
4112
+ if (a.x < 0.0) {
4113
+ let nanValue = fp64_nan(a.x);
4114
+ return vec2f(nanValue, nanValue);
4115
+ }
4116
+
4117
+ let x = prevent_fp64_optimization(1.0 / sqrt(a.x));
4118
+ let yn = prevent_fp64_optimization(a.x * x);
4119
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
4120
+ let ynSqr = twoSqr(yn) * fp64arithmetic.ONE;
4121
+ #else
4122
+ let ynSqr = twoSqr(yn);
4123
+ #endif
4124
+ let diff = prevent_fp64_optimization(sub_fp64(a, ynSqr).x);
4125
+ let prod = twoProd(prevent_fp64_optimization(x * 0.5), diff);
4126
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
4127
+ return sum_fp64(split(yn), prod);
4128
+ #else
4129
+ return sum_fp64(vec2f(yn, 0.0), prod);
4130
+ #endif
4131
+ }
4132
+ `
4133
+ );
4134
+
3361
4135
  // src/modules/math/fp64/fp64-functions-glsl.ts
3362
4136
  var fp64functionShader = (
3363
4137
  /* glsl */
@@ -4036,13 +4810,18 @@ void mat4_vec4_mul_fp64(vec2 b[16], vec2 a[4], out vec2 out_val[4]) {
4036
4810
  // src/modules/math/fp64/fp64.ts
4037
4811
  var defaultUniforms = {
4038
4812
  // Used in LUMA_FP64_CODE_ELIMINATION_WORKAROUND
4039
- ONE: 1
4813
+ ONE: 1,
4814
+ // Runtime split factor for Dekker splitting. Keeping this as a uniform helps
4815
+ // prevent aggressive constant folding in shader compilers.
4816
+ SPLIT: 4097
4040
4817
  };
4041
4818
  var fp64arithmetic = {
4042
4819
  name: "fp64arithmetic",
4820
+ source: fp64arithmeticWGSL,
4821
+ fs: fp64arithmeticShader,
4043
4822
  vs: fp64arithmeticShader,
4044
4823
  defaultUniforms,
4045
- uniformTypes: { ONE: "f32" },
4824
+ uniformTypes: { ONE: "f32", SPLIT: "f32" },
4046
4825
  // Additional Functions
4047
4826
  fp64ify,
4048
4827
  fp64LowPart,
@@ -4255,7 +5034,7 @@ struct skinUniforms {
4255
5034
  jointMatrix: array<mat4x4<f32>, ${SKIN_MAX_JOINTS}>,
4256
5035
  };
4257
5036
 
4258
- @binding(19) @group(0) var<uniform> skin: skinUniforms;
5037
+ @group(0) @binding(auto) var<uniform> skin: skinUniforms;
4259
5038
 
4260
5039
  fn getSkinMatrix(weights: vec4f, joints: vec4u) -> mat4x4<f32> {
4261
5040
  return (weights.x * skin.jointMatrix[joints.x])
@@ -4289,6 +5068,7 @@ mat4 getSkinMatrix(vec4 weights, uvec4 joints) {
4289
5068
  props: {},
4290
5069
  uniforms: {},
4291
5070
  name: "skin",
5071
+ bindingLayout: [{ name: "skin", group: 0 }],
4292
5072
  dependencies: [],
4293
5073
  source: source2,
4294
5074
  vs: vs2,
@@ -4331,10 +5111,7 @@ mat4 getSkinMatrix(vec4 weights, uvec4 joints) {
4331
5111
  };
4332
5112
  },
4333
5113
  uniformTypes: {
4334
- jointMatrix: "mat4x4<f32>"
4335
- },
4336
- uniformSizes: {
4337
- jointMatrix: SKIN_MAX_JOINTS
5114
+ jointMatrix: ["mat4x4<f32>", SKIN_MAX_JOINTS]
4338
5115
  }
4339
5116
  };
4340
5117
 
@@ -4357,77 +5134,51 @@ struct PointLight {
4357
5134
  vec3 attenuation; // 2nd order x:Constant-y:Linear-z:Exponential
4358
5135
  };
4359
5136
 
5137
+ struct SpotLight {
5138
+ vec3 color;
5139
+ vec3 position;
5140
+ vec3 direction;
5141
+ vec3 attenuation;
5142
+ vec2 coneCos;
5143
+ };
5144
+
4360
5145
  struct DirectionalLight {
4361
5146
  vec3 color;
4362
5147
  vec3 direction;
4363
5148
  };
4364
5149
 
5150
+ struct UniformLight {
5151
+ vec3 color;
5152
+ vec3 position;
5153
+ vec3 direction;
5154
+ vec3 attenuation;
5155
+ vec2 coneCos;
5156
+ };
5157
+
4365
5158
  uniform lightingUniforms {
4366
5159
  int enabled;
4367
- int lightType;
4368
-
4369
5160
  int directionalLightCount;
4370
5161
  int pointLightCount;
4371
-
5162
+ int spotLightCount;
4372
5163
  vec3 ambientColor;
4373
-
4374
- vec3 lightColor0;
4375
- vec3 lightPosition0;
4376
- vec3 lightDirection0;
4377
- vec3 lightAttenuation0;
4378
-
4379
- vec3 lightColor1;
4380
- vec3 lightPosition1;
4381
- vec3 lightDirection1;
4382
- vec3 lightAttenuation1;
4383
-
4384
- vec3 lightColor2;
4385
- vec3 lightPosition2;
4386
- vec3 lightDirection2;
4387
- vec3 lightAttenuation2;
4388
-
4389
- vec3 lightColor3;
4390
- vec3 lightPosition3;
4391
- vec3 lightDirection3;
4392
- vec3 lightAttenuation3;
4393
-
4394
- vec3 lightColor4;
4395
- vec3 lightPosition4;
4396
- vec3 lightDirection4;
4397
- vec3 lightAttenuation4;
5164
+ UniformLight lights[5];
4398
5165
  } lighting;
4399
5166
 
4400
5167
  PointLight lighting_getPointLight(int index) {
4401
- switch (index) {
4402
- case 0:
4403
- return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0);
4404
- case 1:
4405
- return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1);
4406
- case 2:
4407
- return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2);
4408
- case 3:
4409
- return PointLight(lighting.lightColor3, lighting.lightPosition3, lighting.lightAttenuation3);
4410
- case 4:
4411
- default:
4412
- return PointLight(lighting.lightColor4, lighting.lightPosition4, lighting.lightAttenuation4);
4413
- }
5168
+ UniformLight light = lighting.lights[index];
5169
+ return PointLight(light.color, light.position, light.attenuation);
5170
+ }
5171
+
5172
+ SpotLight lighting_getSpotLight(int index) {
5173
+ UniformLight light = lighting.lights[lighting.pointLightCount + index];
5174
+ return SpotLight(light.color, light.position, light.direction, light.attenuation, light.coneCos);
4414
5175
  }
4415
5176
 
4416
5177
  DirectionalLight lighting_getDirectionalLight(int index) {
4417
- switch (index) {
4418
- case 0:
4419
- return DirectionalLight(lighting.lightColor0, lighting.lightDirection0);
4420
- case 1:
4421
- return DirectionalLight(lighting.lightColor1, lighting.lightDirection1);
4422
- case 2:
4423
- return DirectionalLight(lighting.lightColor2, lighting.lightDirection2);
4424
- case 3:
4425
- return DirectionalLight(lighting.lightColor3, lighting.lightDirection3);
4426
- case 4:
4427
- default:
4428
- return DirectionalLight(lighting.lightColor4, lighting.lightDirection4);
4429
- }
4430
- }
5178
+ UniformLight light =
5179
+ lighting.lights[lighting.pointLightCount + lighting.spotLightCount + index];
5180
+ return DirectionalLight(light.color, light.direction);
5181
+ }
4431
5182
 
4432
5183
  float getPointLightAttenuation(PointLight pointLight, float distance) {
4433
5184
  return pointLight.attenuation.x
@@ -4435,6 +5186,20 @@ float getPointLightAttenuation(PointLight pointLight, float distance) {
4435
5186
  + pointLight.attenuation.z * distance * distance;
4436
5187
  }
4437
5188
 
5189
+ float getSpotLightAttenuation(SpotLight spotLight, vec3 positionWorldspace) {
5190
+ vec3 light_direction = normalize(positionWorldspace - spotLight.position);
5191
+ float coneFactor = smoothstep(
5192
+ spotLight.coneCos.y,
5193
+ spotLight.coneCos.x,
5194
+ dot(normalize(spotLight.direction), light_direction)
5195
+ );
5196
+ float distanceAttenuation = getPointLightAttenuation(
5197
+ PointLight(spotLight.color, spotLight.position, spotLight.attenuation),
5198
+ distance(spotLight.position, positionWorldspace)
5199
+ );
5200
+ return distanceAttenuation / max(coneFactor, 0.0001);
5201
+ }
5202
+
4438
5203
  // #endif
4439
5204
  `
4440
5205
  );
@@ -4455,100 +5220,85 @@ struct PointLight {
4455
5220
  attenuation: vec3<f32>, // 2nd order x:Constant-y:Linear-z:Exponential
4456
5221
  };
4457
5222
 
5223
+ struct SpotLight {
5224
+ color: vec3<f32>,
5225
+ position: vec3<f32>,
5226
+ direction: vec3<f32>,
5227
+ attenuation: vec3<f32>,
5228
+ coneCos: vec2<f32>,
5229
+ };
5230
+
4458
5231
  struct DirectionalLight {
4459
5232
  color: vec3<f32>,
4460
5233
  direction: vec3<f32>,
4461
5234
  };
4462
5235
 
5236
+ struct UniformLight {
5237
+ color: vec3<f32>,
5238
+ position: vec3<f32>,
5239
+ direction: vec3<f32>,
5240
+ attenuation: vec3<f32>,
5241
+ coneCos: vec2<f32>,
5242
+ };
5243
+
4463
5244
  struct lightingUniforms {
4464
5245
  enabled: i32,
4465
- lightType: i32,
4466
-
4467
5246
  directionalLightCount: i32,
4468
5247
  pointLightCount: i32,
4469
-
5248
+ spotLightCount: i32,
4470
5249
  ambientColor: vec3<f32>,
4471
-
4472
- lightColor0: vec3<f32>,
4473
- lightPosition0: vec3<f32>,
4474
- lightDirection0: vec3<f32>,
4475
- lightAttenuation0: vec3<f32>,
4476
-
4477
- lightColor1: vec3<f32>,
4478
- lightPosition1: vec3<f32>,
4479
- lightDirection1: vec3<f32>,
4480
- lightAttenuation1: vec3<f32>,
4481
-
4482
- lightColor2: vec3<f32>,
4483
- lightPosition2: vec3<f32>,
4484
- lightDirection2: vec3<f32>,
4485
- lightAttenuation2: vec3<f32>,
4486
-
4487
- lightColor3: vec3<f32>,
4488
- lightPosition3: vec3<f32>,
4489
- lightDirection3: vec3<f32>,
4490
- lightAttenuation3: vec3<f32>,
4491
-
4492
- lightColor4: vec3<f32>,
4493
- lightPosition4: vec3<f32>,
4494
- lightDirection4: vec3<f32>,
4495
- lightAttenuation4: vec3<f32>,
5250
+ lights: array<UniformLight, 5>,
4496
5251
  };
4497
5252
 
4498
- // Binding 0:1 is reserved for lighting (Note: could go into separate bind group as it is stable across draw calls)
4499
- @binding(1) @group(0) var<uniform> lighting : lightingUniforms;
5253
+ @group(2) @binding(auto) var<uniform> lighting : lightingUniforms;
4500
5254
 
4501
5255
  fn lighting_getPointLight(index: i32) -> PointLight {
4502
- switch (index) {
4503
- case 0: {
4504
- return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0);
4505
- }
4506
- case 1: {
4507
- return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1);
4508
- }
4509
- case 2: {
4510
- return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2);
4511
- }
4512
- case 3: {
4513
- return PointLight(lighting.lightColor3, lighting.lightPosition3, lighting.lightAttenuation3);
4514
- }
4515
- case 4, default: {
4516
- return PointLight(lighting.lightColor4, lighting.lightPosition4, lighting.lightAttenuation4);
4517
- }
4518
- }
5256
+ let light = lighting.lights[index];
5257
+ return PointLight(light.color, light.position, light.attenuation);
5258
+ }
5259
+
5260
+ fn lighting_getSpotLight(index: i32) -> SpotLight {
5261
+ let light = lighting.lights[lighting.pointLightCount + index];
5262
+ return SpotLight(light.color, light.position, light.direction, light.attenuation, light.coneCos);
4519
5263
  }
4520
5264
 
4521
5265
  fn lighting_getDirectionalLight(index: i32) -> DirectionalLight {
4522
- switch (index) {
4523
- case 0: {
4524
- return DirectionalLight(lighting.lightColor0, lighting.lightDirection0);
4525
- }
4526
- case 1: {
4527
- return DirectionalLight(lighting.lightColor1, lighting.lightDirection1);
4528
- }
4529
- case 2: {
4530
- return DirectionalLight(lighting.lightColor2, lighting.lightDirection2);
4531
- }
4532
- case 3: {
4533
- return DirectionalLight(lighting.lightColor3, lighting.lightDirection3);
4534
- }
4535
- case 4, default: {
4536
- return DirectionalLight(lighting.lightColor4, lighting.lightDirection4);
4537
- }
4538
- }
4539
- }
5266
+ let light = lighting.lights[lighting.pointLightCount + lighting.spotLightCount + index];
5267
+ return DirectionalLight(light.color, light.direction);
5268
+ }
4540
5269
 
4541
5270
  fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
4542
5271
  return pointLight.attenuation.x
4543
5272
  + pointLight.attenuation.y * distance
4544
5273
  + pointLight.attenuation.z * distance * distance;
4545
5274
  }
5275
+
5276
+ fn getSpotLightAttenuation(spotLight: SpotLight, positionWorldspace: vec3<f32>) -> f32 {
5277
+ let lightDirection = normalize(positionWorldspace - spotLight.position);
5278
+ let coneFactor = smoothstep(
5279
+ spotLight.coneCos.y,
5280
+ spotLight.coneCos.x,
5281
+ dot(normalize(spotLight.direction), lightDirection)
5282
+ );
5283
+ let distanceAttenuation = getPointLightAttenuation(
5284
+ PointLight(spotLight.color, spotLight.position, spotLight.attenuation),
5285
+ distance(spotLight.position, positionWorldspace)
5286
+ );
5287
+ return distanceAttenuation / max(coneFactor, 0.0001);
5288
+ }
4546
5289
  `
4547
5290
  );
4548
5291
 
4549
5292
  // src/modules/lighting/lights/lighting.ts
4550
5293
  var MAX_LIGHTS = 5;
4551
5294
  var COLOR_FACTOR = 255;
5295
+ var LIGHT_UNIFORM_TYPE = {
5296
+ color: "vec3<f32>",
5297
+ position: "vec3<f32>",
5298
+ direction: "vec3<f32>",
5299
+ attenuation: "vec3<f32>",
5300
+ coneCos: "vec2<f32>"
5301
+ };
4552
5302
  var lighting = {
4553
5303
  props: {},
4554
5304
  uniforms: {},
@@ -4558,83 +5308,39 @@ fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
4558
5308
  },
4559
5309
  uniformTypes: {
4560
5310
  enabled: "i32",
4561
- lightType: "i32",
4562
5311
  directionalLightCount: "i32",
4563
5312
  pointLightCount: "i32",
5313
+ spotLightCount: "i32",
4564
5314
  ambientColor: "vec3<f32>",
4565
- // TODO define as arrays once we have appropriate uniformTypes
4566
- lightColor0: "vec3<f32>",
4567
- lightPosition0: "vec3<f32>",
4568
- // TODO - could combine direction and attenuation
4569
- lightDirection0: "vec3<f32>",
4570
- lightAttenuation0: "vec3<f32>",
4571
- lightColor1: "vec3<f32>",
4572
- lightPosition1: "vec3<f32>",
4573
- lightDirection1: "vec3<f32>",
4574
- lightAttenuation1: "vec3<f32>",
4575
- lightColor2: "vec3<f32>",
4576
- lightPosition2: "vec3<f32>",
4577
- lightDirection2: "vec3<f32>",
4578
- lightAttenuation2: "vec3<f32>",
4579
- lightColor3: "vec3<f32>",
4580
- lightPosition3: "vec3<f32>",
4581
- lightDirection3: "vec3<f32>",
4582
- lightAttenuation3: "vec3<f32>",
4583
- lightColor4: "vec3<f32>",
4584
- lightPosition4: "vec3<f32>",
4585
- lightDirection4: "vec3<f32>",
4586
- lightAttenuation4: "vec3<f32>"
4587
- },
4588
- defaultUniforms: {
4589
- enabled: 1,
4590
- lightType: 0 /* POINT */,
4591
- directionalLightCount: 0,
4592
- pointLightCount: 0,
4593
- ambientColor: [0.1, 0.1, 0.1],
4594
- lightColor0: [1, 1, 1],
4595
- lightPosition0: [1, 1, 2],
4596
- // TODO - could combine direction and attenuation
4597
- lightDirection0: [1, 1, 1],
4598
- lightAttenuation0: [1, 0, 0],
4599
- lightColor1: [1, 1, 1],
4600
- lightPosition1: [1, 1, 2],
4601
- lightDirection1: [1, 1, 1],
4602
- lightAttenuation1: [1, 0, 0],
4603
- lightColor2: [1, 1, 1],
4604
- lightPosition2: [1, 1, 2],
4605
- lightDirection2: [1, 1, 1],
4606
- lightAttenuation2: [1, 0, 0],
4607
- lightColor3: [1, 1, 1],
4608
- lightPosition3: [1, 1, 2],
4609
- lightDirection3: [1, 1, 1],
4610
- lightAttenuation3: [1, 0, 0],
4611
- lightColor4: [1, 1, 1],
4612
- lightPosition4: [1, 1, 2],
4613
- lightDirection4: [1, 1, 1],
4614
- lightAttenuation4: [1, 0, 0]
5315
+ lights: [LIGHT_UNIFORM_TYPE, MAX_LIGHTS]
4615
5316
  },
5317
+ defaultUniforms: createDefaultLightingUniforms(),
5318
+ bindingLayout: [{ name: "lighting", group: 2 }],
5319
+ firstBindingSlot: 0,
4616
5320
  source: lightingUniformsWGSL,
4617
5321
  vs: lightingUniformsGLSL,
4618
5322
  fs: lightingUniformsGLSL,
4619
5323
  getUniforms: getUniforms2
4620
5324
  };
4621
- function getUniforms2(props, prevUniforms = {}) {
5325
+ function getUniforms2(props, _prevUniforms = {}) {
4622
5326
  props = props ? { ...props } : props;
4623
5327
  if (!props) {
4624
- return { ...lighting.defaultUniforms };
5328
+ return createDefaultLightingUniforms();
4625
5329
  }
4626
5330
  if (props.lights) {
4627
5331
  props = { ...props, ...extractLightTypes(props.lights), lights: void 0 };
4628
5332
  }
4629
- const { ambientLight, pointLights, directionalLights } = props || {};
4630
- const hasLights = ambientLight || pointLights && pointLights.length > 0 || directionalLights && directionalLights.length > 0;
5333
+ const { ambientLight, pointLights, spotLights, directionalLights } = props || {};
5334
+ const hasLights = ambientLight || pointLights && pointLights.length > 0 || spotLights && spotLights.length > 0 || directionalLights && directionalLights.length > 0;
4631
5335
  if (!hasLights) {
4632
- return { ...lighting.defaultUniforms, enabled: 0 };
5336
+ return {
5337
+ ...createDefaultLightingUniforms(),
5338
+ enabled: 0
5339
+ };
4633
5340
  }
4634
5341
  const uniforms = {
4635
- ...lighting.defaultUniforms,
4636
- ...prevUniforms,
4637
- ...getLightSourceUniforms({ ambientLight, pointLights, directionalLights })
5342
+ ...createDefaultLightingUniforms(),
5343
+ ...getLightSourceUniforms({ ambientLight, pointLights, spotLights, directionalLights })
4638
5344
  };
4639
5345
  if (props.enabled !== void 0) {
4640
5346
  uniforms.enabled = props.enabled ? 1 : 0;
@@ -4644,45 +5350,67 @@ fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
4644
5350
  function getLightSourceUniforms({
4645
5351
  ambientLight,
4646
5352
  pointLights = [],
5353
+ spotLights = [],
4647
5354
  directionalLights = []
4648
5355
  }) {
4649
- const lightSourceUniforms = {};
4650
- lightSourceUniforms.ambientColor = convertColor(ambientLight);
5356
+ const lights = createDefaultLightUniforms();
4651
5357
  let currentLight = 0;
4652
5358
  let pointLightCount = 0;
5359
+ let spotLightCount = 0;
4653
5360
  let directionalLightCount = 0;
4654
5361
  for (const pointLight of pointLights) {
4655
5362
  if (currentLight >= MAX_LIGHTS) {
4656
5363
  break;
4657
5364
  }
4658
- lightSourceUniforms.lightType = 0 /* POINT */;
4659
- const i = currentLight;
4660
- lightSourceUniforms[`lightColor${i}`] = convertColor(pointLight);
4661
- lightSourceUniforms[`lightPosition${i}`] = pointLight.position;
4662
- lightSourceUniforms[`lightAttenuation${i}`] = pointLight.attenuation || [1, 0, 0];
5365
+ lights[currentLight] = {
5366
+ ...lights[currentLight],
5367
+ color: convertColor(pointLight),
5368
+ position: pointLight.position,
5369
+ attenuation: pointLight.attenuation || [1, 0, 0]
5370
+ };
4663
5371
  currentLight++;
4664
5372
  pointLightCount++;
4665
5373
  }
5374
+ for (const spotLight of spotLights) {
5375
+ if (currentLight >= MAX_LIGHTS) {
5376
+ break;
5377
+ }
5378
+ lights[currentLight] = {
5379
+ ...lights[currentLight],
5380
+ color: convertColor(spotLight),
5381
+ position: spotLight.position,
5382
+ direction: spotLight.direction,
5383
+ attenuation: spotLight.attenuation || [1, 0, 0],
5384
+ coneCos: getSpotConeCos(spotLight)
5385
+ };
5386
+ currentLight++;
5387
+ spotLightCount++;
5388
+ }
4666
5389
  for (const directionalLight of directionalLights) {
4667
5390
  if (currentLight >= MAX_LIGHTS) {
4668
5391
  break;
4669
5392
  }
4670
- lightSourceUniforms.lightType = 1 /* DIRECTIONAL */;
4671
- const i = currentLight;
4672
- lightSourceUniforms[`lightColor${i}`] = convertColor(directionalLight);
4673
- lightSourceUniforms[`lightDirection${i}`] = directionalLight.direction;
5393
+ lights[currentLight] = {
5394
+ ...lights[currentLight],
5395
+ color: convertColor(directionalLight),
5396
+ direction: directionalLight.direction
5397
+ };
4674
5398
  currentLight++;
4675
5399
  directionalLightCount++;
4676
5400
  }
4677
- if (pointLights.length + directionalLights.length > MAX_LIGHTS) {
5401
+ if (pointLights.length + spotLights.length + directionalLights.length > MAX_LIGHTS) {
4678
5402
  import_core3.log.warn(`MAX_LIGHTS exceeded, truncating to ${MAX_LIGHTS}`)();
4679
5403
  }
4680
- lightSourceUniforms.directionalLightCount = directionalLightCount;
4681
- lightSourceUniforms.pointLightCount = pointLightCount;
4682
- return lightSourceUniforms;
5404
+ return {
5405
+ ambientColor: convertColor(ambientLight),
5406
+ directionalLightCount,
5407
+ pointLightCount,
5408
+ spotLightCount,
5409
+ lights
5410
+ };
4683
5411
  }
4684
5412
  function extractLightTypes(lights) {
4685
- const lightSources = { pointLights: [], directionalLights: [] };
5413
+ const lightSources = { pointLights: [], spotLights: [], directionalLights: [] };
4686
5414
  for (const light of lights || []) {
4687
5415
  switch (light.type) {
4688
5416
  case "ambient":
@@ -4694,6 +5422,9 @@ fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
4694
5422
  case "point":
4695
5423
  lightSources.pointLights?.push(light);
4696
5424
  break;
5425
+ case "spot":
5426
+ lightSources.spotLights?.push(light);
5427
+ break;
4697
5428
  default:
4698
5429
  }
4699
5430
  }
@@ -4703,6 +5434,68 @@ fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
4703
5434
  const { color = [0, 0, 0], intensity = 1 } = colorDef;
4704
5435
  return color.map((component) => component * intensity / COLOR_FACTOR);
4705
5436
  }
5437
+ function createDefaultLightingUniforms() {
5438
+ return {
5439
+ enabled: 1,
5440
+ directionalLightCount: 0,
5441
+ pointLightCount: 0,
5442
+ spotLightCount: 0,
5443
+ ambientColor: [0.1, 0.1, 0.1],
5444
+ lights: createDefaultLightUniforms()
5445
+ };
5446
+ }
5447
+ function createDefaultLightUniforms() {
5448
+ return Array.from({ length: MAX_LIGHTS }, () => createDefaultLightUniform());
5449
+ }
5450
+ function createDefaultLightUniform() {
5451
+ return {
5452
+ color: [1, 1, 1],
5453
+ position: [1, 1, 2],
5454
+ direction: [1, 1, 1],
5455
+ attenuation: [1, 0, 0],
5456
+ coneCos: [1, 0]
5457
+ };
5458
+ }
5459
+ function getSpotConeCos(spotLight) {
5460
+ const innerConeAngle = spotLight.innerConeAngle ?? 0;
5461
+ const outerConeAngle = spotLight.outerConeAngle ?? Math.PI / 4;
5462
+ return [Math.cos(innerConeAngle), Math.cos(outerConeAngle)];
5463
+ }
5464
+
5465
+ // src/modules/lighting/ibl/ibl.ts
5466
+ var iblWGSL = (
5467
+ /* wgsl */
5468
+ `#ifdef USE_IBL
5469
+ @group(2) @binding(auto) var pbr_diffuseEnvSampler: texture_cube<f32>;
5470
+ @group(2) @binding(auto) var pbr_diffuseEnvSamplerSampler: sampler;
5471
+ @group(2) @binding(auto) var pbr_specularEnvSampler: texture_cube<f32>;
5472
+ @group(2) @binding(auto) var pbr_specularEnvSamplerSampler: sampler;
5473
+ @group(2) @binding(auto) var pbr_brdfLUT: texture_2d<f32>;
5474
+ @group(2) @binding(auto) var pbr_brdfLUTSampler: sampler;
5475
+ #endif
5476
+ `
5477
+ );
5478
+ var iblGLSL = (
5479
+ /* glsl */
5480
+ `#ifdef USE_IBL
5481
+ uniform samplerCube pbr_diffuseEnvSampler;
5482
+ uniform samplerCube pbr_specularEnvSampler;
5483
+ uniform sampler2D pbr_brdfLUT;
5484
+ #endif
5485
+ `
5486
+ );
5487
+ var ibl = {
5488
+ name: "ibl",
5489
+ firstBindingSlot: 32,
5490
+ bindingLayout: [
5491
+ { name: "pbr_diffuseEnvSampler", group: 2 },
5492
+ { name: "pbr_specularEnvSampler", group: 2 },
5493
+ { name: "pbr_brdfLUT", group: 2 }
5494
+ ],
5495
+ source: iblWGSL,
5496
+ vs: iblGLSL,
5497
+ fs: iblGLSL
5498
+ };
4706
5499
 
4707
5500
  // src/modules/lighting/no-material/dirlight.ts
4708
5501
  var SOURCE_WGSL = (
@@ -4718,7 +5511,7 @@ struct DirlightInputs {
4718
5511
  normal: DirlightNormal,
4719
5512
  };
4720
5513
 
4721
- @binding(1) @group(0) var<uniform> dirlight : dirlightUniforms;
5514
+ @group(2) @binding(auto) var<uniform> dirlight : dirlightUniforms;
4722
5515
 
4723
5516
  // For vertex
4724
5517
  fn dirlight_setNormal(normal: vec3<f32>) -> DirlightNormal {
@@ -4763,6 +5556,8 @@ vec4 dirlight_filterColor(vec4 color) {
4763
5556
  props: {},
4764
5557
  uniforms: {},
4765
5558
  name: "dirlight",
5559
+ bindingLayout: [{ name: "dirlight", group: 2 }],
5560
+ firstBindingSlot: 16,
4766
5561
  dependencies: [],
4767
5562
  source: SOURCE_WGSL,
4768
5563
  vs: VS_GLSL,
@@ -4789,10 +5584,173 @@ vec4 dirlight_filterColor(vec4 color) {
4789
5584
  return uniforms;
4790
5585
  }
4791
5586
 
5587
+ // src/modules/lighting/lambert-material/lambert-shaders-wgsl.ts
5588
+ var LAMBERT_WGSL = (
5589
+ /* wgsl */
5590
+ `struct lambertMaterialUniforms {
5591
+ unlit: u32,
5592
+ ambient: f32,
5593
+ diffuse: f32,
5594
+ };
5595
+
5596
+ @group(3) @binding(auto) var<uniform> lambertMaterial : lambertMaterialUniforms;
5597
+
5598
+ fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, normal_worldspace: vec3<f32>, color: vec3<f32>) -> vec3<f32> {
5599
+ let lambertian: f32 = max(dot(light_direction, normal_worldspace), 0.0);
5600
+ return lambertian * lambertMaterial.diffuse * surfaceColor * color;
5601
+ }
5602
+
5603
+ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, position_worldspace: vec3<f32>, normal_worldspace: vec3<f32>) -> vec3<f32> {
5604
+ var lightColor: vec3<f32> = surfaceColor;
5605
+
5606
+ if (lambertMaterial.unlit != 0u) {
5607
+ return surfaceColor;
5608
+ }
5609
+
5610
+ if (lighting.enabled == 0) {
5611
+ return lightColor;
5612
+ }
5613
+
5614
+ lightColor = lambertMaterial.ambient * surfaceColor * lighting.ambientColor;
5615
+
5616
+ for (var i: i32 = 0; i < lighting.pointLightCount; i++) {
5617
+ let pointLight: PointLight = lighting_getPointLight(i);
5618
+ let light_position_worldspace: vec3<f32> = pointLight.position;
5619
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
5620
+ let light_attenuation = getPointLightAttenuation(
5621
+ pointLight,
5622
+ distance(light_position_worldspace, position_worldspace)
5623
+ );
5624
+ lightColor += lighting_getLightColor(
5625
+ surfaceColor,
5626
+ light_direction,
5627
+ normal_worldspace,
5628
+ pointLight.color / light_attenuation
5629
+ );
5630
+ }
5631
+
5632
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
5633
+ let spotLight: SpotLight = lighting_getSpotLight(i);
5634
+ let light_position_worldspace: vec3<f32> = spotLight.position;
5635
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
5636
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
5637
+ lightColor += lighting_getLightColor(
5638
+ surfaceColor,
5639
+ light_direction,
5640
+ normal_worldspace,
5641
+ spotLight.color / light_attenuation
5642
+ );
5643
+ }
5644
+
5645
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
5646
+ let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
5647
+ lightColor += lighting_getLightColor(
5648
+ surfaceColor,
5649
+ -directionalLight.direction,
5650
+ normal_worldspace,
5651
+ directionalLight.color
5652
+ );
5653
+ }
5654
+
5655
+ return lightColor;
5656
+ }
5657
+ `
5658
+ );
5659
+
5660
+ // src/modules/lighting/lambert-material/lambert-shaders-glsl.ts
5661
+ var LAMBERT_VS = (
5662
+ /* glsl */
5663
+ `uniform lambertMaterialUniforms {
5664
+ uniform bool unlit;
5665
+ uniform float ambient;
5666
+ uniform float diffuse;
5667
+ } material;
5668
+ `
5669
+ );
5670
+ var LAMBERT_FS = (
5671
+ /* glsl */
5672
+ `uniform lambertMaterialUniforms {
5673
+ uniform bool unlit;
5674
+ uniform float ambient;
5675
+ uniform float diffuse;
5676
+ } material;
5677
+
5678
+ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 normal_worldspace, vec3 color) {
5679
+ float lambertian = max(dot(light_direction, normal_worldspace), 0.0);
5680
+ return lambertian * material.diffuse * surfaceColor * color;
5681
+ }
5682
+
5683
+ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
5684
+ vec3 lightColor = surfaceColor;
5685
+
5686
+ if (material.unlit) {
5687
+ return surfaceColor;
5688
+ }
5689
+
5690
+ if (lighting.enabled == 0) {
5691
+ return lightColor;
5692
+ }
5693
+
5694
+ lightColor = material.ambient * surfaceColor * lighting.ambientColor;
5695
+
5696
+ for (int i = 0; i < lighting.pointLightCount; i++) {
5697
+ PointLight pointLight = lighting_getPointLight(i);
5698
+ vec3 light_position_worldspace = pointLight.position;
5699
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
5700
+ float light_attenuation = getPointLightAttenuation(pointLight, distance(light_position_worldspace, position_worldspace));
5701
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, normal_worldspace, pointLight.color / light_attenuation);
5702
+ }
5703
+
5704
+ for (int i = 0; i < lighting.spotLightCount; i++) {
5705
+ SpotLight spotLight = lighting_getSpotLight(i);
5706
+ vec3 light_position_worldspace = spotLight.position;
5707
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
5708
+ float light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
5709
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, normal_worldspace, spotLight.color / light_attenuation);
5710
+ }
5711
+
5712
+ for (int i = 0; i < lighting.directionalLightCount; i++) {
5713
+ DirectionalLight directionalLight = lighting_getDirectionalLight(i);
5714
+ lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, normal_worldspace, directionalLight.color);
5715
+ }
5716
+
5717
+ return lightColor;
5718
+ }
5719
+ `
5720
+ );
5721
+
5722
+ // src/modules/lighting/lambert-material/lambert-material.ts
5723
+ var lambertMaterial = {
5724
+ name: "lambertMaterial",
5725
+ firstBindingSlot: 0,
5726
+ bindingLayout: [{ name: "lambertMaterial", group: 3 }],
5727
+ dependencies: [lighting],
5728
+ source: LAMBERT_WGSL,
5729
+ vs: LAMBERT_VS,
5730
+ fs: LAMBERT_FS,
5731
+ defines: {
5732
+ LIGHTING_FRAGMENT: true
5733
+ },
5734
+ uniformTypes: {
5735
+ unlit: "i32",
5736
+ ambient: "f32",
5737
+ diffuse: "f32"
5738
+ },
5739
+ defaultUniforms: {
5740
+ unlit: false,
5741
+ ambient: 0.35,
5742
+ diffuse: 0.6
5743
+ },
5744
+ getUniforms(props) {
5745
+ return { ...lambertMaterial.defaultUniforms, ...props };
5746
+ }
5747
+ };
5748
+
4792
5749
  // src/modules/lighting/phong-material/phong-shaders-glsl.ts
4793
5750
  var PHONG_VS = (
4794
5751
  /* glsl */
4795
5752
  `uniform phongMaterialUniforms {
5753
+ uniform bool unlit;
4796
5754
  uniform float ambient;
4797
5755
  uniform float diffuse;
4798
5756
  uniform float shininess;
@@ -4802,9 +5760,8 @@ vec4 dirlight_filterColor(vec4 color) {
4802
5760
  );
4803
5761
  var PHONG_FS = (
4804
5762
  /* glsl */
4805
- `#define MAX_LIGHTS 3
4806
-
4807
- uniform phongMaterialUniforms {
5763
+ `uniform phongMaterialUniforms {
5764
+ uniform bool unlit;
4808
5765
  uniform float ambient;
4809
5766
  uniform float diffuse;
4810
5767
  uniform float shininess;
@@ -4826,6 +5783,10 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 view_d
4826
5783
  vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
4827
5784
  vec3 lightColor = surfaceColor;
4828
5785
 
5786
+ if (material.unlit) {
5787
+ return surfaceColor;
5788
+ }
5789
+
4829
5790
  if (lighting.enabled == 0) {
4830
5791
  return lightColor;
4831
5792
  }
@@ -4841,8 +5802,15 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positio
4841
5802
  lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color / light_attenuation);
4842
5803
  }
4843
5804
 
4844
- int totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
4845
- for (int i = lighting.pointLightCount; i < totalLights; i++) {
5805
+ for (int i = 0; i < lighting.spotLightCount; i++) {
5806
+ SpotLight spotLight = lighting_getSpotLight(i);
5807
+ vec3 light_position_worldspace = spotLight.position;
5808
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
5809
+ float light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
5810
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, spotLight.color / light_attenuation);
5811
+ }
5812
+
5813
+ for (int i = 0; i < lighting.directionalLightCount; i++) {
4846
5814
  DirectionalLight directionalLight = lighting_getDirectionalLight(i);
4847
5815
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
4848
5816
  }
@@ -4856,13 +5824,14 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positio
4856
5824
  var PHONG_WGSL = (
4857
5825
  /* wgsl */
4858
5826
  `struct phongMaterialUniforms {
5827
+ unlit: u32,
4859
5828
  ambient: f32,
4860
5829
  diffuse: f32,
4861
5830
  shininess: f32,
4862
5831
  specularColor: vec3<f32>,
4863
5832
  };
4864
5833
 
4865
- @binding(2) @group(0) var<uniform> phongMaterial : phongMaterialUniforms;
5834
+ @group(3) @binding(auto) var<uniform> phongMaterial : phongMaterialUniforms;
4866
5835
 
4867
5836
  fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, view_direction: vec3<f32>, normal_worldspace: vec3<f32>, color: vec3<f32>) -> vec3<f32> {
4868
5837
  let halfway_direction: vec3<f32> = normalize(light_direction + view_direction);
@@ -4879,6 +5848,10 @@ fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, v
4879
5848
  fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, position_worldspace: vec3<f32>, normal_worldspace: vec3<f32>) -> vec3<f32> {
4880
5849
  var lightColor: vec3<f32> = surfaceColor;
4881
5850
 
5851
+ if (phongMaterial.unlit != 0u) {
5852
+ return surfaceColor;
5853
+ }
5854
+
4882
5855
  if (lighting.enabled == 0) {
4883
5856
  return lightColor;
4884
5857
  }
@@ -4903,8 +5876,21 @@ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, p
4903
5876
  );
4904
5877
  }
4905
5878
 
4906
- let totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
4907
- for (var i: i32 = lighting.pointLightCount; i < totalLights; i++) {
5879
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
5880
+ let spotLight: SpotLight = lighting_getSpotLight(i);
5881
+ let light_position_worldspace: vec3<f32> = spotLight.position;
5882
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
5883
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
5884
+ lightColor += lighting_getLightColor(
5885
+ surfaceColor,
5886
+ light_direction,
5887
+ view_direction,
5888
+ normal_worldspace,
5889
+ spotLight.color / light_attenuation
5890
+ );
5891
+ }
5892
+
5893
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
4908
5894
  let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
4909
5895
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
4910
5896
  }
@@ -4936,8 +5922,21 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
4936
5922
  );
4937
5923
  }
4938
5924
 
4939
- let totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
4940
- for (var i: i32 = lighting.pointLightCount; i < totalLights; i++) {
5925
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
5926
+ let spotLight: SpotLight = lighting_getSpotLight(i);
5927
+ let light_position_worldspace: vec3<f32> = spotLight.position;
5928
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
5929
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
5930
+ lightColor += lighting_getLightColor(
5931
+ surfaceColor,
5932
+ light_direction,
5933
+ view_direction,
5934
+ normal_worldspace,
5935
+ spotLight.color / light_attenuation
5936
+ );
5937
+ }
5938
+
5939
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
4941
5940
  let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
4942
5941
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
4943
5942
  }
@@ -4951,6 +5950,7 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
4951
5950
  var gouraudMaterial = {
4952
5951
  props: {},
4953
5952
  name: "gouraudMaterial",
5953
+ bindingLayout: [{ name: "gouraudMaterial", group: 3 }],
4954
5954
  // Note these are switched between phong and gouraud
4955
5955
  vs: PHONG_FS.replace("phongMaterial", "gouraudMaterial"),
4956
5956
  fs: PHONG_VS.replace("phongMaterial", "gouraudMaterial"),
@@ -4960,12 +5960,14 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
4960
5960
  },
4961
5961
  dependencies: [lighting],
4962
5962
  uniformTypes: {
5963
+ unlit: "i32",
4963
5964
  ambient: "f32",
4964
5965
  diffuse: "f32",
4965
5966
  shininess: "f32",
4966
5967
  specularColor: "vec3<f32>"
4967
5968
  },
4968
5969
  defaultUniforms: {
5970
+ unlit: false,
4969
5971
  ambient: 0.35,
4970
5972
  diffuse: 0.6,
4971
5973
  shininess: 32,
@@ -4983,6 +5985,8 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
4983
5985
  // src/modules/lighting/phong-material/phong-material.ts
4984
5986
  var phongMaterial = {
4985
5987
  name: "phongMaterial",
5988
+ firstBindingSlot: 0,
5989
+ bindingLayout: [{ name: "phongMaterial", group: 3 }],
4986
5990
  dependencies: [lighting],
4987
5991
  // Note these are switched between phong and gouraud
4988
5992
  source: PHONG_WGSL,
@@ -4992,12 +5996,14 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
4992
5996
  LIGHTING_FRAGMENT: true
4993
5997
  },
4994
5998
  uniformTypes: {
5999
+ unlit: "i32",
4995
6000
  ambient: "f32",
4996
6001
  diffuse: "f32",
4997
6002
  shininess: "f32",
4998
6003
  specularColor: "vec3<f32>"
4999
6004
  },
5000
6005
  defaultUniforms: {
6006
+ unlit: false,
5001
6007
  ambient: 0.35,
5002
6008
  diffuse: 0.6,
5003
6009
  shininess: 32,
@@ -5094,10 +6100,12 @@ uniform pbrMaterialUniforms {
5094
6100
  float clearcoatFactor;
5095
6101
  float clearcoatRoughnessFactor;
5096
6102
  bool clearcoatMapEnabled;
6103
+ bool clearcoatRoughnessMapEnabled;
5097
6104
 
5098
6105
  vec3 sheenColorFactor;
5099
6106
  float sheenRoughnessFactor;
5100
6107
  bool sheenColorMapEnabled;
6108
+ bool sheenRoughnessMapEnabled;
5101
6109
 
5102
6110
  float iridescenceFactor;
5103
6111
  float iridescenceIor;
@@ -5147,26 +6155,33 @@ uniform sampler2D pbr_specularIntensitySampler;
5147
6155
  #ifdef HAS_TRANSMISSIONMAP
5148
6156
  uniform sampler2D pbr_transmissionSampler;
5149
6157
  #endif
6158
+ #ifdef HAS_THICKNESSMAP
6159
+ uniform sampler2D pbr_thicknessSampler;
6160
+ #endif
5150
6161
  #ifdef HAS_CLEARCOATMAP
5151
6162
  uniform sampler2D pbr_clearcoatSampler;
6163
+ #endif
6164
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
5152
6165
  uniform sampler2D pbr_clearcoatRoughnessSampler;
5153
6166
  #endif
6167
+ #ifdef HAS_CLEARCOATNORMALMAP
6168
+ uniform sampler2D pbr_clearcoatNormalSampler;
6169
+ #endif
5154
6170
  #ifdef HAS_SHEENCOLORMAP
5155
6171
  uniform sampler2D pbr_sheenColorSampler;
6172
+ #endif
6173
+ #ifdef HAS_SHEENROUGHNESSMAP
5156
6174
  uniform sampler2D pbr_sheenRoughnessSampler;
5157
6175
  #endif
5158
6176
  #ifdef HAS_IRIDESCENCEMAP
5159
6177
  uniform sampler2D pbr_iridescenceSampler;
5160
6178
  #endif
6179
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
6180
+ uniform sampler2D pbr_iridescenceThicknessSampler;
6181
+ #endif
5161
6182
  #ifdef HAS_ANISOTROPYMAP
5162
6183
  uniform sampler2D pbr_anisotropySampler;
5163
6184
  #endif
5164
- #ifdef USE_IBL
5165
- uniform samplerCube pbr_diffuseEnvSampler;
5166
- uniform samplerCube pbr_specularEnvSampler;
5167
- uniform sampler2D pbr_brdfLUT;
5168
- #endif
5169
-
5170
6185
  // Inputs from vertex shader
5171
6186
 
5172
6187
  in vec3 pbr_vPosition;
@@ -5203,6 +6218,8 @@ struct PBRInfo {
5203
6218
  const float M_PI = 3.141592653589793;
5204
6219
  const float c_MinRoughness = 0.04;
5205
6220
 
6221
+ vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor);
6222
+
5206
6223
  vec4 SRGBtoLINEAR(vec4 srgbIn)
5207
6224
  {
5208
6225
  #ifdef MANUAL_SRGB
@@ -5218,11 +6235,9 @@ vec4 SRGBtoLINEAR(vec4 srgbIn)
5218
6235
  #endif //MANUAL_SRGB
5219
6236
  }
5220
6237
 
5221
- // Find the normal for this fragment, pulling either from a predefined normal map
5222
- // or from the interpolated mesh normal and tangent attributes.
5223
- vec3 getNormal()
6238
+ // Build the tangent basis from interpolated attributes or screen-space derivatives.
6239
+ mat3 getTBN()
5224
6240
  {
5225
- // Retrieve the tangent space matrix
5226
6241
  #ifndef HAS_TANGENTS
5227
6242
  vec3 pos_dx = dFdx(pbr_vPosition);
5228
6243
  vec3 pos_dy = dFdy(pbr_vPosition);
@@ -5243,9 +6258,21 @@ vec3 getNormal()
5243
6258
  mat3 tbn = pbr_vTBN;
5244
6259
  #endif
5245
6260
 
6261
+ return tbn;
6262
+ }
6263
+
6264
+ // Find the normal for this fragment, pulling either from a predefined normal map
6265
+ // or from the interpolated mesh normal and tangent attributes.
6266
+ vec3 getMappedNormal(sampler2D normalSampler, mat3 tbn, float normalScale)
6267
+ {
6268
+ vec3 n = texture(normalSampler, pbr_vUV).rgb;
6269
+ return normalize(tbn * ((2.0 * n - 1.0) * vec3(normalScale, normalScale, 1.0)));
6270
+ }
6271
+
6272
+ vec3 getNormal(mat3 tbn)
6273
+ {
5246
6274
  #ifdef HAS_NORMALMAP
5247
- vec3 n = texture(pbr_normalSampler, pbr_vUV).rgb;
5248
- n = normalize(tbn * ((2.0 * n - 1.0) * vec3(pbrMaterial.normalScale, pbrMaterial.normalScale, 1.0)));
6275
+ vec3 n = getMappedNormal(pbr_normalSampler, tbn, pbrMaterial.normalScale);
5249
6276
  #else
5250
6277
  // The tbn matrix is linearly interpolated, so we need to re-normalize
5251
6278
  vec3 n = normalize(tbn[2].xyz);
@@ -5254,6 +6281,15 @@ vec3 getNormal()
5254
6281
  return n;
5255
6282
  }
5256
6283
 
6284
+ vec3 getClearcoatNormal(mat3 tbn, vec3 baseNormal)
6285
+ {
6286
+ #ifdef HAS_CLEARCOATNORMALMAP
6287
+ return getMappedNormal(pbr_clearcoatNormalSampler, tbn, 1.0);
6288
+ #else
6289
+ return baseNormal;
6290
+ #endif
6291
+ }
6292
+
5257
6293
  // Calculation of the lighting contribution from an optional Image Based Light source.
5258
6294
  // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
5259
6295
  // See our README.md on Environment Maps [3] for additional discussion.
@@ -5329,8 +6365,171 @@ float microfacetDistribution(PBRInfo pbrInfo)
5329
6365
  return roughnessSq / (M_PI * f * f);
5330
6366
  }
5331
6367
 
5332
- void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) {
5333
- pbrInfo.NdotL = 1.0;
6368
+ float maxComponent(vec3 value)
6369
+ {
6370
+ return max(max(value.r, value.g), value.b);
6371
+ }
6372
+
6373
+ float getDielectricF0(float ior)
6374
+ {
6375
+ float clampedIor = max(ior, 1.0);
6376
+ float ratio = (clampedIor - 1.0) / (clampedIor + 1.0);
6377
+ return ratio * ratio;
6378
+ }
6379
+
6380
+ vec2 normalizeDirection(vec2 direction)
6381
+ {
6382
+ float directionLength = length(direction);
6383
+ return directionLength > 0.0001 ? direction / directionLength : vec2(1.0, 0.0);
6384
+ }
6385
+
6386
+ vec2 rotateDirection(vec2 direction, float rotation)
6387
+ {
6388
+ float s = sin(rotation);
6389
+ float c = cos(rotation);
6390
+ return vec2(direction.x * c - direction.y * s, direction.x * s + direction.y * c);
6391
+ }
6392
+
6393
+ vec3 getIridescenceTint(float iridescence, float thickness, float NdotV)
6394
+ {
6395
+ if (iridescence <= 0.0) {
6396
+ return vec3(1.0);
6397
+ }
6398
+
6399
+ float phase = 0.015 * thickness * pbrMaterial.iridescenceIor + (1.0 - NdotV) * 6.0;
6400
+ vec3 thinFilmTint =
6401
+ 0.5 + 0.5 * cos(vec3(phase, phase + 2.0943951, phase + 4.1887902));
6402
+ return mix(vec3(1.0), thinFilmTint, iridescence);
6403
+ }
6404
+
6405
+ vec3 getVolumeAttenuation(float thickness)
6406
+ {
6407
+ if (thickness <= 0.0) {
6408
+ return vec3(1.0);
6409
+ }
6410
+
6411
+ vec3 attenuationCoefficient =
6412
+ -log(max(pbrMaterial.attenuationColor, vec3(0.0001))) /
6413
+ max(pbrMaterial.attenuationDistance, 0.0001);
6414
+ return exp(-attenuationCoefficient * thickness);
6415
+ }
6416
+
6417
+ PBRInfo createClearcoatPBRInfo(PBRInfo basePBRInfo, vec3 clearcoatNormal, float clearcoatRoughness)
6418
+ {
6419
+ float perceptualRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
6420
+ float alphaRoughness = perceptualRoughness * perceptualRoughness;
6421
+ float NdotV = clamp(abs(dot(clearcoatNormal, basePBRInfo.v)), 0.001, 1.0);
6422
+
6423
+ return PBRInfo(
6424
+ basePBRInfo.NdotL,
6425
+ NdotV,
6426
+ basePBRInfo.NdotH,
6427
+ basePBRInfo.LdotH,
6428
+ basePBRInfo.VdotH,
6429
+ perceptualRoughness,
6430
+ 0.0,
6431
+ vec3(0.04),
6432
+ vec3(1.0),
6433
+ alphaRoughness,
6434
+ vec3(0.0),
6435
+ vec3(0.04),
6436
+ clearcoatNormal,
6437
+ basePBRInfo.v
6438
+ );
6439
+ }
6440
+
6441
+ vec3 calculateClearcoatContribution(
6442
+ PBRInfo pbrInfo,
6443
+ vec3 lightColor,
6444
+ vec3 clearcoatNormal,
6445
+ float clearcoatFactor,
6446
+ float clearcoatRoughness
6447
+ ) {
6448
+ if (clearcoatFactor <= 0.0) {
6449
+ return vec3(0.0);
6450
+ }
6451
+
6452
+ PBRInfo clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
6453
+ return calculateFinalColor(clearcoatPBRInfo, lightColor) * clearcoatFactor;
6454
+ }
6455
+
6456
+ #ifdef USE_IBL
6457
+ vec3 calculateClearcoatIBLContribution(
6458
+ PBRInfo pbrInfo,
6459
+ vec3 clearcoatNormal,
6460
+ vec3 reflection,
6461
+ float clearcoatFactor,
6462
+ float clearcoatRoughness
6463
+ ) {
6464
+ if (clearcoatFactor <= 0.0) {
6465
+ return vec3(0.0);
6466
+ }
6467
+
6468
+ PBRInfo clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
6469
+ return getIBLContribution(clearcoatPBRInfo, clearcoatNormal, reflection) * clearcoatFactor;
6470
+ }
6471
+ #endif
6472
+
6473
+ vec3 calculateSheenContribution(
6474
+ PBRInfo pbrInfo,
6475
+ vec3 lightColor,
6476
+ vec3 sheenColor,
6477
+ float sheenRoughness
6478
+ ) {
6479
+ if (maxComponent(sheenColor) <= 0.0) {
6480
+ return vec3(0.0);
6481
+ }
6482
+
6483
+ float sheenFresnel = pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0);
6484
+ float sheenVisibility = mix(1.0, pbrInfo.NdotL * pbrInfo.NdotV, sheenRoughness);
6485
+ return pbrInfo.NdotL *
6486
+ lightColor *
6487
+ sheenColor *
6488
+ (0.25 + 0.75 * sheenFresnel) *
6489
+ sheenVisibility *
6490
+ (1.0 - pbrInfo.metalness);
6491
+ }
6492
+
6493
+ float calculateAnisotropyBoost(
6494
+ PBRInfo pbrInfo,
6495
+ vec3 anisotropyTangent,
6496
+ float anisotropyStrength
6497
+ ) {
6498
+ if (anisotropyStrength <= 0.0) {
6499
+ return 1.0;
6500
+ }
6501
+
6502
+ vec3 anisotropyBitangent = normalize(cross(pbrInfo.n, anisotropyTangent));
6503
+ float bitangentViewAlignment = abs(dot(pbrInfo.v, anisotropyBitangent));
6504
+ return mix(1.0, 0.65 + 0.7 * bitangentViewAlignment, anisotropyStrength);
6505
+ }
6506
+
6507
+ vec3 calculateMaterialLightColor(
6508
+ PBRInfo pbrInfo,
6509
+ vec3 lightColor,
6510
+ vec3 clearcoatNormal,
6511
+ float clearcoatFactor,
6512
+ float clearcoatRoughness,
6513
+ vec3 sheenColor,
6514
+ float sheenRoughness,
6515
+ vec3 anisotropyTangent,
6516
+ float anisotropyStrength
6517
+ ) {
6518
+ float anisotropyBoost = calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
6519
+ vec3 color = calculateFinalColor(pbrInfo, lightColor) * anisotropyBoost;
6520
+ color += calculateClearcoatContribution(
6521
+ pbrInfo,
6522
+ lightColor,
6523
+ clearcoatNormal,
6524
+ clearcoatFactor,
6525
+ clearcoatRoughness
6526
+ );
6527
+ color += calculateSheenContribution(pbrInfo, lightColor, sheenColor, sheenRoughness);
6528
+ return color;
6529
+ }
6530
+
6531
+ void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) {
6532
+ pbrInfo.NdotL = 1.0;
5334
6533
  pbrInfo.NdotH = 0.0;
5335
6534
  pbrInfo.LdotH = 0.0;
5336
6535
  pbrInfo.VdotH = 1.0;
@@ -5353,6 +6552,11 @@ void PBRInfo_setPointLight(inout PBRInfo pbrInfo, PointLight pointLight) {
5353
6552
  PBRInfo_setDirectionalLight(pbrInfo, light_direction);
5354
6553
  }
5355
6554
 
6555
+ void PBRInfo_setSpotLight(inout PBRInfo pbrInfo, SpotLight spotLight) {
6556
+ vec3 light_direction = normalize(spotLight.position - pbr_vPosition);
6557
+ PBRInfo_setDirectionalLight(pbrInfo, light_direction);
6558
+ }
6559
+
5356
6560
  vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
5357
6561
  // Calculate the shading terms for the microfacet specular shading model
5358
6562
  vec3 F = specularReflection(pbrInfo);
@@ -5383,6 +6587,8 @@ vec4 pbr_filterColor(vec4 colorUnused)
5383
6587
 
5384
6588
  vec3 color = vec3(0, 0, 0);
5385
6589
 
6590
+ float transmission = 0.0;
6591
+
5386
6592
  if(pbrMaterial.unlit){
5387
6593
  color.rgb = baseColor.rgb;
5388
6594
  }
@@ -5401,14 +6607,252 @@ vec4 pbr_filterColor(vec4 colorUnused)
5401
6607
  #endif
5402
6608
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
5403
6609
  metallic = clamp(metallic, 0.0, 1.0);
6610
+ mat3 tbn = getTBN();
6611
+ vec3 n = getNormal(tbn); // normal at surface point
6612
+ vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
6613
+ float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
6614
+ #ifdef USE_MATERIAL_EXTENSIONS
6615
+ bool useExtendedPBR =
6616
+ pbrMaterial.specularColorMapEnabled ||
6617
+ pbrMaterial.specularIntensityMapEnabled ||
6618
+ abs(pbrMaterial.specularIntensityFactor - 1.0) > 0.0001 ||
6619
+ maxComponent(abs(pbrMaterial.specularColorFactor - vec3(1.0))) > 0.0001 ||
6620
+ abs(pbrMaterial.ior - 1.5) > 0.0001 ||
6621
+ pbrMaterial.transmissionMapEnabled ||
6622
+ pbrMaterial.transmissionFactor > 0.0001 ||
6623
+ pbrMaterial.clearcoatMapEnabled ||
6624
+ pbrMaterial.clearcoatRoughnessMapEnabled ||
6625
+ pbrMaterial.clearcoatFactor > 0.0001 ||
6626
+ pbrMaterial.clearcoatRoughnessFactor > 0.0001 ||
6627
+ pbrMaterial.sheenColorMapEnabled ||
6628
+ pbrMaterial.sheenRoughnessMapEnabled ||
6629
+ maxComponent(pbrMaterial.sheenColorFactor) > 0.0001 ||
6630
+ pbrMaterial.sheenRoughnessFactor > 0.0001 ||
6631
+ pbrMaterial.iridescenceMapEnabled ||
6632
+ pbrMaterial.iridescenceFactor > 0.0001 ||
6633
+ abs(pbrMaterial.iridescenceIor - 1.3) > 0.0001 ||
6634
+ abs(pbrMaterial.iridescenceThicknessRange.x - 100.0) > 0.0001 ||
6635
+ abs(pbrMaterial.iridescenceThicknessRange.y - 400.0) > 0.0001 ||
6636
+ pbrMaterial.anisotropyMapEnabled ||
6637
+ pbrMaterial.anisotropyStrength > 0.0001 ||
6638
+ abs(pbrMaterial.anisotropyRotation) > 0.0001 ||
6639
+ length(pbrMaterial.anisotropyDirection - vec2(1.0, 0.0)) > 0.0001;
6640
+ #else
6641
+ bool useExtendedPBR = false;
6642
+ #endif
6643
+
6644
+ if (!useExtendedPBR) {
6645
+ // Keep the baseline metallic-roughness implementation byte-for-byte equivalent in behavior.
6646
+ float alphaRoughness = perceptualRoughness * perceptualRoughness;
6647
+
6648
+ vec3 f0 = vec3(0.04);
6649
+ vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
6650
+ diffuseColor *= 1.0 - metallic;
6651
+ vec3 specularColor = mix(f0, baseColor.rgb, metallic);
6652
+
6653
+ float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
6654
+ float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
6655
+ vec3 specularEnvironmentR0 = specularColor.rgb;
6656
+ vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
6657
+ vec3 reflection = -normalize(reflect(v, n));
6658
+
6659
+ PBRInfo pbrInfo = PBRInfo(
6660
+ 0.0, // NdotL
6661
+ NdotV,
6662
+ 0.0, // NdotH
6663
+ 0.0, // LdotH
6664
+ 0.0, // VdotH
6665
+ perceptualRoughness,
6666
+ metallic,
6667
+ specularEnvironmentR0,
6668
+ specularEnvironmentR90,
6669
+ alphaRoughness,
6670
+ diffuseColor,
6671
+ specularColor,
6672
+ n,
6673
+ v
6674
+ );
6675
+
6676
+ #ifdef USE_LIGHTS
6677
+ PBRInfo_setAmbientLight(pbrInfo);
6678
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor);
6679
+
6680
+ for(int i = 0; i < lighting.directionalLightCount; i++) {
6681
+ if (i < lighting.directionalLightCount) {
6682
+ PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
6683
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
6684
+ }
6685
+ }
6686
+
6687
+ for(int i = 0; i < lighting.pointLightCount; i++) {
6688
+ if (i < lighting.pointLightCount) {
6689
+ PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
6690
+ float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
6691
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
6692
+ }
6693
+ }
6694
+
6695
+ for(int i = 0; i < lighting.spotLightCount; i++) {
6696
+ if (i < lighting.spotLightCount) {
6697
+ PBRInfo_setSpotLight(pbrInfo, lighting_getSpotLight(i));
6698
+ float attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), pbr_vPosition);
6699
+ color += calculateFinalColor(pbrInfo, lighting_getSpotLight(i).color / attenuation);
6700
+ }
6701
+ }
6702
+ #endif
6703
+
6704
+ #ifdef USE_IBL
6705
+ if (pbrMaterial.IBLenabled) {
6706
+ color += getIBLContribution(pbrInfo, n, reflection);
6707
+ }
6708
+ #endif
6709
+
6710
+ #ifdef HAS_OCCLUSIONMAP
6711
+ if (pbrMaterial.occlusionMapEnabled) {
6712
+ float ao = texture(pbr_occlusionSampler, pbr_vUV).r;
6713
+ color = mix(color, color * ao, pbrMaterial.occlusionStrength);
6714
+ }
6715
+ #endif
6716
+
6717
+ vec3 emissive = pbrMaterial.emissiveFactor;
6718
+ #ifdef HAS_EMISSIVEMAP
6719
+ if (pbrMaterial.emissiveMapEnabled) {
6720
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
6721
+ }
6722
+ #endif
6723
+ color += emissive * pbrMaterial.emissiveStrength;
6724
+
6725
+ #ifdef PBR_DEBUG
6726
+ color = mix(color, baseColor.rgb, pbrMaterial.scaleDiffBaseMR.y);
6727
+ color = mix(color, vec3(metallic), pbrMaterial.scaleDiffBaseMR.z);
6728
+ color = mix(color, vec3(perceptualRoughness), pbrMaterial.scaleDiffBaseMR.w);
6729
+ #endif
6730
+
6731
+ return vec4(pow(color, vec3(1.0 / 2.2)), baseColor.a);
6732
+ }
6733
+
6734
+ float specularIntensity = pbrMaterial.specularIntensityFactor;
6735
+ #ifdef HAS_SPECULARINTENSITYMAP
6736
+ if (pbrMaterial.specularIntensityMapEnabled) {
6737
+ specularIntensity *= texture(pbr_specularIntensitySampler, pbr_vUV).a;
6738
+ }
6739
+ #endif
6740
+
6741
+ vec3 specularFactor = pbrMaterial.specularColorFactor;
6742
+ #ifdef HAS_SPECULARCOLORMAP
6743
+ if (pbrMaterial.specularColorMapEnabled) {
6744
+ specularFactor *= SRGBtoLINEAR(texture(pbr_specularColorSampler, pbr_vUV)).rgb;
6745
+ }
6746
+ #endif
6747
+
6748
+ transmission = pbrMaterial.transmissionFactor;
6749
+ #ifdef HAS_TRANSMISSIONMAP
6750
+ if (pbrMaterial.transmissionMapEnabled) {
6751
+ transmission *= texture(pbr_transmissionSampler, pbr_vUV).r;
6752
+ }
6753
+ #endif
6754
+ transmission = clamp(transmission * (1.0 - metallic), 0.0, 1.0);
6755
+ float thickness = max(pbrMaterial.thicknessFactor, 0.0);
6756
+ #ifdef HAS_THICKNESSMAP
6757
+ thickness *= texture(pbr_thicknessSampler, pbr_vUV).g;
6758
+ #endif
6759
+
6760
+ float clearcoatFactor = pbrMaterial.clearcoatFactor;
6761
+ float clearcoatRoughness = pbrMaterial.clearcoatRoughnessFactor;
6762
+ #ifdef HAS_CLEARCOATMAP
6763
+ if (pbrMaterial.clearcoatMapEnabled) {
6764
+ clearcoatFactor *= texture(pbr_clearcoatSampler, pbr_vUV).r;
6765
+ }
6766
+ #endif
6767
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
6768
+ if (pbrMaterial.clearcoatRoughnessMapEnabled) {
6769
+ clearcoatRoughness *= texture(pbr_clearcoatRoughnessSampler, pbr_vUV).g;
6770
+ }
6771
+ #endif
6772
+ clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
6773
+ clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
6774
+ vec3 clearcoatNormal = getClearcoatNormal(tbn, n);
6775
+
6776
+ vec3 sheenColor = pbrMaterial.sheenColorFactor;
6777
+ float sheenRoughness = pbrMaterial.sheenRoughnessFactor;
6778
+ #ifdef HAS_SHEENCOLORMAP
6779
+ if (pbrMaterial.sheenColorMapEnabled) {
6780
+ sheenColor *= SRGBtoLINEAR(texture(pbr_sheenColorSampler, pbr_vUV)).rgb;
6781
+ }
6782
+ #endif
6783
+ #ifdef HAS_SHEENROUGHNESSMAP
6784
+ if (pbrMaterial.sheenRoughnessMapEnabled) {
6785
+ sheenRoughness *= texture(pbr_sheenRoughnessSampler, pbr_vUV).a;
6786
+ }
6787
+ #endif
6788
+ sheenRoughness = clamp(sheenRoughness, c_MinRoughness, 1.0);
6789
+
6790
+ float iridescence = pbrMaterial.iridescenceFactor;
6791
+ #ifdef HAS_IRIDESCENCEMAP
6792
+ if (pbrMaterial.iridescenceMapEnabled) {
6793
+ iridescence *= texture(pbr_iridescenceSampler, pbr_vUV).r;
6794
+ }
6795
+ #endif
6796
+ iridescence = clamp(iridescence, 0.0, 1.0);
6797
+ float iridescenceThickness = mix(
6798
+ pbrMaterial.iridescenceThicknessRange.x,
6799
+ pbrMaterial.iridescenceThicknessRange.y,
6800
+ 0.5
6801
+ );
6802
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
6803
+ iridescenceThickness = mix(
6804
+ pbrMaterial.iridescenceThicknessRange.x,
6805
+ pbrMaterial.iridescenceThicknessRange.y,
6806
+ texture(pbr_iridescenceThicknessSampler, pbr_vUV).g
6807
+ );
6808
+ #endif
6809
+
6810
+ float anisotropyStrength = clamp(pbrMaterial.anisotropyStrength, 0.0, 1.0);
6811
+ vec2 anisotropyDirection = normalizeDirection(pbrMaterial.anisotropyDirection);
6812
+ #ifdef HAS_ANISOTROPYMAP
6813
+ if (pbrMaterial.anisotropyMapEnabled) {
6814
+ vec3 anisotropySample = texture(pbr_anisotropySampler, pbr_vUV).rgb;
6815
+ anisotropyStrength *= anisotropySample.b;
6816
+ vec2 mappedDirection = anisotropySample.rg * 2.0 - 1.0;
6817
+ if (length(mappedDirection) > 0.0001) {
6818
+ anisotropyDirection = normalize(mappedDirection);
6819
+ }
6820
+ }
6821
+ #endif
6822
+ anisotropyDirection = rotateDirection(anisotropyDirection, pbrMaterial.anisotropyRotation);
6823
+ vec3 anisotropyTangent = normalize(tbn[0] * anisotropyDirection.x + tbn[1] * anisotropyDirection.y);
6824
+ if (length(anisotropyTangent) < 0.0001) {
6825
+ anisotropyTangent = normalize(tbn[0]);
6826
+ }
6827
+ float anisotropyViewAlignment = abs(dot(v, anisotropyTangent));
6828
+ perceptualRoughness = mix(
6829
+ perceptualRoughness,
6830
+ clamp(perceptualRoughness * (1.0 - 0.6 * anisotropyViewAlignment), c_MinRoughness, 1.0),
6831
+ anisotropyStrength
6832
+ );
6833
+
5404
6834
  // Roughness is authored as perceptual roughness; as is convention,
5405
6835
  // convert to material roughness by squaring the perceptual roughness [2].
5406
6836
  float alphaRoughness = perceptualRoughness * perceptualRoughness;
5407
6837
 
5408
- vec3 f0 = vec3(0.04);
5409
- vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
5410
- diffuseColor *= 1.0 - metallic;
5411
- vec3 specularColor = mix(f0, baseColor.rgb, metallic);
6838
+ float dielectricF0 = getDielectricF0(pbrMaterial.ior);
6839
+ vec3 dielectricSpecularF0 = min(
6840
+ vec3(dielectricF0) * specularFactor * specularIntensity,
6841
+ vec3(1.0)
6842
+ );
6843
+ vec3 iridescenceTint = getIridescenceTint(iridescence, iridescenceThickness, NdotV);
6844
+ dielectricSpecularF0 = mix(
6845
+ dielectricSpecularF0,
6846
+ dielectricSpecularF0 * iridescenceTint,
6847
+ iridescence
6848
+ );
6849
+ vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - dielectricSpecularF0);
6850
+ diffuseColor *= (1.0 - metallic) * (1.0 - transmission);
6851
+ vec3 specularColor = mix(dielectricSpecularF0, baseColor.rgb, metallic);
6852
+
6853
+ float baseLayerEnergy = 1.0 - clearcoatFactor * 0.25;
6854
+ diffuseColor *= baseLayerEnergy;
6855
+ specularColor *= baseLayerEnergy;
5412
6856
 
5413
6857
  // Compute reflectance.
5414
6858
  float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
@@ -5420,11 +6864,6 @@ vec4 pbr_filterColor(vec4 colorUnused)
5420
6864
  float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
5421
6865
  vec3 specularEnvironmentR0 = specularColor.rgb;
5422
6866
  vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
5423
-
5424
- vec3 n = getNormal(); // normal at surface point
5425
- vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
5426
-
5427
- float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
5428
6867
  vec3 reflection = -normalize(reflect(v, n));
5429
6868
 
5430
6869
  PBRInfo pbrInfo = PBRInfo(
@@ -5448,13 +6887,33 @@ vec4 pbr_filterColor(vec4 colorUnused)
5448
6887
  #ifdef USE_LIGHTS
5449
6888
  // Apply ambient light
5450
6889
  PBRInfo_setAmbientLight(pbrInfo);
5451
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
6890
+ color += calculateMaterialLightColor(
6891
+ pbrInfo,
6892
+ lighting.ambientColor,
6893
+ clearcoatNormal,
6894
+ clearcoatFactor,
6895
+ clearcoatRoughness,
6896
+ sheenColor,
6897
+ sheenRoughness,
6898
+ anisotropyTangent,
6899
+ anisotropyStrength
6900
+ );
5452
6901
 
5453
6902
  // Apply directional light
5454
6903
  for(int i = 0; i < lighting.directionalLightCount; i++) {
5455
6904
  if (i < lighting.directionalLightCount) {
5456
6905
  PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
5457
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
6906
+ color += calculateMaterialLightColor(
6907
+ pbrInfo,
6908
+ lighting_getDirectionalLight(i).color,
6909
+ clearcoatNormal,
6910
+ clearcoatFactor,
6911
+ clearcoatRoughness,
6912
+ sheenColor,
6913
+ sheenRoughness,
6914
+ anisotropyTangent,
6915
+ anisotropyStrength
6916
+ );
5458
6917
  }
5459
6918
  }
5460
6919
 
@@ -5463,7 +6922,35 @@ vec4 pbr_filterColor(vec4 colorUnused)
5463
6922
  if (i < lighting.pointLightCount) {
5464
6923
  PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
5465
6924
  float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
5466
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
6925
+ color += calculateMaterialLightColor(
6926
+ pbrInfo,
6927
+ lighting_getPointLight(i).color / attenuation,
6928
+ clearcoatNormal,
6929
+ clearcoatFactor,
6930
+ clearcoatRoughness,
6931
+ sheenColor,
6932
+ sheenRoughness,
6933
+ anisotropyTangent,
6934
+ anisotropyStrength
6935
+ );
6936
+ }
6937
+ }
6938
+
6939
+ for(int i = 0; i < lighting.spotLightCount; i++) {
6940
+ if (i < lighting.spotLightCount) {
6941
+ PBRInfo_setSpotLight(pbrInfo, lighting_getSpotLight(i));
6942
+ float attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), pbr_vPosition);
6943
+ color += calculateMaterialLightColor(
6944
+ pbrInfo,
6945
+ lighting_getSpotLight(i).color / attenuation,
6946
+ clearcoatNormal,
6947
+ clearcoatFactor,
6948
+ clearcoatRoughness,
6949
+ sheenColor,
6950
+ sheenRoughness,
6951
+ anisotropyTangent,
6952
+ anisotropyStrength
6953
+ );
5467
6954
  }
5468
6955
  }
5469
6956
  #endif
@@ -5471,7 +6958,16 @@ vec4 pbr_filterColor(vec4 colorUnused)
5471
6958
  // Calculate lighting contribution from image based lighting source (IBL)
5472
6959
  #ifdef USE_IBL
5473
6960
  if (pbrMaterial.IBLenabled) {
5474
- color += getIBLContribution(pbrInfo, n, reflection);
6961
+ color += getIBLContribution(pbrInfo, n, reflection) *
6962
+ calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
6963
+ color += calculateClearcoatIBLContribution(
6964
+ pbrInfo,
6965
+ clearcoatNormal,
6966
+ -normalize(reflect(v, clearcoatNormal)),
6967
+ clearcoatFactor,
6968
+ clearcoatRoughness
6969
+ );
6970
+ color += sheenColor * pbrMaterial.scaleIBLAmbient.x * (1.0 - sheenRoughness) * 0.25;
5475
6971
  }
5476
6972
  #endif
5477
6973
 
@@ -5483,12 +6979,17 @@ vec4 pbr_filterColor(vec4 colorUnused)
5483
6979
  }
5484
6980
  #endif
5485
6981
 
6982
+ vec3 emissive = pbrMaterial.emissiveFactor;
5486
6983
  #ifdef HAS_EMISSIVEMAP
5487
6984
  if (pbrMaterial.emissiveMapEnabled) {
5488
- vec3 emissive = SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb * pbrMaterial.emissiveFactor;
5489
- color += emissive;
6985
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
5490
6986
  }
5491
6987
  #endif
6988
+ color += emissive * pbrMaterial.emissiveStrength;
6989
+
6990
+ if (transmission > 0.0) {
6991
+ color = mix(color, color * getVolumeAttenuation(thickness), transmission);
6992
+ }
5492
6993
 
5493
6994
  // This section uses mix to override final color for reference app visualization
5494
6995
  // of various parameters in the lighting equation.
@@ -5508,7 +7009,8 @@ vec4 pbr_filterColor(vec4 colorUnused)
5508
7009
 
5509
7010
  }
5510
7011
 
5511
- return vec4(pow(color,vec3(1.0/2.2)), baseColor.a);
7012
+ float alpha = clamp(baseColor.a * (1.0 - transmission), 0.0, 1.0);
7013
+ return vec4(pow(color,vec3(1.0/2.2)), alpha);
5512
7014
  }
5513
7015
  `
5514
7016
  );
@@ -5574,6 +7076,42 @@ struct pbrMaterialUniforms {
5574
7076
 
5575
7077
  alphaCutoffEnabled: i32,
5576
7078
  alphaCutoff: f32, // #ifdef ALPHA_CUTOFF
7079
+
7080
+ specularColorFactor: vec3f,
7081
+ specularIntensityFactor: f32,
7082
+ specularColorMapEnabled: i32,
7083
+ specularIntensityMapEnabled: i32,
7084
+
7085
+ ior: f32,
7086
+
7087
+ transmissionFactor: f32,
7088
+ transmissionMapEnabled: i32,
7089
+
7090
+ thicknessFactor: f32,
7091
+ attenuationDistance: f32,
7092
+ attenuationColor: vec3f,
7093
+
7094
+ clearcoatFactor: f32,
7095
+ clearcoatRoughnessFactor: f32,
7096
+ clearcoatMapEnabled: i32,
7097
+ clearcoatRoughnessMapEnabled: i32,
7098
+
7099
+ sheenColorFactor: vec3f,
7100
+ sheenRoughnessFactor: f32,
7101
+ sheenColorMapEnabled: i32,
7102
+ sheenRoughnessMapEnabled: i32,
7103
+
7104
+ iridescenceFactor: f32,
7105
+ iridescenceIor: f32,
7106
+ iridescenceThicknessRange: vec2f,
7107
+ iridescenceMapEnabled: i32,
7108
+
7109
+ anisotropyStrength: f32,
7110
+ anisotropyRotation: f32,
7111
+ anisotropyDirection: vec2f,
7112
+ anisotropyMapEnabled: i32,
7113
+
7114
+ emissiveStrength: f32,
5577
7115
 
5578
7116
  // IBL
5579
7117
  IBLenabled: i32,
@@ -5586,38 +7124,77 @@ struct pbrMaterialUniforms {
5586
7124
  // #endif
5587
7125
  }
5588
7126
 
5589
- @binding(2) @group(0) var<uniform> pbrMaterial : pbrMaterialUniforms;
7127
+ @group(3) @binding(auto) var<uniform> pbrMaterial : pbrMaterialUniforms;
5590
7128
 
5591
7129
  // Samplers
5592
7130
  #ifdef HAS_BASECOLORMAP
5593
- @binding(3) @group(0) var pbr_baseColorSampler: texture_2d<f32>;
5594
- @binding(4) @group(0) var pbr_baseColorSamplerSampler: sampler;
7131
+ @group(3) @binding(auto) var pbr_baseColorSampler: texture_2d<f32>;
7132
+ @group(3) @binding(auto) var pbr_baseColorSamplerSampler: sampler;
5595
7133
  #endif
5596
7134
  #ifdef HAS_NORMALMAP
5597
- @binding(5) @group(0) var pbr_normalSampler: texture_2d<f32>;
5598
- @binding(6) @group(0) var pbr_normalSamplerSampler: sampler;
7135
+ @group(3) @binding(auto) var pbr_normalSampler: texture_2d<f32>;
7136
+ @group(3) @binding(auto) var pbr_normalSamplerSampler: sampler;
5599
7137
  #endif
5600
7138
  #ifdef HAS_EMISSIVEMAP
5601
- @binding(7) @group(0) var pbr_emissiveSampler: texture_2d<f32>;
5602
- @binding(8) @group(0) var pbr_emissiveSamplerSampler: sampler;
7139
+ @group(3) @binding(auto) var pbr_emissiveSampler: texture_2d<f32>;
7140
+ @group(3) @binding(auto) var pbr_emissiveSamplerSampler: sampler;
5603
7141
  #endif
5604
7142
  #ifdef HAS_METALROUGHNESSMAP
5605
- @binding(9) @group(0) var pbr_metallicRoughnessSampler: texture_2d<f32>;
5606
- @binding(10) @group(0) var pbr_metallicRoughnessSamplerSampler: sampler;
7143
+ @group(3) @binding(auto) var pbr_metallicRoughnessSampler: texture_2d<f32>;
7144
+ @group(3) @binding(auto) var pbr_metallicRoughnessSamplerSampler: sampler;
5607
7145
  #endif
5608
7146
  #ifdef HAS_OCCLUSIONMAP
5609
- @binding(11) @group(0) var pbr_occlusionSampler: texture_2d<f32>;
5610
- @binding(12) @group(0) var pbr_occlusionSamplerSampler: sampler;
7147
+ @group(3) @binding(auto) var pbr_occlusionSampler: texture_2d<f32>;
7148
+ @group(3) @binding(auto) var pbr_occlusionSamplerSampler: sampler;
5611
7149
  #endif
5612
- #ifdef USE_IBL
5613
- @binding(13) @group(0) var pbr_diffuseEnvSampler: texture_cube<f32>;
5614
- @binding(14) @group(0) var pbr_diffuseEnvSamplerSampler: sampler;
5615
- @binding(15) @group(0) var pbr_specularEnvSampler: texture_cube<f32>;
5616
- @binding(16) @group(0) var pbr_specularEnvSamplerSampler: sampler;
5617
- @binding(17) @group(0) var pbr_BrdfLUT: texture_2d<f32>;
5618
- @binding(18) @group(0) var pbr_BrdfLUTSampler: sampler;
7150
+ #ifdef HAS_SPECULARCOLORMAP
7151
+ @group(3) @binding(auto) var pbr_specularColorSampler: texture_2d<f32>;
7152
+ @group(3) @binding(auto) var pbr_specularColorSamplerSampler: sampler;
7153
+ #endif
7154
+ #ifdef HAS_SPECULARINTENSITYMAP
7155
+ @group(3) @binding(auto) var pbr_specularIntensitySampler: texture_2d<f32>;
7156
+ @group(3) @binding(auto) var pbr_specularIntensitySamplerSampler: sampler;
7157
+ #endif
7158
+ #ifdef HAS_TRANSMISSIONMAP
7159
+ @group(3) @binding(auto) var pbr_transmissionSampler: texture_2d<f32>;
7160
+ @group(3) @binding(auto) var pbr_transmissionSamplerSampler: sampler;
7161
+ #endif
7162
+ #ifdef HAS_THICKNESSMAP
7163
+ @group(3) @binding(auto) var pbr_thicknessSampler: texture_2d<f32>;
7164
+ @group(3) @binding(auto) var pbr_thicknessSamplerSampler: sampler;
7165
+ #endif
7166
+ #ifdef HAS_CLEARCOATMAP
7167
+ @group(3) @binding(auto) var pbr_clearcoatSampler: texture_2d<f32>;
7168
+ @group(3) @binding(auto) var pbr_clearcoatSamplerSampler: sampler;
7169
+ #endif
7170
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
7171
+ @group(3) @binding(auto) var pbr_clearcoatRoughnessSampler: texture_2d<f32>;
7172
+ @group(3) @binding(auto) var pbr_clearcoatRoughnessSamplerSampler: sampler;
7173
+ #endif
7174
+ #ifdef HAS_CLEARCOATNORMALMAP
7175
+ @group(3) @binding(auto) var pbr_clearcoatNormalSampler: texture_2d<f32>;
7176
+ @group(3) @binding(auto) var pbr_clearcoatNormalSamplerSampler: sampler;
7177
+ #endif
7178
+ #ifdef HAS_SHEENCOLORMAP
7179
+ @group(3) @binding(auto) var pbr_sheenColorSampler: texture_2d<f32>;
7180
+ @group(3) @binding(auto) var pbr_sheenColorSamplerSampler: sampler;
7181
+ #endif
7182
+ #ifdef HAS_SHEENROUGHNESSMAP
7183
+ @group(3) @binding(auto) var pbr_sheenRoughnessSampler: texture_2d<f32>;
7184
+ @group(3) @binding(auto) var pbr_sheenRoughnessSamplerSampler: sampler;
7185
+ #endif
7186
+ #ifdef HAS_IRIDESCENCEMAP
7187
+ @group(3) @binding(auto) var pbr_iridescenceSampler: texture_2d<f32>;
7188
+ @group(3) @binding(auto) var pbr_iridescenceSamplerSampler: sampler;
7189
+ #endif
7190
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
7191
+ @group(3) @binding(auto) var pbr_iridescenceThicknessSampler: texture_2d<f32>;
7192
+ @group(3) @binding(auto) var pbr_iridescenceThicknessSamplerSampler: sampler;
7193
+ #endif
7194
+ #ifdef HAS_ANISOTROPYMAP
7195
+ @group(3) @binding(auto) var pbr_anisotropySampler: texture_2d<f32>;
7196
+ @group(3) @binding(auto) var pbr_anisotropySamplerSampler: sampler;
5619
7197
  #endif
5620
-
5621
7198
  // Encapsulate the various inputs used by the various functions in the shading equation
5622
7199
  // We store values in this struct to simplify the integration of alternative implementations
5623
7200
  // of the shading terms, outlined in the Readme.MD Appendix.
@@ -5658,11 +7235,9 @@ fn SRGBtoLINEAR(srgbIn: vec4f ) -> vec4f
5658
7235
  return vec4f(linOut, srgbIn.w);
5659
7236
  }
5660
7237
 
5661
- // Find the normal for this fragment, pulling either from a predefined normal map
5662
- // or from the interpolated mesh normal and tangent attributes.
5663
- fn getNormal() -> vec3f
7238
+ // Build the tangent basis from interpolated attributes or screen-space derivatives.
7239
+ fn getTBN() -> mat3x3f
5664
7240
  {
5665
- // Retrieve the tangent space matrix
5666
7241
  let pos_dx: vec3f = dpdx(fragmentInputs.pbr_vPosition);
5667
7242
  let pos_dy: vec3f = dpdy(fragmentInputs.pbr_vPosition);
5668
7243
  let tex_dx: vec3f = dpdx(vec3f(fragmentInputs.pbr_vUV, 0.0));
@@ -5680,16 +7255,52 @@ fn getNormal() -> vec3f
5680
7255
  tbn = fragmentInputs.pbr_vTBN;
5681
7256
  #endif
5682
7257
 
7258
+ return tbn;
7259
+ }
7260
+
7261
+ // Find the normal for this fragment, pulling either from a predefined normal map
7262
+ // or from the interpolated mesh normal and tangent attributes.
7263
+ fn getMappedNormal(
7264
+ normalSampler: texture_2d<f32>,
7265
+ normalSamplerBinding: sampler,
7266
+ tbn: mat3x3f,
7267
+ normalScale: f32
7268
+ ) -> vec3f
7269
+ {
7270
+ let n = textureSample(normalSampler, normalSamplerBinding, fragmentInputs.pbr_vUV).rgb;
7271
+ return normalize(tbn * ((2.0 * n - 1.0) * vec3f(normalScale, normalScale, 1.0)));
7272
+ }
7273
+
7274
+ fn getNormal(tbn: mat3x3f) -> vec3f
7275
+ {
5683
7276
  // The tbn matrix is linearly interpolated, so we need to re-normalize
5684
7277
  var n: vec3f = normalize(tbn[2].xyz);
5685
7278
  #ifdef HAS_NORMALMAP
5686
- n = textureSample(pbr_normalSampler, pbr_normalSamplerSampler, fragmentInputs.pbr_vUV).rgb;
5687
- n = normalize(tbn * ((2.0 * n - 1.0) * vec3f(pbrMaterial.normalScale, pbrMaterial.normalScale, 1.0)));
7279
+ n = getMappedNormal(
7280
+ pbr_normalSampler,
7281
+ pbr_normalSamplerSampler,
7282
+ tbn,
7283
+ pbrMaterial.normalScale
7284
+ );
5688
7285
  #endif
5689
7286
 
5690
7287
  return n;
5691
7288
  }
5692
7289
 
7290
+ fn getClearcoatNormal(tbn: mat3x3f, baseNormal: vec3f) -> vec3f
7291
+ {
7292
+ #ifdef HAS_CLEARCOATNORMALMAP
7293
+ return getMappedNormal(
7294
+ pbr_clearcoatNormalSampler,
7295
+ pbr_clearcoatNormalSamplerSampler,
7296
+ tbn,
7297
+ 1.0
7298
+ );
7299
+ #else
7300
+ return baseNormal;
7301
+ #endif
7302
+ }
7303
+
5693
7304
  // Calculation of the lighting contribution from an optional Image Based Light source.
5694
7305
  // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
5695
7306
  // See our README.md on Environment Maps [3] for additional discussion.
@@ -5700,17 +7311,25 @@ fn getIBLContribution(pbrInfo: PBRInfo, n: vec3f, reflection: vec3f) -> vec3f
5700
7311
  let lod: f32 = pbrInfo.perceptualRoughness * mipCount;
5701
7312
  // retrieve a scale and bias to F0. See [1], Figure 3
5702
7313
  let brdf = SRGBtoLINEAR(
5703
- textureSample(
5704
- pbr_BrdfLUT,
5705
- pbr_BrdfLUTSampler,
5706
- vec2f(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness)
7314
+ textureSampleLevel(
7315
+ pbr_brdfLUT,
7316
+ pbr_brdfLUTSampler,
7317
+ vec2f(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness),
7318
+ 0.0
5707
7319
  )
5708
7320
  ).rgb;
5709
7321
  let diffuseLight =
5710
- SRGBtoLINEAR(textureSample(pbr_diffuseEnvSampler, pbr_diffuseEnvSamplerSampler, n)).rgb;
5711
- let specularLightDefault =
5712
- SRGBtoLINEAR(textureSample(pbr_specularEnvSampler, pbr_specularEnvSamplerSampler, reflection)).rgb;
5713
- var specularLight = specularLightDefault;
7322
+ SRGBtoLINEAR(
7323
+ textureSampleLevel(pbr_diffuseEnvSampler, pbr_diffuseEnvSamplerSampler, n, 0.0)
7324
+ ).rgb;
7325
+ var specularLight = SRGBtoLINEAR(
7326
+ textureSampleLevel(
7327
+ pbr_specularEnvSampler,
7328
+ pbr_specularEnvSamplerSampler,
7329
+ reflection,
7330
+ 0.0
7331
+ )
7332
+ ).rgb;
5714
7333
  #ifdef USE_TEX_LOD
5715
7334
  specularLight = SRGBtoLINEAR(
5716
7335
  textureSampleLevel(
@@ -5771,6 +7390,172 @@ fn microfacetDistribution(pbrInfo: PBRInfo) -> f32 {
5771
7390
  return roughnessSq / (M_PI * f * f);
5772
7391
  }
5773
7392
 
7393
+ fn maxComponent(value: vec3f) -> f32 {
7394
+ return max(max(value.r, value.g), value.b);
7395
+ }
7396
+
7397
+ fn getDielectricF0(ior: f32) -> f32 {
7398
+ let clampedIor = max(ior, 1.0);
7399
+ let ratio = (clampedIor - 1.0) / (clampedIor + 1.0);
7400
+ return ratio * ratio;
7401
+ }
7402
+
7403
+ fn normalizeDirection(direction: vec2f) -> vec2f {
7404
+ let directionLength = length(direction);
7405
+ if (directionLength > 0.0001) {
7406
+ return direction / directionLength;
7407
+ }
7408
+
7409
+ return vec2f(1.0, 0.0);
7410
+ }
7411
+
7412
+ fn rotateDirection(direction: vec2f, rotation: f32) -> vec2f {
7413
+ let s = sin(rotation);
7414
+ let c = cos(rotation);
7415
+ return vec2f(direction.x * c - direction.y * s, direction.x * s + direction.y * c);
7416
+ }
7417
+
7418
+ fn getIridescenceTint(iridescence: f32, thickness: f32, NdotV: f32) -> vec3f {
7419
+ if (iridescence <= 0.0) {
7420
+ return vec3f(1.0);
7421
+ }
7422
+
7423
+ let phase = 0.015 * thickness * pbrMaterial.iridescenceIor + (1.0 - NdotV) * 6.0;
7424
+ let thinFilmTint =
7425
+ 0.5 +
7426
+ 0.5 *
7427
+ cos(vec3f(phase, phase + 2.0943951, phase + 4.1887902));
7428
+ return mix(vec3f(1.0), thinFilmTint, iridescence);
7429
+ }
7430
+
7431
+ fn getVolumeAttenuation(thickness: f32) -> vec3f {
7432
+ if (thickness <= 0.0) {
7433
+ return vec3f(1.0);
7434
+ }
7435
+
7436
+ let attenuationCoefficient =
7437
+ -log(max(pbrMaterial.attenuationColor, vec3f(0.0001))) /
7438
+ max(pbrMaterial.attenuationDistance, 0.0001);
7439
+ return exp(-attenuationCoefficient * thickness);
7440
+ }
7441
+
7442
+ fn createClearcoatPBRInfo(
7443
+ basePBRInfo: PBRInfo,
7444
+ clearcoatNormal: vec3f,
7445
+ clearcoatRoughness: f32
7446
+ ) -> PBRInfo {
7447
+ let perceptualRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
7448
+ let alphaRoughness = perceptualRoughness * perceptualRoughness;
7449
+ let NdotV = clamp(abs(dot(clearcoatNormal, basePBRInfo.v)), 0.001, 1.0);
7450
+
7451
+ return PBRInfo(
7452
+ basePBRInfo.NdotL,
7453
+ NdotV,
7454
+ basePBRInfo.NdotH,
7455
+ basePBRInfo.LdotH,
7456
+ basePBRInfo.VdotH,
7457
+ perceptualRoughness,
7458
+ 0.0,
7459
+ vec3f(0.04),
7460
+ vec3f(1.0),
7461
+ alphaRoughness,
7462
+ vec3f(0.0),
7463
+ vec3f(0.04),
7464
+ clearcoatNormal,
7465
+ basePBRInfo.v
7466
+ );
7467
+ }
7468
+
7469
+ fn calculateClearcoatContribution(
7470
+ pbrInfo: PBRInfo,
7471
+ lightColor: vec3f,
7472
+ clearcoatNormal: vec3f,
7473
+ clearcoatFactor: f32,
7474
+ clearcoatRoughness: f32
7475
+ ) -> vec3f {
7476
+ if (clearcoatFactor <= 0.0) {
7477
+ return vec3f(0.0);
7478
+ }
7479
+
7480
+ let clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
7481
+ return calculateFinalColor(clearcoatPBRInfo, lightColor) * clearcoatFactor;
7482
+ }
7483
+
7484
+ #ifdef USE_IBL
7485
+ fn calculateClearcoatIBLContribution(
7486
+ pbrInfo: PBRInfo,
7487
+ clearcoatNormal: vec3f,
7488
+ reflection: vec3f,
7489
+ clearcoatFactor: f32,
7490
+ clearcoatRoughness: f32
7491
+ ) -> vec3f {
7492
+ if (clearcoatFactor <= 0.0) {
7493
+ return vec3f(0.0);
7494
+ }
7495
+
7496
+ let clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
7497
+ return getIBLContribution(clearcoatPBRInfo, clearcoatNormal, reflection) * clearcoatFactor;
7498
+ }
7499
+ #endif
7500
+
7501
+ fn calculateSheenContribution(
7502
+ pbrInfo: PBRInfo,
7503
+ lightColor: vec3f,
7504
+ sheenColor: vec3f,
7505
+ sheenRoughness: f32
7506
+ ) -> vec3f {
7507
+ if (maxComponent(sheenColor) <= 0.0) {
7508
+ return vec3f(0.0);
7509
+ }
7510
+
7511
+ let sheenFresnel = pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0);
7512
+ let sheenVisibility = mix(1.0, pbrInfo.NdotL * pbrInfo.NdotV, sheenRoughness);
7513
+ return pbrInfo.NdotL *
7514
+ lightColor *
7515
+ sheenColor *
7516
+ (0.25 + 0.75 * sheenFresnel) *
7517
+ sheenVisibility *
7518
+ (1.0 - pbrInfo.metalness);
7519
+ }
7520
+
7521
+ fn calculateAnisotropyBoost(
7522
+ pbrInfo: PBRInfo,
7523
+ anisotropyTangent: vec3f,
7524
+ anisotropyStrength: f32
7525
+ ) -> f32 {
7526
+ if (anisotropyStrength <= 0.0) {
7527
+ return 1.0;
7528
+ }
7529
+
7530
+ let anisotropyBitangent = normalize(cross(pbrInfo.n, anisotropyTangent));
7531
+ let bitangentViewAlignment = abs(dot(pbrInfo.v, anisotropyBitangent));
7532
+ return mix(1.0, 0.65 + 0.7 * bitangentViewAlignment, anisotropyStrength);
7533
+ }
7534
+
7535
+ fn calculateMaterialLightColor(
7536
+ pbrInfo: PBRInfo,
7537
+ lightColor: vec3f,
7538
+ clearcoatNormal: vec3f,
7539
+ clearcoatFactor: f32,
7540
+ clearcoatRoughness: f32,
7541
+ sheenColor: vec3f,
7542
+ sheenRoughness: f32,
7543
+ anisotropyTangent: vec3f,
7544
+ anisotropyStrength: f32
7545
+ ) -> vec3f {
7546
+ let anisotropyBoost = calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
7547
+ var color = calculateFinalColor(pbrInfo, lightColor) * anisotropyBoost;
7548
+ color += calculateClearcoatContribution(
7549
+ pbrInfo,
7550
+ lightColor,
7551
+ clearcoatNormal,
7552
+ clearcoatFactor,
7553
+ clearcoatRoughness
7554
+ );
7555
+ color += calculateSheenContribution(pbrInfo, lightColor, sheenColor, sheenRoughness);
7556
+ return color;
7557
+ }
7558
+
5774
7559
  fn PBRInfo_setAmbientLight(pbrInfo: ptr<function, PBRInfo>) {
5775
7560
  (*pbrInfo).NdotL = 1.0;
5776
7561
  (*pbrInfo).NdotH = 0.0;
@@ -5795,6 +7580,11 @@ fn PBRInfo_setPointLight(pbrInfo: ptr<function, PBRInfo>, pointLight: PointLight
5795
7580
  PBRInfo_setDirectionalLight(pbrInfo, light_direction);
5796
7581
  }
5797
7582
 
7583
+ fn PBRInfo_setSpotLight(pbrInfo: ptr<function, PBRInfo>, spotLight: SpotLight) {
7584
+ let light_direction = normalize(spotLight.position - fragmentInputs.pbr_vPosition);
7585
+ PBRInfo_setDirectionalLight(pbrInfo, light_direction);
7586
+ }
7587
+
5798
7588
  fn calculateFinalColor(pbrInfo: PBRInfo, lightColor: vec3<f32>) -> vec3<f32> {
5799
7589
  // Calculate the shading terms for the microfacet specular shading model
5800
7590
  let F = specularReflection(pbrInfo);
@@ -5824,6 +7614,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5824
7614
  #endif
5825
7615
 
5826
7616
  var color = vec3<f32>(0.0, 0.0, 0.0);
7617
+ var transmission = 0.0;
5827
7618
 
5828
7619
  if (pbrMaterial.unlit != 0u) {
5829
7620
  color = baseColor.rgb;
@@ -5846,14 +7637,308 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5846
7637
  #endif
5847
7638
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
5848
7639
  metallic = clamp(metallic, 0.0, 1.0);
7640
+ let tbn = getTBN();
7641
+ let n = getNormal(tbn); // normal at surface point
7642
+ let v = normalize(pbrProjection.camera - fragmentInputs.pbr_vPosition); // Vector from surface point to camera
7643
+ let NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
7644
+ var useExtendedPBR = false;
7645
+ #ifdef USE_MATERIAL_EXTENSIONS
7646
+ useExtendedPBR =
7647
+ pbrMaterial.specularColorMapEnabled != 0 ||
7648
+ pbrMaterial.specularIntensityMapEnabled != 0 ||
7649
+ abs(pbrMaterial.specularIntensityFactor - 1.0) > 0.0001 ||
7650
+ maxComponent(abs(pbrMaterial.specularColorFactor - vec3f(1.0))) > 0.0001 ||
7651
+ abs(pbrMaterial.ior - 1.5) > 0.0001 ||
7652
+ pbrMaterial.transmissionMapEnabled != 0 ||
7653
+ pbrMaterial.transmissionFactor > 0.0001 ||
7654
+ pbrMaterial.clearcoatMapEnabled != 0 ||
7655
+ pbrMaterial.clearcoatRoughnessMapEnabled != 0 ||
7656
+ pbrMaterial.clearcoatFactor > 0.0001 ||
7657
+ pbrMaterial.clearcoatRoughnessFactor > 0.0001 ||
7658
+ pbrMaterial.sheenColorMapEnabled != 0 ||
7659
+ pbrMaterial.sheenRoughnessMapEnabled != 0 ||
7660
+ maxComponent(pbrMaterial.sheenColorFactor) > 0.0001 ||
7661
+ pbrMaterial.sheenRoughnessFactor > 0.0001 ||
7662
+ pbrMaterial.iridescenceMapEnabled != 0 ||
7663
+ pbrMaterial.iridescenceFactor > 0.0001 ||
7664
+ abs(pbrMaterial.iridescenceIor - 1.3) > 0.0001 ||
7665
+ abs(pbrMaterial.iridescenceThicknessRange.x - 100.0) > 0.0001 ||
7666
+ abs(pbrMaterial.iridescenceThicknessRange.y - 400.0) > 0.0001 ||
7667
+ pbrMaterial.anisotropyMapEnabled != 0 ||
7668
+ pbrMaterial.anisotropyStrength > 0.0001 ||
7669
+ abs(pbrMaterial.anisotropyRotation) > 0.0001 ||
7670
+ length(pbrMaterial.anisotropyDirection - vec2f(1.0, 0.0)) > 0.0001;
7671
+ #endif
7672
+
7673
+ if (!useExtendedPBR) {
7674
+ let alphaRoughness = perceptualRoughness * perceptualRoughness;
7675
+
7676
+ let f0 = vec3<f32>(0.04);
7677
+ var diffuseColor = baseColor.rgb * (vec3<f32>(1.0) - f0);
7678
+ diffuseColor *= 1.0 - metallic;
7679
+ let specularColor = mix(f0, baseColor.rgb, metallic);
7680
+
7681
+ let reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
7682
+ let reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
7683
+ let specularEnvironmentR0 = specularColor;
7684
+ let specularEnvironmentR90 = vec3<f32>(1.0, 1.0, 1.0) * reflectance90;
7685
+ let reflection = -normalize(reflect(v, n));
7686
+
7687
+ var pbrInfo = PBRInfo(
7688
+ 0.0, // NdotL
7689
+ NdotV,
7690
+ 0.0, // NdotH
7691
+ 0.0, // LdotH
7692
+ 0.0, // VdotH
7693
+ perceptualRoughness,
7694
+ metallic,
7695
+ specularEnvironmentR0,
7696
+ specularEnvironmentR90,
7697
+ alphaRoughness,
7698
+ diffuseColor,
7699
+ specularColor,
7700
+ n,
7701
+ v
7702
+ );
7703
+
7704
+ #ifdef USE_LIGHTS
7705
+ PBRInfo_setAmbientLight(&pbrInfo);
7706
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor);
7707
+
7708
+ for (var i = 0; i < lighting.directionalLightCount; i++) {
7709
+ if (i < lighting.directionalLightCount) {
7710
+ PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
7711
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
7712
+ }
7713
+ }
7714
+
7715
+ for (var i = 0; i < lighting.pointLightCount; i++) {
7716
+ if (i < lighting.pointLightCount) {
7717
+ PBRInfo_setPointLight(&pbrInfo, lighting_getPointLight(i));
7718
+ let attenuation = getPointLightAttenuation(
7719
+ lighting_getPointLight(i),
7720
+ distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
7721
+ );
7722
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
7723
+ }
7724
+ }
7725
+
7726
+ for (var i = 0; i < lighting.spotLightCount; i++) {
7727
+ if (i < lighting.spotLightCount) {
7728
+ PBRInfo_setSpotLight(&pbrInfo, lighting_getSpotLight(i));
7729
+ let attenuation = getSpotLightAttenuation(
7730
+ lighting_getSpotLight(i),
7731
+ fragmentInputs.pbr_vPosition
7732
+ );
7733
+ color += calculateFinalColor(pbrInfo, lighting_getSpotLight(i).color / attenuation);
7734
+ }
7735
+ }
7736
+ #endif
7737
+
7738
+ #ifdef USE_IBL
7739
+ if (pbrMaterial.IBLenabled != 0) {
7740
+ color += getIBLContribution(pbrInfo, n, reflection);
7741
+ }
7742
+ #endif
7743
+
7744
+ #ifdef HAS_OCCLUSIONMAP
7745
+ if (pbrMaterial.occlusionMapEnabled != 0) {
7746
+ let ao =
7747
+ textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, fragmentInputs.pbr_vUV).r;
7748
+ color = mix(color, color * ao, pbrMaterial.occlusionStrength);
7749
+ }
7750
+ #endif
7751
+
7752
+ var emissive = pbrMaterial.emissiveFactor;
7753
+ #ifdef HAS_EMISSIVEMAP
7754
+ if (pbrMaterial.emissiveMapEnabled != 0u) {
7755
+ emissive *= SRGBtoLINEAR(
7756
+ textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
7757
+ ).rgb;
7758
+ }
7759
+ #endif
7760
+ color += emissive * pbrMaterial.emissiveStrength;
7761
+
7762
+ #ifdef PBR_DEBUG
7763
+ color = mix(color, baseColor.rgb, pbrMaterial.scaleDiffBaseMR.y);
7764
+ color = mix(color, vec3<f32>(metallic), pbrMaterial.scaleDiffBaseMR.z);
7765
+ color = mix(color, vec3<f32>(perceptualRoughness), pbrMaterial.scaleDiffBaseMR.w);
7766
+ #endif
7767
+
7768
+ return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), baseColor.a);
7769
+ }
7770
+
7771
+ var specularIntensity = pbrMaterial.specularIntensityFactor;
7772
+ #ifdef HAS_SPECULARINTENSITYMAP
7773
+ if (pbrMaterial.specularIntensityMapEnabled != 0) {
7774
+ specularIntensity *= textureSample(
7775
+ pbr_specularIntensitySampler,
7776
+ pbr_specularIntensitySamplerSampler,
7777
+ fragmentInputs.pbr_vUV
7778
+ ).a;
7779
+ }
7780
+ #endif
7781
+
7782
+ var specularFactor = pbrMaterial.specularColorFactor;
7783
+ #ifdef HAS_SPECULARCOLORMAP
7784
+ if (pbrMaterial.specularColorMapEnabled != 0) {
7785
+ specularFactor *= SRGBtoLINEAR(
7786
+ textureSample(
7787
+ pbr_specularColorSampler,
7788
+ pbr_specularColorSamplerSampler,
7789
+ fragmentInputs.pbr_vUV
7790
+ )
7791
+ ).rgb;
7792
+ }
7793
+ #endif
7794
+
7795
+ transmission = pbrMaterial.transmissionFactor;
7796
+ #ifdef HAS_TRANSMISSIONMAP
7797
+ if (pbrMaterial.transmissionMapEnabled != 0) {
7798
+ transmission *= textureSample(
7799
+ pbr_transmissionSampler,
7800
+ pbr_transmissionSamplerSampler,
7801
+ fragmentInputs.pbr_vUV
7802
+ ).r;
7803
+ }
7804
+ #endif
7805
+ transmission = clamp(transmission * (1.0 - metallic), 0.0, 1.0);
7806
+ var thickness = max(pbrMaterial.thicknessFactor, 0.0);
7807
+ #ifdef HAS_THICKNESSMAP
7808
+ thickness *= textureSample(
7809
+ pbr_thicknessSampler,
7810
+ pbr_thicknessSamplerSampler,
7811
+ fragmentInputs.pbr_vUV
7812
+ ).g;
7813
+ #endif
7814
+
7815
+ var clearcoatFactor = pbrMaterial.clearcoatFactor;
7816
+ var clearcoatRoughness = pbrMaterial.clearcoatRoughnessFactor;
7817
+ #ifdef HAS_CLEARCOATMAP
7818
+ if (pbrMaterial.clearcoatMapEnabled != 0) {
7819
+ clearcoatFactor *= textureSample(
7820
+ pbr_clearcoatSampler,
7821
+ pbr_clearcoatSamplerSampler,
7822
+ fragmentInputs.pbr_vUV
7823
+ ).r;
7824
+ }
7825
+ #endif
7826
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
7827
+ if (pbrMaterial.clearcoatRoughnessMapEnabled != 0) {
7828
+ clearcoatRoughness *= textureSample(
7829
+ pbr_clearcoatRoughnessSampler,
7830
+ pbr_clearcoatRoughnessSamplerSampler,
7831
+ fragmentInputs.pbr_vUV
7832
+ ).g;
7833
+ }
7834
+ #endif
7835
+ clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
7836
+ clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
7837
+ let clearcoatNormal = getClearcoatNormal(tbn, n);
7838
+
7839
+ var sheenColor = pbrMaterial.sheenColorFactor;
7840
+ var sheenRoughness = pbrMaterial.sheenRoughnessFactor;
7841
+ #ifdef HAS_SHEENCOLORMAP
7842
+ if (pbrMaterial.sheenColorMapEnabled != 0) {
7843
+ sheenColor *= SRGBtoLINEAR(
7844
+ textureSample(
7845
+ pbr_sheenColorSampler,
7846
+ pbr_sheenColorSamplerSampler,
7847
+ fragmentInputs.pbr_vUV
7848
+ )
7849
+ ).rgb;
7850
+ }
7851
+ #endif
7852
+ #ifdef HAS_SHEENROUGHNESSMAP
7853
+ if (pbrMaterial.sheenRoughnessMapEnabled != 0) {
7854
+ sheenRoughness *= textureSample(
7855
+ pbr_sheenRoughnessSampler,
7856
+ pbr_sheenRoughnessSamplerSampler,
7857
+ fragmentInputs.pbr_vUV
7858
+ ).a;
7859
+ }
7860
+ #endif
7861
+ sheenRoughness = clamp(sheenRoughness, c_MinRoughness, 1.0);
7862
+
7863
+ var iridescence = pbrMaterial.iridescenceFactor;
7864
+ #ifdef HAS_IRIDESCENCEMAP
7865
+ if (pbrMaterial.iridescenceMapEnabled != 0) {
7866
+ iridescence *= textureSample(
7867
+ pbr_iridescenceSampler,
7868
+ pbr_iridescenceSamplerSampler,
7869
+ fragmentInputs.pbr_vUV
7870
+ ).r;
7871
+ }
7872
+ #endif
7873
+ iridescence = clamp(iridescence, 0.0, 1.0);
7874
+ var iridescenceThickness = mix(
7875
+ pbrMaterial.iridescenceThicknessRange.x,
7876
+ pbrMaterial.iridescenceThicknessRange.y,
7877
+ 0.5
7878
+ );
7879
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
7880
+ iridescenceThickness = mix(
7881
+ pbrMaterial.iridescenceThicknessRange.x,
7882
+ pbrMaterial.iridescenceThicknessRange.y,
7883
+ textureSample(
7884
+ pbr_iridescenceThicknessSampler,
7885
+ pbr_iridescenceThicknessSamplerSampler,
7886
+ fragmentInputs.pbr_vUV
7887
+ ).g
7888
+ );
7889
+ #endif
7890
+
7891
+ var anisotropyStrength = clamp(pbrMaterial.anisotropyStrength, 0.0, 1.0);
7892
+ var anisotropyDirection = normalizeDirection(pbrMaterial.anisotropyDirection);
7893
+ #ifdef HAS_ANISOTROPYMAP
7894
+ if (pbrMaterial.anisotropyMapEnabled != 0) {
7895
+ let anisotropySample = textureSample(
7896
+ pbr_anisotropySampler,
7897
+ pbr_anisotropySamplerSampler,
7898
+ fragmentInputs.pbr_vUV
7899
+ ).rgb;
7900
+ anisotropyStrength *= anisotropySample.b;
7901
+ let mappedDirection = anisotropySample.rg * 2.0 - 1.0;
7902
+ if (length(mappedDirection) > 0.0001) {
7903
+ anisotropyDirection = normalize(mappedDirection);
7904
+ }
7905
+ }
7906
+ #endif
7907
+ anisotropyDirection = rotateDirection(anisotropyDirection, pbrMaterial.anisotropyRotation);
7908
+ var anisotropyTangent =
7909
+ normalize(tbn[0] * anisotropyDirection.x + tbn[1] * anisotropyDirection.y);
7910
+ if (length(anisotropyTangent) < 0.0001) {
7911
+ anisotropyTangent = normalize(tbn[0]);
7912
+ }
7913
+ let anisotropyViewAlignment = abs(dot(v, anisotropyTangent));
7914
+ perceptualRoughness = mix(
7915
+ perceptualRoughness,
7916
+ clamp(perceptualRoughness * (1.0 - 0.6 * anisotropyViewAlignment), c_MinRoughness, 1.0),
7917
+ anisotropyStrength
7918
+ );
7919
+
5849
7920
  // Roughness is authored as perceptual roughness; as is convention,
5850
7921
  // convert to material roughness by squaring the perceptual roughness [2].
5851
7922
  let alphaRoughness = perceptualRoughness * perceptualRoughness;
5852
7923
 
5853
- let f0 = vec3<f32>(0.04);
5854
- var diffuseColor = baseColor.rgb * (vec3<f32>(1.0) - f0);
5855
- diffuseColor *= 1.0 - metallic;
5856
- let specularColor = mix(f0, baseColor.rgb, metallic);
7924
+ let dielectricF0 = getDielectricF0(pbrMaterial.ior);
7925
+ var dielectricSpecularF0 = min(
7926
+ vec3f(dielectricF0) * specularFactor * specularIntensity,
7927
+ vec3f(1.0)
7928
+ );
7929
+ let iridescenceTint = getIridescenceTint(iridescence, iridescenceThickness, NdotV);
7930
+ dielectricSpecularF0 = mix(
7931
+ dielectricSpecularF0,
7932
+ dielectricSpecularF0 * iridescenceTint,
7933
+ iridescence
7934
+ );
7935
+ var diffuseColor = baseColor.rgb * (vec3f(1.0) - dielectricSpecularF0);
7936
+ diffuseColor *= (1.0 - metallic) * (1.0 - transmission);
7937
+ var specularColor = mix(dielectricSpecularF0, baseColor.rgb, metallic);
7938
+
7939
+ let baseLayerEnergy = 1.0 - clearcoatFactor * 0.25;
7940
+ diffuseColor *= baseLayerEnergy;
7941
+ specularColor *= baseLayerEnergy;
5857
7942
 
5858
7943
  // Compute reflectance.
5859
7944
  let reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
@@ -5865,11 +7950,6 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5865
7950
  let reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
5866
7951
  let specularEnvironmentR0 = specularColor;
5867
7952
  let specularEnvironmentR90 = vec3<f32>(1.0, 1.0, 1.0) * reflectance90;
5868
-
5869
- let n = getNormal(); // normal at surface point
5870
- let v = normalize(pbrProjection.camera - fragmentInputs.pbr_vPosition); // Vector from surface point to camera
5871
-
5872
- let NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
5873
7953
  let reflection = -normalize(reflect(v, n));
5874
7954
 
5875
7955
  var pbrInfo = PBRInfo(
@@ -5892,13 +7972,33 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5892
7972
  #ifdef USE_LIGHTS
5893
7973
  // Apply ambient light
5894
7974
  PBRInfo_setAmbientLight(&pbrInfo);
5895
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
7975
+ color += calculateMaterialLightColor(
7976
+ pbrInfo,
7977
+ lighting.ambientColor,
7978
+ clearcoatNormal,
7979
+ clearcoatFactor,
7980
+ clearcoatRoughness,
7981
+ sheenColor,
7982
+ sheenRoughness,
7983
+ anisotropyTangent,
7984
+ anisotropyStrength
7985
+ );
5896
7986
 
5897
7987
  // Apply directional light
5898
7988
  for (var i = 0; i < lighting.directionalLightCount; i++) {
5899
7989
  if (i < lighting.directionalLightCount) {
5900
7990
  PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
5901
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
7991
+ color += calculateMaterialLightColor(
7992
+ pbrInfo,
7993
+ lighting_getDirectionalLight(i).color,
7994
+ clearcoatNormal,
7995
+ clearcoatFactor,
7996
+ clearcoatRoughness,
7997
+ sheenColor,
7998
+ sheenRoughness,
7999
+ anisotropyTangent,
8000
+ anisotropyStrength
8001
+ );
5902
8002
  }
5903
8003
  }
5904
8004
 
@@ -5910,7 +8010,35 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5910
8010
  lighting_getPointLight(i),
5911
8011
  distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
5912
8012
  );
5913
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
8013
+ color += calculateMaterialLightColor(
8014
+ pbrInfo,
8015
+ lighting_getPointLight(i).color / attenuation,
8016
+ clearcoatNormal,
8017
+ clearcoatFactor,
8018
+ clearcoatRoughness,
8019
+ sheenColor,
8020
+ sheenRoughness,
8021
+ anisotropyTangent,
8022
+ anisotropyStrength
8023
+ );
8024
+ }
8025
+ }
8026
+
8027
+ for (var i = 0; i < lighting.spotLightCount; i++) {
8028
+ if (i < lighting.spotLightCount) {
8029
+ PBRInfo_setSpotLight(&pbrInfo, lighting_getSpotLight(i));
8030
+ let attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), fragmentInputs.pbr_vPosition);
8031
+ color += calculateMaterialLightColor(
8032
+ pbrInfo,
8033
+ lighting_getSpotLight(i).color / attenuation,
8034
+ clearcoatNormal,
8035
+ clearcoatFactor,
8036
+ clearcoatRoughness,
8037
+ sheenColor,
8038
+ sheenRoughness,
8039
+ anisotropyTangent,
8040
+ anisotropyStrength
8041
+ );
5914
8042
  }
5915
8043
  }
5916
8044
  #endif
@@ -5918,7 +8046,16 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5918
8046
  // Calculate lighting contribution from image based lighting source (IBL)
5919
8047
  #ifdef USE_IBL
5920
8048
  if (pbrMaterial.IBLenabled != 0) {
5921
- color += getIBLContribution(pbrInfo, n, reflection);
8049
+ color += getIBLContribution(pbrInfo, n, reflection) *
8050
+ calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
8051
+ color += calculateClearcoatIBLContribution(
8052
+ pbrInfo,
8053
+ clearcoatNormal,
8054
+ -normalize(reflect(v, clearcoatNormal)),
8055
+ clearcoatFactor,
8056
+ clearcoatRoughness
8057
+ );
8058
+ color += sheenColor * pbrMaterial.scaleIBLAmbient.x * (1.0 - sheenRoughness) * 0.25;
5922
8059
  }
5923
8060
  #endif
5924
8061
 
@@ -5931,14 +8068,19 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5931
8068
  }
5932
8069
  #endif
5933
8070
 
8071
+ var emissive = pbrMaterial.emissiveFactor;
5934
8072
  #ifdef HAS_EMISSIVEMAP
5935
8073
  if (pbrMaterial.emissiveMapEnabled != 0u) {
5936
- let emissive = SRGBtoLINEAR(
8074
+ emissive *= SRGBtoLINEAR(
5937
8075
  textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
5938
- ).rgb * pbrMaterial.emissiveFactor;
5939
- color += emissive;
8076
+ ).rgb;
5940
8077
  }
5941
8078
  #endif
8079
+ color += emissive * pbrMaterial.emissiveStrength;
8080
+
8081
+ if (transmission > 0.0) {
8082
+ color = mix(color, color * getVolumeAttenuation(thickness), transmission);
8083
+ }
5942
8084
 
5943
8085
  // This section uses mix to override final color for reference app visualization
5944
8086
  // of various parameters in the lighting equation.
@@ -5957,7 +8099,8 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5957
8099
  #endif
5958
8100
  }
5959
8101
 
5960
- return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), baseColor.a);
8102
+ let alpha = clamp(baseColor.a * (1.0 - transmission), 0.0, 1.0);
8103
+ return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), alpha);
5961
8104
  }
5962
8105
  `
5963
8106
  );
@@ -5982,11 +8125,12 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5982
8125
  camera: vec3<f32>
5983
8126
  };
5984
8127
 
5985
- @binding(0) @group(0) var<uniform> pbrProjection: pbrProjectionUniforms;
8128
+ @group(0) @binding(auto) var<uniform> pbrProjection: pbrProjectionUniforms;
5986
8129
  `
5987
8130
  );
5988
8131
  var pbrProjection = {
5989
8132
  name: "pbrProjection",
8133
+ bindingLayout: [{ name: "pbrProjection", group: 0 }],
5990
8134
  source: wgslUniformBlock,
5991
8135
  vs: uniformBlock,
5992
8136
  fs: uniformBlock,
@@ -6004,8 +8148,75 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
6004
8148
  var pbrMaterial = {
6005
8149
  props: {},
6006
8150
  uniforms: {},
8151
+ defaultUniforms: {
8152
+ unlit: false,
8153
+ baseColorMapEnabled: false,
8154
+ baseColorFactor: [1, 1, 1, 1],
8155
+ normalMapEnabled: false,
8156
+ normalScale: 1,
8157
+ emissiveMapEnabled: false,
8158
+ emissiveFactor: [0, 0, 0],
8159
+ metallicRoughnessValues: [1, 1],
8160
+ metallicRoughnessMapEnabled: false,
8161
+ occlusionMapEnabled: false,
8162
+ occlusionStrength: 1,
8163
+ alphaCutoffEnabled: false,
8164
+ alphaCutoff: 0.5,
8165
+ IBLenabled: false,
8166
+ scaleIBLAmbient: [1, 1],
8167
+ scaleDiffBaseMR: [0, 0, 0, 0],
8168
+ scaleFGDSpec: [0, 0, 0, 0],
8169
+ specularColorFactor: [1, 1, 1],
8170
+ specularIntensityFactor: 1,
8171
+ specularColorMapEnabled: false,
8172
+ specularIntensityMapEnabled: false,
8173
+ ior: 1.5,
8174
+ transmissionFactor: 0,
8175
+ transmissionMapEnabled: false,
8176
+ thicknessFactor: 0,
8177
+ attenuationDistance: 1e9,
8178
+ attenuationColor: [1, 1, 1],
8179
+ clearcoatFactor: 0,
8180
+ clearcoatRoughnessFactor: 0,
8181
+ clearcoatMapEnabled: false,
8182
+ clearcoatRoughnessMapEnabled: false,
8183
+ sheenColorFactor: [0, 0, 0],
8184
+ sheenRoughnessFactor: 0,
8185
+ sheenColorMapEnabled: false,
8186
+ sheenRoughnessMapEnabled: false,
8187
+ iridescenceFactor: 0,
8188
+ iridescenceIor: 1.3,
8189
+ iridescenceThicknessRange: [100, 400],
8190
+ iridescenceMapEnabled: false,
8191
+ anisotropyStrength: 0,
8192
+ anisotropyRotation: 0,
8193
+ anisotropyDirection: [1, 0],
8194
+ anisotropyMapEnabled: false,
8195
+ emissiveStrength: 1
8196
+ },
6007
8197
  name: "pbrMaterial",
6008
- dependencies: [lighting, pbrProjection],
8198
+ firstBindingSlot: 0,
8199
+ bindingLayout: [
8200
+ { name: "pbrMaterial", group: 3 },
8201
+ { name: "pbr_baseColorSampler", group: 3 },
8202
+ { name: "pbr_normalSampler", group: 3 },
8203
+ { name: "pbr_emissiveSampler", group: 3 },
8204
+ { name: "pbr_metallicRoughnessSampler", group: 3 },
8205
+ { name: "pbr_occlusionSampler", group: 3 },
8206
+ { name: "pbr_specularColorSampler", group: 3 },
8207
+ { name: "pbr_specularIntensitySampler", group: 3 },
8208
+ { name: "pbr_transmissionSampler", group: 3 },
8209
+ { name: "pbr_thicknessSampler", group: 3 },
8210
+ { name: "pbr_clearcoatSampler", group: 3 },
8211
+ { name: "pbr_clearcoatRoughnessSampler", group: 3 },
8212
+ { name: "pbr_clearcoatNormalSampler", group: 3 },
8213
+ { name: "pbr_sheenColorSampler", group: 3 },
8214
+ { name: "pbr_sheenRoughnessSampler", group: 3 },
8215
+ { name: "pbr_iridescenceSampler", group: 3 },
8216
+ { name: "pbr_iridescenceThicknessSampler", group: 3 },
8217
+ { name: "pbr_anisotropySampler", group: 3 }
8218
+ ],
8219
+ dependencies: [lighting, ibl, pbrProjection],
6009
8220
  source: source3,
6010
8221
  vs: vs3,
6011
8222
  fs: fs4,
@@ -6019,10 +8230,16 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
6019
8230
  HAS_SPECULARCOLORMAP: false,
6020
8231
  HAS_SPECULARINTENSITYMAP: false,
6021
8232
  HAS_TRANSMISSIONMAP: false,
8233
+ HAS_THICKNESSMAP: false,
6022
8234
  HAS_CLEARCOATMAP: false,
8235
+ HAS_CLEARCOATROUGHNESSMAP: false,
8236
+ HAS_CLEARCOATNORMALMAP: false,
6023
8237
  HAS_SHEENCOLORMAP: false,
8238
+ HAS_SHEENROUGHNESSMAP: false,
6024
8239
  HAS_IRIDESCENCEMAP: false,
8240
+ HAS_IRIDESCENCETHICKNESSMAP: false,
6025
8241
  HAS_ANISOTROPYMAP: false,
8242
+ USE_MATERIAL_EXTENSIONS: false,
6026
8243
  ALPHA_CUTOFF: false,
6027
8244
  USE_IBL: false,
6028
8245
  PBR_DEBUG: false
@@ -6048,14 +8265,6 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
6048
8265
  alphaCutoffEnabled: "i32",
6049
8266
  alphaCutoff: "f32",
6050
8267
  // #ifdef ALPHA_CUTOFF
6051
- // IBL
6052
- IBLenabled: "i32",
6053
- scaleIBLAmbient: "vec2<f32>",
6054
- // #ifdef USE_IBL
6055
- // debugging flags used for shader output of intermediate PBR variables
6056
- // #ifdef PBR_DEBUG
6057
- scaleDiffBaseMR: "vec4<f32>",
6058
- scaleFGDSpec: "vec4<f32>",
6059
8268
  specularColorFactor: "vec3<f32>",
6060
8269
  specularIntensityFactor: "f32",
6061
8270
  specularColorMapEnabled: "i32",
@@ -6069,9 +8278,11 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
6069
8278
  clearcoatFactor: "f32",
6070
8279
  clearcoatRoughnessFactor: "f32",
6071
8280
  clearcoatMapEnabled: "i32",
8281
+ clearcoatRoughnessMapEnabled: "i32",
6072
8282
  sheenColorFactor: "vec3<f32>",
6073
8283
  sheenRoughnessFactor: "f32",
6074
8284
  sheenColorMapEnabled: "i32",
8285
+ sheenRoughnessMapEnabled: "i32",
6075
8286
  iridescenceFactor: "f32",
6076
8287
  iridescenceIor: "f32",
6077
8288
  iridescenceThicknessRange: "vec2<f32>",
@@ -6080,7 +8291,15 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
6080
8291
  anisotropyRotation: "f32",
6081
8292
  anisotropyDirection: "vec2<f32>",
6082
8293
  anisotropyMapEnabled: "i32",
6083
- emissiveStrength: "f32"
8294
+ emissiveStrength: "f32",
8295
+ // IBL
8296
+ IBLenabled: "i32",
8297
+ scaleIBLAmbient: "vec2<f32>",
8298
+ // #ifdef USE_IBL
8299
+ // debugging flags used for shader output of intermediate PBR variables
8300
+ // #ifdef PBR_DEBUG
8301
+ scaleDiffBaseMR: "vec4<f32>",
8302
+ scaleFGDSpec: "vec4<f32>"
6084
8303
  }
6085
8304
  };
6086
8305
  return __toCommonJS(bundle_exports);