rayzee 10.0.0 → 11.1.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 (176) hide show
  1. package/README.md +252 -44
  2. package/dist/ActiveColor-YvuQq44O.js +13 -0
  3. package/dist/ArchiveImporter-Hn35o6Z5.js +5145 -0
  4. package/dist/AssetConfig-DSEntE65.js +30 -0
  5. package/dist/BidirectionalIntegrator-CBw8hkeu.js +946 -0
  6. package/dist/BufferLayout-D_IBo2ci.js +150 -0
  7. package/dist/ColorManagement-oeEk7oSu.js +1488 -0
  8. package/dist/EngineEvents-Ci8dVWCP.js +73 -0
  9. package/dist/EngineIssues-CIZnXHsF.js +72 -0
  10. package/dist/FileFormats-sPGJeCRc.js +102 -0
  11. package/dist/Logger-dDDGj9Av.js +97 -0
  12. package/dist/{NeuralRetouch-CYgkQY8Z.js → NeuralRetouch-BzNpiVHZ.js} +8 -6
  13. package/dist/{NeuralSuperRes-PkgDLQcD.js → NeuralSuperRes-Bk7bHg2B.js} +55 -120
  14. package/dist/PathTracer-72XufKAr.js +29976 -0
  15. package/dist/PhysicalSky-BgjdV88k.js +1219 -0
  16. package/dist/RayzeeRenderer-1QRO97eq.js +3551 -0
  17. package/dist/SolarLimb-CPaeZVwz.js +721 -0
  18. package/dist/ToneMapGPU-CjU3osDd.js +177 -0
  19. package/dist/ViewTransforms-CYN7bq2w.js +255 -0
  20. package/dist/WorkingMatrix-C6y233hT.js +27 -0
  21. package/dist/ZipReader-gDSWr0I5.js +98 -0
  22. package/dist/addons/archives.es.js +3 -0
  23. package/dist/addons/bidirectional.es.js +2 -0
  24. package/dist/addons/color.es.js +3 -0
  25. package/dist/addons/formats.es.js +2 -0
  26. package/dist/addons/physical-sky.es.js +2 -0
  27. package/dist/addons/storage.es.js +3 -0
  28. package/dist/archiveFormats-tnAoUL1j.js +94 -0
  29. package/dist/identity-DrQsGT2o.js +39 -0
  30. package/dist/openStorage-DwcBRUj7.js +820 -0
  31. package/dist/rayzee-core.es.js +12 -0
  32. package/dist/rayzee.es.js +2551 -44024
  33. package/dist/rayzee.umd.js +528 -487
  34. package/dist/sceneCachePolicy-C67jbci-.js +443 -0
  35. package/dist/shared-BGBcSxBQ.js +17 -0
  36. package/package.json +23 -2
  37. package/src/Color/ActiveColor.js +34 -0
  38. package/src/Color/BasicColor.js +147 -0
  39. package/src/Color/ColorManagement.js +7 -31
  40. package/src/Color/ViewTransforms.js +5 -1
  41. package/src/EngineDefaults.js +62 -1029
  42. package/src/EngineEvents.js +2 -0
  43. package/src/EngineIssues.js +2 -0
  44. package/src/Headless.js +6 -5
  45. package/src/Passes/AIUpscaler.js +58 -141
  46. package/src/Passes/OIDNDenoiser.js +92 -65
  47. package/src/Passes/OIDNTemporalHistory.js +1 -1
  48. package/src/PathTracerApp.js +620 -4026
  49. package/src/Pipeline/PipelineContext.js +0 -4
  50. package/src/Platform.js +36 -0
  51. package/src/Processor/ArchiveCache.js +20 -7
  52. package/src/Processor/ArchiveImporter.js +1265 -0
  53. package/src/Processor/ArchiveReader.js +22 -4
  54. package/src/Processor/AssetLoader.js +327 -1763
  55. package/src/Processor/AtmosphereModel.js +1 -2
  56. package/src/Processor/BVHBuilder.js +14 -126
  57. package/src/Processor/BVHLeafFold.js +4 -6
  58. package/src/Processor/BVHRefitter.js +67 -46
  59. package/src/Processor/BufferLayout.js +291 -0
  60. package/src/Processor/EmissiveTriangleBuilder.js +1 -1
  61. package/src/Processor/EnvironmentExactTable.js +187 -0
  62. package/src/Processor/EquirectHDRInfo.js +30 -223
  63. package/src/Processor/FileFormats.js +116 -0
  64. package/src/Processor/GLTFDecoders.js +85 -0
  65. package/src/Processor/GLTFDiffuseTransmission.js +63 -0
  66. package/src/Processor/GeometryExtractor.js +15 -5
  67. package/src/Processor/HostMemory.js +7 -1
  68. package/src/Processor/IESParser.js +47 -35
  69. package/src/Processor/InstanceTable.js +1 -1
  70. package/src/Processor/KernelManager.js +35 -2
  71. package/src/Processor/MaterialPacking.js +10 -8
  72. package/src/Processor/PBRT/PBRTMaterials.js +254 -42
  73. package/src/Processor/PBRT/PBRTParser.js +49 -19
  74. package/src/Processor/PBRT/PBRTScattering.js +93 -0
  75. package/src/Processor/PBRT/PBRTSceneBuilder.js +1050 -75
  76. package/src/Processor/PBRT/PBRTTextureBake.js +179 -0
  77. package/src/Processor/PBRT/PFM.js +59 -0
  78. package/src/Processor/PackedRayBuffer.js +116 -9
  79. package/src/Processor/ParallelBVHBuilder.js +3 -6
  80. package/src/Processor/PhysicalSky.js +5 -4
  81. package/src/Processor/PlatformImageLoader.js +50 -6
  82. package/src/Processor/QueueManager.js +39 -5
  83. package/src/Processor/ReinsertionOptimizer.js +2 -2
  84. package/src/Processor/SceneMetadata.js +30 -5
  85. package/src/Processor/SceneProcessor.js +53 -62
  86. package/src/Processor/ShaderBuilder.js +6 -16
  87. package/src/Processor/SimpleSky.js +1 -62
  88. package/src/Processor/SolarLimb.js +3 -0
  89. package/src/Processor/SplitBLAS.js +1 -1
  90. package/src/Processor/StorageTexturePool.js +22 -1
  91. package/src/Processor/TLASBuilder.js +1 -3
  92. package/src/Processor/TextureBuckets.js +237 -0
  93. package/src/Processor/TextureCreator.js +181 -8
  94. package/src/Processor/ToneMapGPU.js +64 -15
  95. package/src/Processor/Workers/BVHRefitWorker.js +5 -2
  96. package/src/Processor/Workers/BVHSubtreeWorker.js +1 -34
  97. package/src/Processor/Workers/BVHWorker.js +5 -18
  98. package/src/Processor/Workers/CDFWorker.js +6 -183
  99. package/src/Processor/Workers/TexturesWorker.js +103 -18
  100. package/src/Processor/archiveFormats.js +7 -0
  101. package/src/Processor/utils.js +2 -1
  102. package/src/RayzeeRenderer.js +3900 -0
  103. package/src/RenderSettings.js +68 -62
  104. package/src/Stages/ASVGF.js +13 -14
  105. package/src/Stages/AutoExposure.js +17 -17
  106. package/src/Stages/BilateralFilter.js +2 -1
  107. package/src/Stages/Compositor.js +8 -23
  108. package/src/Stages/DenoiserSettings.js +182 -0
  109. package/src/Stages/EdgeFilter.js +2 -1
  110. package/src/Stages/MotionVector.js +1 -1
  111. package/src/Stages/NRD.js +11 -5
  112. package/src/Stages/NormalDepth.js +12 -8
  113. package/src/Stages/PathTracer.js +336 -43
  114. package/src/Stages/PathTracerStage.js +69 -66
  115. package/src/Stages/Variance.js +1 -1
  116. package/src/Storage/BLASCache.js +1 -1
  117. package/src/Storage/CDFCache.js +18 -17
  118. package/src/Storage/DownloadCache.js +1 -1
  119. package/src/Storage/SceneGraphCodec.js +46 -3
  120. package/src/Storage/SpillStore.js +1 -1
  121. package/src/Storage/StorageManager.js +45 -9
  122. package/src/Storage/areas.js +4 -0
  123. package/src/TSL/BVHTraversal.js +50 -25
  124. package/src/TSL/Bidirectional.js +208 -0
  125. package/src/TSL/BidirectionalLamps.js +132 -0
  126. package/src/TSL/Clearcoat.js +15 -6
  127. package/src/TSL/Common.js +20 -15
  128. package/src/TSL/ConnectKernel.js +167 -0
  129. package/src/TSL/Debugger.js +1 -1
  130. package/src/TSL/Displacement.js +4 -3
  131. package/src/TSL/EmissiveSampling.js +65 -18
  132. package/src/TSL/Environment.js +77 -106
  133. package/src/TSL/EnvironmentCDF.js +85 -39
  134. package/src/TSL/ExtendKernel.js +8 -3
  135. package/src/TSL/FinalWriteKernel.js +4 -4
  136. package/src/TSL/GenerateKernel.js +18 -4
  137. package/src/TSL/HitFacet.js +35 -5
  138. package/src/TSL/LightBVHSampling.js +40 -9
  139. package/src/TSL/LightGenerateKernel.js +293 -0
  140. package/src/TSL/LightGuide.js +198 -0
  141. package/src/TSL/LightSplatKernel.js +180 -0
  142. package/src/TSL/LightVisibility.js +111 -0
  143. package/src/TSL/LightsCore.js +13 -38
  144. package/src/TSL/LightsDirect.js +28 -29
  145. package/src/TSL/LightsIndirect.js +3 -1
  146. package/src/TSL/LightsSampling.js +239 -51
  147. package/src/TSL/MaterialEvaluation.js +113 -31
  148. package/src/TSL/MaterialProperties.js +44 -17
  149. package/src/TSL/MaterialTransmission.js +52 -47
  150. package/src/TSL/MergeKernel.js +224 -0
  151. package/src/TSL/PathTracerCore.js +65 -29
  152. package/src/TSL/Random.js +5 -1
  153. package/src/TSL/SceneResources.js +51 -0
  154. package/src/TSL/ShadeKernel.js +985 -101
  155. package/src/TSL/Struct.js +10 -0
  156. package/src/TSL/Sun.js +1 -1
  157. package/src/TSL/TextureSampling.js +66 -61
  158. package/src/addons/archives.js +14 -0
  159. package/src/addons/bidirectional.js +13 -0
  160. package/src/addons/color.js +32 -0
  161. package/src/addons/formats.js +16 -0
  162. package/src/addons/physicalSky.js +14 -0
  163. package/src/addons/storage.js +19 -0
  164. package/src/core.js +31 -0
  165. package/src/index.js +13 -23
  166. package/src/integrators/BidirectionalIntegrator.js +838 -0
  167. package/src/managers/AnimationManager.js +88 -6
  168. package/src/managers/CameraManager.js +103 -46
  169. package/src/managers/DenoisingManager.js +14 -10
  170. package/src/managers/EnvironmentManager.js +125 -103
  171. package/src/managers/GoboManager.js +9 -0
  172. package/src/managers/MaterialDataManager.js +48 -37
  173. package/src/managers/UniformManager.js +32 -10
  174. package/dist/ToneMapGPU-B6XIe3DU.js +0 -544
  175. package/dist/rolldown-runtime-CAFD8bLK.js +0 -11
  176. package/src/Processor/TreeletOptimizer.js +0 -574
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Triangle record: 5 uvec4 lanes (80 bytes). The buffer is declared `uvec4` so packed lanes
3
+ * keep their exact bit pattern — a packSnorm2x16 result can land in the f32 NaN range, and an
4
+ * f32 lane may canonicalise it (the same reason the G-buffer is uvec4). Positions and UVs are
5
+ * written as f32 through a Float32Array view of the same memory and read back with
6
+ * `uintBitsToFloat`, so they carry full precision; only normals are compressed.
7
+ *
8
+ * Was 32 floats (128 B) with 4 dead padding lanes. At 128 B the 2 GB V8 ArrayBuffer cap put a
9
+ * hard ceiling of 16.7M triangles on the scene — 80 B lifts that to 26.8M and cuts geometry
10
+ * VRAM by the same 37.5%. Each normal now rides in its position's spare .w lane, so the three
11
+ * vec4 loads the intersection test already does carry the normals with them.
12
+ */
13
+ export const TRIANGLE_DATA_LAYOUT = {
14
+ FLOATS_PER_TRIANGLE: 20,
15
+
16
+ POSITION_A_OFFSET: 0, // f32 xyz + packed normal A
17
+ POSITION_B_OFFSET: 4,
18
+ POSITION_C_OFFSET: 8,
19
+
20
+ NORMAL_A_PACKED_OFFSET: 3, // oct16 in each position's .w lane
21
+ NORMAL_B_PACKED_OFFSET: 7,
22
+ NORMAL_C_PACKED_OFFSET: 11,
23
+
24
+ UV_AB_OFFSET: 12, // f32 uvA.xy, uvB.xy
25
+ UV_C_OFFSET: 16, // f32 uvC.xy
26
+ MATERIAL_FLAGS_OFFSET: 18, // materialIndex | side << 24 | shadowBlockerBits << 26 (two bits)
27
+ MESH_INDEX_OFFSET: 19
28
+ };
29
+
30
+ export const TRI_MATERIAL_MASK = 0xffffff;
31
+ export const TRI_SIDE_SHIFT = 24; // 0 front, 1 back, 2 double
32
+ export const TRI_BLOCKER_SHIFT = 26; // 1 = blocks shadow rays whatever the settings
33
+ export const TRI_BLOCKER_ALPHA_SHIFT = 27; // 1 = blocks them unless alpha-cutout shadows are on
34
+
35
+ /**
36
+ * How a shadow ray settles on this material without fetching it, mirroring traceShadowRay:
37
+ * bit 0 set = always a blocker; bit 1 set = a blocker while alpha-cutout shadows are off
38
+ * (MASK/BLEND with nothing else letting light through); 0 = light may pass, so the shadow
39
+ * traversal has to find the nearest such surface.
40
+ */
41
+ export function shadowBlockerBits( material ) {
42
+
43
+ if ( ! material ) return 0;
44
+ const solid = ( material.transmission || 0 ) === 0
45
+ && ( ( material.transparent | 0 ) === 0 || ( material.opacity ?? 1 ) >= 1 );
46
+ if ( ! solid ) return 0;
47
+ return ( material.alphaMode | 0 ) === 0 ? 1 : 2;
48
+
49
+ }
50
+
51
+ /**
52
+ * Material index plus the per-triangle flags the shader reads without touching the
53
+ * material buffer: `side` for inline culling and the two shadow-blocker bits.
54
+ */
55
+ export function packTriangleFlags( materialIndex, material ) {
56
+
57
+ return ( ( materialIndex & TRI_MATERIAL_MASK )
58
+ | ( ( material?.side ?? 0 ) << TRI_SIDE_SHIFT )
59
+ | ( shadowBlockerBits( material ) << TRI_BLOCKER_SHIFT ) ) >>> 0;
60
+
61
+ }
62
+
63
+ /**
64
+ * Octahedral-encode a unit normal into one u32 (two snorm16). Worst-case error is ~0.03°,
65
+ * well under what normal maps and barycentric interpolation already contribute.
66
+ * A degenerate (zero-length) normal encodes as +Z rather than NaN.
67
+ */
68
+ export function packNormalOct( x, y, z ) {
69
+
70
+ const len = Math.sqrt( x * x + y * y + z * z );
71
+ if ( len > 0 ) {
72
+
73
+ x /= len; y /= len; z /= len;
74
+
75
+ } else {
76
+
77
+ x = 0; y = 0; z = 1;
78
+
79
+ }
80
+
81
+ const sum = Math.abs( x ) + Math.abs( y ) + Math.abs( z );
82
+ let u = x / sum, v = y / sum;
83
+ if ( z < 0 ) {
84
+
85
+ const au = u, av = v;
86
+ u = ( 1 - Math.abs( av ) ) * ( au >= 0 ? 1 : - 1 );
87
+ v = ( 1 - Math.abs( au ) ) * ( av >= 0 ? 1 : - 1 );
88
+
89
+ }
90
+
91
+ const qu = Math.round( Math.min( 1, Math.max( - 1, u ) ) * 32767 ) & 0xffff;
92
+ const qv = Math.round( Math.min( 1, Math.max( - 1, v ) ) * 32767 ) & 0xffff;
93
+ return ( ( qv << 16 ) | qu ) >>> 0;
94
+
95
+ }
96
+
97
+ /** Inverse of packNormalOct; writes into `out` (length >= 3) and returns it. */
98
+ export function unpackNormalOct( packed, out ) {
99
+
100
+ const u = ( ( packed << 16 ) >> 16 ) / 32767;
101
+ const v = ( packed >> 16 ) / 32767;
102
+ let x = u, y = v, z = 1 - Math.abs( u ) - Math.abs( v );
103
+ if ( z < 0 ) {
104
+
105
+ const ax = x, ay = y;
106
+ x = ( 1 - Math.abs( ay ) ) * ( ax >= 0 ? 1 : - 1 );
107
+ y = ( 1 - Math.abs( ax ) ) * ( ay >= 0 ? 1 : - 1 );
108
+
109
+ }
110
+
111
+ const len = Math.sqrt( x * x + y * y + z * z ) || 1;
112
+ out[ 0 ] = x / len; out[ 1 ] = y / len; out[ 2 ] = z / len;
113
+ return out;
114
+
115
+ }
116
+
117
+ // Material data layout constants — single source of truth for material buffer offsets.
118
+ // Shared between CPU writers (TextureCreator, MaterialDataManager) and GPU readers (Common.js getMaterial).
119
+ export const MATERIAL_DATA_LAYOUT = {
120
+
121
+ SLOTS_PER_MATERIAL: 34, // vec4 slots per material
122
+ FLOATS_PER_MATERIAL: 136, // total floats per material (34 × 4)
123
+
124
+ // ── Flat float offsets (CPU side) ────────────────────────────────
125
+ // Used as: data[ materialIndex * FLOATS_PER_MATERIAL + offset ]
126
+ // Ordered for cache-line coherence: shadow/culling → BxDF core → maps → extended → transforms
127
+
128
+ // Slot 0: ior + transmission + thickness + emissiveIntensity [shadow]
129
+ IOR: 0, TRANSMISSION: 1, THICKNESS: 2, EMISSIVE_INTENSITY: 3,
130
+ // Slot 1: attenuationColor.rgb + attenuationDistance [shadow]
131
+ ATTENUATION_COLOR: 4, ATTENUATION_DISTANCE: 7,
132
+ // Slot 2: opacity + side + transparent + alphaTest [shadow + culling]
133
+ OPACITY: 8, SIDE: 9, TRANSPARENT: 10, ALPHA_TEST: 11,
134
+ // Slot 3: alphaMode + depthWrite + normalScale [shadow]
135
+ ALPHA_MODE: 12, DEPTH_WRITE: 13, NORMAL_SCALE: 14,
136
+ // Slot 4: color.rgb + metalness [BxDF core]
137
+ COLOR: 16, METALNESS: 19,
138
+ // Slot 5: emissive.rgb + roughness [BxDF core]
139
+ EMISSIVE: 20, ROUGHNESS: 23,
140
+ // Slot 6: map indices (albedo, normal, roughness, metalness) [maps]
141
+ ALBEDO_MAP_INDEX: 24, NORMAL_MAP_INDEX: 25, ROUGHNESS_MAP_INDEX: 26, METALNESS_MAP_INDEX: 27,
142
+ // Slot 7: map indices (emissive, bump) + clearcoat [maps]
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,
146
+ // Slot 9: sheenColor.rgb + diffuseTransmission [extended BxDF]
147
+ SHEEN_COLOR: 36, DIFFUSE_TRANSMISSION: 39,
148
+ // Slot 10: specularIntensity + specularColor.rgb [extended BxDF]
149
+ SPECULAR_INTENSITY: 40, SPECULAR_COLOR: 41,
150
+ // Slot 11: iridescence + iridescenceIOR + iridescenceThicknessRange [extended BxDF]
151
+ IRIDESCENCE: 44, IRIDESCENCE_IOR: 45, IRIDESCENCE_THICKNESS_RANGE: 46,
152
+ // Slot 12: bumpScale + displacementScale + displacementMapIndex + (padding)
153
+ BUMP_SCALE: 48, DISPLACEMENT_SCALE: 49, DISPLACEMENT_MAP_INDEX: 50,
154
+
155
+ // ── Transform float offsets (8 floats each: 7 matrix values + 1 padding) ──
156
+ ALBEDO_TRANSFORM: 52,
157
+ NORMAL_TRANSFORM: 60,
158
+ ROUGHNESS_TRANSFORM: 68,
159
+ METALNESS_TRANSFORM: 76,
160
+ EMISSIVE_TRANSFORM: 84,
161
+ BUMP_TRANSFORM: 92,
162
+ DISPLACEMENT_TRANSFORM: 100,
163
+
164
+ // ── Subsurface scattering (3 slots appended after transforms) ────
165
+ // Slot 27: subsurfaceColor.rgb (scatter albedo) + subsurface weight
166
+ SUBSURFACE_COLOR: 108, SUBSURFACE: 111,
167
+ // Slot 28: subsurfaceRadius.rgb (mean free path) + radius scale
168
+ SUBSURFACE_RADIUS: 112, SUBSURFACE_RADIUS_SCALE: 115,
169
+ // Slot 29: subsurfaceAnisotropy g (116) + surface anisotropy (strength 117, rotation 118, map index 119)
170
+ SUBSURFACE_ANISOTROPY: 116, ANISOTROPY: 117, ANISOTROPY_ROTATION: 118, ANISOTROPY_MAP_INDEX: 119,
171
+ // Slot 30: extension-texture map indices A (transmission, clearcoat, clearcoatRoughness, sheenColor)
172
+ TRANSMISSION_MAP_INDEX: 120, CLEARCOAT_MAP_INDEX: 121, CLEARCOAT_ROUGHNESS_MAP_INDEX: 122, SHEEN_COLOR_MAP_INDEX: 123,
173
+ // Slot 31: extension-texture map indices B (sheenRoughness, iridescence, iridescenceThickness, specularIntensity)
174
+ SHEEN_ROUGHNESS_MAP_INDEX: 124, IRIDESCENCE_MAP_INDEX: 125, IRIDESCENCE_THICKNESS_MAP_INDEX: 126, SPECULAR_INTENSITY_MAP_INDEX: 127,
175
+ // Slot 32: extension-texture map index C (specularColor) + diffuseTransmissionColor.rgb
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
179
+ DIFFUSE_TRANSMISSION_MAP_INDEX: 132, DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX: 133,
180
+
181
+ // ── Vec4 slot indices (GPU/TSL side) ─────────────────────────────
182
+ // Used with getDatafromStorageBuffer( buf, matIdx, int(slot), int(SLOTS_PER_MATERIAL) )
183
+ SLOT: {
184
+ IOR_TRANSMISSION: 0, // [shadow] ior, transmission, thickness, emissiveIntensity
185
+ ATTENUATION: 1, // [shadow] attenuationColor, attenuationDistance
186
+ OPACITY_ALPHA: 2, // [shadow+culling] opacity, side, transparent, alphaTest
187
+ ALPHA_MODE: 3, // [shadow] alphaMode, depthWrite, normalScale
188
+ COLOR_METALNESS: 4, // [BxDF] color.rgb, metalness
189
+ EMISSIVE_ROUGHNESS: 5, // [BxDF] emissive.rgb, roughness
190
+ MAP_INDICES_A: 6, // [maps] albedo, normal, roughness, metalness
191
+ MAP_INDICES_B: 7, // [maps] emissive, bump, clearcoat, clearcoatRoughness
192
+ DISPERSION_SHEEN: 8, // [extended] dispersion, visible, sheen, sheenRoughness
193
+ SHEEN_COLOR: 9, // [extended] sheenColor, diffuseTransmission
194
+ SPECULAR: 10, // [extended] specularIntensity, specularColor
195
+ IRIDESCENCE: 11, // [extended] iridescence, iridescenceIOR, iridescenceThicknessRange
196
+ BUMP_DISPLACEMENT: 12, // bumpScale, displacementScale, displacementMapIndex
197
+ ALBEDO_TRANSFORM_A: 13, ALBEDO_TRANSFORM_B: 14,
198
+ NORMAL_TRANSFORM_A: 15, NORMAL_TRANSFORM_B: 16,
199
+ ROUGHNESS_TRANSFORM_A: 17, ROUGHNESS_TRANSFORM_B: 18,
200
+ METALNESS_TRANSFORM_A: 19, METALNESS_TRANSFORM_B: 20,
201
+ EMISSIVE_TRANSFORM_A: 21, EMISSIVE_TRANSFORM_B: 22,
202
+ BUMP_TRANSFORM_A: 23, BUMP_TRANSFORM_B: 24,
203
+ DISPLACEMENT_TRANSFORM_A: 25, DISPLACEMENT_TRANSFORM_B: 26,
204
+ SUBSURFACE_A: 27, // subsurfaceColor.rgb, subsurface weight
205
+ SUBSURFACE_B: 28, // subsurfaceRadius.rgb, subsurfaceRadiusScale
206
+ SUBSURFACE_C: 29, // subsurfaceAnisotropy g, anisotropy, anisotropyRotation, anisotropyMapIndex
207
+ EXT_MAP_INDICES_A: 30, // transmission, clearcoat, clearcoatRoughness, sheenColor map indices
208
+ EXT_MAP_INDICES_B: 31, // sheenRoughness, iridescence, iridescenceThickness, specularIntensity map indices
209
+ EXT_MAP_INDICES_C: 32, // specularColor map index, diffuseTransmissionColor
210
+ EXT_MAP_INDICES_D: 33, // diffuseTransmission, diffuseTransmissionColor map indices + 2 reserved
211
+ },
212
+
213
+ };
214
+
215
+ // glTF/three.js spell "no volume absorption" as attenuationDistance = Infinity, while the GPU
216
+ // contract is `> 0 = on` (calculateBeerLawAbsorption). Every writer into ATTENUATION_DISTANCE
217
+ // collapses both spellings to 0 so no shader divides by Inf.
218
+ export const normalizeAttenuationDistance = d => ( Number.isFinite( d ) && d > 0 ? d : 0 );
219
+
220
+ /**
221
+ * Node tags, written into slot [3] of a BVH node as a raw u32 bit pattern.
222
+ *
223
+ * Node indices, triangle offsets and counts are integers living inside a Float32Array. Written as
224
+ * float *values* they round silently past 2^24 (16,777,216): in a 24M-node scene half of every
225
+ * BLAS pointer landed on a neighbouring node and that geometry vanished from the render with no
226
+ * error at all. Every one of those fields is written as a u32 bit pattern instead, exact to 2^30.
227
+ *
228
+ * Tags sit above {@link BVH_MAX_INDEX} so a single unsigned compare separates a leaf from an inner
229
+ * node's left-child index. All three are ordinary finite floats — nothing lands in the NaN range,
230
+ * which an f32 storage buffer is free to canonicalise.
231
+ */
232
+ export const BVH_MAX_INDEX = 0x40000000; // 2^30
233
+
234
+ /**
235
+ * Slot [1] of a BLAS-pointer leaf holds its placement index, which {@link BVH_MAX_INDEX} keeps
236
+ * below 2^30. Bit 30 is therefore free to say the leaf's matrix is identity: geometry no other
237
+ * placement shares is baked to world space at extraction, and a ray reaching it needs no
238
+ * transform at all. Mask the bit off before using the slot as an index.
239
+ */
240
+ export const TLAS_LEAF_IDENTITY = 0x40000000;
241
+ export const TLAS_PLACEMENT_MASK = 0x3fffffff;
242
+
243
+ export const BVH_LEAF_MARKERS = {
244
+ TRIANGLE_LEAF: 0x40000000, // leaf containing triangle references
245
+ BLAS_POINTER_LEAF: 0x40000001, // TLAS leaf pointing to a BLAS root node
246
+ FRONTIER: 0x40000002, // parallel-build placeholder, overwritten during assembly
247
+ };
248
+
249
+ /**
250
+ * A triangle leaf of at most this many triangles is folded into its parent: the child's slot ([3]
251
+ * or [7]) holds `~( first << 4 | count )`, the very value traversal pushes, so an inner node is
252
+ * read exactly as before and only popping a negative entry differs. Node indices stay below
253
+ * {@link BVH_MAX_INDEX}, leaf tags between it and 2^31, folded leaves from 2^31 up — so the count
254
+ * has four bits and the first triangle must stay below {@link BVH_FOLDED_FIRST_LIMIT}.
255
+ */
256
+ export const BVH_FOLDED_LEAF_MAX = 15;
257
+ export const BVH_FOLDED_FIRST_LIMIT = 1 << 27;
258
+
259
+ /** A u32 view over a float buffer, for writing index fields as exact bit patterns. */
260
+ export function bvhIndexView( f32 ) {
261
+
262
+ return new Uint32Array( f32.buffer, f32.byteOffset, f32.length );
263
+
264
+ }
265
+
266
+ /**
267
+ * Refuse to build a BVH whose indices would collide with the leaf tags.
268
+ *
269
+ * Throws rather than degrades: the failure this replaces was a scene that rendered with half its
270
+ * geometry silently missing, which is far worse than a scene that refuses to load. Guarding the
271
+ * totals covers every individual write, since no index can exceed the count it indexes into.
272
+ *
273
+ * @param {number} count - node or triangle total about to be indexed
274
+ * @param {string} what - what the count is, for the message
275
+ * @throws {RangeError}
276
+ */
277
+ export function assertBVHIndexFits( count, what ) {
278
+
279
+ if ( count >= BVH_MAX_INDEX ) {
280
+
281
+ throw new RangeError(
282
+ `${what} is ${count.toLocaleString()}, at or past the BVH index limit of ` +
283
+ `${BVH_MAX_INDEX.toLocaleString()}. Node indices are stored as u32 bit patterns and the ` +
284
+ 'leaf tags occupy everything above that.'
285
+ );
286
+
287
+ }
288
+
289
+ return count;
290
+
291
+ }
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { DataTexture, RGBAFormat, FloatType, NearestFilter } from 'three';
10
- import { TRIANGLE_DATA_LAYOUT, TRI_MATERIAL_MASK } from '../EngineDefaults.js';
10
+ import { TRIANGLE_DATA_LAYOUT, TRI_MATERIAL_MASK } from './BufferLayout.js';
11
11
  import { LightBVHBuilder } from './LightBVHBuilder.js';
