rayzee 11.9.0 → 11.11.0

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 (43) hide show
  1. package/dist/{ArchiveImporter-BJxszU_k.js → ArchiveImporter-D5XOC3tN.js} +27 -12
  2. package/dist/{BidirectionalIntegrator-DWg66vAd.js → BidirectionalIntegrator-DqG0BEIz.js} +295 -295
  3. package/dist/{BufferLayout-Bzgccg1o.js → BufferLayout-BROyMqO1.js} +3 -1
  4. package/dist/{NeuralRetouch-BD9E-Yht.js → NeuralRetouch-BkxZPOYq.js} +1 -1
  5. package/dist/{NeuralSuperRes-C8Mf3tJX.js → NeuralSuperRes-a8xDtvXM.js} +1 -1
  6. package/dist/{PathTracer-DeE9UJ1n.js → PathTracer-C_gliSWI.js} +3037 -3002
  7. package/dist/{PhysicalSky-cKy75-oR.js → PhysicalSky-DWK8pS0U.js} +45 -45
  8. package/dist/{RayzeeRenderer-CzLyBA3G.js → RayzeeRenderer-B4ZehMg_.js} +40 -40
  9. package/dist/{SceneMetadata-C17UfOEp.js → SceneMetadata-Bb3iwWhh.js} +1 -1
  10. package/dist/SolarLimb-ZYl-Pwl0.js +821 -0
  11. package/dist/addons/archives.es.js +1 -1
  12. package/dist/addons/bidirectional.es.js +1 -1
  13. package/dist/addons/physical-sky.es.js +1 -1
  14. package/dist/rayzee-core.es.js +6 -6
  15. package/dist/rayzee.es.js +95 -95
  16. package/dist/rayzee.umd.js +110 -110
  17. package/dist/{sceneCachePolicy-ywbGKHD3.js → sceneCachePolicy-BHNKvrHR.js} +12 -5
  18. package/package.json +1 -1
  19. package/src/EngineDefaults.js +3 -0
  20. package/src/Processor/BufferLayout.js +7 -6
  21. package/src/Processor/GeometryExtractor.js +8 -1
  22. package/src/Processor/MaterialPacking.js +5 -8
  23. package/src/Processor/PBRT/PBRTMaterials.js +2 -0
  24. package/src/Processor/PBRT/PBRTSceneBuilder.js +4 -3
  25. package/src/Processor/SceneProcessor.js +6 -2
  26. package/src/Processor/USD/USDScene.js +17 -1
  27. package/src/Processor/utils.js +1 -156
  28. package/src/Stages/PathTracer.js +9 -0
  29. package/src/Storage/SceneGraphCodec.js +2 -0
  30. package/src/TSL/Clearcoat.js +4 -4
  31. package/src/TSL/Common.js +36 -25
  32. package/src/TSL/EmissiveSampling.js +1 -1
  33. package/src/TSL/LightsSampling.js +1 -1
  34. package/src/TSL/MaterialEvaluation.js +34 -14
  35. package/src/TSL/MaterialProperties.js +37 -1
  36. package/src/TSL/MaterialTransmission.js +3 -9
  37. package/src/TSL/PathTracerCore.js +3 -2
  38. package/src/TSL/SceneResources.js +12 -1
  39. package/src/TSL/ShadeKernel.js +63 -14
  40. package/src/TSL/Struct.js +6 -0
  41. package/src/TSL/TextureSampling.js +29 -23
  42. package/src/managers/MaterialDataManager.js +21 -1
  43. package/dist/SolarLimb-DRYy8tln.js +0 -774
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./EngineEvents-CLckhYbL.js";
2
2
  import { a as t, i as n, r } from "./Logger-dDDGj9Av.js";
3
- import { f as i, v as a, y as o } from "./BufferLayout-Bzgccg1o.js";
3
+ import { f as i, v as a, y as o } from "./BufferLayout-BROyMqO1.js";
4
4
  import { n as s } from "./EngineIssues-CfJiBaNj.js";
5
5
  import { n as c, r as l } from "./shared-BGBcSxBQ.js";
6
6
  import { n as u } from "./AssetConfig-DSEntE65.js";
@@ -224,6 +224,7 @@ var E = de({
224
224
  ],
225
225
  clearcoat: 0,
226
226
  clearcoatRoughness: 0,
227
+ clearcoatNormalScale: [1, 1],
227
228
  iridescence: 0,
228
229
  iridescenceIOR: 1.3,
229
230
  iridescenceThicknessRange: [100, 400],
@@ -256,7 +257,8 @@ var E = de({
256
257
  1,
257
258
  1,
258
259
  1
259
- ]
260
+ ],
261
+ diffuseRoughness: 0
260
262
  });