12
12
  import { createLogger, fmt } from '../utils/Logger.js';
13
13
  import { convertLinearTriple, getWorkingMatrix } from '../Color/WorkingMatrix.js';
@@ -0,0 +1,187 @@
1
+ /**
2
+ * An environment's sampling table: piecewise constant over cells of at most EXACT_TABLE_MAX_WIDTH × half that,
3
+ * so a direction's density is its cell's share, read back from the same cumulative sums it is drawn from.
4
+ * MIS-compensated (Karlík et al. 2019): each texel weighs what it has above the mean, and BSDF-sampled rays
5
+ * cover the rest. Drawn by inverting the sums, which keeps the samples' stratification (an alias table does not:
6
+ * it doubled a furnace's noise). Two guides per entry (Chen & Hsu's cutpoints, GUIDES_PER_ENTRY) land most
7
+ * draws on their entry at once. Pure math: CDFWorker runs it too.
8
+ */
9
+
10
+ export const EXACT_TABLE_MAX_WIDTH = 1024;
11
+
12
+ // Every cell keeps this share of the mean, so light tracing or NEE alone covers the whole sphere.
13
+ const FLOOR = 1e-4;
14
+
15
+ /** Guides per running-sum entry: step g of n · GUIDES_PER_ENTRY points at the first entry above it. */
16
+ export const GUIDES_PER_ENTRY = 2;
17
+
18
+ /** Cells the table has for a width × height environment; TSL/Environment.js derives the same. */
19
+ export function exactTableSize( width, height ) {
20
+
21
+ const k = Math.max( 1, Math.ceil( width / EXACT_TABLE_MAX_WIDTH ) );
22
+ return { k, width: Math.ceil( width / k ), height: Math.ceil( height / k ) };
23
+
24
+ }
25
+
26
+ // For each of m equal steps of an n-entry running sum, the first entry above the step's start; n − 1 past the end.
27
+ function cutpoints( cdf, offset, n, out, outOffset, m ) {
28
+
29
+ let x = 0;
30
+ for ( let g = 0; g < m; g ++ ) {
31
+
32
+ while ( x < n - 1 && cdf[ offset + x ] <= g / m ) x ++;
33
+ out[ outOffset + g ] = x;
34
+
35
+ }
36
+
37
+ }
38
+
39
+ /**
40
+ * @param {Float32Array} floatData - RGBA, row 0 the nadir
41
+ * @param {{ filtered?: boolean }} [options] - filtered false: each texel weighs as itself (the physical sky's GPU twin)
42
+ * @returns {{ exactWidth: number, exactHeight: number, exactConditional: Float32Array, exactMarginal: Float32Array,
43
+ * exactRowGuide: Float32Array, exactMarginalGuide: Float32Array, radianceIntegral: number }}
44
+ * each row's running sum over its cells and the rows' over all, both ending at 1, and their guides (GUIDES_PER_ENTRY
45
+ * an entry); ∫ luminance dω.
46
+ */
47
+ export function buildExactEnvironmentTable( floatData, width, height, { filtered = true } = {} ) {
48
+
49
+ const { k, width: w, height: h } = exactTableSize( width, height );
50
+ const lum = new Float32Array( width * height );
51
+ let raw = 0;
52
+ for ( let y = 0; y < height; y ++ ) {
53
+
54
+ const sinTheta = Math.sin( Math.PI * ( y + 0.5 ) / height );
55
+ for ( let x = 0; x < width; x ++ ) {
56
+
57
+ const i = 4 * ( y * width + x );
58
+ const l = 0.2126 * floatData[ i ] + 0.7152 * floatData[ i + 1 ] + 0.0722 * floatData[ i + 2 ];
59
+ lum[ y * width + x ] = Number.isFinite( l ) && l > 0 ? l : 0;
60
+ raw += lum[ y * width + x ] * sinTheta;
61
+
62
+ }
63
+
64
+ }
65
+
66
+ // Each texel weighs as the bilinear filter's mean over it — 1/8, 6/8, 1/8 of its neighbours along each axis — so
67
+ // a cell has weight wherever the filtered map has light, and a sharp texel's neighbours only their share of it.
68
+ const TAP = [ 1 / 8, 6 / 8, 1 / 8 ];
69
+ const eachTexel = ( visit ) => {
70
+
71
+ for ( let y = 0; y < height; y ++ ) {
72
+
73
+ const sinTheta = Math.sin( Math.PI * ( y + 0.5 ) / height );
74
+ const row = Math.floor( y / k ) * w;
75
+ const ys = [ Math.max( y - 1, 0 ), y, Math.min( y + 1, height - 1 ) ];
76
+ for ( let x = 0; x < width; x ++ ) {
77
+
78
+ let value = lum[ y * width + x ];
79
+ if ( filtered ) {
80
+
81
+ const xs = [ ( x + width - 1 ) % width, x, ( x + 1 ) % width ];
82
+ value = 0;
83
+ for ( let j = 0; j < 3; j ++ ) for ( let i = 0; i < 3; i ++ ) value += TAP[ j ] * TAP[ i ] * lum[ ys[ j ] * width + xs[ i ] ];
84
+
85
+ }
86
+
87
+ visit( row + Math.floor( x / k ), value * sinTheta );
88
+
89
+ }
90
+
91
+ }
92
+
93
+ };
94
+
95
+ let filteredTotal = 0;
96
+ eachTexel( ( cell, weight ) => void ( filteredTotal += weight ) );
97
+ const mean = filteredTotal / ( width * height );
98
+ const cells = new Float64Array( w * h );
99
+ let compensated = 0;
100
+ eachTexel( ( cell, weight ) => {
101
+
102
+ cells[ cell ] += Math.max( weight - mean, 0 );
103
+ compensated += Math.max( weight - mean, 0 );
104
+
105
+ } );
106
+ // A flat map has nothing above its mean: then the raw weights.
107
+ if ( ! ( compensated > 0 ) ) {
108
+
109
+ cells.fill( 0 );
110
+ eachTexel( ( cell, weight ) => void ( cells[ cell ] += weight ) );
111
+
112
+ }
113
+
114
+ const floor = filteredTotal > 0 ? FLOOR * filteredTotal / ( w * h ) : 0;
115
+ const exactConditional = new Float32Array( w * h );
116
+ const exactMarginal = new Float32Array( h );
117
+ const rowSums = new Float64Array( h );
118
+ let total = 0;
119
+ for ( let y = 0; y < h; y ++ ) {
120
+
121
+ let sum = 0;
122
+ for ( let x = 0; x < w; x ++ ) sum += cells[ y * w + x ] + floor;
123
+ let cumulative = 0;
124
+ for ( let x = 0; x < w; x ++ ) {
125
+
126
+ cumulative += cells[ y * w + x ] + floor;
127
+ exactConditional[ y * w + x ] = sum > 0 ? ( x === w - 1 ? 1 : cumulative / sum ) : 0;
128
+
129
+ }
130
+
131
+ rowSums[ y ] = sum;
132
+ total += sum;
133
+
134
+ }
135
+
136
+ let cumulative = 0;
137
+ for ( let y = 0; y < h; y ++ ) {
138
+
139
+ cumulative += rowSums[ y ];
140
+ exactMarginal[ y ] = total > 0 ? ( y === h - 1 ? 1 : cumulative / total ) : 0;
141
+
142
+ }
143
+
144
+ const G = GUIDES_PER_ENTRY;
145
+ const exactRowGuide = new Float32Array( G * w * h );
146
+ const exactMarginalGuide = new Float32Array( G * h );
147
+ for ( let y = 0; y < h; y ++ ) cutpoints( exactConditional, y * w, w, exactRowGuide, G * y * w, G * w );
148
+ cutpoints( exactMarginal, 0, h, exactMarginalGuide, 0, G * h );
149
+
150
+ return {
151
+ exactWidth: w, exactHeight: h, exactConditional, exactMarginal, exactRowGuide, exactMarginalGuide,
152
+ radianceIntegral: 2 * Math.PI * Math.PI * raw / ( width * height ),
153
+ };
154
+
155
+ }
156
+
157
+ /**
158
+ * The table as the CDF texture holds it, ( w + 1 ) × h RGBA floats: texel ( x, y ) holds row y's entry x — its running
159
+ * sum, the one below it (0 at the first), and the guides of steps 2x and 2x + 1 — and texel ( w, y ) the rows' entry y
160
+ * likewise. TSL/Environment.js reads this layout.
161
+ */
162
+ export function packExactTable( { exactWidth: w, exactHeight: h, exactConditional, exactMarginal, exactRowGuide, exactMarginalGuide } ) {
163
+
164
+ const stride = w + 1;
165
+ const data = new Float32Array( 4 * stride * h );
166
+ for ( let y = 0; y < h; y ++ ) {
167
+
168
+ let o = 4 * y * stride;
169
+ for ( let x = 0, i = y * w; x < w; x ++, i ++, o += 4 ) {
170
+
171
+ data[ o ] = exactConditional[ i ];
172
+ data[ o + 1 ] = x > 0 ? exactConditional[ i - 1 ] : 0;
173
+ data[ o + 2 ] = exactRowGuide[ 2 * i ];
174
+ data[ o + 3 ] = exactRowGuide[ 2 * i + 1 ];
175
+
176
+ }
177
+
178
+ data[ o ] = exactMarginal[ y ];
179
+ data[ o + 1 ] = y > 0 ? exactMarginal[ y - 1 ] : 0;
180
+ data[ o + 2 ] = exactMarginalGuide[ 2 * y ];
181
+ data[ o + 3 ] = exactMarginalGuide[ 2 * y + 1 ];
182
+
183
+ }
184
+
185
+ return { data, width: stride, height: h };
186
+
187
+ }