261
263
  function de(e) {
262
264
  for (let t of Object.values(e)) typeof t == "object" && Object.freeze(t);
@@ -774,6 +776,7 @@ function W(e) {
774
776
  anisotropyMap: e.anisotropyMap,
775
777
  clearcoatMap: e.clearcoatMap,
776
778
  clearcoatRoughnessMap: e.clearcoatRoughnessMap,
779
+ clearcoatNormalMap: e.clearcoatNormalMap,
777
780
  transmissionMap: e.transmissionMap,
778
781
  sheenColorMap: e.sheenColorMap,
779
782
  sheenRoughnessMap: e.sheenRoughnessMap,
@@ -1216,6 +1219,7 @@ var Be = class {
1216
1219
  sheenColor: o("sheenColor"),
1217
1220
  clearcoat: o("clearcoat"),
1218
1221
  clearcoatRoughness: o("clearcoatRoughness"),
1222
+ clearcoatNormalScale: o("clearcoatNormalScale"),
1219
1223
  iridescence: o("iridescence"),
1220
1224
  iridescenceIOR: o("iridescenceIOR"),
1221
1225
  iridescenceThicknessRange: o("iridescenceThicknessRange"),
@@ -1230,6 +1234,7 @@ var Be = class {
1230
1234
  specularColor: o("specularColor"),
1231
1235
  diffuseTransmission: o("diffuseTransmission"),
1232
1236
  diffuseTransmissionColor: o("diffuseTransmissionColor"),
1237
+ diffuseRoughness: o("diffuseRoughness"),
1233
1238
  normalScale: o("normalScale"),
1234
1239
  bumpScale: o("bumpScale"),
1235
1240
  displacementScale: o("displacementScale"),
@@ -1249,6 +1254,7 @@ var Be = class {
1249
1254
  anisotropyMap: this.processTexture(i.anisotropyMap, this.anisotropyMaps),
1250
1255
  clearcoatMap: this.processTexture(i.clearcoatMap, this.clearcoatMaps),
1251
1256
  clearcoatRoughnessMap: this.processTexture(i.clearcoatRoughnessMap, this.clearcoatRoughnessMaps),
1257
+ clearcoatNormalMap: this.processTexture(i.clearcoatNormalMap, this.clearcoatNormalMaps),
1252
1258
  transmissionMap: this.processTexture(i.transmissionMap, this.transmissionMaps),
1253
1259
  thicknessMap: this.processTexture(e.thicknessMap, []),
1254
1260
  sheenColorMap: this.processTexture(i.sheenColorMap, this.sheenColorMaps),
@@ -1260,7 +1266,7 @@ var Be = class {
1260
1266
  iridescenceMap: this.processTexture(i.iridescenceMap, this.iridescenceMaps),
1261
1267
  iridescenceThicknessMap: this.processTexture(i.iridescenceThicknessMap, this.iridescenceThicknessMaps),
1262
1268
  mapMatrix: this.getTextureMatrix(i.map),
1263
- normalMapMatrices: this.getTextureMatrix(i.normalMap),
1269
+ normalMapMatrices: this.getTextureMatrix(i.normalMap ?? i.clearcoatNormalMap),
1264
1270
  bumpMapMatrices: this.getTextureMatrix(i.bumpMap),
1265
1271
  roughnessMapMatrices: this.getTextureMatrix(i.roughnessMap),
1266
1272
  metalnessMapMatrices: this.getTextureMatrix(i.metalnessMap),
@@ -1364,7 +1370,7 @@ var Be = class {
1364
1370
  if (e.children) for (let t of e.children) this._traverseMaterialsOnly(t);
1365
1371
  }
1366
1372
  resetArrays() {
1367
- this._droppedTextures = 0, this.triangles = null, this._streamedStore = !1, this.triangleData = null, this.triangleFloatChunks = null, this.triangleFloats = null, this.triangleCount = 0, this.currentTriangleIndex = 0, this.materials = [], this.materialTriangleCounts = [], this.meshes = [], this.meshTriangleRanges = [], this._geometryRanges = /* @__PURE__ */ new Map(), this.expandedTriangleCount = 0, this._allocatePlacements(0), this.maps = [], this.normalMaps = [], this.bumpMaps = [], this.metalnessMaps = [], this.emissiveMaps = [], this.roughnessMaps = [], this.displacementMaps = [], this.anisotropyMaps = [], this.transmissionMaps = [], this.clearcoatMaps = [], this.clearcoatRoughnessMaps = [], this.sheenColorMaps = [], this.sheenRoughnessMaps = [], this.iridescenceMaps = [], this.iridescenceThicknessMaps = [], this.specularIntensityMaps = [], this.specularColorMaps = [], this.diffuseTransmissionMaps = [], this.diffuseTransmissionColorMaps = [], this.directionalLights = [], this.cameras = [], this._materialUuidMap = /* @__PURE__ */ new Map(), this._textureIndexCache = /* @__PURE__ */ new WeakMap();
1373
+ this._droppedTextures = 0, this.triangles = null, this._streamedStore = !1, this.triangleData = null, this.triangleFloatChunks = null, this.triangleFloats = null, this.triangleCount = 0, this.currentTriangleIndex = 0, this.materials = [], this.materialTriangleCounts = [], this.meshes = [], this.meshTriangleRanges = [], this._geometryRanges = /* @__PURE__ */ new Map(), this.expandedTriangleCount = 0, this._allocatePlacements(0), this.maps = [], this.normalMaps = [], this.bumpMaps = [], this.metalnessMaps = [], this.emissiveMaps = [], this.roughnessMaps = [], this.displacementMaps = [], this.anisotropyMaps = [], this.transmissionMaps = [], this.clearcoatMaps = [], this.clearcoatRoughnessMaps = [], this.clearcoatNormalMaps = [], this.sheenColorMaps = [], this.sheenRoughnessMaps = [], this.iridescenceMaps = [], this.iridescenceThicknessMaps = [], this.specularIntensityMaps = [], this.specularColorMaps = [], this.diffuseTransmissionMaps = [], this.diffuseTransmissionColorMaps = [], this.directionalLights = [], this.cameras = [], this._materialUuidMap = /* @__PURE__ */ new Map(), this._textureIndexCache = /* @__PURE__ */ new WeakMap();
1368
1374
  }
1369
1375
  getExtractedData() {
1370
1376
  return {
@@ -1391,6 +1397,7 @@ var Be = class {
1391
1397
  transmissionMaps: this.transmissionMaps,
1392
1398
  clearcoatMaps: this.clearcoatMaps,
1393
1399
  clearcoatRoughnessMaps: this.clearcoatRoughnessMaps,
1400
+ clearcoatNormalMaps: this.clearcoatNormalMaps,
1394
1401
  sheenColorMaps: this.sheenColorMaps,
1395
1402
  sheenRoughnessMaps: this.sheenRoughnessMaps,
1396
1403
  iridescenceMaps: this.iridescenceMaps,
@@ -1597,7 +1604,7 @@ var vt = class {
1597
1604
  samples: this.samples.slice()
1598
1605
  };
1599
1606
  }
1600
- }, yt = "11.9.0", $ = 1e4, bt = 15e5;
1607
+ }, yt = "11.11.0", $ = 1e4, bt = 15e5;
1601
1608
  function xt(e, t, n = $) {
1602
1609
  return e >= n && t / bt < e / 3;
1603
1610
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rayzee",
3
- "version": "11.9.0",
3
+ "version": "11.11.0",
4
4
  "type": "module",
5
5
  "description": "Real-time WebGPU path tracing engine built on Three.js",
6
6
  "main": "dist/rayzee.umd.js",
@@ -111,6 +111,7 @@ export const MATERIAL_DEFAULTS = deepFreeze( {
111
111
  specularColor: [ 1, 1, 1 ],
112
112
  clearcoat: 0,
113
113
  clearcoatRoughness: 0,
114
+ clearcoatNormalScale: [ 1, 1 ],
114
115
  iridescence: 0,
115
116
  iridescenceIOR: 1.3,
116
117
  iridescenceThicknessRange: [ 100, 400 ],
@@ -133,6 +134,8 @@ export const MATERIAL_DEFAULTS = deepFreeze( {
133
134
  // KHR_materials_diffuse_transmission: the share of the diffuse lobe passed through to the other side.
134
135
  diffuseTransmission: 0,
135
136
  diffuseTransmissionColor: [ 1, 1, 1 ],
137
+ // The diffuse lobe's roughness (OpenPBR base_diffuse_roughness): 0 is Lambert, above it EON.
138
+ diffuseRoughness: 0,
136
139
  } );
137
140
 
138
141
  function deepFreeze( object ) {
@@ -141,8 +141,8 @@ export const MATERIAL_DATA_LAYOUT = {
141
141
  ALBEDO_MAP_INDEX: 24, NORMAL_MAP_INDEX: 25, ROUGHNESS_MAP_INDEX: 26, METALNESS_MAP_INDEX: 27,
142
142
  // Slot 7: map indices (emissive, bump) + clearcoat [maps]
143
143
  EMISSIVE_MAP_INDEX: 28, BUMP_MAP_INDEX: 29, CLEARCOAT: 30, CLEARCOAT_ROUGHNESS: 31,
144
- // Slot 8: dispersion + visible + sheen + sheenRoughness [extended BxDF]
145
- DISPERSION: 32, VISIBLE: 33, SHEEN: 34, SHEEN_ROUGHNESS: 35,
144
+ // Slot 8: dispersion + diffuseRoughness + sheen + sheenRoughness [extended BxDF]
145
+ DISPERSION: 32, DIFFUSE_ROUGHNESS: 33, SHEEN: 34, SHEEN_ROUGHNESS: 35,
146
146
  // Slot 9: sheenColor.rgb + diffuseTransmission [extended BxDF]
147
147
  SHEEN_COLOR: 36, DIFFUSE_TRANSMISSION: 39,
148
148
  // Slot 10: specularIntensity + specularColor.rgb [extended BxDF]
@@ -174,9 +174,10 @@ export const MATERIAL_DATA_LAYOUT = {
174
174
  SHEEN_ROUGHNESS_MAP_INDEX: 124, IRIDESCENCE_MAP_INDEX: 125, IRIDESCENCE_THICKNESS_MAP_INDEX: 126, SPECULAR_INTENSITY_MAP_INDEX: 127,
175
175
  // Slot 32: extension-texture map index C (specularColor) + diffuseTransmissionColor.rgb
176
176
  SPECULAR_COLOR_MAP_INDEX: 128, DIFFUSE_TRANSMISSION_COLOR: 129,
177
- // Slot 33: extension-texture map indices D (diffuseTransmission, diffuseTransmissionColor) + 2 reserved;
178
- // read only for a material with diffuse transmission
177
+ // Slot 33: extension-texture map indices D (diffuseTransmission, diffuseTransmissionColor) + the clear coat's normal
178
+ // map index and scale; read only for a material with diffuse transmission or a coat
179
179
  DIFFUSE_TRANSMISSION_MAP_INDEX: 132, DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX: 133,
180
+ CLEARCOAT_NORMAL_MAP_INDEX: 134, CLEARCOAT_NORMAL_SCALE: 135,
180
181
 
181
182
  // ── Vec4 slot indices (GPU/TSL side) ─────────────────────────────
182
183
  // Used with getDatafromStorageBuffer( buf, matIdx, int(slot), int(SLOTS_PER_MATERIAL) )
@@ -189,7 +190,7 @@ export const MATERIAL_DATA_LAYOUT = {
189
190
  EMISSIVE_ROUGHNESS: 5, // [BxDF] emissive.rgb, roughness
190
191
  MAP_INDICES_A: 6, // [maps] albedo, normal, roughness, metalness
191
192
  MAP_INDICES_B: 7, // [maps] emissive, bump, clearcoat, clearcoatRoughness
192
- DISPERSION_SHEEN: 8, // [extended] dispersion, visible, sheen, sheenRoughness
193
+ DISPERSION_SHEEN: 8, // [extended] dispersion, diffuseRoughness, sheen, sheenRoughness
193
194
  SHEEN_COLOR: 9, // [extended] sheenColor, diffuseTransmission
194
195
  SPECULAR: 10, // [extended] specularIntensity, specularColor
195
196
  IRIDESCENCE: 11, // [extended] iridescence, iridescenceIOR, iridescenceThicknessRange
@@ -207,7 +208,7 @@ export const MATERIAL_DATA_LAYOUT = {
207
208
  EXT_MAP_INDICES_A: 30, // transmission, clearcoat, clearcoatRoughness, sheenColor map indices
208
209
  EXT_MAP_INDICES_B: 31, // sheenRoughness, iridescence, iridescenceThickness, specularIntensity map indices
209
210
  EXT_MAP_INDICES_C: 32, // specularColor map index, diffuseTransmissionColor
210
- EXT_MAP_INDICES_D: 33, // diffuseTransmission, diffuseTransmissionColor map indices + 2 reserved
211
+ EXT_MAP_INDICES_D: 33, // diffuseTransmission, diffuseTransmissionColor, clearcoatNormal map indices, clearcoatNormalScale
211
212
  },
212
213
 
213
214
  };
@@ -53,6 +53,7 @@ export function resolveMaterialTextures( material ) {
53
53
  anisotropyMap: material.anisotropyMap,
54
54
  clearcoatMap: material.clearcoatMap,
55
55
  clearcoatRoughnessMap: material.clearcoatRoughnessMap,
56
+ clearcoatNormalMap: material.clearcoatNormalMap,
56
57
  transmissionMap: material.transmissionMap,
57
58
  sheenColorMap: material.sheenColorMap,
58
59
  sheenRoughnessMap: material.sheenRoughnessMap,
@@ -1210,6 +1211,7 @@ export class GeometryExtractor {
1210
1211
  sheenColor: pick( 'sheenColor' ),
1211
1212
  clearcoat: pick( 'clearcoat' ),
1212
1213
  clearcoatRoughness: pick( 'clearcoatRoughness' ),
1214
+ clearcoatNormalScale: pick( 'clearcoatNormalScale' ),
1213
1215
  iridescence: pick( 'iridescence' ),
1214
1216
  iridescenceIOR: pick( 'iridescenceIOR' ),
1215
1217
  iridescenceThicknessRange: pick( 'iridescenceThicknessRange' ),
@@ -1228,6 +1230,7 @@ export class GeometryExtractor {
1228
1230
 
1229
1231
  diffuseTransmission: pick( 'diffuseTransmission' ),
1230
1232
  diffuseTransmissionColor: pick( 'diffuseTransmissionColor' ),
1233
+ diffuseRoughness: pick( 'diffuseRoughness' ),
1231
1234
 
1232
1235
  normalScale: pick( 'normalScale' ),
1233
1236
  bumpScale: pick( 'bumpScale' ),
@@ -1255,6 +1258,7 @@ export class GeometryExtractor {
1255
1258
  // render effect yet (see gap-plan Phase 4.4).
1256
1259
  clearcoatMap: this.processTexture( textures.clearcoatMap, this.clearcoatMaps ),
1257
1260
  clearcoatRoughnessMap: this.processTexture( textures.clearcoatRoughnessMap, this.clearcoatRoughnessMaps ),
1261
+ clearcoatNormalMap: this.processTexture( textures.clearcoatNormalMap, this.clearcoatNormalMaps ),
1258
1262
  transmissionMap: this.processTexture( textures.transmissionMap, this.transmissionMaps ),
1259
1263
  thicknessMap: this.processTexture( material.thicknessMap, [] ),
1260
1264
  sheenColorMap: this.processTexture( textures.sheenColorMap, this.sheenColorMaps ),
@@ -1267,7 +1271,8 @@ export class GeometryExtractor {
1267
1271
  iridescenceThicknessMap: this.processTexture( textures.iridescenceThicknessMap, this.iridescenceThicknessMaps ),
1268
1272
 
1269
1273
  mapMatrix: this.getTextureMatrix( textures.map ),
1270
- normalMapMatrices: this.getTextureMatrix( textures.normalMap ),
1274
+ // The coat's normal map shares the base one's transform, or brings its own when there is none.
1275
+ normalMapMatrices: this.getTextureMatrix( textures.normalMap ?? textures.clearcoatNormalMap ),
1271
1276
  bumpMapMatrices: this.getTextureMatrix( textures.bumpMap ),
1272
1277
  roughnessMapMatrices: this.getTextureMatrix( textures.roughnessMap ),
1273
1278
  metalnessMapMatrices: this.getTextureMatrix( textures.metalnessMap ),
@@ -1655,6 +1660,7 @@ export class GeometryExtractor {
1655
1660
  this.transmissionMaps = [];
1656
1661
  this.clearcoatMaps = [];
1657
1662
  this.clearcoatRoughnessMaps = [];
1663
+ this.clearcoatNormalMaps = [];
1658
1664
  this.sheenColorMaps = [];
1659
1665
  this.sheenRoughnessMaps = [];
1660
1666
  this.iridescenceMaps = [];
@@ -1699,6 +1705,7 @@ export class GeometryExtractor {
1699
1705
  transmissionMaps: this.transmissionMaps,
1700
1706
  clearcoatMaps: this.clearcoatMaps,
1701
1707
  clearcoatRoughnessMaps: this.clearcoatRoughnessMaps,
1708
+ clearcoatNormalMaps: this.clearcoatNormalMaps,
1702
1709
  sheenColorMaps: this.sheenColorMaps,
1703
1710
  sheenRoughnessMaps: this.sheenRoughnessMaps,
1704
1711
  iridescenceMaps: this.iridescenceMaps,
@@ -14,11 +14,6 @@ const TRANSFORMS = [
14
14
  [ 'displacementMapMatrices', M.DISPLACEMENT_TRANSFORM ],
15
15
  ];
16
16
 
17
- /** Floats no field writes; the packer gives them fixed values. */
18
- export const MATERIAL_RESERVED_FLOATS = Object.freeze( [
19
- M.VISIBLE, M.DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX + 1, M.DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX + 2,
20
- ] );
21
-
22
17
  /**
23
18
  * Weights and roughnesses, [0, 1] in glTF and three.js. Past 1 an energy split like
24
19
  * 1 − clearcoat·E goes negative: a glTF chrome with clearcoatFactor 4 added negative light.
@@ -26,6 +21,7 @@ export const MATERIAL_RESERVED_FLOATS = Object.freeze( [
26
21
  export const UNIT_RANGE_PROPERTIES = Object.freeze( new Set( [
27
22
  'metalness', 'roughness', 'transmission', 'opacity', 'clearcoat', 'clearcoatRoughness',
28
23
  'sheen', 'sheenRoughness', 'specularIntensity', 'iridescence', 'subsurface', 'anisotropy', 'diffuseTransmission',
24
+ 'diffuseRoughness',
29
25
  ] ) );
30
26
 
31
27
  export const clampUnit = ( value ) => Math.min( Math.max( value, 0 ), 1 );
@@ -112,6 +108,7 @@ export function packMaterial( data, base, mat ) {
112
108
  u( M.CLEARCOAT_ROUGHNESS, mat.clearcoatRoughness, D.clearcoatRoughness );
113
109
 
114
110
  s( M.DISPERSION, mat.dispersion, D.dispersion );
111
+ u( M.DIFFUSE_ROUGHNESS, mat.diffuseRoughness, D.diffuseRoughness );
115
112
  u( M.SHEEN, mat.sheen, D.sheen );
116
113
  u( M.SHEEN_ROUGHNESS, mat.sheenRoughness, D.sheenRoughness );
117
114
 
@@ -163,8 +160,8 @@ export function packMaterial( data, base, mat ) {
163
160
  data[ base + M.DIFFUSE_TRANSMISSION_MAP_INDEX ] = mapIndex( mat.diffuseTransmissionMap );
164
161
  data[ base + M.DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX ] = mapIndex( mat.diffuseTransmissionColorMap );
165
162
 
166
- data[ base + M.VISIBLE ] = 1;
167
- data[ base + M.DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX + 1 ] = 0;
168
- data[ base + M.DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX + 2 ] = 0;
163
+ data[ base + M.CLEARCOAT_NORMAL_MAP_INDEX ] = mapIndex( mat.clearcoatNormalMap );
164
+ const coatScale = mat.clearcoatNormalScale;
165
+ s( M.CLEARCOAT_NORMAL_SCALE, typeof coatScale === 'number' ? coatScale : coatScale?.x ?? coatScale?.[ 0 ], D.clearcoatNormalScale[ 0 ] );
169
166
 
170
167
  }
@@ -594,6 +594,8 @@ export async function buildMaterial( def, ctx ) {
594
594
 
595
595
  const normalMap = pString( params, 'normalmap', null );
596
596
  if ( normalMap && ctx.resolveNormalMap ) mat.normalMap = await ctx.resolveNormalMap( normalMap );
597
+ // pbrt shades a coat in the same normal-mapped frame as what it covers.
598
+ if ( mat.clearcoat > 0 && mat.normalMap ) mat.clearcoatNormalMap = mat.normalMap;
597
599
 
598
600
  mat.roughness = Math.max( 0, Math.min( 1, mat.roughness ) );
599
601
  return mat;
@@ -111,7 +111,7 @@ const DEFAULT_CURVE_SIDES = { flat: 1, ribbon: 1, cylinder: 2 };
111
111
  const DEFAULT_CURVE_TOLERANCE = 0.05;
112
112
 
113
113
  /** Bumped whenever the same scene files build a different graph, so a stored graph is not reused. */
114
- export const PBRT_BUILD_REVISION = 13;
114
+ export const PBRT_BUILD_REVISION = 14;
115
115
 
116
116
  function samePlacements( a, b ) {
117
117
 
@@ -249,7 +249,7 @@ function setMapping( texture, { su, sv, du, dv, uv = null } ) {
249
249
  }
250
250
 
251
251
  // Material maps in the order that picks a surface's one uv set: its base colour's, else the first it has.
252
- const UV_MAPS = [ 'map', 'roughnessMap', 'clearcoatRoughnessMap', 'normalMap' ];
252
+ const UV_MAPS = [ 'map', 'roughnessMap', 'clearcoatRoughnessMap', 'normalMap', 'clearcoatNormalMap' ];
253
253
  const MAP_SCALARS = { roughnessMap: 'roughness', clearcoatRoughnessMap: 'clearcoatRoughness' };
254
254
 
255
255
  /** The position mapping a material's textures are read in, or null for the mesh's own uv. */
@@ -2064,7 +2064,8 @@ export class PBRTSceneBuilder {
2064
2064
  `${material.clearcoat}|${material.clearcoatRoughness}|${material.clearcoatRoughnessMap?.uuid ?? '-'}|${material.opacity}|${material.side}|` +
2065
2065
  `${material.normalMap ? material.normalMap.uuid : '-'}|${material.attenuationDistance}|${material.attenuationColor.toArray()}|` +
2066
2066
  `${material.specularIntensity}|${material.diffuseTransmission ?? 0}|${material.diffuseTransmissionColor?.toArray() ?? '-'}|` +
2067
- `${material.subsurface ?? 0}|${material.subsurfaceColor?.toArray() ?? '-'}|${material.subsurfaceRadius ?? '-'}|${material.subsurfaceAnisotropy ?? 0}`;
2067
+ `${material.subsurface ?? 0}|${material.subsurfaceColor?.toArray() ?? '-'}|${material.subsurfaceRadius ?? '-'}|${material.subsurfaceAnisotropy ?? 0}|` +
2068
+ `${material.diffuseRoughness ?? 0}`;
2068
2069
 
2069
2070
  const existing = this._materialBySignature.get( key );
2070
2071
  if ( existing ) return existing;
@@ -66,7 +66,7 @@ const log = createLogger( 'scene' );
66
66
  // The per-type texture lists an extraction hands over (GeometryExtractor.getExtractedData).
67
67
  const TEXTURE_LISTS = Object.freeze( [
68
68
  'maps', 'normalMaps', 'bumpMaps', 'roughnessMaps', 'metalnessMaps', 'emissiveMaps', 'displacementMaps',
69
- 'anisotropyMaps', 'transmissionMaps', 'clearcoatMaps', 'clearcoatRoughnessMaps', 'sheenColorMaps',
69
+ 'anisotropyMaps', 'transmissionMaps', 'clearcoatMaps', 'clearcoatRoughnessMaps', 'clearcoatNormalMaps', 'sheenColorMaps',
70
70
  'sheenRoughnessMaps', 'iridescenceMaps', 'iridescenceThicknessMaps', 'specularIntensityMaps', 'specularColorMaps',
71
71
  'diffuseTransmissionMaps', 'diffuseTransmissionColorMaps',
72
72
  ] );
@@ -186,6 +186,7 @@ export class SceneProcessor {
186
186
  this.transmissionMaps = [];
187
187
  this.clearcoatMaps = [];
188
188
  this.clearcoatRoughnessMaps = [];
189
+ this.clearcoatNormalMaps = [];
189
190
  this.sheenColorMaps = [];
190
191
  this.sheenRoughnessMaps = [];
191
192
  this.iridescenceMaps = [];
@@ -2107,7 +2108,7 @@ export class SceneProcessor {
2107
2108
  const srgbTypes = [ this.maps, this.emissiveMaps, this.sheenColorMaps, this.specularColorMaps, this.diffuseTransmissionColorMaps ];
2108
2109
  const linearTypes = [
2109
2110
  this.normalMaps, this.bumpMaps, this.roughnessMaps, this.metalnessMaps, this.displacementMaps,
2110
- this.anisotropyMaps, this.transmissionMaps, this.clearcoatMaps, this.clearcoatRoughnessMaps,
2111
+ this.anisotropyMaps, this.transmissionMaps, this.clearcoatMaps, this.clearcoatRoughnessMaps, this.clearcoatNormalMaps,
2111
2112
  this.sheenRoughnessMaps, this.iridescenceMaps, this.iridescenceThicknessMaps, this.specularIntensityMaps,
2112
2113
  this.diffuseTransmissionMaps,
2113
2114
  ];
@@ -2194,6 +2195,7 @@ export class SceneProcessor {
2194
2195
  transmission: remapType( this.transmissionMaps, linearLists, linearDedup, this._linearTexPacked, linearShapes ),
2195
2196
  clearcoat: remapType( this.clearcoatMaps, linearLists, linearDedup, this._linearTexPacked, linearShapes ),
2196
2197
  clearcoatRoughness: remapType( this.clearcoatRoughnessMaps, linearLists, linearDedup, this._linearTexPacked, linearShapes ),
2198
+ clearcoatNormal: remapType( this.clearcoatNormalMaps, linearLists, linearDedup, this._linearTexPacked, linearShapes ),
2197
2199
  sheenColor: remapType( this.sheenColorMaps, srgbLists, srgbDedup, this._srgbTexPacked, srgbShapes ),
2198
2200
  sheenRoughness: remapType( this.sheenRoughnessMaps, linearLists, linearDedup, this._linearTexPacked, linearShapes ),
2199
2201
  iridescence: remapType( this.iridescenceMaps, linearLists, linearDedup, this._linearTexPacked, linearShapes ),
@@ -2230,6 +2232,7 @@ export class SceneProcessor {
2230
2232
  mat.transmissionMap = fix( mat.transmissionMap, remap.transmission );
2231
2233
  mat.clearcoatMap = fix( mat.clearcoatMap, remap.clearcoat );
2232
2234
  mat.clearcoatRoughnessMap = fix( mat.clearcoatRoughnessMap, remap.clearcoatRoughness );
2235
+ mat.clearcoatNormalMap = fix( mat.clearcoatNormalMap, remap.clearcoatNormal );
2233
2236
  mat.sheenColorMap = fix( mat.sheenColorMap, remap.sheenColor );
2234
2237
  mat.sheenRoughnessMap = fix( mat.sheenRoughnessMap, remap.sheenRoughness );
2235
2238
  mat.iridescenceMap = fix( mat.iridescenceMap, remap.iridescence );
@@ -2331,6 +2334,7 @@ export class SceneProcessor {
2331
2334
  this.transmissionMaps = [];
2332
2335
  this.clearcoatMaps = [];
2333
2336
  this.clearcoatRoughnessMaps = [];
2337
+ this.clearcoatNormalMaps = [];
2334
2338
  this.sheenColorMaps = [];
2335
2339
  this.sheenRoughnessMaps = [];
2336
2340
  this.iridescenceMaps = [];
@@ -1910,6 +1910,14 @@ export class USDSceneReader {
1910
1910
 
1911
1911
  }
1912
1912
 
1913
+ /** A diffuse roughness input as a constant; one a texture drives is not read. */
1914
+ mtlxDiffuseRoughness( term ) {
1915
+
1916
+ if ( term?.texture ) this.warnOnce( 'mtlx:diffuseroughness', 'MaterialX diffuse roughness from a texture is not read' );
1917
+ return isNum( term?.value ) ? term.value : 0;
1918
+
1919
+ }
1920
+
1913
1921
  /** OpenPBR Surface 1.1, which Blender writes for its Principled BSDF. */
1914
1922
  async openPbrSurface( shader ) {
1915
1923
 
@@ -1934,6 +1942,7 @@ export class USDSceneReader {
1934
1942
  if ( Array.isArray( specularColor ) ) mat.specularColor.setRGB( specularColor[ 0 ], specularColor[ 1 ], specularColor[ 2 ] );
1935
1943
  mat.ior = await num( 'specular_ior', 1.5 );
1936
1944
  mat.anisotropy = await num( 'specular_roughness_anisotropy', 0 );
1945
+ mat.diffuseRoughness = this.mtlxDiffuseRoughness( await t( 'base_diffuse_roughness' ) );
1937
1946
 
1938
1947
  const transmission = await num( 'transmission_weight', 0 );
1939
1948
  if ( transmission > 0 ) {
@@ -2037,6 +2046,8 @@ export class USDSceneReader {
2037
2046
  mat.ior = await num( 'specular_IOR', 1.5 );
2038
2047
  mat.anisotropy = await num( 'specular_anisotropy', 0 );
2039
2048
  mat.anisotropyRotation = await num( 'specular_rotation', 0 ) * Math.PI * 2;
2049
+ // MaterialX renders this one without energy compensation; EON returns what that loses.
2050
+ mat.diffuseRoughness = this.mtlxDiffuseRoughness( await t( 'diffuse_roughness' ) );
2040
2051
 
2041
2052
  const transmission = await num( 'transmission', 0 );
2042
2053
  if ( transmission > 0 ) {
@@ -2220,6 +2231,9 @@ export class USDSceneReader {
2220
2231
 
2221
2232
  mat.metalness = sum( [ 'conductor' ] ) / base;
2222
2233
  mat.transmission = sum( [ 'transmission' ] ) / base;
2234
+ const diffuse = lobes.filter( l => l.kind === 'diffuse' && l.w > 0 );
2235
+ const diffuseWeight = sum( [ 'diffuse' ] );
2236
+ if ( diffuseWeight > 0 ) mat.diffuseRoughness = diffuse.reduce( ( s, l ) => s + l.roughness * l.w, 0 ) / diffuseWeight;
2223
2237
 
2224
2238
  } else {
2225
2239
 
@@ -2330,8 +2344,10 @@ export class USDSceneReader {
2330
2344
  switch ( mtlxNode( id ) ) {
2331
2345
 
2332
2346
  case 'oren_nayar_diffuse_bsdf':
2347
+ lobes.push( { kind: 'diffuse', w: weight, color: await t( 'color' ), normal: await t( 'normal' ), roughness: this.mtlxDiffuseRoughness( await t( 'roughness' ) ) } );
2348
+ return;
2333
2349
  case 'burley_diffuse_bsdf':
2334
- lobes.push( { kind: 'diffuse', w: weight, color: await t( 'color' ), normal: await t( 'normal' ) } );
2350
+ lobes.push( { kind: 'diffuse', w: weight, color: await t( 'color' ), normal: await t( 'normal' ), roughness: 0 } );
2335
2351
  return;
2336
2352
  case 'conductor_bsdf': {
2337
2353
 
@@ -1,4 +1,4 @@
1
- import { SphereGeometry, Mesh, MeshPhysicalMaterial, Group, Object3D, Color, Vector3, ShaderMaterial, Vector2, Matrix4, GLSL3 } from 'three';
1
+ import { SphereGeometry, Mesh, MeshPhysicalMaterial, Group, Object3D, ShaderMaterial, Vector2, Matrix4, GLSL3 } from 'three';
2
2
  import { EngineEvents } from '../EngineEvents.js';
3
3
 
4
4
  let _statusCallback = null;
@@ -171,8 +171,6 @@ export function disposeEngineOwnedResources( object ) {
171
171
 
172
172
  }
173
173
 
174
-
175
-
176
174
  /**
177
175
  Metalness (increases left to right):
178
176
  0.00 0.25 0.50 0.75 1.00
@@ -534,156 +532,3 @@ export function createLRUCache( maxSize ) {
534
532
  };
535
533
 
536
534
  }
537
-
538
- export function getDisneyUniforms( material ) {
539
-
540
- // Default Disney BRDF uniform values
541
- const defaults = {
542
- baseColor: new Color( 0xffffff ),
543
- subsurface: 0.0,
544
- metallic: 0.0,
545
- specular: 0.5,
546
- specularTint: 0.0,
547
- roughness: 0.5,
548
- anisotropic: 0.0,
549
- sheen: 0.0,
550
- sheenTint: 0.5,
551
- clearcoat: 0.0,
552
- clearcoatGloss: 0.0,
553
- ior: 1.5,
554
- transmission: 0.0,
555
- opacity: 1.0,
556
- emissive: new Color( 0x000000 ),
557
- emissiveIntensity: 1.0
558
- };
559
-
560
- // Initialize uniforms object with default values wrapped in uniform objects
561
- const uniforms = Object.entries( defaults ).reduce( ( acc, [ key, value ] ) => {
562
-
563
- acc[ key ] = { value: value };
564
- return acc;
565
-
566
- }, {} );
567
-
568
- // If no material is provided, return default uniforms
569
- if ( ! material ) return uniforms;
570
-
571
- // Map material properties to Disney uniforms
572
- if ( material.isMeshStandardMaterial ) {
573
-
574
- uniforms.baseColor.value = material.color;
575
- uniforms.roughness.value = material.roughness;
576
- uniforms.metallic.value = material.metallic;
577
- uniforms.emissive.value = material.emissive;
578
- uniforms.emissiveIntensity.value = material.emissiveIntensity;
579
- uniforms.opacity.value = material.opacity;
580
-
581
- // Extract maps if they exist
582
- if ( material.map ) {
583
-
584
- uniforms.baseColorMap = { value: material.map };
585
-
586
- }
587
-
588
- if ( material.roughnessMap ) {
589
-
590
- uniforms.roughnessMap = { value: material.roughnessMap };
591
-
592
- }
593
-
594
- if ( material.metalnessMap ) {
595
-
596
- uniforms.metallicMap = { value: material.metalnessMap };
597
-
598
- }
599
-
600
- if ( material.normalMap ) {
601
-
602
- uniforms.normalMap = { value: material.normalMap };
603
- uniforms.normalScale = { value: material.normalScale };
604
-
605
- }
606
-
607
- if ( material.emissiveMap ) {
608
-
609
- uniforms.emissiveMap = { value: material.emissiveMap };
610
-
611
- }
612
-
613
- } else if ( material.isMeshPhysicalMaterial ) {
614
-
615
- // Include all MeshStandardMaterial properties
616
- uniforms.baseColor.value = material.color;
617
- uniforms.roughness.value = material.roughness;
618
- uniforms.metallic.value = material.metallic;
619
- uniforms.emissive.value = material.emissive;
620
- uniforms.emissiveIntensity.value = material.emissiveIntensity;
621
- uniforms.opacity.value = material.opacity;
622
-
623
- // Add physical material specific properties
624
- uniforms.clearcoat.value = material.clearcoat;
625
- uniforms.clearcoatGloss.value = 1 - material.clearcoatRoughness;
626
- uniforms.ior.value = material.ior;
627
- uniforms.transmission.value = material.transmission;
628
-
629
- // Extract maps
630
- if ( material.clearcoatMap ) {
631
-
632
- uniforms.clearcoatMap = { value: material.clearcoatMap };
633
-
634
- }
635
-
636
- if ( material.clearcoatRoughnessMap ) {
637
-
638
- uniforms.clearcoatGlossMap = { value: material.clearcoatRoughnessMap };
639
-
640
- }
641
-
642
- if ( material.clearcoatNormalMap ) {
643
-
644
- uniforms.clearcoatNormalMap = { value: material.clearcoatNormalMap };
645
-
646
- }
647
-
648
- if ( material.transmissionMap ) {
649
-
650
- uniforms.transmissionMap = { value: material.transmissionMap };
651
-
652
- }
653
-
654
- } else if ( material.isMeshBasicMaterial ||
655
- material.isMeshLambertMaterial ||
656
- material.isMeshPhongMaterial ) {
657
-
658
- // Basic conversion for simpler materials
659
- uniforms.baseColor.value = material.color;
660
- uniforms.opacity.value = material.opacity;
661
- uniforms.emissive.value = material.emissive || new Color( 0x000000 );
662
-
663
- // Estimate roughness and metallic from shininess if available
664
- if ( 'shininess' in material ) {
665
-
666
- uniforms.roughness.value = 1 - Math.min( 1, material.shininess / 100 );
667
- uniforms.specular.value = material.specular ?
668
- ( material.specular.r + material.specular.g + material.specular.b ) / 3 :
669
- 0.5;
670
-
671
- }
672
-
673
- // Extract maps
674
- if ( material.map ) {
675
-
676
- uniforms.baseColorMap = { value: material.map };
677
-
678
- }
679
-
680
- }
681
-
682
- // Add common Three.js uniform values needed for shading
683
- uniforms.cameraPosition = { value: new Vector3() };
684
- uniforms.time = { value: 0 };
685
-
686
- return uniforms;
687
-
688
- }
689
-
@@ -428,6 +428,7 @@ export class PathTracer extends PathTracerStage {
428
428
  const needed = this._layersToCompile();
429
429
  if ( MATERIAL_LAYERS.some( ( layer ) => needed[ layer ] && ! this._compiledLayers?.[ layer ] ) ) this._outputsChanged = true;
430
430
  if ( ! this._compiledTransmissionMaps && this.materialData.hasTransmissionMaps() ) this._outputsChanged = true;
431
+ if ( ! this._compiledClearcoatNormals && this._clearcoatNormalsNeeded() ) this._outputsChanged = true;
431
432
 
432
433
  }
433
434
 
@@ -1460,6 +1461,13 @@ export class PathTracer extends PathTracerStage {
1460
1461
 
1461
1462
  }
1462
1463
 
1464
+ /** Whether a coat can lean apart from its base: never for an integrator of its own (its vertices keep one normal). */
1465
+ _clearcoatNormalsNeeded() {
1466
+
1467
+ return ! this._integrator && this.materialData.hasClearcoatNormals();
1468
+
1469
+ }
1470
+
1463
1471
  /** The material layers the scene uses. */
1464
1472
  _layersToCompile() {
1465
1473
 
@@ -1877,6 +1885,7 @@ export class PathTracer extends PathTracerStage {
1877
1885
  materialLayers: this._compiledLayers = this._layersToCompile(),
1878
1886
  lightRadius: this._compiledLightRadius = this._lightRadiusNeeded(),
1879
1887
  transmissionMaps: this._compiledTransmissionMaps = this.materialData.hasTransmissionMaps(),
1888
+ clearcoatNormals: this._compiledClearcoatNormals = this._clearcoatNormalsNeeded(),
1880
1889
  };
1881
1890
  const own = ( call ) => withSceneResources( call, resources );
1882
1891
  const freshEnvTex = _env.environmentTexture ? texture( _env.environmentTexture ) : texNodes.envTex;
@@ -171,6 +171,7 @@ function encodeMaterial( material, meta ) {
171
171
  if ( material.ior !== undefined ) json.exactIor = material.ior;
172
172
  // Engine properties three.js does not know, so toJSON drops them.
173
173
  if ( material.diffuseTransmission !== undefined ) json.diffuseTransmission = material.diffuseTransmission;
174
+ if ( material.diffuseRoughness !== undefined ) json.diffuseRoughness = material.diffuseRoughness;
174
175
  for ( const key of ENGINE_TEXTURES ) if ( material[ key ]?.isTexture ) ( json.engineTextures ??= {} )[ key ] = material[ key ].uuid;
175
176
  return json;
176
177
 
@@ -460,6 +461,7 @@ export async function decodeSceneGraph( manifest, data, { loadTexture } ) {
460
461
 
461
462
  if ( json.exactIor !== undefined ) material.ior = json.exactIor;
462
463
  if ( json.diffuseTransmission !== undefined ) material.diffuseTransmission = json.diffuseTransmission;
464
+ if ( json.diffuseRoughness !== undefined ) material.diffuseRoughness = json.diffuseRoughness;
463
465
  for ( const [ key, uuid ] of Object.entries( json.engineTextures ?? {} ) ) if ( textures[ uuid ] ) material[ key ] = textures[ uuid ];
464
466
  materials[ json.uuid ] = material;
465
467
 
@@ -15,7 +15,7 @@ import { struct } from './patches.js';
15
15
  import { AnisoFrame, DotProducts,
16
16
  BRDFWeights,
17
17
  } from './Struct.js';
18
- import { MIN_CLEARCOAT_ROUGHNESS, computeDotProductsAniso, anisoTangentFrame, constructTBN } from './Common.js';
18
+ import { MIN_CLEARCOAT_ROUGHNESS, computeDotProductsAniso, anisoTangentFrame, constructTBN, coatNormal } from './Common.js';
19
19
  import { computeAnisoAlphas, calculateBRDFWeightsFromMaterial, calculateBSDFSamplingPDF, diffuseTransmissionPdf } from './MaterialProperties.js';
20
20
  import { ImportanceSampleCosine, sampleGGXVNDF, sampleGGXVNDFAniso } from './MaterialSampling.js';
21
21
  import { evaluateMaterialResponseFromDots, evaluateDiffuseTransmission } from './MaterialEvaluation.js';
@@ -34,7 +34,7 @@ export const ClearcoatResult = struct( {
34
34
  export const sampleClearcoat = Fn( ( [
35
35
  ray, hitInfo, material, randomSample, rngState,
36
36
  pixelCoord, resolution, frame, dimBase,
37
- ] ) => {
37
+ ], builder ) => {
38
38
 
39
39
  const N = hitInfo.normal;
40
40
  const V = ray.direction.negate();
@@ -62,7 +62,7 @@ export const sampleClearcoat = Fn( ( [
62
62
  If( rand.lessThan( clearcoatWeight ), () => {
63
63
 
64
64
  // Sample clearcoat layer (VNDF — see the base-specular note below)
65
- const ccTBN = constructTBN( { N } );
65
+ const ccTBN = constructTBN( { N: coatNormal( N, material, builder ) } );
66
66
  H.assign( ccTBN.mul( sampleGGXVNDF( { V: ccTBN.transpose().mul( V ), roughness: clearcoatRoughness, Xi: randomSample } ) ) );
67
67
  L.assign( reflect( V.negate(), H ) );
68
68
 
@@ -108,7 +108,7 @@ export const sampleClearcoat = Fn( ( [
108
108
 
109
109
  // Evaluate complete BRDF
110
110
  // The same BRDF every other site evaluates — see the clearcoat note in MaterialEvaluation.
111
- const brdf = transmitted.select( evaluateDiffuseTransmission( material, max( dot( N, V ), 0.001 ) ), evaluateMaterialResponseFromDots( material, dots ) );
111
+ const brdf = transmitted.select( evaluateDiffuseTransmission( material, N, V ), evaluateMaterialResponseFromDots( material, dots ) );
112
112
 
113
113
  // Return brdf, L direction, and pdf packed together
114
114
  // Caller needs L and pdf - return as struct-like output