@aura3d/rendering 2.0.4 → 3.0.1

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 (374) hide show
  1. package/dist/AtmosphereWetness.d.ts +61 -0
  2. package/dist/AtmosphereWetness.d.ts.map +1 -0
  3. package/dist/AtmosphereWetness.js +99 -0
  4. package/dist/AtmosphereWetness.js.map +1 -0
  5. package/dist/CascadedShadowMaps.d.ts.map +1 -1
  6. package/dist/CascadedShadowMaps.js +3 -1
  7. package/dist/CascadedShadowMaps.js.map +1 -1
  8. package/dist/ClusteredForwardLighting.d.ts +25 -1
  9. package/dist/ClusteredForwardLighting.d.ts.map +1 -1
  10. package/dist/ClusteredForwardLighting.js +80 -3
  11. package/dist/ClusteredForwardLighting.js.map +1 -1
  12. package/dist/DayNightSky.d.ts +63 -0
  13. package/dist/DayNightSky.d.ts.map +1 -0
  14. package/dist/DayNightSky.js +156 -0
  15. package/dist/DayNightSky.js.map +1 -0
  16. package/dist/EnvironmentPresetPack.d.ts +30 -0
  17. package/dist/EnvironmentPresetPack.d.ts.map +1 -0
  18. package/dist/EnvironmentPresetPack.js +49 -0
  19. package/dist/EnvironmentPresetPack.js.map +1 -0
  20. package/dist/ExtensionScalarAtlas.d.ts +18 -0
  21. package/dist/ExtensionScalarAtlas.d.ts.map +1 -0
  22. package/dist/ExtensionScalarAtlas.js +107 -0
  23. package/dist/ExtensionScalarAtlas.js.map +1 -0
  24. package/dist/ForwardPass.d.ts +65 -0
  25. package/dist/ForwardPass.d.ts.map +1 -1
  26. package/dist/ForwardPass.js +196 -2
  27. package/dist/ForwardPass.js.map +1 -1
  28. package/dist/InstancedPBRMaterial.d.ts +1 -0
  29. package/dist/InstancedPBRMaterial.d.ts.map +1 -1
  30. package/dist/InstancedPBRMaterial.js +3 -0
  31. package/dist/InstancedPBRMaterial.js.map +1 -1
  32. package/dist/InstancingDiagnostics.d.ts +69 -0
  33. package/dist/InstancingDiagnostics.d.ts.map +1 -0
  34. package/dist/InstancingDiagnostics.js +98 -0
  35. package/dist/InstancingDiagnostics.js.map +1 -0
  36. package/dist/LeanWebGL2Device.d.ts +265 -0
  37. package/dist/LeanWebGL2Device.d.ts.map +1 -0
  38. package/dist/LeanWebGL2Device.js +4001 -0
  39. package/dist/LeanWebGL2Device.js.map +1 -0
  40. package/dist/LightingRig.d.ts +19 -1
  41. package/dist/LightingRig.d.ts.map +1 -1
  42. package/dist/LightingRig.js +61 -1
  43. package/dist/LightingRig.js.map +1 -1
  44. package/dist/MaterialBinding.js +1 -1
  45. package/dist/MaterialBinding.js.map +1 -1
  46. package/dist/MeshConsolidation.d.ts +12 -0
  47. package/dist/MeshConsolidation.d.ts.map +1 -1
  48. package/dist/MeshConsolidation.js +28 -0
  49. package/dist/MeshConsolidation.js.map +1 -1
  50. package/dist/MorphTargetPlan.d.ts +29 -2
  51. package/dist/MorphTargetPlan.d.ts.map +1 -1
  52. package/dist/MorphTargetPlan.js +23 -4
  53. package/dist/MorphTargetPlan.js.map +1 -1
  54. package/dist/NormalMappedPBRMaterial.d.ts.map +1 -1
  55. package/dist/NormalMappedPBRMaterial.js +22 -2
  56. package/dist/NormalMappedPBRMaterial.js.map +1 -1
  57. package/dist/OceanSurface.d.ts +75 -0
  58. package/dist/OceanSurface.d.ts.map +1 -1
  59. package/dist/OceanSurface.js +211 -0
  60. package/dist/OceanSurface.js.map +1 -1
  61. package/dist/PBRMaterial.d.ts +1 -0
  62. package/dist/PBRMaterial.d.ts.map +1 -1
  63. package/dist/PBRMaterial.js +23 -2
  64. package/dist/PBRMaterial.js.map +1 -1
  65. package/dist/PbrReference.d.ts +48 -0
  66. package/dist/PbrReference.d.ts.map +1 -1
  67. package/dist/PbrReference.js +126 -0
  68. package/dist/PbrReference.js.map +1 -1
  69. package/dist/PlanarReflection.d.ts +221 -0
  70. package/dist/PlanarReflection.d.ts.map +1 -0
  71. package/dist/PlanarReflection.js +541 -0
  72. package/dist/PlanarReflection.js.map +1 -0
  73. package/dist/PostProcessPass.d.ts +32 -0
  74. package/dist/PostProcessPass.d.ts.map +1 -1
  75. package/dist/PostProcessPass.js +79 -36
  76. package/dist/PostProcessPass.js.map +1 -1
  77. package/dist/ProjectionMath.d.ts +3 -0
  78. package/dist/ProjectionMath.d.ts.map +1 -0
  79. package/dist/ProjectionMath.js +26 -0
  80. package/dist/ProjectionMath.js.map +1 -0
  81. package/dist/ReflectionSurfaces.d.ts +17 -0
  82. package/dist/ReflectionSurfaces.d.ts.map +1 -1
  83. package/dist/ReflectionSurfaces.js +103 -1
  84. package/dist/ReflectionSurfaces.js.map +1 -1
  85. package/dist/RenderDevice.d.ts +92 -0
  86. package/dist/RenderDevice.d.ts.map +1 -1
  87. package/dist/RenderDevice.js +85 -0
  88. package/dist/RenderDevice.js.map +1 -1
  89. package/dist/Renderer.d.ts +9 -0
  90. package/dist/Renderer.d.ts.map +1 -1
  91. package/dist/Renderer.js +97 -9
  92. package/dist/Renderer.js.map +1 -1
  93. package/dist/RendererPostprocessPlan.d.ts +64 -3
  94. package/dist/RendererPostprocessPlan.d.ts.map +1 -1
  95. package/dist/RendererPostprocessPlan.js +116 -12
  96. package/dist/RendererPostprocessPlan.js.map +1 -1
  97. package/dist/RendererVisualPipelineReport.d.ts.map +1 -1
  98. package/dist/RendererVisualPipelineReport.js +3 -1
  99. package/dist/RendererVisualPipelineReport.js.map +1 -1
  100. package/dist/Sampler.d.ts +29 -0
  101. package/dist/Sampler.d.ts.map +1 -1
  102. package/dist/Sampler.js +47 -0
  103. package/dist/Sampler.js.map +1 -1
  104. package/dist/ScreenSpaceReflectionPass.d.ts +43 -0
  105. package/dist/ScreenSpaceReflectionPass.d.ts.map +1 -0
  106. package/dist/ScreenSpaceReflectionPass.js +58 -0
  107. package/dist/ScreenSpaceReflectionPass.js.map +1 -0
  108. package/dist/SdfText.d.ts +250 -0
  109. package/dist/SdfText.d.ts.map +1 -0
  110. package/dist/SdfText.js +551 -0
  111. package/dist/SdfText.js.map +1 -0
  112. package/dist/ShaderChunks.d.ts.map +1 -1
  113. package/dist/ShaderChunks.js +30 -12
  114. package/dist/ShaderChunks.js.map +1 -1
  115. package/dist/ShaderLibrary.d.ts.map +1 -1
  116. package/dist/ShaderLibrary.js +601 -186
  117. package/dist/ShaderLibrary.js.map +1 -1
  118. package/dist/ShaderLibraryCore.d.ts +3 -0
  119. package/dist/ShaderLibraryCore.d.ts.map +1 -1
  120. package/dist/ShaderLibraryCore.js +73 -12
  121. package/dist/ShaderLibraryCore.js.map +1 -1
  122. package/dist/ShadowMap.d.ts +24 -0
  123. package/dist/ShadowMap.d.ts.map +1 -1
  124. package/dist/ShadowMap.js +43 -0
  125. package/dist/ShadowMap.js.map +1 -1
  126. package/dist/SkinnedLitMaterial.d.ts.map +1 -1
  127. package/dist/SkinnedLitMaterial.js +2 -0
  128. package/dist/SkinnedLitMaterial.js.map +1 -1
  129. package/dist/SkinningBounds.d.ts +10 -0
  130. package/dist/SkinningBounds.d.ts.map +1 -1
  131. package/dist/SkinningBounds.js +27 -0
  132. package/dist/SkinningBounds.js.map +1 -1
  133. package/dist/SpriteFlipbook.d.ts +60 -0
  134. package/dist/SpriteFlipbook.d.ts.map +1 -0
  135. package/dist/SpriteFlipbook.js +128 -0
  136. package/dist/SpriteFlipbook.js.map +1 -0
  137. package/dist/TemporalHistory.d.ts +37 -0
  138. package/dist/TemporalHistory.d.ts.map +1 -0
  139. package/dist/TemporalHistory.js +135 -0
  140. package/dist/TemporalHistory.js.map +1 -0
  141. package/dist/TemporalMath.d.ts +3 -0
  142. package/dist/TemporalMath.d.ts.map +1 -0
  143. package/dist/TemporalMath.js +9 -0
  144. package/dist/TemporalMath.js.map +1 -0
  145. package/dist/TerrainTiles.d.ts +120 -0
  146. package/dist/TerrainTiles.d.ts.map +1 -0
  147. package/dist/TerrainTiles.js +221 -0
  148. package/dist/TerrainTiles.js.map +1 -0
  149. package/dist/TexturedPBRMaterial.d.ts +4 -0
  150. package/dist/TexturedPBRMaterial.d.ts.map +1 -1
  151. package/dist/TexturedPBRMaterial.js +88 -5
  152. package/dist/TexturedPBRMaterial.js.map +1 -1
  153. package/dist/TexturedUnlitMaterial.d.ts.map +1 -1
  154. package/dist/TexturedUnlitMaterial.js +20 -2
  155. package/dist/TexturedUnlitMaterial.js.map +1 -1
  156. package/dist/UnlitMaterial.d.ts.map +1 -1
  157. package/dist/UnlitMaterial.js +20 -2
  158. package/dist/UnlitMaterial.js.map +1 -1
  159. package/dist/VertexFormat.d.ts +6 -0
  160. package/dist/VertexFormat.d.ts.map +1 -1
  161. package/dist/VertexFormat.js +12 -0
  162. package/dist/VertexFormat.js.map +1 -1
  163. package/dist/VolumetricFog.d.ts +55 -0
  164. package/dist/VolumetricFog.d.ts.map +1 -0
  165. package/dist/VolumetricFog.js +91 -0
  166. package/dist/VolumetricFog.js.map +1 -0
  167. package/dist/WaterSurface.d.ts +77 -0
  168. package/dist/WaterSurface.d.ts.map +1 -0
  169. package/dist/WaterSurface.js +133 -0
  170. package/dist/WaterSurface.js.map +1 -0
  171. package/dist/WebGL2Device.d.ts +73 -0
  172. package/dist/WebGL2Device.d.ts.map +1 -1
  173. package/dist/WebGL2Device.js +836 -79
  174. package/dist/WebGL2Device.js.map +1 -1
  175. package/dist/WebGPUDevice.d.ts +62 -1
  176. package/dist/WebGPUDevice.d.ts.map +1 -1
  177. package/dist/WebGPUDevice.js +844 -51
  178. package/dist/WebGPUDevice.js.map +1 -1
  179. package/dist/WebGPUExtensionAtlas.d.ts +6 -0
  180. package/dist/WebGPUExtensionAtlas.d.ts.map +1 -0
  181. package/dist/WebGPUExtensionAtlas.js +225 -0
  182. package/dist/WebGPUExtensionAtlas.js.map +1 -0
  183. package/dist/WebGPUSkinningLimits.d.ts +40 -0
  184. package/dist/WebGPUSkinningLimits.d.ts.map +1 -1
  185. package/dist/WebGPUSkinningLimits.js +32 -0
  186. package/dist/WebGPUSkinningLimits.js.map +1 -1
  187. package/dist/effects/CollisionModule.d.ts +9 -0
  188. package/dist/effects/CollisionModule.d.ts.map +1 -1
  189. package/dist/effects/CollisionModule.js +10 -0
  190. package/dist/effects/CollisionModule.js.map +1 -1
  191. package/dist/effects/ColorModule.d.ts +7 -0
  192. package/dist/effects/ColorModule.d.ts.map +1 -1
  193. package/dist/effects/ColorModule.js +23 -0
  194. package/dist/effects/ColorModule.js.map +1 -1
  195. package/dist/effects/ForceModule.d.ts +35 -0
  196. package/dist/effects/ForceModule.d.ts.map +1 -1
  197. package/dist/effects/ForceModule.js +49 -0
  198. package/dist/effects/ForceModule.js.map +1 -1
  199. package/dist/effects/GPUParticleBackend.d.ts +188 -0
  200. package/dist/effects/GPUParticleBackend.d.ts.map +1 -1
  201. package/dist/effects/GPUParticleBackend.js +710 -1
  202. package/dist/effects/GPUParticleBackend.js.map +1 -1
  203. package/dist/effects/HeightfieldModule.d.ts +58 -0
  204. package/dist/effects/HeightfieldModule.d.ts.map +1 -0
  205. package/dist/effects/HeightfieldModule.js +129 -0
  206. package/dist/effects/HeightfieldModule.js.map +1 -0
  207. package/dist/effects/LightingModule.d.ts +34 -0
  208. package/dist/effects/LightingModule.d.ts.map +1 -0
  209. package/dist/effects/LightingModule.js +54 -0
  210. package/dist/effects/LightingModule.js.map +1 -0
  211. package/dist/effects/ParticleDiagnostics.d.ts +8 -0
  212. package/dist/effects/ParticleDiagnostics.d.ts.map +1 -1
  213. package/dist/effects/ParticleDiagnostics.js +3 -0
  214. package/dist/effects/ParticleDiagnostics.js.map +1 -1
  215. package/dist/effects/ParticleModule.d.ts +8 -0
  216. package/dist/effects/ParticleModule.d.ts.map +1 -1
  217. package/dist/effects/ParticleModule.js.map +1 -1
  218. package/dist/effects/ParticleRenderer.d.ts +20 -0
  219. package/dist/effects/ParticleRenderer.d.ts.map +1 -1
  220. package/dist/effects/ParticleRenderer.js +57 -7
  221. package/dist/effects/ParticleRenderer.js.map +1 -1
  222. package/dist/effects/ParticleSystem.d.ts +45 -1
  223. package/dist/effects/ParticleSystem.d.ts.map +1 -1
  224. package/dist/effects/ParticleSystem.js +373 -4
  225. package/dist/effects/ParticleSystem.js.map +1 -1
  226. package/dist/effects/ResidentGPUParticleRenderer.d.ts +158 -0
  227. package/dist/effects/ResidentGPUParticleRenderer.d.ts.map +1 -0
  228. package/dist/effects/ResidentGPUParticleRenderer.js +576 -0
  229. package/dist/effects/ResidentGPUParticleRenderer.js.map +1 -0
  230. package/dist/effects/SizeModule.d.ts +4 -0
  231. package/dist/effects/SizeModule.d.ts.map +1 -1
  232. package/dist/effects/SizeModule.js +16 -0
  233. package/dist/effects/SizeModule.js.map +1 -1
  234. package/dist/effects/SubEmitterModule.d.ts +61 -0
  235. package/dist/effects/SubEmitterModule.d.ts.map +1 -0
  236. package/dist/effects/SubEmitterModule.js +119 -0
  237. package/dist/effects/SubEmitterModule.js.map +1 -0
  238. package/dist/effects/TrailModule.d.ts +38 -0
  239. package/dist/effects/TrailModule.d.ts.map +1 -1
  240. package/dist/effects/TrailModule.js +95 -2
  241. package/dist/effects/TrailModule.js.map +1 -1
  242. package/dist/effects/TurbulenceModule.d.ts +43 -0
  243. package/dist/effects/TurbulenceModule.d.ts.map +1 -0
  244. package/dist/effects/TurbulenceModule.js +130 -0
  245. package/dist/effects/TurbulenceModule.js.map +1 -0
  246. package/dist/effects/index.d.ts +4 -0
  247. package/dist/effects/index.d.ts.map +1 -1
  248. package/dist/effects/index.js +4 -0
  249. package/dist/effects/index.js.map +1 -1
  250. package/dist/extension-scalar-atlas.d.ts +3 -0
  251. package/dist/extension-scalar-atlas.d.ts.map +1 -0
  252. package/dist/extension-scalar-atlas.js +2 -0
  253. package/dist/extension-scalar-atlas.js.map +1 -0
  254. package/dist/index.d.ts +58 -19
  255. package/dist/index.d.ts.map +1 -1
  256. package/dist/index.js +32 -10
  257. package/dist/index.js.map +1 -1
  258. package/dist/lean/LeanProductionRenderer.d.ts.map +1 -1
  259. package/dist/lean/LeanProductionRenderer.js +2 -2
  260. package/dist/lean/LeanProductionRenderer.js.map +1 -1
  261. package/dist/lean-core-runtime.d.ts +13 -0
  262. package/dist/lean-core-runtime.d.ts.map +1 -0
  263. package/dist/lean-core-runtime.js +10 -0
  264. package/dist/lean-core-runtime.js.map +1 -0
  265. package/dist/materials/MaterialExtensions.js +3 -3
  266. package/dist/materials/MaterialExtensions.js.map +1 -1
  267. package/dist/postprocess/NativeBloomPyramid.d.ts +44 -0
  268. package/dist/postprocess/NativeBloomPyramid.d.ts.map +1 -0
  269. package/dist/postprocess/NativeBloomPyramid.js +84 -0
  270. package/dist/postprocess/NativeBloomPyramid.js.map +1 -0
  271. package/dist/postprocess/NativeLdrEffectLuts.d.ts +10 -1
  272. package/dist/postprocess/NativeLdrEffectLuts.d.ts.map +1 -1
  273. package/dist/postprocess/NativeLdrEffectLuts.js +19 -2
  274. package/dist/postprocess/NativeLdrEffectLuts.js.map +1 -1
  275. package/dist/postprocess/index.d.ts +2 -0
  276. package/dist/postprocess/index.d.ts.map +1 -1
  277. package/dist/postprocess/index.js +1 -0
  278. package/dist/postprocess/index.js.map +1 -1
  279. package/dist/production-runtime/PBRHDRPipeline.d.ts +22 -0
  280. package/dist/production-runtime/PBRHDRPipeline.d.ts.map +1 -1
  281. package/dist/production-runtime/PBRHDRPipeline.js +125 -0
  282. package/dist/production-runtime/PBRHDRPipeline.js.map +1 -1
  283. package/dist/production-runtime/ProductionRendererTypes.d.ts +1 -0
  284. package/dist/production-runtime/ProductionRendererTypes.d.ts.map +1 -1
  285. package/dist/production-runtime/ProductionRendererTypes.js.map +1 -1
  286. package/dist/production-runtime/ProductionRuntimeRenderer.d.ts +3 -0
  287. package/dist/production-runtime/ProductionRuntimeRenderer.d.ts.map +1 -1
  288. package/dist/production-runtime/ProductionRuntimeRenderer.js +8 -1
  289. package/dist/production-runtime/ProductionRuntimeRenderer.js.map +1 -1
  290. package/dist/production-runtime/ProductionWebGL2Renderer.d.ts +4 -0
  291. package/dist/production-runtime/ProductionWebGL2Renderer.d.ts.map +1 -1
  292. package/dist/production-runtime/ProductionWebGL2Renderer.js +15 -0
  293. package/dist/production-runtime/ProductionWebGL2Renderer.js.map +1 -1
  294. package/dist/production-runtime/ProductionWebGPURenderer.d.ts +58 -0
  295. package/dist/production-runtime/ProductionWebGPURenderer.d.ts.map +1 -1
  296. package/dist/production-runtime/ProductionWebGPURenderer.js +127 -0
  297. package/dist/production-runtime/ProductionWebGPURenderer.js.map +1 -1
  298. package/dist/production-runtime/framegraph/FrameGraph.d.ts +4 -2
  299. package/dist/production-runtime/framegraph/FrameGraph.d.ts.map +1 -1
  300. package/dist/production-runtime/framegraph/FrameGraph.js +62 -4
  301. package/dist/production-runtime/framegraph/FrameGraph.js.map +1 -1
  302. package/dist/production-runtime/framegraph/NativeFrameGraphBindings.d.ts +32 -0
  303. package/dist/production-runtime/framegraph/NativeFrameGraphBindings.d.ts.map +1 -0
  304. package/dist/production-runtime/framegraph/NativeFrameGraphBindings.js +111 -0
  305. package/dist/production-runtime/framegraph/NativeFrameGraphBindings.js.map +1 -0
  306. package/dist/production-runtime/framegraph/RenderPass.d.ts +23 -0
  307. package/dist/production-runtime/framegraph/RenderPass.d.ts.map +1 -1
  308. package/dist/production-runtime/framegraph/RenderPass.js +87 -1
  309. package/dist/production-runtime/framegraph/RenderPass.js.map +1 -1
  310. package/dist/production-runtime/index.d.ts +7 -4
  311. package/dist/production-runtime/index.d.ts.map +1 -1
  312. package/dist/production-runtime/index.js +4 -2
  313. package/dist/production-runtime/index.js.map +1 -1
  314. package/dist/production-runtime/passes/DepthPrepass.d.ts +18 -1
  315. package/dist/production-runtime/passes/DepthPrepass.d.ts.map +1 -1
  316. package/dist/production-runtime/passes/DepthPrepass.js +41 -2
  317. package/dist/production-runtime/passes/DepthPrepass.js.map +1 -1
  318. package/dist/production-runtime/passes/FramegraphTopology.d.ts +13 -0
  319. package/dist/production-runtime/passes/FramegraphTopology.d.ts.map +1 -0
  320. package/dist/production-runtime/passes/FramegraphTopology.js +59 -0
  321. package/dist/production-runtime/passes/FramegraphTopology.js.map +1 -0
  322. package/dist/production-runtime/passes/OpaquePass.d.ts +19 -1
  323. package/dist/production-runtime/passes/OpaquePass.d.ts.map +1 -1
  324. package/dist/production-runtime/passes/OpaquePass.js +43 -2
  325. package/dist/production-runtime/passes/OpaquePass.js.map +1 -1
  326. package/dist/production-runtime/passes/ShadowPass.d.ts +19 -1
  327. package/dist/production-runtime/passes/ShadowPass.d.ts.map +1 -1
  328. package/dist/production-runtime/passes/ShadowPass.js +45 -2
  329. package/dist/production-runtime/passes/ShadowPass.js.map +1 -1
  330. package/dist/production-runtime/passes/SkyboxPass.d.ts +17 -1
  331. package/dist/production-runtime/passes/SkyboxPass.d.ts.map +1 -1
  332. package/dist/production-runtime/passes/SkyboxPass.js +41 -2
  333. package/dist/production-runtime/passes/SkyboxPass.js.map +1 -1
  334. package/dist/production-runtime/passes/ToneMappingPass.d.ts +26 -1
  335. package/dist/production-runtime/passes/ToneMappingPass.d.ts.map +1 -1
  336. package/dist/production-runtime/passes/ToneMappingPass.js +62 -2
  337. package/dist/production-runtime/passes/ToneMappingPass.js.map +1 -1
  338. package/dist/production-runtime/passes/TransparentPass.d.ts +19 -1
  339. package/dist/production-runtime/passes/TransparentPass.d.ts.map +1 -1
  340. package/dist/production-runtime/passes/TransparentPass.js +47 -2
  341. package/dist/production-runtime/passes/TransparentPass.js.map +1 -1
  342. package/dist/reflection-surfaces.d.ts +5 -0
  343. package/dist/reflection-surfaces.d.ts.map +1 -0
  344. package/dist/reflection-surfaces.js +3 -0
  345. package/dist/reflection-surfaces.js.map +1 -0
  346. package/dist/shadows/CascadeHysteresis.d.ts +67 -0
  347. package/dist/shadows/CascadeHysteresis.d.ts.map +1 -0
  348. package/dist/shadows/CascadeHysteresis.js +104 -0
  349. package/dist/shadows/CascadeHysteresis.js.map +1 -0
  350. package/dist/shadows/ContactShadows.d.ts +52 -0
  351. package/dist/shadows/ContactShadows.d.ts.map +1 -1
  352. package/dist/shadows/ContactShadows.js +122 -0
  353. package/dist/shadows/ContactShadows.js.map +1 -1
  354. package/dist/shadows/SpotShadowMaps.d.ts +77 -0
  355. package/dist/shadows/SpotShadowMaps.d.ts.map +1 -0
  356. package/dist/shadows/SpotShadowMaps.js +106 -0
  357. package/dist/shadows/SpotShadowMaps.js.map +1 -0
  358. package/dist/webgpu/WebGPUPostProcess.d.ts +2 -0
  359. package/dist/webgpu/WebGPUPostProcess.d.ts.map +1 -1
  360. package/dist/webgpu/WebGPUPostProcess.js +1 -0
  361. package/dist/webgpu/WebGPUPostProcess.js.map +1 -1
  362. package/dist/webgpu/WebGPUPostShaders.d.ts +77 -0
  363. package/dist/webgpu/WebGPUPostShaders.d.ts.map +1 -0
  364. package/dist/webgpu/WebGPUPostShaders.js +261 -0
  365. package/dist/webgpu/WebGPUPostShaders.js.map +1 -0
  366. package/dist/webgpu/WebGPUTemporal.d.ts +17 -0
  367. package/dist/webgpu/WebGPUTemporal.d.ts.map +1 -0
  368. package/dist/webgpu/WebGPUTemporal.js +64 -0
  369. package/dist/webgpu/WebGPUTemporal.js.map +1 -0
  370. package/dist/webgpu.d.ts +6 -0
  371. package/dist/webgpu.d.ts.map +1 -1
  372. package/dist/webgpu.js +4 -0
  373. package/dist/webgpu.js.map +1 -1
  374. package/package.json +15 -3
@@ -1,6 +1,6 @@
1
1
  import { SHADER_CHUNKS, validateShaderChunks } from "./ShaderChunks.js";
2
2
  import { registerAnimationToonShader } from "./animation/AnimationToonMaterial.js";
3
- import { ShaderLibrary, registerLeanUnlitShader, registerLeanPbrShader, registerLeanDepthShader, registerLeanEnvironmentBackgroundShader, DEFAULT_DEPTH_SHADER_MARKER, DEFAULT_DEPTH_SHADER_NAME, DEFAULT_ENVIRONMENT_BACKGROUND_SHADER_MARKER, DEFAULT_ENVIRONMENT_BACKGROUND_SHADER_NAME, DEFAULT_INSTANCED_PBR_SHADER_MARKER, DEFAULT_INSTANCED_PBR_SHADER_NAME, DEFAULT_INSTANCED_UNLIT_SHADER_MARKER, DEFAULT_INSTANCED_UNLIT_SHADER_NAME, DEFAULT_MORPH_UNLIT_SHADER_MARKER, DEFAULT_MORPH_UNLIT_SHADER_NAME, DEFAULT_NORMAL_MAPPED_PBR_SHADER_MARKER, DEFAULT_NORMAL_MAPPED_PBR_SHADER_NAME, DEFAULT_PBR_SHADER_MARKER, DEFAULT_PBR_SHADER_NAME, DEFAULT_SCREEN_SPACE_LINE_SHADER_MARKER, DEFAULT_SCREEN_SPACE_LINE_SHADER_NAME, DEFAULT_SKINNED_LIT_EIGHT_INFLUENCE_SHADER_MARKER, DEFAULT_SKINNED_LIT_EIGHT_INFLUENCE_SHADER_NAME, DEFAULT_SKINNED_LIT_SHADER_MARKER, DEFAULT_SKINNED_LIT_SHADER_NAME, DEFAULT_SKINNED_UNLIT_EIGHT_INFLUENCE_SHADER_MARKER, DEFAULT_SKINNED_UNLIT_EIGHT_INFLUENCE_SHADER_NAME, DEFAULT_SKINNED_UNLIT_SHADER_MARKER, DEFAULT_SKINNED_UNLIT_SHADER_NAME, DEFAULT_TEXTURED_PBR_CLEARCOAT_SPECULAR_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_CLEARCOAT_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_CLEARCOAT_TRANSMISSION_VOLUME_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_IRIDESCENCE_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_SHADER_MARKER, DEFAULT_TEXTURED_PBR_SHADER_NAME, DEFAULT_TEXTURED_PBR_SPECULAR_SHEEN_ANISOTROPY_IRIDESCENCE_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_SPECULAR_SHEEN_ANISOTROPY_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_TRANSMISSION_VOLUME_TEXTURES_VARIANT, DEFAULT_TEXTURED_UNLIT_SHADER_MARKER, DEFAULT_TEXTURED_UNLIT_SHADER_NAME, DEFAULT_UNLIT_SHADER_MARKER, DEFAULT_UNLIT_SHADER_NAME, } from "./ShaderLibraryCore.js";
3
+ import { ShaderLibrary, registerLeanUnlitShader, registerLeanPbrShader, registerLeanDepthShader, registerLeanEnvironmentBackgroundShader, DEFAULT_DEPTH_SHADER_MARKER, DEFAULT_DEPTH_SHADER_NAME, DEFAULT_ENVIRONMENT_BACKGROUND_SHADER_MARKER, DEFAULT_ENVIRONMENT_BACKGROUND_SHADER_NAME, DEFAULT_INSTANCED_PBR_SHADER_MARKER, DEFAULT_INSTANCED_PBR_SHADER_NAME, DEFAULT_INSTANCED_UNLIT_SHADER_MARKER, DEFAULT_INSTANCED_UNLIT_SHADER_NAME, DEFAULT_MORPH_UNLIT_SHADER_MARKER, DEFAULT_MORPH_UNLIT_SHADER_NAME, DEFAULT_NORMAL_MAPPED_PBR_SHADER_MARKER, DEFAULT_NORMAL_MAPPED_PBR_SHADER_NAME, DEFAULT_PBR_SHADER_MARKER, DEFAULT_PBR_SHADER_NAME, DEFAULT_SCREEN_SPACE_LINE_SHADER_MARKER, DEFAULT_SCREEN_SPACE_LINE_SHADER_NAME, DEFAULT_SKINNED_LIT_EIGHT_INFLUENCE_SHADER_MARKER, DEFAULT_SKINNED_LIT_EIGHT_INFLUENCE_SHADER_NAME, DEFAULT_SKINNED_LIT_SHADER_MARKER, DEFAULT_SKINNED_LIT_SHADER_NAME, DEFAULT_SKINNED_UNLIT_EIGHT_INFLUENCE_SHADER_MARKER, DEFAULT_SKINNED_UNLIT_EIGHT_INFLUENCE_SHADER_NAME, DEFAULT_SKINNED_UNLIT_SHADER_MARKER, DEFAULT_SKINNED_UNLIT_SHADER_NAME, DEFAULT_TEXTURED_PBR_CLEARCOAT_SHEEN_ANISOTROPY_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_CLEARCOAT_IRIDESCENCE_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_EXTENSION_ATLAS_VARIANT, DEFAULT_TEXTURED_PBR_CLEARCOAT_SPECULAR_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_CLEARCOAT_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_CLEARCOAT_TRANSMISSION_VOLUME_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_IRIDESCENCE_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_SHADER_MARKER, DEFAULT_TEXTURED_PBR_SHADER_NAME, DEFAULT_TEXTURED_PBR_SPECULAR_SHEEN_ANISOTROPY_IRIDESCENCE_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_SPECULAR_SHEEN_ANISOTROPY_TEXTURES_VARIANT, DEFAULT_TEXTURED_PBR_TRANSMISSION_VOLUME_TEXTURES_VARIANT, DEFAULT_TEXTURED_UNLIT_SHADER_MARKER, DEFAULT_TEXTURED_UNLIT_SHADER_NAME, DEFAULT_UNLIT_SHADER_MARKER, DEFAULT_UNLIT_SHADER_NAME, } from "./ShaderLibraryCore.js";
4
4
  export * from "./ShaderLibraryCore.js";
5
5
  export function createDefaultShaderLibrary() {
6
6
  const library = new ShaderLibrary();
@@ -94,6 +94,7 @@ uniform float u_metallic;
94
94
  uniform float u_roughness;
95
95
  uniform vec3 u_environmentColor;
96
96
  uniform float u_environmentIntensity;
97
+ uniform float u_materialEnvironmentIntensity;
97
98
  uniform vec3 u_environmentSkyColor;
98
99
  uniform vec3 u_environmentHorizonColor;
99
100
  uniform vec3 u_environmentGroundColor;
@@ -141,6 +142,19 @@ uniform float u_pointShadowSlopeBias;
141
142
  uniform vec2 u_pointShadowTexelSize;
142
143
  uniform float u_pointShadowPcfSampleCount;
143
144
  uniform vec4 u_pointShadowPcfSamples[32];
145
+ uniform sampler2D u_spotShadowMapTexture;
146
+ uniform float u_spotShadowMapEnabled;
147
+ uniform vec3 u_spotShadowLightPosition;
148
+ uniform vec3 u_spotShadowLightDirection;
149
+ uniform mat4 u_spotShadowMatrix;
150
+ uniform vec2 u_spotShadowCone;
151
+ uniform float u_spotShadowRange;
152
+ uniform float u_spotShadowStrength;
153
+ uniform float u_spotShadowBias;
154
+ uniform float u_spotShadowSlopeBias;
155
+ uniform vec2 u_spotShadowTexelSize;
156
+ uniform float u_spotShadowPcfSampleCount;
157
+ uniform vec4 u_spotShadowPcfSamples[32];
144
158
  uniform float u_outputColorSpace;
145
159
  uniform vec3 u_cameraPosition;
146
160
  uniform float u_environmentFogEnabled;
@@ -150,6 +164,9 @@ uniform float u_environmentFogNear;
150
164
  uniform float u_environmentFogFar;
151
165
  uniform float u_environmentFogDensity;
152
166
  uniform float u_environmentFogHeightFalloff;
167
+ uniform float u_volumetricIntensity;
168
+ uniform vec3 u_volumetricLightDirection;
169
+ uniform vec3 u_volumetricLightColor;
153
170
  uniform float u_environmentFogHeightReference;
154
171
  uniform float u_environmentFogMaxOpacity;
155
172
  in vec3 v_normal;
@@ -166,15 +183,6 @@ float a3dForwardShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirectio
166
183
  vec3 receiverNormal = normalize(normal);
167
184
  vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
168
185
  float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
169
- // Slope-scaled depth bias must be evaluated per PCF sample, not once for the kernel
170
- // centre. A sample offset N texels away on a receiver sloped relative to the light sees a
171
- // depth difference proportional to N, so a centre-only bias under-compensates every outer
172
- // tap and the receiver shadows itself. Scaling by each sample's own texel distance keeps
173
- // wide kernels acne-free without inflating the constant bias into peter-panning.
174
- // The depth gradient across one shadow texel is tan(angle between receiver normal and
175
- // light), not (1 - N.L). The linear form collapses toward zero far faster than the real
176
- // gradient grows, so it under-biases exactly the grazing angles that need the most
177
- // compensation. Clamped so a near-perpendicular receiver cannot demand unbounded bias.
178
186
  float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
179
187
  float slopeTexelBias = slopeTangent * u_shadowMapSlopeBias * max(u_shadowMapTexelSize.x, u_shadowMapTexelSize.y);
180
188
  float projectedDepth = projected.z * 0.5 + 0.5;
@@ -207,9 +215,6 @@ float a3dPointShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection)
207
215
  vec4 rect = u_pointShadowFaceRects[faceIndex];
208
216
  vec2 uv = rect.xy + localUv * rect.zw;
209
217
  float normalDotLight = clamp(abs(dot(normalize(normal), normalize(lightDirection))), 0.0, 1.0);
210
- // Same per-sample, tangent-scaled slope bias as the directional and atlas paths. These
211
- // cubemap-face variants inline the sample fetch, but they share the identical acne
212
- // mechanism: a centre-only, (1 - N.L)-scaled bias under-compensates every outer tap.
213
218
  float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
214
219
  float slopeTexelBias = slopeTangent * u_pointShadowSlopeBias * max(u_pointShadowTexelSize.x, u_pointShadowTexelSize.y);
215
220
  float projectedDepth = projected.z * 0.5 + 0.5;
@@ -228,6 +233,44 @@ float a3dPointShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection)
228
233
  float occlusion = totalWeight > 0.0 ? shadowed / totalWeight : 0.0;
229
234
  return mix(1.0, 1.0 - occlusion, clamp(u_pointShadowStrength, 0.0, 1.0));
230
235
  }
236
+ float a3dSpotShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection) {
237
+ if (u_spotShadowMapEnabled < 0.5) return 1.0;
238
+ vec3 toFragment = worldPosition - u_spotShadowLightPosition;
239
+ float distanceToLight = max(length(toFragment), 0.0001);
240
+ if (distanceToLight > u_spotShadowRange) return 1.0;
241
+ vec3 coneAxis = u_spotShadowLightDirection / max(length(u_spotShadowLightDirection), 0.0001);
242
+ if (dot(toFragment / distanceToLight, coneAxis) < cos(u_spotShadowCone.x)) return 1.0;
243
+ vec4 lightPosition = u_spotShadowMatrix * vec4(worldPosition, 1.0);
244
+ vec3 projected = lightPosition.xyz / max(lightPosition.w, 0.0001);
245
+ vec2 uv = projected.xy * 0.5 + 0.5;
246
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return 1.0;
247
+ vec3 receiverNormal = normalize(normal);
248
+ vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
249
+ float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
250
+ float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
251
+ float slopeTexelBias = slopeTangent * u_spotShadowSlopeBias * max(u_spotShadowTexelSize.x, u_spotShadowTexelSize.y);
252
+ float projectedDepth = projected.z * 0.5 + 0.5;
253
+ float shadowed = 0.0;
254
+ float totalWeight = 0.0;
255
+ int sampleCount = clamp(int(u_spotShadowPcfSampleCount), 1, 32);
256
+ for (int i = 0; i < 32; ++i) {
257
+ if (i >= sampleCount) break;
258
+ vec4 sampleData = u_spotShadowPcfSamples[i];
259
+ float weight = max(sampleData.z, 0.0);
260
+ vec2 offset = sampleData.xy * u_spotShadowTexelSize;
261
+ float storedDepth = texture(u_spotShadowMapTexture, uv + offset).r;
262
+ float sampleTexelDistance = max(1.0, length(sampleData.xy));
263
+ float receiverDepth = projectedDepth - u_spotShadowBias - slopeTexelBias * sampleTexelDistance;
264
+ shadowed += (receiverDepth > storedDepth ? 1.0 : 0.0) * weight;
265
+ totalWeight += weight;
266
+ }
267
+ float occlusion = totalWeight > 0.0 ? shadowed / totalWeight : 0.0;
268
+ return mix(1.0, 1.0 - occlusion, clamp(u_spotShadowStrength, 0.0, 1.0));
269
+ }
270
+ float a3dResolveSpotShadowOverride(float baseFactor, float kind, float shadowFlag, vec3 worldPosition, vec3 normal, vec3 lightDirection) {
271
+ float spotGate = step(1.5, kind) * (1.0 - step(2.5, kind)) * step(0.5, u_spotShadowMapEnabled) * step(0.5, shadowFlag);
272
+ return mix(baseFactor, a3dSpotShadowFactor(worldPosition, normal, lightDirection), spotGate);
273
+ }
231
274
  vec2 a3dEnvironmentEquirectUv(vec3 direction, float rotation) {
232
275
  vec3 d = normalize(direction);
233
276
  float u = atan(d.z, d.x) / 6.28318530718 + 0.5 + rotation;
@@ -273,16 +316,11 @@ vec3 a3dPbrEnvironmentDiffuseInput(vec3 normal) {
273
316
  float horizonBlend = 1.0 - abs(normal.y);
274
317
  vec3 proceduralDiffuse = mix(u_environmentGroundColor, u_environmentSkyColor, skyBlend);
275
318
  proceduralDiffuse = mix(proceduralDiffuse, u_environmentHorizonColor, clamp(horizonBlend, 0.0, 1.0) * 0.55);
276
- // The ambient term must be added to the procedural contribution, not replaced by it. A mix
277
- // here discarded u_environmentColor * u_environmentIntensity entirely whenever a procedural
278
- // map was present, which is the normal case: raising ambient intensity from 0.18 to 3.0 on
279
- // the product-turntable kit produced a byte-identical frame. Ambient and a sky gradient are
280
- // separate physical contributions, so they sum.
281
319
  vec3 environmentDiffuse = ambientEnvironment + proceduralDiffuse * u_environmentMapIntensity * proceduralEnvironmentWeight;
282
320
  float sampledEnvironmentWeight = step(0.0001, u_environmentMapTextureEnabled * u_environmentMapTextureIntensity);
283
321
  float diffuseEnvironmentLod = max(u_environmentMapTextureMipCount - 1.0, 0.0);
284
322
  vec3 sampledDiffuse = a3dPbrDecodeEnvironmentSample(a3dPbrEnvironmentSampleRaw(normal, diffuseEnvironmentLod));
285
- return mix(environmentDiffuse, ambientEnvironment + sampledDiffuse * u_environmentMapTextureIntensity, sampledEnvironmentWeight);
323
+ return mix(environmentDiffuse, ambientEnvironment + sampledDiffuse * u_environmentMapTextureIntensity * u_materialEnvironmentIntensity, sampledEnvironmentWeight);
286
324
  }
287
325
  vec3 a3dPbrEnvironmentSpecularInput(vec3 normal, vec3 viewDirection, float roughness) {
288
326
  float proceduralEnvironmentWeight = step(0.0001, u_environmentMapIntensity);
@@ -304,7 +342,7 @@ vec3 a3dPbrEnvironmentSpecularInput(vec3 normal, vec3 viewDirection, float rough
304
342
  vec3 sampledSpecular = a3dPbrBoundHdrSpecularRadiance(a3dPbrDecodeEnvironmentSample(a3dPbrEnvironmentSampleRaw(reflectionDirection, environmentLod)));
305
343
  float nDotV = clamp(dot(normal, viewDirection), 0.0, 1.0);
306
344
  sampledSpecular = a3dPbrClampSampledSpecularEdgeEnergy(sampledSpecular, nDotV, clampedRoughness);
307
- sampledSpecular *= u_environmentMapTextureSpecularIntensity * sampledEnvironmentWeight * mix(0.84, 0.58, clampedRoughness);
345
+ sampledSpecular *= u_environmentMapTextureSpecularIntensity * u_materialEnvironmentIntensity * sampledEnvironmentWeight * mix(0.84, 0.58, clampedRoughness);
308
346
  return proceduralSpecular + sampledSpecular;
309
347
  }
310
348
  vec2 a3dPbrEnvironmentBrdfInput(vec3 normal, vec3 viewDirection, float roughness) {
@@ -312,10 +350,16 @@ vec2 a3dPbrEnvironmentBrdfInput(vec3 normal, vec3 viewDirection, float roughness
312
350
  vec2 lut = texture(u_environmentBrdfLutTexture, vec2(nDotV, clamp(roughness, 0.0, 1.0))).rg;
313
351
  return mix(vec2(1.0, 0.0), lut, step(0.0001, u_environmentBrdfLutEnabled));
314
352
  }
353
+ vec3 a3dLinearToSrgb(vec3 linear) {
354
+ vec3 clamped = max(linear, vec3(0.0));
355
+ vec3 low = clamped * 12.92;
356
+ vec3 high = 1.055 * pow(clamped, vec3(1.0 / 2.4)) - 0.055;
357
+ return mix(low, high, step(vec3(0.0031308), clamped));
358
+ }
315
359
  vec3 a3dPbrEncodeOutput(vec3 linearColor) {
316
360
  vec3 color = max(linearColor, vec3(0.0));
317
361
  vec3 filmic = clamp((color * (2.51 * color + 0.03)) / (color * (2.43 * color + 0.59) + 0.14), vec3(0.0), vec3(1.0));
318
- vec3 srgb = pow(filmic, vec3(1.0 / 2.2));
362
+ vec3 srgb = a3dLinearToSrgb(filmic);
319
363
  return mix(color, srgb, step(0.5, u_outputColorSpace));
320
364
  }
321
365
  void main() {
@@ -376,7 +420,7 @@ void main() {
376
420
  float inner = cos(spotShadowLayer.x * max(1.0 - spotShadowLayer.y, 0.001));
377
421
  attenuation *= smoothstep(outer, inner, cone);
378
422
  }
379
- float shadowFactor = mix(1.0, kind > 0.5 && kind < 1.5 ? a3dPointShadowFactor(v_worldPosition, normal, lightDirection) : a3dForwardShadowFactor(v_worldPosition, normal, lightDirection), step(0.5, spotShadowLayer.z));
423
+ float shadowFactor = a3dResolveSpotShadowOverride(mix(1.0, kind > 0.5 && kind < 1.5 ? a3dPointShadowFactor(v_worldPosition, normal, lightDirection) : a3dForwardShadowFactor(v_worldPosition, normal, lightDirection), step(0.5, spotShadowLayer.z)), kind, spotShadowLayer.z, v_worldPosition, normal, lightDirection);
380
424
  shaded += a3dPbrDirectLight(normal, viewDirection, lightDirection, colorIntensity.rgb, colorIntensity.a * attenuation * shadowFactor, base, u_metallic, u_roughness, 1.0, vec3(1.0));
381
425
  }
382
426
  float alpha = u_baseColor.a * v_instanceColor.a;
@@ -395,10 +439,18 @@ precision highp float;
395
439
  layout(location = 0) in vec3 a_position;
396
440
  layout(location = 2) in vec2 a_uv;
397
441
  layout(location = 4) in vec4 a_color;
442
+ layout(location = 8) in vec4 a_instanceMatrix0;
443
+ layout(location = 9) in vec4 a_instanceMatrix1;
444
+ layout(location = 10) in vec4 a_instanceMatrix2;
445
+ layout(location = 11) in vec4 a_instanceMatrix3;
446
+ layout(location = 12) in vec4 a_instanceColor;
398
447
  uniform mat4 u_modelViewProjection;
399
448
  uniform vec2 u_baseColorTextureOffset;
400
449
  uniform vec2 u_baseColorTextureScale;
401
450
  uniform float u_baseColorTextureRotation;
451
+ uniform mat4 u_instanceMatrices[64];
452
+ uniform float u_instanceCount;
453
+ uniform float u_instanceAttributeMode;
402
454
  out vec2 v_uv;
403
455
  out vec4 v_vertexColor;
404
456
  void main() {
@@ -406,8 +458,16 @@ void main() {
406
458
  float c = cos(u_baseColorTextureRotation);
407
459
  float s = sin(u_baseColorTextureRotation);
408
460
  v_uv = vec2(scaledUv.x * c - scaledUv.y * s, scaledUv.x * s + scaledUv.y * c) + u_baseColorTextureOffset;
409
- v_vertexColor = a_color;
410
- gl_Position = u_modelViewProjection * vec4(a_position, 1.0);
461
+ if (u_instanceCount > 0.5) {
462
+ int instanceIndex = clamp(gl_InstanceID, 0, max(int(u_instanceCount) - 1, 0));
463
+ mat4 attributeMatrix = mat4(a_instanceMatrix0, a_instanceMatrix1, a_instanceMatrix2, a_instanceMatrix3);
464
+ mat4 instanceMatrix = u_instanceAttributeMode > 0.5 ? attributeMatrix : u_instanceMatrices[instanceIndex];
465
+ v_vertexColor = a_color * a_instanceColor;
466
+ gl_Position = u_modelViewProjection * instanceMatrix * vec4(a_position, 1.0);
467
+ } else {
468
+ v_vertexColor = a_color;
469
+ gl_Position = u_modelViewProjection * vec4(a_position, 1.0);
470
+ }
411
471
  }
412
472
  `,
413
473
  fragment: `#version 300 es
@@ -508,6 +568,7 @@ uniform float u_baseColorTextureEnabled;
508
568
  uniform sampler2D u_normalTexture;
509
569
  uniform float u_normalTextureEnabled;
510
570
  uniform float u_normalScale;
571
+ uniform float u_wrinkleStrength;
511
572
  uniform sampler2D u_metallicRoughnessTexture;
512
573
  uniform float u_metallicRoughnessTextureEnabled;
513
574
  uniform sampler2D u_occlusionTexture;
@@ -520,6 +581,7 @@ uniform float u_metallic;
520
581
  uniform float u_roughness;
521
582
  uniform vec3 u_environmentColor;
522
583
  uniform float u_environmentIntensity;
584
+ uniform float u_materialEnvironmentIntensity;
523
585
  uniform vec3 u_environmentSkyColor;
524
586
  uniform vec3 u_environmentHorizonColor;
525
587
  uniform vec3 u_environmentGroundColor;
@@ -593,6 +655,19 @@ uniform float u_pointShadowSlopeBias;
593
655
  uniform vec2 u_pointShadowTexelSize;
594
656
  uniform float u_pointShadowPcfSampleCount;
595
657
  uniform vec4 u_pointShadowPcfSamples[32];
658
+ uniform sampler2D u_spotShadowMapTexture;
659
+ uniform float u_spotShadowMapEnabled;
660
+ uniform vec3 u_spotShadowLightPosition;
661
+ uniform vec3 u_spotShadowLightDirection;
662
+ uniform mat4 u_spotShadowMatrix;
663
+ uniform vec2 u_spotShadowCone;
664
+ uniform float u_spotShadowRange;
665
+ uniform float u_spotShadowStrength;
666
+ uniform float u_spotShadowBias;
667
+ uniform float u_spotShadowSlopeBias;
668
+ uniform vec2 u_spotShadowTexelSize;
669
+ uniform float u_spotShadowPcfSampleCount;
670
+ uniform vec4 u_spotShadowPcfSamples[32];
596
671
  uniform float u_outputColorSpace;
597
672
  uniform vec3 u_cameraPosition;
598
673
  uniform float u_environmentFogEnabled;
@@ -602,6 +677,9 @@ uniform float u_environmentFogNear;
602
677
  uniform float u_environmentFogFar;
603
678
  uniform float u_environmentFogDensity;
604
679
  uniform float u_environmentFogHeightFalloff;
680
+ uniform float u_volumetricIntensity;
681
+ uniform vec3 u_volumetricLightDirection;
682
+ uniform vec3 u_volumetricLightColor;
605
683
  uniform float u_environmentFogHeightReference;
606
684
  uniform float u_environmentFogMaxOpacity;
607
685
  in vec3 v_normal;
@@ -649,6 +727,17 @@ vec4 a3dPbrEnvironmentSampleRaw(vec3 direction, float lod) {
649
727
  vec4 cubeSample = textureLod(u_environmentCubeMapTexture, a3dEnvironmentCubeDirection(direction, u_environmentMapTextureRotation), lod);
650
728
  return mix(equirectSample, cubeSample, step(0.5, u_environmentCubeMapTextureEnabled));
651
729
  }
730
+ /**
731
+ * Procedural wrinkle detail (E1 face-rig demo): high-frequency normal perturbation driven
732
+ * by u_wrinkleStrength, which the engine resolves per frame from live morph weights
733
+ * (resolveWrinkleMapStrength). Stable in world space (no time input); strength 0 uploads
734
+ * 0 and the guarded call site below leaves the normal bit-identical.
735
+ */
736
+ vec3 a3dWrinkleDetail(vec3 p) {
737
+ float f = sin(p.x * 230.0) * sin(p.y * 197.0 + 1.7) * sin(p.z * 211.0 + 0.6);
738
+ float g = sin(p.x * 97.0 + 0.4) * sin(p.y * 113.0 + 2.1) * sin(p.z * 103.0 + 1.2);
739
+ return vec3(f, g, f * g) * 0.35;
740
+ }
652
741
  vec3 a3dSkinnedPbrNormal(vec3 baseNormal, vec4 tangentFrame, vec2 uv) {
653
742
  vec3 sampled = texture(u_normalTexture, uv).xyz * 2.0 - 1.0;
654
743
  vec3 n = normalize(baseNormal);
@@ -665,15 +754,6 @@ float a3dForwardShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirectio
665
754
  vec3 receiverNormal = normalize(normal);
666
755
  vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
667
756
  float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
668
- // Slope-scaled depth bias must be evaluated per PCF sample, not once for the kernel
669
- // centre. A sample offset N texels away on a receiver sloped relative to the light sees a
670
- // depth difference proportional to N, so a centre-only bias under-compensates every outer
671
- // tap and the receiver shadows itself. Scaling by each sample's own texel distance keeps
672
- // wide kernels acne-free without inflating the constant bias into peter-panning.
673
- // The depth gradient across one shadow texel is tan(angle between receiver normal and
674
- // light), not (1 - N.L). The linear form collapses toward zero far faster than the real
675
- // gradient grows, so it under-biases exactly the grazing angles that need the most
676
- // compensation. Clamped so a near-perpendicular receiver cannot demand unbounded bias.
677
757
  float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
678
758
  float slopeTexelBias = slopeTangent * u_shadowMapSlopeBias * max(u_shadowMapTexelSize.x, u_shadowMapTexelSize.y);
679
759
  float projectedDepth = projected.z * 0.5 + 0.5;
@@ -715,15 +795,6 @@ float a3dPointShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection)
715
795
  vec3 receiverNormal = normalize(normal);
716
796
  vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
717
797
  float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
718
- // Slope-scaled depth bias must be evaluated per PCF sample, not once for the kernel
719
- // centre. A sample offset N texels away on a receiver sloped relative to the light sees a
720
- // depth difference proportional to N, so a centre-only bias under-compensates every outer
721
- // tap and the receiver shadows itself. Scaling by each sample's own texel distance keeps
722
- // wide kernels acne-free without inflating the constant bias into peter-panning.
723
- // The depth gradient across one shadow texel is tan(angle between receiver normal and
724
- // light), not (1 - N.L). The linear form collapses toward zero far faster than the real
725
- // gradient grows, so it under-biases exactly the grazing angles that need the most
726
- // compensation. Clamped so a near-perpendicular receiver cannot demand unbounded bias.
727
798
  float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
728
799
  float slopeTexelBias = slopeTangent * u_pointShadowSlopeBias * max(u_pointShadowTexelSize.x, u_pointShadowTexelSize.y);
729
800
  float projectedDepth = projected.z * 0.5 + 0.5;
@@ -744,15 +815,62 @@ float a3dPointShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection)
744
815
  float occlusion = totalWeight > 0.0 ? shadowed / totalWeight : 0.0;
745
816
  return mix(1.0, 1.0 - occlusion, clamp(u_pointShadowStrength, 0.0, 1.0));
746
817
  }
818
+ float a3dSpotShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection) {
819
+ if (u_spotShadowMapEnabled < 0.5) return 1.0;
820
+ vec3 toFragment = worldPosition - u_spotShadowLightPosition;
821
+ float distanceToLight = max(length(toFragment), 0.0001);
822
+ if (distanceToLight > u_spotShadowRange) return 1.0;
823
+ vec3 coneAxis = u_spotShadowLightDirection / max(length(u_spotShadowLightDirection), 0.0001);
824
+ if (dot(toFragment / distanceToLight, coneAxis) < cos(u_spotShadowCone.x)) return 1.0;
825
+ vec4 lightPosition = u_spotShadowMatrix * vec4(worldPosition, 1.0);
826
+ vec3 projected = lightPosition.xyz / max(lightPosition.w, 0.0001);
827
+ vec2 uv = projected.xy * 0.5 + 0.5;
828
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return 1.0;
829
+ vec3 receiverNormal = normalize(normal);
830
+ vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
831
+ float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
832
+ float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
833
+ float slopeTexelBias = slopeTangent * u_spotShadowSlopeBias * max(u_spotShadowTexelSize.x, u_spotShadowTexelSize.y);
834
+ float projectedDepth = projected.z * 0.5 + 0.5;
835
+ float shadowed = 0.0;
836
+ float totalWeight = 0.0;
837
+ int sampleCount = clamp(int(u_spotShadowPcfSampleCount), 1, 32);
838
+ for (int i = 0; i < 32; ++i) {
839
+ if (i >= sampleCount) break;
840
+ vec4 sampleData = u_spotShadowPcfSamples[i];
841
+ float weight = max(sampleData.z, 0.0);
842
+ vec2 offset = sampleData.xy * u_spotShadowTexelSize;
843
+ float storedDepth = texture(u_spotShadowMapTexture, uv + offset).r;
844
+ float sampleTexelDistance = max(1.0, length(sampleData.xy));
845
+ float receiverDepth = projectedDepth - u_spotShadowBias - slopeTexelBias * sampleTexelDistance;
846
+ shadowed += (receiverDepth > storedDepth ? 1.0 : 0.0) * weight;
847
+ totalWeight += weight;
848
+ }
849
+ float occlusion = totalWeight > 0.0 ? shadowed / totalWeight : 0.0;
850
+ return mix(1.0, 1.0 - occlusion, clamp(u_spotShadowStrength, 0.0, 1.0));
851
+ }
852
+ float a3dResolveSpotShadowOverride(float baseFactor, float kind, float shadowFlag, vec3 worldPosition, vec3 normal, vec3 lightDirection) {
853
+ float spotGate = step(1.5, kind) * (1.0 - step(2.5, kind)) * step(0.5, u_spotShadowMapEnabled) * step(0.5, shadowFlag);
854
+ return mix(baseFactor, a3dSpotShadowFactor(worldPosition, normal, lightDirection), spotGate);
855
+ }
856
+ vec3 a3dLinearToSrgb(vec3 linear) {
857
+ vec3 clamped = max(linear, vec3(0.0));
858
+ vec3 low = clamped * 12.92;
859
+ vec3 high = 1.055 * pow(clamped, vec3(1.0 / 2.4)) - 0.055;
860
+ return mix(low, high, step(vec3(0.0031308), clamped));
861
+ }
747
862
  vec3 a3dPbrEncodeOutput(vec3 linearColor) {
748
863
  vec3 color = max(linearColor, vec3(0.0));
749
864
  vec3 filmic = clamp((color * (2.51 * color + 0.03)) / (color * (2.43 * color + 0.59) + 0.14), vec3(0.0), vec3(1.0));
750
- vec3 srgb = pow(filmic, vec3(1.0 / 2.2));
865
+ vec3 srgb = a3dLinearToSrgb(filmic);
751
866
  return mix(color, srgb, step(0.5, u_outputColorSpace));
752
867
  }
753
868
  void main() {
754
869
  vec3 normal = normalize(v_normal);
755
870
  normal = mix(normal, a3dSkinnedPbrNormal(v_normal, v_tangent, v_uv), step(0.5, u_normalTextureEnabled));
871
+ if (u_wrinkleStrength > 0.0) {
872
+ normal = normalize(normal + u_wrinkleStrength * a3dWrinkleDetail(v_worldPosition));
873
+ }
756
874
  if (!gl_FrontFacing) normal = -normal;
757
875
  vec3 viewDirection = normalize(u_cameraPosition - v_worldPosition);
758
876
  vec4 sampledBaseColor = texture(u_baseColorTexture, v_uv);
@@ -795,11 +913,6 @@ void main() {
795
913
  float horizonBlend = 1.0 - abs(normal.y);
796
914
  vec3 proceduralDiffuse = mix(u_environmentGroundColor, u_environmentSkyColor, skyBlend);
797
915
  proceduralDiffuse = mix(proceduralDiffuse, u_environmentHorizonColor, clamp(horizonBlend, 0.0, 1.0) * 0.55);
798
- // The ambient term must be added to the procedural contribution, not replaced by it. A mix
799
- // here discarded u_environmentColor * u_environmentIntensity entirely whenever a procedural
800
- // map was present, which is the normal case: raising ambient intensity from 0.18 to 3.0 on
801
- // the product-turntable kit produced a byte-identical frame. Ambient and a sky gradient are
802
- // separate physical contributions, so they sum.
803
916
  vec3 environmentDiffuse = ambientEnvironment + proceduralDiffuse * u_environmentMapIntensity * proceduralEnvironmentWeight;
804
917
  vec3 reflectionDirection = reflect(-viewDirection, normal);
805
918
  float sampledEnvironmentWeight = step(0.0001, u_environmentMapTextureEnabled * u_environmentMapTextureIntensity);
@@ -822,11 +935,11 @@ void main() {
822
935
  float nDotV = clamp(dot(normal, viewDirection), 0.0, 1.0);
823
936
  vec2 brdfLut = texture(u_environmentBrdfLutTexture, vec2(nDotV, clampedRoughness)).rg;
824
937
  sampledSpecular = a3dPbrClampSampledSpecularEdgeEnergy(sampledSpecular, nDotV, clampedRoughness);
825
- sampledSpecular *= u_environmentMapTextureSpecularIntensity * sampledEnvironmentWeight * mix(1.1, 0.65, roughness);
938
+ sampledSpecular *= u_environmentMapTextureSpecularIntensity * u_materialEnvironmentIntensity * sampledEnvironmentWeight * mix(1.1, 0.65, roughness);
826
939
  vec3 shaded = a3dPbrEnvironmentLightSplitSum(
827
940
  normal,
828
941
  viewDirection,
829
- mix(environmentDiffuse, ambientEnvironment + sampledDiffuse * u_environmentMapTextureIntensity, sampledEnvironmentWeight),
942
+ mix(environmentDiffuse, ambientEnvironment + sampledDiffuse * u_environmentMapTextureIntensity * u_materialEnvironmentIntensity, sampledEnvironmentWeight),
830
943
  proceduralSpecular + sampledSpecular,
831
944
  mix(vec2(1.0, 0.0), brdfLut, step(0.0001, u_environmentBrdfLutEnabled)),
832
945
  materialBase,
@@ -881,7 +994,7 @@ void main() {
881
994
  viewDirection,
882
995
  lightDirection,
883
996
  colorIntensity.rgb,
884
- colorIntensity.a * attenuation * mix(1.0, kind > 0.5 && kind < 1.5 ? a3dPointShadowFactor(v_worldPosition, normal, lightDirection) : a3dForwardShadowFactor(v_worldPosition, normal, lightDirection), step(0.5, spotShadowLayer.z)),
997
+ colorIntensity.a * attenuation * a3dResolveSpotShadowOverride(mix(1.0, kind > 0.5 && kind < 1.5 ? a3dPointShadowFactor(v_worldPosition, normal, lightDirection) : a3dForwardShadowFactor(v_worldPosition, normal, lightDirection), step(0.5, spotShadowLayer.z)), kind, spotShadowLayer.z, v_worldPosition, normal, lightDirection),
885
998
  materialBase,
886
999
  metallic,
887
1000
  roughness,
@@ -966,8 +1079,6 @@ void main() {
966
1079
  a_weights.x + a_weights.y + a_weights.z + a_weights.w +
967
1080
  a_weights1.x + a_weights1.y + a_weights1.z + a_weights1.w;
968
1081
  vec4 skinnedPosition = v_weightSum > 0.0001 ? skin * vec4(a_position, 1.0) : vec4(a_position, 1.0);
969
- // Normals and tangents use the same skin matrix with w = 0 so translation is
970
- // excluded; renormalizing happens in the fragment stage.
971
1082
  vec3 skinnedNormal = (v_weightSum > 0.0001 ? skin * vec4(a_normal, 0.0) : vec4(a_normal, 0.0)).xyz;
972
1083
  vec3 skinnedTangent = (v_weightSum > 0.0001 ? skin * vec4(a_tangent.xyz, 0.0) : vec4(a_tangent.xyz, 0.0)).xyz;
973
1084
  v_normal = mat3(u_normalMatrix) * skinnedNormal;
@@ -987,6 +1098,7 @@ uniform float u_baseColorTextureEnabled;
987
1098
  uniform sampler2D u_normalTexture;
988
1099
  uniform float u_normalTextureEnabled;
989
1100
  uniform float u_normalScale;
1101
+ uniform float u_wrinkleStrength;
990
1102
  uniform sampler2D u_metallicRoughnessTexture;
991
1103
  uniform float u_metallicRoughnessTextureEnabled;
992
1104
  uniform sampler2D u_occlusionTexture;
@@ -999,6 +1111,7 @@ uniform float u_metallic;
999
1111
  uniform float u_roughness;
1000
1112
  uniform vec3 u_environmentColor;
1001
1113
  uniform float u_environmentIntensity;
1114
+ uniform float u_materialEnvironmentIntensity;
1002
1115
  uniform vec3 u_environmentSkyColor;
1003
1116
  uniform vec3 u_environmentHorizonColor;
1004
1117
  uniform vec3 u_environmentGroundColor;
@@ -1072,6 +1185,19 @@ uniform float u_pointShadowSlopeBias;
1072
1185
  uniform vec2 u_pointShadowTexelSize;
1073
1186
  uniform float u_pointShadowPcfSampleCount;
1074
1187
  uniform vec4 u_pointShadowPcfSamples[32];
1188
+ uniform sampler2D u_spotShadowMapTexture;
1189
+ uniform float u_spotShadowMapEnabled;
1190
+ uniform vec3 u_spotShadowLightPosition;
1191
+ uniform vec3 u_spotShadowLightDirection;
1192
+ uniform mat4 u_spotShadowMatrix;
1193
+ uniform vec2 u_spotShadowCone;
1194
+ uniform float u_spotShadowRange;
1195
+ uniform float u_spotShadowStrength;
1196
+ uniform float u_spotShadowBias;
1197
+ uniform float u_spotShadowSlopeBias;
1198
+ uniform vec2 u_spotShadowTexelSize;
1199
+ uniform float u_spotShadowPcfSampleCount;
1200
+ uniform vec4 u_spotShadowPcfSamples[32];
1075
1201
  uniform float u_outputColorSpace;
1076
1202
  uniform vec3 u_cameraPosition;
1077
1203
  uniform float u_environmentFogEnabled;
@@ -1081,6 +1207,9 @@ uniform float u_environmentFogNear;
1081
1207
  uniform float u_environmentFogFar;
1082
1208
  uniform float u_environmentFogDensity;
1083
1209
  uniform float u_environmentFogHeightFalloff;
1210
+ uniform float u_volumetricIntensity;
1211
+ uniform vec3 u_volumetricLightDirection;
1212
+ uniform vec3 u_volumetricLightColor;
1084
1213
  uniform float u_environmentFogHeightReference;
1085
1214
  uniform float u_environmentFogMaxOpacity;
1086
1215
  in vec3 v_normal;
@@ -1128,6 +1257,17 @@ vec4 a3dPbrEnvironmentSampleRaw(vec3 direction, float lod) {
1128
1257
  vec4 cubeSample = textureLod(u_environmentCubeMapTexture, a3dEnvironmentCubeDirection(direction, u_environmentMapTextureRotation), lod);
1129
1258
  return mix(equirectSample, cubeSample, step(0.5, u_environmentCubeMapTextureEnabled));
1130
1259
  }
1260
+ /**
1261
+ * Procedural wrinkle detail (E1 face-rig demo): high-frequency normal perturbation driven
1262
+ * by u_wrinkleStrength, which the engine resolves per frame from live morph weights
1263
+ * (resolveWrinkleMapStrength). Stable in world space (no time input); strength 0 uploads
1264
+ * 0 and the guarded call site below leaves the normal bit-identical.
1265
+ */
1266
+ vec3 a3dWrinkleDetail(vec3 p) {
1267
+ float f = sin(p.x * 230.0) * sin(p.y * 197.0 + 1.7) * sin(p.z * 211.0 + 0.6);
1268
+ float g = sin(p.x * 97.0 + 0.4) * sin(p.y * 113.0 + 2.1) * sin(p.z * 103.0 + 1.2);
1269
+ return vec3(f, g, f * g) * 0.35;
1270
+ }
1131
1271
  vec3 a3dSkinnedPbrNormal(vec3 baseNormal, vec4 tangentFrame, vec2 uv) {
1132
1272
  vec3 sampled = texture(u_normalTexture, uv).xyz * 2.0 - 1.0;
1133
1273
  vec3 n = normalize(baseNormal);
@@ -1144,15 +1284,6 @@ float a3dForwardShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirectio
1144
1284
  vec3 receiverNormal = normalize(normal);
1145
1285
  vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
1146
1286
  float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
1147
- // Slope-scaled depth bias must be evaluated per PCF sample, not once for the kernel
1148
- // centre. A sample offset N texels away on a receiver sloped relative to the light sees a
1149
- // depth difference proportional to N, so a centre-only bias under-compensates every outer
1150
- // tap and the receiver shadows itself. Scaling by each sample's own texel distance keeps
1151
- // wide kernels acne-free without inflating the constant bias into peter-panning.
1152
- // The depth gradient across one shadow texel is tan(angle between receiver normal and
1153
- // light), not (1 - N.L). The linear form collapses toward zero far faster than the real
1154
- // gradient grows, so it under-biases exactly the grazing angles that need the most
1155
- // compensation. Clamped so a near-perpendicular receiver cannot demand unbounded bias.
1156
1287
  float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
1157
1288
  float slopeTexelBias = slopeTangent * u_shadowMapSlopeBias * max(u_shadowMapTexelSize.x, u_shadowMapTexelSize.y);
1158
1289
  float projectedDepth = projected.z * 0.5 + 0.5;
@@ -1194,15 +1325,6 @@ float a3dPointShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection)
1194
1325
  vec3 receiverNormal = normalize(normal);
1195
1326
  vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
1196
1327
  float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
1197
- // Slope-scaled depth bias must be evaluated per PCF sample, not once for the kernel
1198
- // centre. A sample offset N texels away on a receiver sloped relative to the light sees a
1199
- // depth difference proportional to N, so a centre-only bias under-compensates every outer
1200
- // tap and the receiver shadows itself. Scaling by each sample's own texel distance keeps
1201
- // wide kernels acne-free without inflating the constant bias into peter-panning.
1202
- // The depth gradient across one shadow texel is tan(angle between receiver normal and
1203
- // light), not (1 - N.L). The linear form collapses toward zero far faster than the real
1204
- // gradient grows, so it under-biases exactly the grazing angles that need the most
1205
- // compensation. Clamped so a near-perpendicular receiver cannot demand unbounded bias.
1206
1328
  float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
1207
1329
  float slopeTexelBias = slopeTangent * u_pointShadowSlopeBias * max(u_pointShadowTexelSize.x, u_pointShadowTexelSize.y);
1208
1330
  float projectedDepth = projected.z * 0.5 + 0.5;
@@ -1223,15 +1345,62 @@ float a3dPointShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection)
1223
1345
  float occlusion = totalWeight > 0.0 ? shadowed / totalWeight : 0.0;
1224
1346
  return mix(1.0, 1.0 - occlusion, clamp(u_pointShadowStrength, 0.0, 1.0));
1225
1347
  }
1348
+ float a3dSpotShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection) {
1349
+ if (u_spotShadowMapEnabled < 0.5) return 1.0;
1350
+ vec3 toFragment = worldPosition - u_spotShadowLightPosition;
1351
+ float distanceToLight = max(length(toFragment), 0.0001);
1352
+ if (distanceToLight > u_spotShadowRange) return 1.0;
1353
+ vec3 coneAxis = u_spotShadowLightDirection / max(length(u_spotShadowLightDirection), 0.0001);
1354
+ if (dot(toFragment / distanceToLight, coneAxis) < cos(u_spotShadowCone.x)) return 1.0;
1355
+ vec4 lightPosition = u_spotShadowMatrix * vec4(worldPosition, 1.0);
1356
+ vec3 projected = lightPosition.xyz / max(lightPosition.w, 0.0001);
1357
+ vec2 uv = projected.xy * 0.5 + 0.5;
1358
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return 1.0;
1359
+ vec3 receiverNormal = normalize(normal);
1360
+ vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
1361
+ float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
1362
+ float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
1363
+ float slopeTexelBias = slopeTangent * u_spotShadowSlopeBias * max(u_spotShadowTexelSize.x, u_spotShadowTexelSize.y);
1364
+ float projectedDepth = projected.z * 0.5 + 0.5;
1365
+ float shadowed = 0.0;
1366
+ float totalWeight = 0.0;
1367
+ int sampleCount = clamp(int(u_spotShadowPcfSampleCount), 1, 32);
1368
+ for (int i = 0; i < 32; ++i) {
1369
+ if (i >= sampleCount) break;
1370
+ vec4 sampleData = u_spotShadowPcfSamples[i];
1371
+ float weight = max(sampleData.z, 0.0);
1372
+ vec2 offset = sampleData.xy * u_spotShadowTexelSize;
1373
+ float storedDepth = texture(u_spotShadowMapTexture, uv + offset).r;
1374
+ float sampleTexelDistance = max(1.0, length(sampleData.xy));
1375
+ float receiverDepth = projectedDepth - u_spotShadowBias - slopeTexelBias * sampleTexelDistance;
1376
+ shadowed += (receiverDepth > storedDepth ? 1.0 : 0.0) * weight;
1377
+ totalWeight += weight;
1378
+ }
1379
+ float occlusion = totalWeight > 0.0 ? shadowed / totalWeight : 0.0;
1380
+ return mix(1.0, 1.0 - occlusion, clamp(u_spotShadowStrength, 0.0, 1.0));
1381
+ }
1382
+ float a3dResolveSpotShadowOverride(float baseFactor, float kind, float shadowFlag, vec3 worldPosition, vec3 normal, vec3 lightDirection) {
1383
+ float spotGate = step(1.5, kind) * (1.0 - step(2.5, kind)) * step(0.5, u_spotShadowMapEnabled) * step(0.5, shadowFlag);
1384
+ return mix(baseFactor, a3dSpotShadowFactor(worldPosition, normal, lightDirection), spotGate);
1385
+ }
1386
+ vec3 a3dLinearToSrgb(vec3 linear) {
1387
+ vec3 clamped = max(linear, vec3(0.0));
1388
+ vec3 low = clamped * 12.92;
1389
+ vec3 high = 1.055 * pow(clamped, vec3(1.0 / 2.4)) - 0.055;
1390
+ return mix(low, high, step(vec3(0.0031308), clamped));
1391
+ }
1226
1392
  vec3 a3dPbrEncodeOutput(vec3 linearColor) {
1227
1393
  vec3 color = max(linearColor, vec3(0.0));
1228
1394
  vec3 filmic = clamp((color * (2.51 * color + 0.03)) / (color * (2.43 * color + 0.59) + 0.14), vec3(0.0), vec3(1.0));
1229
- vec3 srgb = pow(filmic, vec3(1.0 / 2.2));
1395
+ vec3 srgb = a3dLinearToSrgb(filmic);
1230
1396
  return mix(color, srgb, step(0.5, u_outputColorSpace));
1231
1397
  }
1232
1398
  void main() {
1233
1399
  vec3 normal = normalize(v_normal);
1234
1400
  normal = mix(normal, a3dSkinnedPbrNormal(v_normal, v_tangent, v_uv), step(0.5, u_normalTextureEnabled));
1401
+ if (u_wrinkleStrength > 0.0) {
1402
+ normal = normalize(normal + u_wrinkleStrength * a3dWrinkleDetail(v_worldPosition));
1403
+ }
1235
1404
  if (!gl_FrontFacing) normal = -normal;
1236
1405
  vec3 viewDirection = normalize(u_cameraPosition - v_worldPosition);
1237
1406
  vec4 sampledBaseColor = texture(u_baseColorTexture, v_uv);
@@ -1274,11 +1443,6 @@ void main() {
1274
1443
  float horizonBlend = 1.0 - abs(normal.y);
1275
1444
  vec3 proceduralDiffuse = mix(u_environmentGroundColor, u_environmentSkyColor, skyBlend);
1276
1445
  proceduralDiffuse = mix(proceduralDiffuse, u_environmentHorizonColor, clamp(horizonBlend, 0.0, 1.0) * 0.55);
1277
- // The ambient term must be added to the procedural contribution, not replaced by it. A mix
1278
- // here discarded u_environmentColor * u_environmentIntensity entirely whenever a procedural
1279
- // map was present, which is the normal case: raising ambient intensity from 0.18 to 3.0 on
1280
- // the product-turntable kit produced a byte-identical frame. Ambient and a sky gradient are
1281
- // separate physical contributions, so they sum.
1282
1446
  vec3 environmentDiffuse = ambientEnvironment + proceduralDiffuse * u_environmentMapIntensity * proceduralEnvironmentWeight;
1283
1447
  vec3 reflectionDirection = reflect(-viewDirection, normal);
1284
1448
  float sampledEnvironmentWeight = step(0.0001, u_environmentMapTextureEnabled * u_environmentMapTextureIntensity);
@@ -1301,11 +1465,11 @@ void main() {
1301
1465
  float nDotV = clamp(dot(normal, viewDirection), 0.0, 1.0);
1302
1466
  vec2 brdfLut = texture(u_environmentBrdfLutTexture, vec2(nDotV, clampedRoughness)).rg;
1303
1467
  sampledSpecular = a3dPbrClampSampledSpecularEdgeEnergy(sampledSpecular, nDotV, clampedRoughness);
1304
- sampledSpecular *= u_environmentMapTextureSpecularIntensity * sampledEnvironmentWeight * mix(1.1, 0.65, roughness);
1468
+ sampledSpecular *= u_environmentMapTextureSpecularIntensity * u_materialEnvironmentIntensity * sampledEnvironmentWeight * mix(1.1, 0.65, roughness);
1305
1469
  vec3 shaded = a3dPbrEnvironmentLightSplitSum(
1306
1470
  normal,
1307
1471
  viewDirection,
1308
- mix(environmentDiffuse, ambientEnvironment + sampledDiffuse * u_environmentMapTextureIntensity, sampledEnvironmentWeight),
1472
+ mix(environmentDiffuse, ambientEnvironment + sampledDiffuse * u_environmentMapTextureIntensity * u_materialEnvironmentIntensity, sampledEnvironmentWeight),
1309
1473
  proceduralSpecular + sampledSpecular,
1310
1474
  mix(vec2(1.0, 0.0), brdfLut, step(0.0001, u_environmentBrdfLutEnabled)),
1311
1475
  materialBase,
@@ -1360,7 +1524,7 @@ void main() {
1360
1524
  viewDirection,
1361
1525
  lightDirection,
1362
1526
  colorIntensity.rgb,
1363
- colorIntensity.a * attenuation * mix(1.0, kind > 0.5 && kind < 1.5 ? a3dPointShadowFactor(v_worldPosition, normal, lightDirection) : a3dForwardShadowFactor(v_worldPosition, normal, lightDirection), step(0.5, spotShadowLayer.z)),
1527
+ colorIntensity.a * attenuation * a3dResolveSpotShadowOverride(mix(1.0, kind > 0.5 && kind < 1.5 ? a3dPointShadowFactor(v_worldPosition, normal, lightDirection) : a3dForwardShadowFactor(v_worldPosition, normal, lightDirection), step(0.5, spotShadowLayer.z)), kind, spotShadowLayer.z, v_worldPosition, normal, lightDirection),
1364
1528
  materialBase,
1365
1529
  metallic,
1366
1530
  roughness,
@@ -1382,18 +1546,15 @@ void main() {
1382
1546
  precision highp float;
1383
1547
  layout(location = 0) in vec3 a_position;
1384
1548
  uniform mat4 u_modelViewProjection;
1385
- // Uniform fast path (<= 4 targets / 64 verts):
1386
1549
  uniform vec4 u_morphPositionDeltas[256];
1387
1550
  uniform vec4 u_morphWeights;
1388
1551
  uniform float u_morphTargetCount;
1389
- // Texture-backed path (lifts the 4/64 cap; see MorphTargetPlan.ts createMorphTargetPlan):
1390
1552
  uniform sampler2D u_morphDeltaTexture;
1391
- uniform float u_morphUsesTexture; // > 0.5 => sample deltas from the texture
1392
- uniform float u_morphTextureWidth; // = vertexCount
1393
- uniform float u_morphRowsPerTarget; // 1 = positions only, 2 = positions + normals
1394
- uniform float u_morphWeightArray[64]; // per-target weights for the texture path
1553
+ uniform float u_morphUsesTexture;
1554
+ uniform float u_morphTextureWidth;
1555
+ uniform float u_morphRowsPerTarget;
1556
+ uniform float u_morphWeightArray[64];
1395
1557
  vec3 sampleMorphTexel(float row, float vertex) {
1396
- // Fetch RGBA float texel at (x=vertex, y=row) using normalized coords + nearest filtering.
1397
1558
  float u = (vertex + 0.5) / max(1.0, u_morphTextureWidth);
1398
1559
  float height = u_morphTargetCount * u_morphRowsPerTarget;
1399
1560
  float v = (row + 0.5) / max(1.0, height);
@@ -1443,21 +1604,42 @@ layout(location = 2) in vec2 a_uv;
1443
1604
  layout(location = 3) in vec4 a_tangent;
1444
1605
  layout(location = 4) in vec4 a_color;
1445
1606
  layout(location = 7) in vec2 a_uv1;
1607
+ layout(location = 8) in vec4 a_instanceMatrix0;
1608
+ layout(location = 9) in vec4 a_instanceMatrix1;
1609
+ layout(location = 10) in vec4 a_instanceMatrix2;
1610
+ layout(location = 11) in vec4 a_instanceMatrix3;
1611
+ layout(location = 12) in vec4 a_instanceColor;
1446
1612
  uniform mat4 u_modelViewProjection;
1447
1613
  uniform mat4 u_modelMatrix;
1448
1614
  uniform mat4 u_normalMatrix;
1615
+ uniform mat4 u_instanceMatrices[64];
1616
+ uniform float u_instanceCount;
1617
+ uniform float u_instanceAttributeMode;
1449
1618
  out vec3 v_normal;
1450
1619
  out vec4 v_tangent;
1451
1620
  out vec3 v_worldPosition;
1452
1621
  out vec2 v_uv;
1453
1622
  out vec4 v_vertexColor;
1454
1623
  void main() {
1455
- v_normal = mat3(u_normalMatrix) * a_normal;
1456
- v_tangent = vec4(mat3(u_normalMatrix) * a_tangent.xyz, a_tangent.w);
1457
- v_worldPosition = (u_modelMatrix * vec4(a_position, 1.0)).xyz;
1458
- v_uv = a_uv;
1459
- v_vertexColor = a_color;
1460
- gl_Position = u_modelViewProjection * vec4(a_position, 1.0);
1624
+ if (u_instanceCount > 0.5) {
1625
+ int instanceIndex = clamp(gl_InstanceID, 0, max(int(u_instanceCount) - 1, 0));
1626
+ mat4 attributeMatrix = mat4(a_instanceMatrix0, a_instanceMatrix1, a_instanceMatrix2, a_instanceMatrix3);
1627
+ mat4 instanceMatrix = u_instanceAttributeMode > 0.5 ? attributeMatrix : u_instanceMatrices[instanceIndex];
1628
+ vec4 worldPosition = u_modelMatrix * instanceMatrix * vec4(a_position, 1.0);
1629
+ v_normal = mat3(u_normalMatrix) * transpose(inverse(mat3(instanceMatrix))) * a_normal;
1630
+ v_tangent = vec4(mat3(u_normalMatrix) * (mat3(instanceMatrix) * a_tangent.xyz), a_tangent.w);
1631
+ v_worldPosition = worldPosition.xyz;
1632
+ v_uv = a_uv;
1633
+ v_vertexColor = a_color * a_instanceColor;
1634
+ gl_Position = u_modelViewProjection * instanceMatrix * vec4(a_position, 1.0);
1635
+ } else {
1636
+ v_normal = mat3(u_normalMatrix) * a_normal;
1637
+ v_tangent = vec4(mat3(u_normalMatrix) * a_tangent.xyz, a_tangent.w);
1638
+ v_worldPosition = (u_modelMatrix * vec4(a_position, 1.0)).xyz;
1639
+ v_uv = a_uv;
1640
+ v_vertexColor = a_color;
1641
+ gl_Position = u_modelViewProjection * vec4(a_position, 1.0);
1642
+ }
1461
1643
  }
1462
1644
  `,
1463
1645
  fragment: `#version 300 es
@@ -1471,6 +1653,7 @@ uniform float u_metallic;
1471
1653
  uniform float u_roughness;
1472
1654
  uniform vec3 u_environmentColor;
1473
1655
  uniform float u_environmentIntensity;
1656
+ uniform float u_materialEnvironmentIntensity;
1474
1657
  uniform vec3 u_environmentSkyColor;
1475
1658
  uniform vec3 u_environmentHorizonColor;
1476
1659
  uniform vec3 u_environmentGroundColor;
@@ -1539,6 +1722,19 @@ uniform float u_pointShadowSlopeBias;
1539
1722
  uniform vec2 u_pointShadowTexelSize;
1540
1723
  uniform float u_pointShadowPcfSampleCount;
1541
1724
  uniform vec4 u_pointShadowPcfSamples[32];
1725
+ uniform sampler2D u_spotShadowMapTexture;
1726
+ uniform float u_spotShadowMapEnabled;
1727
+ uniform vec3 u_spotShadowLightPosition;
1728
+ uniform vec3 u_spotShadowLightDirection;
1729
+ uniform mat4 u_spotShadowMatrix;
1730
+ uniform vec2 u_spotShadowCone;
1731
+ uniform float u_spotShadowRange;
1732
+ uniform float u_spotShadowStrength;
1733
+ uniform float u_spotShadowBias;
1734
+ uniform float u_spotShadowSlopeBias;
1735
+ uniform vec2 u_spotShadowTexelSize;
1736
+ uniform float u_spotShadowPcfSampleCount;
1737
+ uniform vec4 u_spotShadowPcfSamples[32];
1542
1738
  uniform float u_outputColorSpace;
1543
1739
  uniform vec3 u_cameraPosition;
1544
1740
  uniform float u_environmentFogEnabled;
@@ -1548,6 +1744,9 @@ uniform float u_environmentFogNear;
1548
1744
  uniform float u_environmentFogFar;
1549
1745
  uniform float u_environmentFogDensity;
1550
1746
  uniform float u_environmentFogHeightFalloff;
1747
+ uniform float u_volumetricIntensity;
1748
+ uniform vec3 u_volumetricLightDirection;
1749
+ uniform vec3 u_volumetricLightColor;
1551
1750
  uniform float u_environmentFogHeightReference;
1552
1751
  uniform float u_environmentFogMaxOpacity;
1553
1752
  in vec3 v_normal;
@@ -1573,15 +1772,6 @@ float a3dForwardShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirectio
1573
1772
  vec3 receiverNormal = normalize(normal);
1574
1773
  vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
1575
1774
  float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
1576
- // Slope-scaled depth bias must be evaluated per PCF sample, not once for the kernel
1577
- // centre. A sample offset N texels away on a receiver sloped relative to the light sees a
1578
- // depth difference proportional to N, so a centre-only bias under-compensates every outer
1579
- // tap and the receiver shadows itself. Scaling by each sample's own texel distance keeps
1580
- // wide kernels acne-free without inflating the constant bias into peter-panning.
1581
- // The depth gradient across one shadow texel is tan(angle between receiver normal and
1582
- // light), not (1 - N.L). The linear form collapses toward zero far faster than the real
1583
- // gradient grows, so it under-biases exactly the grazing angles that need the most
1584
- // compensation. Clamped so a near-perpendicular receiver cannot demand unbounded bias.
1585
1775
  float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
1586
1776
  float slopeTexelBias = slopeTangent * u_shadowMapSlopeBias * max(u_shadowMapTexelSize.x, u_shadowMapTexelSize.y);
1587
1777
  float projectedDepth = projected.z * 0.5 + 0.5;
@@ -1614,9 +1804,6 @@ float a3dPointShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection)
1614
1804
  vec4 rect = u_pointShadowFaceRects[faceIndex];
1615
1805
  vec2 uv = rect.xy + localUv * rect.zw;
1616
1806
  float normalDotLight = clamp(abs(dot(normalize(normal), normalize(lightDirection))), 0.0, 1.0);
1617
- // Same per-sample, tangent-scaled slope bias as the directional and atlas paths. These
1618
- // cubemap-face variants inline the sample fetch, but they share the identical acne
1619
- // mechanism: a centre-only, (1 - N.L)-scaled bias under-compensates every outer tap.
1620
1807
  float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
1621
1808
  float slopeTexelBias = slopeTangent * u_pointShadowSlopeBias * max(u_pointShadowTexelSize.x, u_pointShadowTexelSize.y);
1622
1809
  float projectedDepth = projected.z * 0.5 + 0.5;
@@ -1635,6 +1822,44 @@ float a3dPointShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection)
1635
1822
  float occlusion = totalWeight > 0.0 ? shadowed / totalWeight : 0.0;
1636
1823
  return mix(1.0, 1.0 - occlusion, clamp(u_pointShadowStrength, 0.0, 1.0));
1637
1824
  }
1825
+ float a3dSpotShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection) {
1826
+ if (u_spotShadowMapEnabled < 0.5) return 1.0;
1827
+ vec3 toFragment = worldPosition - u_spotShadowLightPosition;
1828
+ float distanceToLight = max(length(toFragment), 0.0001);
1829
+ if (distanceToLight > u_spotShadowRange) return 1.0;
1830
+ vec3 coneAxis = u_spotShadowLightDirection / max(length(u_spotShadowLightDirection), 0.0001);
1831
+ if (dot(toFragment / distanceToLight, coneAxis) < cos(u_spotShadowCone.x)) return 1.0;
1832
+ vec4 lightPosition = u_spotShadowMatrix * vec4(worldPosition, 1.0);
1833
+ vec3 projected = lightPosition.xyz / max(lightPosition.w, 0.0001);
1834
+ vec2 uv = projected.xy * 0.5 + 0.5;
1835
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return 1.0;
1836
+ vec3 receiverNormal = normalize(normal);
1837
+ vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
1838
+ float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
1839
+ float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
1840
+ float slopeTexelBias = slopeTangent * u_spotShadowSlopeBias * max(u_spotShadowTexelSize.x, u_spotShadowTexelSize.y);
1841
+ float projectedDepth = projected.z * 0.5 + 0.5;
1842
+ float shadowed = 0.0;
1843
+ float totalWeight = 0.0;
1844
+ int sampleCount = clamp(int(u_spotShadowPcfSampleCount), 1, 32);
1845
+ for (int i = 0; i < 32; ++i) {
1846
+ if (i >= sampleCount) break;
1847
+ vec4 sampleData = u_spotShadowPcfSamples[i];
1848
+ float weight = max(sampleData.z, 0.0);
1849
+ vec2 offset = sampleData.xy * u_spotShadowTexelSize;
1850
+ float storedDepth = texture(u_spotShadowMapTexture, uv + offset).r;
1851
+ float sampleTexelDistance = max(1.0, length(sampleData.xy));
1852
+ float receiverDepth = projectedDepth - u_spotShadowBias - slopeTexelBias * sampleTexelDistance;
1853
+ shadowed += (receiverDepth > storedDepth ? 1.0 : 0.0) * weight;
1854
+ totalWeight += weight;
1855
+ }
1856
+ float occlusion = totalWeight > 0.0 ? shadowed / totalWeight : 0.0;
1857
+ return mix(1.0, 1.0 - occlusion, clamp(u_spotShadowStrength, 0.0, 1.0));
1858
+ }
1859
+ float a3dResolveSpotShadowOverride(float baseFactor, float kind, float shadowFlag, vec3 worldPosition, vec3 normal, vec3 lightDirection) {
1860
+ float spotGate = step(1.5, kind) * (1.0 - step(2.5, kind)) * step(0.5, u_spotShadowMapEnabled) * step(0.5, shadowFlag);
1861
+ return mix(baseFactor, a3dSpotShadowFactor(worldPosition, normal, lightDirection), spotGate);
1862
+ }
1638
1863
  vec2 a3dEnvironmentEquirectUv(vec3 direction, float rotation) {
1639
1864
  vec3 d = normalize(direction);
1640
1865
  float u = atan(d.z, d.x) / 6.28318530718 + 0.5 + rotation;
@@ -1680,16 +1905,11 @@ vec3 a3dPbrEnvironmentDiffuseInput(vec3 normal) {
1680
1905
  float horizonBlend = 1.0 - abs(normal.y);
1681
1906
  vec3 proceduralDiffuse = mix(u_environmentGroundColor, u_environmentSkyColor, skyBlend);
1682
1907
  proceduralDiffuse = mix(proceduralDiffuse, u_environmentHorizonColor, clamp(horizonBlend, 0.0, 1.0) * 0.55);
1683
- // The ambient term must be added to the procedural contribution, not replaced by it. A mix
1684
- // here discarded u_environmentColor * u_environmentIntensity entirely whenever a procedural
1685
- // map was present, which is the normal case: raising ambient intensity from 0.18 to 3.0 on
1686
- // the product-turntable kit produced a byte-identical frame. Ambient and a sky gradient are
1687
- // separate physical contributions, so they sum.
1688
1908
  vec3 environmentDiffuse = ambientEnvironment + proceduralDiffuse * u_environmentMapIntensity * proceduralEnvironmentWeight;
1689
1909
  float sampledEnvironmentWeight = step(0.0001, u_environmentMapTextureEnabled * u_environmentMapTextureIntensity);
1690
1910
  float diffuseEnvironmentLod = max(u_environmentMapTextureMipCount - 1.0, 0.0);
1691
1911
  vec3 sampledDiffuse = a3dPbrDecodeEnvironmentSample(a3dPbrEnvironmentSampleRaw(normal, diffuseEnvironmentLod));
1692
- return mix(environmentDiffuse, ambientEnvironment + sampledDiffuse * u_environmentMapTextureIntensity, sampledEnvironmentWeight);
1912
+ return mix(environmentDiffuse, ambientEnvironment + sampledDiffuse * u_environmentMapTextureIntensity * u_materialEnvironmentIntensity, sampledEnvironmentWeight);
1693
1913
  }
1694
1914
  vec3 a3dPbrEnvironmentSpecularInput(vec3 normal, vec3 viewDirection, float roughness) {
1695
1915
  float proceduralEnvironmentWeight = step(0.0001, u_environmentMapIntensity);
@@ -1704,7 +1924,7 @@ vec3 a3dPbrEnvironmentSpecularInput(vec3 normal, vec3 viewDirection, float rough
1704
1924
  vec3 sampledSpecular = a3dPbrBoundHdrSpecularRadiance(a3dPbrDecodeEnvironmentSample(a3dPbrEnvironmentSampleRaw(reflectionDirection, environmentLod)));
1705
1925
  float nDotV = clamp(dot(normal, viewDirection), 0.0, 1.0);
1706
1926
  sampledSpecular = a3dPbrClampSampledSpecularEdgeEnergy(sampledSpecular, nDotV, clampedRoughness);
1707
- sampledSpecular *= u_environmentMapTextureSpecularIntensity * sampledEnvironmentWeight * mix(0.84, 0.58, clampedRoughness);
1927
+ sampledSpecular *= u_environmentMapTextureSpecularIntensity * u_materialEnvironmentIntensity * sampledEnvironmentWeight * mix(0.84, 0.58, clampedRoughness);
1708
1928
  return proceduralSpecular + sampledSpecular;
1709
1929
  }
1710
1930
  vec2 a3dPbrEnvironmentBrdfInput(vec3 normal, vec3 viewDirection, float roughness) {
@@ -1712,10 +1932,16 @@ vec2 a3dPbrEnvironmentBrdfInput(vec3 normal, vec3 viewDirection, float roughness
1712
1932
  vec2 lut = texture(u_environmentBrdfLutTexture, vec2(nDotV, clamp(roughness, 0.0, 1.0))).rg;
1713
1933
  return mix(vec2(1.0, 0.0), lut, step(0.0001, u_environmentBrdfLutEnabled));
1714
1934
  }
1935
+ vec3 a3dLinearToSrgb(vec3 linear) {
1936
+ vec3 clamped = max(linear, vec3(0.0));
1937
+ vec3 low = clamped * 12.92;
1938
+ vec3 high = 1.055 * pow(clamped, vec3(1.0 / 2.4)) - 0.055;
1939
+ return mix(low, high, step(vec3(0.0031308), clamped));
1940
+ }
1715
1941
  vec3 a3dPbrEncodeOutput(vec3 linearColor) {
1716
1942
  vec3 color = max(linearColor, vec3(0.0));
1717
1943
  vec3 filmic = clamp((color * (2.51 * color + 0.03)) / (color * (2.43 * color + 0.59) + 0.14), vec3(0.0), vec3(1.0));
1718
- vec3 srgb = pow(filmic, vec3(1.0 / 2.2));
1944
+ vec3 srgb = a3dLinearToSrgb(filmic);
1719
1945
  return mix(color, srgb, step(0.5, u_outputColorSpace));
1720
1946
  }
1721
1947
  void main() {
@@ -1776,7 +2002,7 @@ void main() {
1776
2002
  float inner = cos(spotShadowLayer.x * max(1.0 - spotShadowLayer.y, 0.001));
1777
2003
  attenuation *= smoothstep(outer, inner, cone);
1778
2004
  }
1779
- float shadowFactor = mix(1.0, kind > 0.5 && kind < 1.5 ? a3dPointShadowFactor(v_worldPosition, mappedNormal, lightDirection) : a3dForwardShadowFactor(v_worldPosition, mappedNormal, lightDirection), step(0.5, spotShadowLayer.z));
2005
+ float shadowFactor = a3dResolveSpotShadowOverride(mix(1.0, kind > 0.5 && kind < 1.5 ? a3dPointShadowFactor(v_worldPosition, mappedNormal, lightDirection) : a3dForwardShadowFactor(v_worldPosition, mappedNormal, lightDirection), step(0.5, spotShadowLayer.z)), kind, spotShadowLayer.z, v_worldPosition, mappedNormal, lightDirection);
1780
2006
  shaded += a3dPbrDirectLight(mappedNormal, viewDirection, lightDirection, colorIntensity.rgb, colorIntensity.a * attenuation * shadowFactor, materialBase, u_metallic, u_roughness, u_specularFactor, u_specularColorFactor);
1781
2007
  }
1782
2008
  float alpha = u_baseColor.a * v_vertexColor.a;
@@ -1790,13 +2016,16 @@ void main() {
1790
2016
  name: DEFAULT_TEXTURED_PBR_SHADER_NAME,
1791
2017
  marker: DEFAULT_TEXTURED_PBR_SHADER_MARKER,
1792
2018
  variants: [
2019
+ { name: DEFAULT_TEXTURED_PBR_EXTENSION_ATLAS_VARIANT, defines: { A3D_PBR_EXTENSION_ATLAS: true, A3D_PBR_CLEARCOAT_TEXTURES: true, A3D_PBR_SPECULAR_SHEEN_ANISOTROPY_TEXTURES: true, A3D_PBR_IRIDESCENCE_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true } },
2020
+ { name: DEFAULT_TEXTURED_PBR_CLEARCOAT_SHEEN_ANISOTROPY_TEXTURES_VARIANT, defines: { A3D_PBR_CLEARCOAT_TEXTURES: true, A3D_PBR_SPECULAR_SHEEN_ANISOTROPY_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true, A3D_PBR_NO_SPOT_SHADOW: true } },
2021
+ { name: DEFAULT_TEXTURED_PBR_CLEARCOAT_IRIDESCENCE_TEXTURES_VARIANT, defines: { A3D_PBR_CLEARCOAT_TEXTURES: true, A3D_PBR_IRIDESCENCE_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true } },
1793
2022
  { name: DEFAULT_TEXTURED_PBR_CLEARCOAT_TEXTURES_VARIANT, defines: { A3D_PBR_CLEARCOAT_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true } },
1794
2023
  { name: DEFAULT_TEXTURED_PBR_TRANSMISSION_VOLUME_TEXTURES_VARIANT, defines: { A3D_PBR_TRANSMISSION_VOLUME_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true } },
1795
2024
  { name: DEFAULT_TEXTURED_PBR_SPECULAR_SHEEN_ANISOTROPY_TEXTURES_VARIANT, defines: { A3D_PBR_SPECULAR_TEXTURES: true, A3D_PBR_SPECULAR_SHEEN_ANISOTROPY_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true } },
1796
2025
  { name: DEFAULT_TEXTURED_PBR_IRIDESCENCE_TEXTURES_VARIANT, defines: { A3D_PBR_IRIDESCENCE_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true } },
1797
- { name: DEFAULT_TEXTURED_PBR_CLEARCOAT_TRANSMISSION_VOLUME_TEXTURES_VARIANT, defines: { A3D_PBR_CLEARCOAT_TEXTURES: true, A3D_PBR_TRANSMISSION_VOLUME_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true } },
2026
+ { name: DEFAULT_TEXTURED_PBR_CLEARCOAT_TRANSMISSION_VOLUME_TEXTURES_VARIANT, defines: { A3D_PBR_CLEARCOAT_TEXTURES: true, A3D_PBR_TRANSMISSION_VOLUME_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true, A3D_PBR_NO_SPOT_SHADOW: true } },
1798
2027
  { name: DEFAULT_TEXTURED_PBR_CLEARCOAT_SPECULAR_TEXTURES_VARIANT, defines: { A3D_PBR_CLEARCOAT_TEXTURES: true, A3D_PBR_SPECULAR_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true } },
1799
- { name: DEFAULT_TEXTURED_PBR_SPECULAR_SHEEN_ANISOTROPY_IRIDESCENCE_TEXTURES_VARIANT, defines: { A3D_PBR_SPECULAR_TEXTURES: true, A3D_PBR_SPECULAR_SHEEN_ANISOTROPY_TEXTURES: true, A3D_PBR_IRIDESCENCE_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true } }
2028
+ { name: DEFAULT_TEXTURED_PBR_SPECULAR_SHEEN_ANISOTROPY_IRIDESCENCE_TEXTURES_VARIANT, defines: { A3D_PBR_SPECULAR_TEXTURES: true, A3D_PBR_SPECULAR_SHEEN_ANISOTROPY_TEXTURES: true, A3D_PBR_IRIDESCENCE_TEXTURES: true, A3D_PBR_DISABLE_TRANSMISSION_BACKDROP: true, A3D_PBR_DISABLE_CLUSTERED_LIGHTING: true, A3D_PBR_NO_SPOT_SHADOW: true } },
1800
2029
  ],
1801
2030
  vertex: `#version 300 es
1802
2031
  // ${DEFAULT_TEXTURED_PBR_SHADER_MARKER}
@@ -1807,9 +2036,17 @@ layout(location = 2) in vec2 a_uv;
1807
2036
  layout(location = 3) in vec4 a_tangent;
1808
2037
  layout(location = 4) in vec4 a_color;
1809
2038
  layout(location = 7) in vec2 a_uv1;
2039
+ layout(location = 8) in vec4 a_instanceMatrix0;
2040
+ layout(location = 9) in vec4 a_instanceMatrix1;
2041
+ layout(location = 10) in vec4 a_instanceMatrix2;
2042
+ layout(location = 11) in vec4 a_instanceMatrix3;
2043
+ layout(location = 12) in vec4 a_instanceColor;
1810
2044
  uniform mat4 u_modelViewProjection;
1811
2045
  uniform mat4 u_modelMatrix;
1812
2046
  uniform mat4 u_normalMatrix;
2047
+ uniform mat4 u_instanceMatrices[64];
2048
+ uniform float u_instanceCount;
2049
+ uniform float u_instanceAttributeMode;
1813
2050
  out vec3 v_normal;
1814
2051
  out vec4 v_tangent;
1815
2052
  out vec3 v_worldPosition;
@@ -1819,11 +2056,23 @@ out vec4 v_vertexColor;
1819
2056
  void main() {
1820
2057
  v_uv = a_uv;
1821
2058
  v_uv1 = a_uv1;
1822
- v_normal = mat3(u_normalMatrix) * a_normal;
1823
- v_tangent = vec4(mat3(u_normalMatrix) * a_tangent.xyz, a_tangent.w);
1824
- v_worldPosition = (u_modelMatrix * vec4(a_position, 1.0)).xyz;
1825
- v_vertexColor = a_color;
1826
- gl_Position = u_modelViewProjection * vec4(a_position, 1.0);
2059
+ if (u_instanceCount > 0.5) {
2060
+ int instanceIndex = clamp(gl_InstanceID, 0, max(int(u_instanceCount) - 1, 0));
2061
+ mat4 attributeMatrix = mat4(a_instanceMatrix0, a_instanceMatrix1, a_instanceMatrix2, a_instanceMatrix3);
2062
+ mat4 instanceMatrix = u_instanceAttributeMode > 0.5 ? attributeMatrix : u_instanceMatrices[instanceIndex];
2063
+ vec4 worldPosition = u_modelMatrix * instanceMatrix * vec4(a_position, 1.0);
2064
+ v_normal = mat3(u_normalMatrix) * transpose(inverse(mat3(instanceMatrix))) * a_normal;
2065
+ v_tangent = vec4(mat3(u_normalMatrix) * (mat3(instanceMatrix) * a_tangent.xyz), a_tangent.w);
2066
+ v_worldPosition = worldPosition.xyz;
2067
+ v_vertexColor = a_color * a_instanceColor;
2068
+ gl_Position = u_modelViewProjection * instanceMatrix * vec4(a_position, 1.0);
2069
+ } else {
2070
+ v_normal = mat3(u_normalMatrix) * a_normal;
2071
+ v_tangent = vec4(mat3(u_normalMatrix) * a_tangent.xyz, a_tangent.w);
2072
+ v_worldPosition = (u_modelMatrix * vec4(a_position, 1.0)).xyz;
2073
+ v_vertexColor = a_color;
2074
+ gl_Position = u_modelViewProjection * vec4(a_position, 1.0);
2075
+ }
1827
2076
  }
1828
2077
  `,
1829
2078
  fragment: `#version 300 es
@@ -1837,6 +2086,7 @@ uniform float u_metallic;
1837
2086
  uniform float u_roughness;
1838
2087
  uniform vec3 u_environmentColor;
1839
2088
  uniform float u_environmentIntensity;
2089
+ uniform float u_materialEnvironmentIntensity;
1840
2090
  uniform vec3 u_environmentSkyColor;
1841
2091
  uniform vec3 u_environmentHorizonColor;
1842
2092
  uniform vec3 u_environmentGroundColor;
@@ -1921,6 +2171,21 @@ uniform float u_pointShadowSlopeBias;
1921
2171
  uniform vec2 u_pointShadowTexelSize;
1922
2172
  uniform float u_pointShadowPcfSampleCount;
1923
2173
  uniform vec4 u_pointShadowPcfSamples[32];
2174
+ #ifndef A3D_PBR_NO_SPOT_SHADOW
2175
+ uniform sampler2D u_spotShadowMapTexture;
2176
+ uniform float u_spotShadowMapEnabled;
2177
+ uniform vec3 u_spotShadowLightPosition;
2178
+ uniform vec3 u_spotShadowLightDirection;
2179
+ uniform mat4 u_spotShadowMatrix;
2180
+ uniform vec2 u_spotShadowCone;
2181
+ uniform float u_spotShadowRange;
2182
+ uniform float u_spotShadowStrength;
2183
+ uniform float u_spotShadowBias;
2184
+ uniform float u_spotShadowSlopeBias;
2185
+ uniform vec2 u_spotShadowTexelSize;
2186
+ uniform float u_spotShadowPcfSampleCount;
2187
+ uniform vec4 u_spotShadowPcfSamples[32];
2188
+ #endif
1924
2189
  uniform sampler2D u_baseColorTexture;
1925
2190
  uniform float u_baseColorTextureEnabled;
1926
2191
  uniform vec2 u_baseColorTextureOffset;
@@ -1959,14 +2224,26 @@ uniform vec2 u_occlusionTextureWrap;
1959
2224
  uniform float u_occlusionStrength;
1960
2225
  uniform float u_clearcoatNormalScale;
1961
2226
  #ifdef A3D_PBR_CLEARCOAT_TEXTURES
2227
+ #ifndef A3D_PBR_EXTENSION_ATLAS
1962
2228
  uniform sampler2D u_clearcoatTexture;
2229
+ #else
2230
+ uniform vec4 u_clearcoatAtlasRect;
2231
+ uniform float u_clearcoatAtlasY;
2232
+ uniform vec4 u_clearcoatAtlasFilter;
2233
+ #endif
1963
2234
  uniform float u_clearcoatTextureEnabled;
1964
2235
  uniform vec2 u_clearcoatTextureOffset;
1965
2236
  uniform vec2 u_clearcoatTextureScale;
1966
2237
  uniform float u_clearcoatTextureRotation;
1967
2238
  uniform float u_clearcoatTextureTexCoord;
1968
2239
  uniform vec2 u_clearcoatTextureWrap;
2240
+ #ifndef A3D_PBR_EXTENSION_ATLAS
1969
2241
  uniform sampler2D u_clearcoatRoughnessTexture;
2242
+ #else
2243
+ uniform vec4 u_clearcoatRoughnessAtlasRect;
2244
+ uniform float u_clearcoatRoughnessAtlasY;
2245
+ uniform vec4 u_clearcoatRoughnessAtlasFilter;
2246
+ #endif
1970
2247
  uniform float u_clearcoatRoughnessTextureEnabled;
1971
2248
  uniform vec2 u_clearcoatRoughnessTextureOffset;
1972
2249
  uniform vec2 u_clearcoatRoughnessTextureScale;
@@ -2035,7 +2312,13 @@ uniform vec2 u_sheenColorTextureScale;
2035
2312
  uniform float u_sheenColorTextureRotation;
2036
2313
  uniform float u_sheenColorTextureTexCoord;
2037
2314
  uniform vec2 u_sheenColorTextureWrap;
2315
+ #ifndef A3D_PBR_EXTENSION_ATLAS
2038
2316
  uniform sampler2D u_sheenRoughnessTexture;
2317
+ #else
2318
+ uniform vec4 u_sheenRoughnessAtlasRect;
2319
+ uniform float u_sheenRoughnessAtlasY;
2320
+ uniform vec4 u_sheenRoughnessAtlasFilter;
2321
+ #endif
2039
2322
  uniform float u_sheenRoughnessTextureEnabled;
2040
2323
  uniform vec2 u_sheenRoughnessTextureOffset;
2041
2324
  uniform vec2 u_sheenRoughnessTextureScale;
@@ -2051,14 +2334,26 @@ uniform float u_anisotropyTextureTexCoord;
2051
2334
  uniform vec2 u_anisotropyTextureWrap;
2052
2335
  #endif
2053
2336
  #ifdef A3D_PBR_IRIDESCENCE_TEXTURES
2337
+ #ifndef A3D_PBR_EXTENSION_ATLAS
2054
2338
  uniform sampler2D u_iridescenceTexture;
2339
+ #else
2340
+ uniform vec4 u_iridescenceAtlasRect;
2341
+ uniform float u_iridescenceAtlasY;
2342
+ uniform vec4 u_iridescenceAtlasFilter;
2343
+ #endif
2055
2344
  uniform float u_iridescenceTextureEnabled;
2056
2345
  uniform vec2 u_iridescenceTextureOffset;
2057
2346
  uniform vec2 u_iridescenceTextureScale;
2058
2347
  uniform float u_iridescenceTextureRotation;
2059
2348
  uniform float u_iridescenceTextureTexCoord;
2060
2349
  uniform vec2 u_iridescenceTextureWrap;
2350
+ #ifndef A3D_PBR_EXTENSION_ATLAS
2061
2351
  uniform sampler2D u_iridescenceThicknessTexture;
2352
+ #else
2353
+ uniform vec4 u_iridescenceThicknessAtlasRect;
2354
+ uniform float u_iridescenceThicknessAtlasY;
2355
+ uniform vec4 u_iridescenceThicknessAtlasFilter;
2356
+ #endif
2062
2357
  uniform float u_iridescenceThicknessTextureEnabled;
2063
2358
  uniform vec2 u_iridescenceThicknessTextureOffset;
2064
2359
  uniform vec2 u_iridescenceThicknessTextureScale;
@@ -2076,6 +2371,9 @@ uniform float u_environmentFogNear;
2076
2371
  uniform float u_environmentFogFar;
2077
2372
  uniform float u_environmentFogDensity;
2078
2373
  uniform float u_environmentFogHeightFalloff;
2374
+ uniform float u_volumetricIntensity;
2375
+ uniform vec3 u_volumetricLightDirection;
2376
+ uniform vec3 u_volumetricLightColor;
2079
2377
  uniform float u_environmentFogHeightReference;
2080
2378
  uniform float u_environmentFogMaxOpacity;
2081
2379
  in vec3 v_normal;
@@ -2110,10 +2408,16 @@ vec2 a3dTexturedPbrWrapUv(vec2 uv, vec2 wrapMode) {
2110
2408
  a3dTexturedPbrWrapCoordinate(uv.y, wrapMode.y)
2111
2409
  );
2112
2410
  }
2411
+ vec3 a3dTexturedPbrEncodeLinearToSrgb(vec3 linear) {
2412
+ vec3 clamped = max(linear, vec3(0.0));
2413
+ vec3 low = clamped * 12.92;
2414
+ vec3 high = 1.055 * pow(clamped, vec3(1.0 / 2.4)) - 0.055;
2415
+ return mix(low, high, step(vec3(0.0031308), clamped));
2416
+ }
2113
2417
  vec3 a3dTexturedPbrEncodeOutput(vec3 linearColor) {
2114
2418
  vec3 color = max(linearColor, vec3(0.0));
2115
2419
  vec3 filmic = clamp((color * (2.51 * color + 0.03)) / (color * (2.43 * color + 0.59) + 0.14), vec3(0.0), vec3(1.0));
2116
- vec3 srgb = pow(filmic, vec3(1.0 / 2.2));
2420
+ vec3 srgb = a3dTexturedPbrEncodeLinearToSrgb(filmic);
2117
2421
  return mix(color, srgb, step(0.5, u_outputColorSpace));
2118
2422
  }
2119
2423
  vec3 a3dTexturedPbrDecodeSrgb(vec3 encodedColor) {
@@ -2158,15 +2462,6 @@ float a3dTexturedPbrShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDire
2158
2462
  vec3 receiverNormal = normalize(normal);
2159
2463
  vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
2160
2464
  float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
2161
- // Slope-scaled depth bias must be evaluated per PCF sample, not once for the kernel
2162
- // centre. A sample offset N texels away on a receiver sloped relative to the light sees a
2163
- // depth difference proportional to N, so a centre-only bias under-compensates every outer
2164
- // tap and the receiver shadows itself. Scaling by each sample's own texel distance keeps
2165
- // wide kernels acne-free without inflating the constant bias into peter-panning.
2166
- // The depth gradient across one shadow texel is tan(angle between receiver normal and
2167
- // light), not (1 - N.L). The linear form collapses toward zero far faster than the real
2168
- // gradient grows, so it under-biases exactly the grazing angles that need the most
2169
- // compensation. Clamped so a near-perpendicular receiver cannot demand unbounded bias.
2170
2465
  float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
2171
2466
  float slopeTexelBias = slopeTangent * u_shadowMapSlopeBias * max(u_shadowMapTexelSize.x, u_shadowMapTexelSize.y);
2172
2467
  float projectedDepth = projected.z * 0.5 + 0.5;
@@ -2199,9 +2494,6 @@ float a3dTexturedPbrPointShadowFactor(vec3 worldPosition, vec3 normal, vec3 ligh
2199
2494
  vec4 rect = u_pointShadowFaceRects[faceIndex];
2200
2495
  vec2 uv = rect.xy + localUv * rect.zw;
2201
2496
  float normalDotLight = clamp(abs(dot(normalize(normal), normalize(lightDirection))), 0.0, 1.0);
2202
- // Same per-sample, tangent-scaled slope bias as the directional and atlas paths. These
2203
- // cubemap-face variants inline the sample fetch, but they share the identical acne
2204
- // mechanism: a centre-only, (1 - N.L)-scaled bias under-compensates every outer tap.
2205
2497
  float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
2206
2498
  float slopeTexelBias = slopeTangent * u_pointShadowSlopeBias * max(u_pointShadowTexelSize.x, u_pointShadowTexelSize.y);
2207
2499
  float projectedDepth = projected.z * 0.5 + 0.5;
@@ -2220,6 +2512,46 @@ float a3dTexturedPbrPointShadowFactor(vec3 worldPosition, vec3 normal, vec3 ligh
2220
2512
  float occlusion = totalWeight > 0.0 ? shadowed / totalWeight : 0.0;
2221
2513
  return mix(1.0, 1.0 - occlusion, clamp(u_pointShadowStrength, 0.0, 1.0));
2222
2514
  }
2515
+ #ifndef A3D_PBR_NO_SPOT_SHADOW
2516
+ float a3dTexturedPbrSpotShadowFactor(vec3 worldPosition, vec3 normal, vec3 lightDirection) {
2517
+ if (u_spotShadowMapEnabled < 0.5) return 1.0;
2518
+ vec3 toFragment = worldPosition - u_spotShadowLightPosition;
2519
+ float distanceToLight = max(length(toFragment), 0.0001);
2520
+ if (distanceToLight > u_spotShadowRange) return 1.0;
2521
+ vec3 coneAxis = u_spotShadowLightDirection / max(length(u_spotShadowLightDirection), 0.0001);
2522
+ if (dot(toFragment / distanceToLight, coneAxis) < cos(u_spotShadowCone.x)) return 1.0;
2523
+ vec4 lightPosition = u_spotShadowMatrix * vec4(worldPosition, 1.0);
2524
+ vec3 projected = lightPosition.xyz / max(lightPosition.w, 0.0001);
2525
+ vec2 uv = projected.xy * 0.5 + 0.5;
2526
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return 1.0;
2527
+ vec3 receiverNormal = normalize(normal);
2528
+ vec3 receiverLightDirection = lightDirection / max(length(lightDirection), 0.0001);
2529
+ float normalDotLight = clamp(abs(dot(receiverNormal, receiverLightDirection)), 0.0, 1.0);
2530
+ float slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
2531
+ float slopeTexelBias = slopeTangent * u_spotShadowSlopeBias * max(u_spotShadowTexelSize.x, u_spotShadowTexelSize.y);
2532
+ float projectedDepth = projected.z * 0.5 + 0.5;
2533
+ float shadowed = 0.0;
2534
+ float totalWeight = 0.0;
2535
+ int sampleCount = clamp(int(u_spotShadowPcfSampleCount), 1, 32);
2536
+ for (int i = 0; i < 32; ++i) {
2537
+ if (i >= sampleCount) break;
2538
+ vec4 sampleData = u_spotShadowPcfSamples[i];
2539
+ float weight = max(sampleData.z, 0.0);
2540
+ vec2 offset = sampleData.xy * u_spotShadowTexelSize;
2541
+ float storedDepth = texture(u_spotShadowMapTexture, uv + offset).r;
2542
+ float sampleTexelDistance = max(1.0, length(sampleData.xy));
2543
+ float receiverDepth = projectedDepth - u_spotShadowBias - slopeTexelBias * sampleTexelDistance;
2544
+ shadowed += (receiverDepth > storedDepth ? 1.0 : 0.0) * weight;
2545
+ totalWeight += weight;
2546
+ }
2547
+ float occlusion = totalWeight > 0.0 ? shadowed / totalWeight : 0.0;
2548
+ return mix(1.0, 1.0 - occlusion, clamp(u_spotShadowStrength, 0.0, 1.0));
2549
+ }
2550
+ float a3dTexturedPbrResolveSpotShadowOverride(float baseFactor, float kind, float shadowFlag, vec3 worldPosition, vec3 normal, vec3 lightDirection) {
2551
+ float spotGate = step(1.5, kind) * (1.0 - step(2.5, kind)) * step(0.5, u_spotShadowMapEnabled) * step(0.5, shadowFlag);
2552
+ return mix(baseFactor, a3dTexturedPbrSpotShadowFactor(worldPosition, normal, lightDirection), spotGate);
2553
+ }
2554
+ #endif
2223
2555
  vec2 a3dTexturedPbrEnvironmentUv(vec3 direction, float rotation) {
2224
2556
  vec3 d = normalize(direction);
2225
2557
  float u = atan(d.z, d.x) / 6.28318530718 + 0.5 + rotation;
@@ -2259,16 +2591,11 @@ vec3 a3dTexturedPbrEnvironmentDiffuseInput(vec3 normal) {
2259
2591
  float horizonBlend = 1.0 - abs(normal.y);
2260
2592
  vec3 proceduralDiffuse = mix(u_environmentGroundColor, u_environmentSkyColor, skyBlend);
2261
2593
  proceduralDiffuse = mix(proceduralDiffuse, u_environmentHorizonColor, clamp(horizonBlend, 0.0, 1.0) * 0.55);
2262
- // The ambient term must be added to the procedural contribution, not replaced by it. A mix
2263
- // here discarded u_environmentColor * u_environmentIntensity entirely whenever a procedural
2264
- // map was present, which is the normal case: raising ambient intensity from 0.18 to 3.0 on
2265
- // the product-turntable kit produced a byte-identical frame. Ambient and a sky gradient are
2266
- // separate physical contributions, so they sum.
2267
2594
  vec3 environmentDiffuse = ambientEnvironment + proceduralDiffuse * u_environmentMapIntensity * proceduralEnvironmentWeight;
2268
2595
  float sampledEnvironmentWeight = step(0.0001, u_environmentMapTextureEnabled * u_environmentMapTextureIntensity);
2269
2596
  float diffuseEnvironmentLod = max(u_environmentMapTextureMipCount - 1.0, 0.0);
2270
2597
  vec3 sampledDiffuse = a3dTexturedPbrDecodeEnvironmentSample(a3dTexturedPbrEnvironmentSampleRaw(normal, diffuseEnvironmentLod));
2271
- return mix(environmentDiffuse, ambientEnvironment + sampledDiffuse * u_environmentMapTextureIntensity, sampledEnvironmentWeight);
2598
+ return mix(environmentDiffuse, ambientEnvironment + sampledDiffuse * u_environmentMapTextureIntensity * u_materialEnvironmentIntensity, sampledEnvironmentWeight);
2272
2599
  }
2273
2600
  vec3 a3dTexturedPbrEnvironmentSpecularInput(vec3 normal, vec3 viewDirection, float roughness) {
2274
2601
  float materialEnvironmentSpecularScale = clamp(u_materialEnvironmentSpecularScale, 0.0, 1.0);
@@ -2278,11 +2605,6 @@ vec3 a3dTexturedPbrEnvironmentSpecularInput(vec3 normal, vec3 viewDirection, flo
2278
2605
  float clampedRoughness = clamp(roughness, 0.0, 1.0);
2279
2606
  float nDotV = clamp(dot(normal, viewDirection), 0.0, 1.0);
2280
2607
  float reflectionBand = pow(clamp(reflectionDirection.y * 0.5 + 0.5, 0.0, 1.0), mix(18.0, 2.0, clampedRoughness));
2281
- // Keep the procedural environment from becoming a uniform white veil on
2282
- // saturated textured base colors. A high roughness floor spread neutral
2283
- // specular over every fragment, while image-based lighting and Three.js keep
2284
- // the strongest neutral energy in reflection bands and glancing highlights.
2285
- // The lower floor preserves ambient readability without erasing texture chroma.
2286
2608
  float roughEnvironmentFloor = mix(0.012, 0.16, clampedRoughness);
2287
2609
  float proceduralSpecularResponse = max(reflectionBand, roughEnvironmentFloor);
2288
2610
  float horizonStripe = pow(clamp(1.0 - abs(reflectionDirection.y), 0.0, 1.0), mix(14.0, 3.0, clampedRoughness));
@@ -2297,18 +2619,30 @@ vec3 a3dTexturedPbrEnvironmentSpecularInput(vec3 normal, vec3 viewDirection, flo
2297
2619
  float environmentLod = clampedRoughness * max(u_environmentMapTextureMipCount - 1.0, 0.0);
2298
2620
  vec3 sampledSpecular = a3dTexturedPbrBoundHdrSpecularRadiance(a3dTexturedPbrDecodeEnvironmentSample(a3dTexturedPbrEnvironmentSampleRaw(reflectionDirection, environmentLod)));
2299
2621
  sampledSpecular = a3dTexturedPbrClampSampledSpecularEdgeEnergy(sampledSpecular, nDotV, clampedRoughness);
2300
- sampledSpecular *= u_environmentMapTextureSpecularIntensity * sampledEnvironmentWeight * mix(1.1, 0.65, clampedRoughness);
2622
+ sampledSpecular *= u_environmentMapTextureSpecularIntensity * u_materialEnvironmentIntensity * sampledEnvironmentWeight * mix(1.1, 0.65, clampedRoughness);
2301
2623
  float proceduralSpecularScale = materialFiniteSpecularScale;
2302
2624
  float sampledSpecularScale = materialEnvironmentSpecularScale;
2303
2625
  return proceduralSpecular * proceduralSpecularScale + sampledSpecular * sampledSpecularScale;
2304
2626
  }
2305
- vec3 a3dTexturedPbrIridescenceColor(float minimumThickness, float maximumThickness, float iridescenceIor, float nDotV) {
2306
- float thickness = clamp((minimumThickness + maximumThickness) * 0.5, 0.0, 1200.0);
2307
- float opticalThicknessPhase = clamp((thickness - 100.0) / 1100.0, 0.0, 1.0) * 6.2831853;
2308
- float iorShift = clamp((iridescenceIor - 1.0) / 2.0, 0.0, 1.0) * 0.65;
2309
- float viewPhase = pow(1.0 - clamp(nDotV, 0.0, 1.0), 1.25) * (3.2 + iridescenceIor * 1.4);
2310
- float phase = opticalThicknessPhase + iorShift + viewPhase;
2311
- return clamp(0.5 + 0.5 * cos(phase + vec3(0.0, 2.0943951, 4.1887902)), vec3(0.0), vec3(1.0));
2627
+ vec3 a3dTexturedPbrThinFilmFresnel(vec3 substrateF0, float filmIor, float thickness, float cosine) {
2628
+ float c1 = clamp(cosine, 0.0001, 1.0);
2629
+ if (thickness <= 0.0) return a3dFresnelSchlick(substrateF0, c1);
2630
+ float n2 = max(filmIor, 1.0001);
2631
+ vec3 rootF0 = sqrt(clamp(substrateF0, vec3(0.0), vec3(0.98)));
2632
+ vec3 n3 = (vec3(1.0) + rootF0) / max(vec3(1.0) - rootF0, vec3(0.0001));
2633
+ float sinSquared = 1.0 - c1 * c1;
2634
+ float c2 = sqrt(max(0.0, 1.0 - sinSquared / (n2 * n2)));
2635
+ vec3 c3 = sqrt(max(vec3(0.0), vec3(1.0) - sinSquared / (n3 * n3)));
2636
+ float r12s = (c1 - n2 * c2) / max(c1 + n2 * c2, 0.0001);
2637
+ float r12p = (n2 * c1 - c2) / max(n2 * c1 + c2, 0.0001);
2638
+ vec3 r23s = (n2 * c2 - n3 * c3) / max(n2 * c2 + n3 * c3, vec3(0.0001));
2639
+ vec3 r23p = (n3 * c2 - n2 * c3) / max(n3 * c2 + n2 * c3, vec3(0.0001));
2640
+ vec3 phase = 4.0 * A3D_PI * n2 * max(thickness, 0.0) * c2 / vec3(650.0, 510.0, 475.0);
2641
+ vec3 crossS = 2.0 * r12s * r23s * cos(phase);
2642
+ vec3 crossP = 2.0 * r12p * r23p * cos(phase);
2643
+ vec3 Rs = (r12s * r12s + r23s * r23s + crossS) / max(vec3(1.0) + r12s * r12s * r23s * r23s + crossS, vec3(0.0001));
2644
+ vec3 Rp = (r12p * r12p + r23p * r23p + crossP) / max(vec3(1.0) + r12p * r12p * r23p * r23p + crossP, vec3(0.0001));
2645
+ return clamp((Rs + Rp) * 0.5, vec3(0.0), vec3(1.0));
2312
2646
  }
2313
2647
  float a3dTexturedPbrCharlieSheen(float nDotH, float sheenRoughness) {
2314
2648
  float alpha = max(0.07, sheenRoughness * sheenRoughness);
@@ -2359,7 +2693,10 @@ vec3 a3dTexturedPbrExtensionDirectLight(
2359
2693
  float iridescence,
2360
2694
  float iridescenceIor,
2361
2695
  float iridescenceThicknessMinimum,
2362
- float iridescenceThicknessMaximum
2696
+ float iridescenceThicknessMaximum,
2697
+ vec3 substrateF0,
2698
+ vec3 substrateNormal,
2699
+ float substrateRoughness
2363
2700
  ) {
2364
2701
  vec3 N = normalize(normal);
2365
2702
  vec3 V = normalize(viewDirection);
@@ -2382,9 +2719,14 @@ vec3 a3dTexturedPbrExtensionDirectLight(
2382
2719
  float anisotropicDistribution = a3dTexturedPbrAnisotropicDistribution(N, H, tangentFrame, clearcoatRough, anisotropy, anisotropyRotation);
2383
2720
  float anisotropyShape = anisotropicDistribution / (anisotropicDistribution + 3.0);
2384
2721
  vec3 anisotropyLobe = vec3(clamp(anisotropy, 0.0, 1.0) * anisotropyShape * 0.72);
2385
- vec3 iridescenceColor = a3dTexturedPbrIridescenceColor(iridescenceThicknessMinimum, iridescenceThicknessMaximum, iridescenceIor, nDotV);
2386
- vec3 iridescenceLobe = iridescenceColor * clamp(iridescence, 0.0, 1.0) * clearcoatF * pow(a3dSaturate(1.0 - nDotV), 2.0) * 0.12;
2387
- return (clearcoatLobe + sheenLobe + anisotropyLobe + iridescenceLobe) * lightColor * lightIntensity * nDotL;
2722
+ vec3 filmF = a3dTexturedPbrThinFilmFresnel(substrateF0, iridescenceIor, (iridescenceThicknessMinimum + iridescenceThicknessMaximum) * 0.5, vDotH);
2723
+ vec3 filmN = normalize(substrateNormal);
2724
+ float filmNdotV = max(a3dSaturate(dot(filmN, V)), A3D_EPSILON);
2725
+ float filmNdotL = a3dSaturate(dot(filmN, L));
2726
+ float filmD = a3dDistributionGGX(a3dSaturate(dot(filmN, H)), substrateRoughness);
2727
+ float filmG = a3dGeometrySmithGGXCorrelated(filmNdotV, filmNdotL, substrateRoughness);
2728
+ vec3 iridescenceLobe = (filmF - a3dFresnelSchlick(substrateF0, vDotH)) * clamp(iridescence, 0.0, 1.0) * filmD * filmG;
2729
+ return ((clearcoatLobe + sheenLobe + anisotropyLobe) * nDotL + iridescenceLobe * filmNdotL) * lightColor * lightIntensity;
2388
2730
  }
2389
2731
  vec3 a3dTexturedPbrExtensionEnvironmentLight(
2390
2732
  vec3 normal,
@@ -2400,7 +2742,10 @@ vec3 a3dTexturedPbrExtensionEnvironmentLight(
2400
2742
  float iridescence,
2401
2743
  float iridescenceIor,
2402
2744
  float iridescenceThicknessMinimum,
2403
- float iridescenceThicknessMaximum
2745
+ float iridescenceThicknessMaximum,
2746
+ vec3 substrateF0,
2747
+ vec3 substrateNormal,
2748
+ vec3 substrateSpecularRadiance
2404
2749
  ) {
2405
2750
  float nDotV = max(a3dSaturate(dot(normalize(normal), normalize(viewDirection))), A3D_EPSILON);
2406
2751
  vec3 clearcoatF = a3dFresnelSchlickRoughness(vec3(0.04), nDotV, clamp(clearcoatRoughness, 0.18, 1.0));
@@ -2417,10 +2762,66 @@ vec3 a3dTexturedPbrExtensionEnvironmentLight(
2417
2762
  float anisotropicDistribution = a3dTexturedPbrAnisotropicDistribution(N, environmentHalf, tangentFrame, clearcoatRoughness, anisotropy, anisotropyRotation);
2418
2763
  float anisotropyShape = anisotropicDistribution / (anisotropicDistribution + 3.0);
2419
2764
  vec3 anisotropyLobe = boundedSpecularRadiance * clamp(anisotropy, 0.0, 1.0) * anisotropyShape * 0.72;
2420
- vec3 iridescenceColor = a3dTexturedPbrIridescenceColor(iridescenceThicknessMinimum, iridescenceThicknessMaximum, iridescenceIor, nDotV);
2421
- vec3 iridescenceLobe = boundedSpecularRadiance * iridescenceColor * clamp(iridescence, 0.0, 1.0) * pow(a3dSaturate(1.0 - nDotV), 2.0) * 0.09;
2765
+ float filmNdotV = max(a3dSaturate(dot(normalize(substrateNormal), normalize(viewDirection))), A3D_EPSILON);
2766
+ vec3 filmF = a3dTexturedPbrThinFilmFresnel(substrateF0, iridescenceIor, (iridescenceThicknessMinimum + iridescenceThicknessMaximum) * 0.5, filmNdotV);
2767
+ vec3 iridescenceLobe = substrateSpecularRadiance * (filmF - a3dFresnelSchlick(substrateF0, filmNdotV)) * clamp(iridescence, 0.0, 1.0);
2422
2768
  return clearcoatLobe + sheenLobe + anisotropyLobe + iridescenceLobe;
2423
2769
  }
2770
+
2771
+ #ifdef A3D_PBR_EXTENSION_ATLAS
2772
+ uniform sampler2D u_extensionScalarAtlas;
2773
+ int a3dAtlasWrapIndex(int x, int size, float mode) {
2774
+ if (mode < 0.5) return clamp(x, 0, size - 1);
2775
+ int period = mode < 1.5 ? size : 2 * size;
2776
+ int index = ((x % period) + period) % period;
2777
+ return mode < 1.5 ? index : (index < size ? index : period - 1 - index);
2778
+ }
2779
+ vec4 a3dAtlasFetch(ivec2 point, ivec2 size, ivec2 origin, vec2 wrapMode) {
2780
+ ivec2 wrapped = ivec2(a3dAtlasWrapIndex(point.x,size.x,wrapMode.x), a3dAtlasWrapIndex(point.y,size.y,wrapMode.y));
2781
+ return texelFetch(u_extensionScalarAtlas, origin + wrapped, 0);
2782
+ }
2783
+ vec4 a3dAtlasLevel(vec2 uv, vec4 rect, float originY, vec2 wrapMode, int level, bool nearestFilter) {
2784
+ ivec2 size = ivec2(rect.yz);
2785
+ ivec2 origin = ivec2(int(rect.x),int(originY));
2786
+ for (int i=0; i<16; i++) {
2787
+ if (i >= level) break;
2788
+ origin.y += size.y;
2789
+ size = max(size / 2, ivec2(1));
2790
+ }
2791
+ vec2 pixel = uv * vec2(size) - 0.5;
2792
+ if (nearestFilter) return a3dAtlasFetch(ivec2(floor(pixel + 0.5)),size,origin,wrapMode);
2793
+ ivec2 low = ivec2(floor(pixel));
2794
+ vec2 fraction = fract(pixel);
2795
+ return mix(mix(a3dAtlasFetch(low,size,origin,wrapMode),a3dAtlasFetch(low+ivec2(1,0),size,origin,wrapMode),fraction.x),
2796
+ mix(a3dAtlasFetch(low+ivec2(0,1),size,origin,wrapMode),a3dAtlasFetch(low+ivec2(1,1),size,origin,wrapMode),fraction.x),fraction.y);
2797
+ }
2798
+ vec4 a3dAtlasSample(vec2 uv, vec4 rect, float originY, vec2 wrapMode, vec4 filterMode) {
2799
+ vec2 dx=dFdx(uv), dy=dFdy(uv);
2800
+ float lx=length(dx*rect.yz), ly=length(dy*rect.yz);
2801
+ float major=max(lx,ly), minor=max(min(lx,ly),0.000001);
2802
+ bool magnify=major<=1.0;
2803
+ float taps=magnify ? 1.0 : clamp(ceil(major/max(minor,1.0)),1.0,floor(filterMode.w));
2804
+ vec2 axis=lx>=ly ? dx : dy;
2805
+ float lod=clamp(log2(max(major/taps,1.0)),0.0,rect.w);
2806
+ if (filterMode.z<0.5) lod=0.0;
2807
+ else if (filterMode.z<1.5) lod=floor(lod+0.5);
2808
+ int low=int(floor(lod)), high=min(low+1,int(rect.w));
2809
+ bool nearestFilter=magnify ? filterMode.x>0.5 : filterMode.y>0.5;
2810
+ vec4 total=vec4(0.0);
2811
+ for (int i=0;i<16;i++) {
2812
+ if (float(i)>=taps) break;
2813
+ vec2 sampleUv=uv+axis*((float(i)+0.5)/taps-0.5);
2814
+ total+=mix(a3dAtlasLevel(sampleUv,rect,originY,wrapMode,low,nearestFilter),a3dAtlasLevel(sampleUv,rect,originY,wrapMode,high,nearestFilter),fract(lod));
2815
+ }
2816
+ return total/taps;
2817
+ }
2818
+ #endif
2819
+
2820
+
2821
+
2822
+
2823
+
2824
+
2424
2825
  void main() {
2425
2826
  vec2 baseColorUv = a3dTexturedPbrUv(u_baseColorTextureTexCoord, u_baseColorTextureOffset, u_baseColorTextureScale, u_baseColorTextureRotation);
2426
2827
  vec2 normalUv = a3dTexturedPbrUv(u_normalTextureTexCoord, u_normalTextureOffset, u_normalTextureScale, u_normalTextureRotation);
@@ -2534,10 +2935,18 @@ void main() {
2534
2935
  float anisotropy = clamp(u_anisotropyStrength, 0.0, 1.0);
2535
2936
  float anisotropyRotation = u_anisotropyRotation;
2536
2937
  float iridescence = clamp(u_iridescenceFactor, 0.0, 1.0);
2537
- float iridescenceThickness = mix(u_iridescenceThicknessMinimum, u_iridescenceThicknessMaximum, 0.5);
2938
+ float iridescenceThickness = u_iridescenceThicknessMaximum;
2538
2939
  #ifdef A3D_PBR_CLEARCOAT_TEXTURES
2940
+ #ifdef A3D_PBR_EXTENSION_ATLAS
2941
+ clearcoat = clamp(clearcoat * mix(1.0, a3dAtlasSample(clearcoatUv, u_clearcoatAtlasRect, u_clearcoatAtlasY, u_clearcoatTextureWrap, u_clearcoatAtlasFilter).r, step(0.5, u_clearcoatTextureEnabled)), 0.0, 1.0);
2942
+ #else
2539
2943
  clearcoat = clamp(clearcoat * mix(1.0, texture(u_clearcoatTexture, a3dTexturedPbrWrapUv(clearcoatUv, u_clearcoatTextureWrap)).r, step(0.5, u_clearcoatTextureEnabled)), 0.0, 1.0);
2944
+ #endif
2945
+ #ifdef A3D_PBR_EXTENSION_ATLAS
2946
+ clearcoatRoughness = clamp(clearcoatRoughness * mix(1.0, a3dAtlasSample(clearcoatRoughnessUv, u_clearcoatRoughnessAtlasRect, u_clearcoatRoughnessAtlasY, u_clearcoatRoughnessTextureWrap, u_clearcoatRoughnessAtlasFilter).g, step(0.5, u_clearcoatRoughnessTextureEnabled)), 0.0, 1.0);
2947
+ #else
2540
2948
  clearcoatRoughness = clamp(clearcoatRoughness * mix(1.0, texture(u_clearcoatRoughnessTexture, a3dTexturedPbrWrapUv(clearcoatRoughnessUv, u_clearcoatRoughnessTextureWrap)).g, step(0.5, u_clearcoatRoughnessTextureEnabled)), 0.0, 1.0);
2949
+ #endif
2541
2950
  clearcoatRoughness = max(clearcoatRoughness, 0.19 + (1.0 - clearcoatNormalBoost) * 0.12);
2542
2951
  #endif
2543
2952
  #ifdef A3D_PBR_TRANSMISSION_VOLUME_TEXTURES
@@ -2552,12 +2961,29 @@ void main() {
2552
2961
  #endif
2553
2962
  #ifdef A3D_PBR_SPECULAR_SHEEN_ANISOTROPY_TEXTURES
2554
2963
  sheenColor *= mix(vec3(1.0), a3dTexturedPbrDecodeSrgb(texture(u_sheenColorTexture, a3dTexturedPbrWrapUv(sheenColorUv, u_sheenColorTextureWrap)).rgb), step(0.5, u_sheenColorTextureEnabled));
2964
+ #ifdef A3D_PBR_EXTENSION_ATLAS
2965
+ sheenRoughness = clamp(sheenRoughness * mix(1.0, a3dAtlasSample(sheenRoughnessUv, u_sheenRoughnessAtlasRect, u_sheenRoughnessAtlasY, u_sheenRoughnessTextureWrap, u_sheenRoughnessAtlasFilter).a, step(0.5, u_sheenRoughnessTextureEnabled)), 0.0, 1.0);
2966
+ #else
2555
2967
  sheenRoughness = clamp(sheenRoughness * mix(1.0, texture(u_sheenRoughnessTexture, a3dTexturedPbrWrapUv(sheenRoughnessUv, u_sheenRoughnessTextureWrap)).a, step(0.5, u_sheenRoughnessTextureEnabled)), 0.0, 1.0);
2556
- anisotropy = clamp(anisotropy * mix(1.0, texture(u_anisotropyTexture, a3dTexturedPbrWrapUv(anisotropyUv, u_anisotropyTextureWrap)).b, step(0.5, u_anisotropyTextureEnabled)), 0.0, 1.0);
2968
+ #endif
2969
+ vec3 anisotropySample = texture(u_anisotropyTexture, a3dTexturedPbrWrapUv(anisotropyUv, u_anisotropyTextureWrap)).rgb;
2970
+ anisotropy = clamp(anisotropy * mix(1.0, anisotropySample.b, step(0.5, u_anisotropyTextureEnabled)), 0.0, 1.0);
2971
+ vec2 anisotropyDirection = anisotropySample.rg * 2.0 - 1.0;
2972
+ if (u_anisotropyTextureEnabled > 0.5 && dot(anisotropyDirection, anisotropyDirection) > 0.000001) {
2973
+ anisotropyRotation += atan(anisotropyDirection.y, anisotropyDirection.x);
2974
+ }
2557
2975
  #endif
2558
2976
  #ifdef A3D_PBR_IRIDESCENCE_TEXTURES
2977
+ #ifdef A3D_PBR_EXTENSION_ATLAS
2978
+ iridescence = clamp(iridescence * mix(1.0, a3dAtlasSample(iridescenceUv, u_iridescenceAtlasRect, u_iridescenceAtlasY, u_iridescenceTextureWrap, u_iridescenceAtlasFilter).r, step(0.5, u_iridescenceTextureEnabled)), 0.0, 1.0);
2979
+ #else
2559
2980
  iridescence = clamp(iridescence * mix(1.0, texture(u_iridescenceTexture, a3dTexturedPbrWrapUv(iridescenceUv, u_iridescenceTextureWrap)).r, step(0.5, u_iridescenceTextureEnabled)), 0.0, 1.0);
2981
+ #endif
2982
+ #ifdef A3D_PBR_EXTENSION_ATLAS
2983
+ iridescenceThickness = mix(iridescenceThickness, mix(u_iridescenceThicknessMinimum, u_iridescenceThicknessMaximum, a3dAtlasSample(iridescenceThicknessUv, u_iridescenceThicknessAtlasRect, u_iridescenceThicknessAtlasY, u_iridescenceThicknessTextureWrap, u_iridescenceThicknessAtlasFilter).g), step(0.5, u_iridescenceThicknessTextureEnabled));
2984
+ #else
2560
2985
  iridescenceThickness = mix(iridescenceThickness, mix(u_iridescenceThicknessMinimum, u_iridescenceThicknessMaximum, texture(u_iridescenceThicknessTexture, a3dTexturedPbrWrapUv(iridescenceThicknessUv, u_iridescenceThicknessTextureWrap)).g), step(0.5, u_iridescenceThicknessTextureEnabled));
2986
+ #endif
2561
2987
  #endif
2562
2988
  vec3 base = a3dApplyAdvancedPbrLobes(
2563
2989
  texturedBase.rgb,
@@ -2577,13 +3003,14 @@ void main() {
2577
3003
  sheenRoughness,
2578
3004
  anisotropy,
2579
3005
  anisotropyRotation,
2580
- iridescence,
3006
+ 0.0,
2581
3007
  u_iridescenceIor,
2582
3008
  iridescenceThickness,
2583
3009
  iridescenceThickness,
2584
3010
  u_dispersion
2585
3011
  );
2586
3012
  vec3 viewDirection = normalize(u_cameraPosition - v_worldPosition);
3013
+ vec3 iridescenceSubstrateF0 = mix(vec3(pow((u_ior - 1.0) / max(u_ior + 1.0, 0.0001), 2.0)) * specularColor * specular, texturedBase.rgb, metallic);
2587
3014
  float materialRedPaintGate = smoothstep(0.28, 0.72, texturedBase.r)
2588
3015
  * (1.0 - smoothstep(0.05, 0.18, max(texturedBase.g, texturedBase.b)))
2589
3016
  * smoothstep(0.015, 0.07, u_materialEnvironmentSpecularScale);
@@ -2618,7 +3045,10 @@ void main() {
2618
3045
  iridescence,
2619
3046
  u_iridescenceIor,
2620
3047
  iridescenceThickness,
2621
- iridescenceThickness
3048
+ iridescenceThickness,
3049
+ iridescenceSubstrateF0,
3050
+ mappedNormal,
3051
+ a3dTexturedPbrEnvironmentSpecularInput(mappedNormal, viewDirection, roughness)
2622
3052
  );
2623
3053
  float texturedTransmissionAmount = clamp(max(transmission, diffuseTransmission), 0.0, 1.0);
2624
3054
  float texturedSampledEnvironmentWeight = step(0.0001, u_environmentMapTextureEnabled * u_environmentMapTextureIntensity);
@@ -2651,10 +3081,6 @@ void main() {
2651
3081
  vec2 backdropOffset = normalize(texturedRefractionDirection.xy + vec2(0.0001)) * u_transmissionBackdropRefractionScale * mix(1.25, 0.55, roughness) * clamp(u_ior - 1.0, 0.0, 2.5);
2652
3082
  float backdropLod = clamp((roughness + volumeThickness * 0.08) * max(u_transmissionBackdropMipCount - 1.0, 0.0), 0.0, max(u_transmissionBackdropMipCount - 1.0, 0.0));
2653
3083
  vec3 backdropRadiance = a3dTexturedPbrDecodeSrgb(textureLod(u_transmissionBackdropTexture, clamp(backdropUv + backdropOffset, vec2(0.001), vec2(0.999)), backdropLod).rgb);
2654
- // Scene-color refraction travels through the same participating volume as
2655
- // environment refraction. Omitting texturedVolumeTint here made thickness,
2656
- // attenuationDistance, and attenuationColor byte-identical whenever the
2657
- // renderer-owned backdrop path was active.
2658
3084
  texturedRefractionRadiance = mix(
2659
3085
  texturedRefractionRadiance,
2660
3086
  backdropRadiance * texturedVolumeTint * texturedTransmissionAmount * mix(1.0, 1.22, texturedBackdropWeight),
@@ -2720,6 +3146,9 @@ void main() {
2720
3146
  attenuation *= smoothstep(outer, inner, cone);
2721
3147
  }
2722
3148
  float shadowFactor = mix(1.0, kind > 0.5 && kind < 1.5 ? a3dTexturedPbrPointShadowFactor(v_worldPosition, mappedNormal, lightDirection) : a3dTexturedPbrShadowFactor(v_worldPosition, mappedNormal, lightDirection), step(0.5, spotShadowLayer.z));
3149
+ #ifndef A3D_PBR_NO_SPOT_SHADOW
3150
+ shadowFactor = a3dTexturedPbrResolveSpotShadowOverride(shadowFactor, kind, spotShadowLayer.z, v_worldPosition, mappedNormal, lightDirection);
3151
+ #endif
2723
3152
  float directLightIntensity = colorIntensity.a
2724
3153
  * attenuation
2725
3154
  * shadowFactor
@@ -2746,7 +3175,10 @@ void main() {
2746
3175
  iridescence,
2747
3176
  u_iridescenceIor,
2748
3177
  iridescenceThickness,
2749
- iridescenceThickness
3178
+ iridescenceThickness,
3179
+ iridescenceSubstrateF0,
3180
+ mappedNormal,
3181
+ roughness
2750
3182
  ) * directExtensionSpecularScale;
2751
3183
  }
2752
3184
  float alpha = texturedBase.a;
@@ -2781,28 +3213,18 @@ out float v_lineDistance;
2781
3213
  out float v_halfWidthPixels;
2782
3214
 
2783
3215
  void main() {
2784
- // a_position holds this vertex's anchor endpoint in world space. It is referenced
2785
- // with zero weight so the attribute is not optimized out of the linked program:
2786
- // bounds computation, frustum culling, and the material's attribute contract all
2787
- // depend on the geometry declaring a real position stream.
2788
3216
  vec3 anchorWorld = a_position;
2789
3217
  vec4 clipStart = u_modelViewProjection * vec4(a_lineStart + anchorWorld * 0.0, 1.0);
2790
3218
  vec4 clipEnd = u_modelViewProjection * vec4(a_lineEnd, 1.0);
2791
3219
 
2792
- // Guard against a zero or negative w: a vertex behind the eye would otherwise
2793
- // produce a wildly wrong NDC position and a smeared quad.
2794
3220
  float wStart = max(abs(clipStart.w), 1e-5);
2795
3221
  float wEnd = max(abs(clipEnd.w), 1e-5);
2796
3222
 
2797
- // Project to device pixels. Expansion happens here, so the resulting width is a
2798
- // pixel quantity and is therefore independent of depth, FOV, and viewport size.
2799
3223
  vec2 pixelStart = (clipStart.xy / wStart) * 0.5 * u_lineResolution;
2800
3224
  vec2 pixelEnd = (clipEnd.xy / wEnd) * 0.5 * u_lineResolution;
2801
3225
 
2802
3226
  vec2 pixelDirection = pixelEnd - pixelStart;
2803
3227
  float pixelLength = length(pixelDirection);
2804
- // A segment that projects to a single pixel has no screen direction; pick an
2805
- // arbitrary axis so the cap still renders instead of collapsing.
2806
3228
  vec2 direction = pixelLength > 1e-4 ? pixelDirection / pixelLength : vec2(1.0, 0.0);
2807
3229
  vec2 normal = vec2(-direction.y, direction.x);
2808
3230
 
@@ -2811,14 +3233,10 @@ void main() {
2811
3233
 
2812
3234
  float along = a_lineCorner.y;
2813
3235
  vec2 anchorPixel = mix(pixelStart, pixelEnd, along);
2814
- // Square caps push the quad outward along the segment direction so the stroke
2815
- // extends half a width past each endpoint, matching Three.js LineMaterial behaviour.
2816
3236
  float capExtension = u_lineSquareCaps > 0.5 ? halfWidth : 0.0;
2817
3237
  vec2 capOffset = direction * (along > 0.5 ? capExtension : -capExtension);
2818
3238
  vec2 pixelPosition = anchorPixel + normal * (a_lineCorner.x * halfWidth) + capOffset;
2819
3239
 
2820
- // Back to clip space, preserving the anchor endpoint's own depth so the line
2821
- // depth-sorts against the rest of the scene correctly.
2822
3240
  vec4 anchorClip = along > 0.5 ? clipEnd : clipStart;
2823
3241
  float anchorW = along > 0.5 ? wEnd : wStart;
2824
3242
  vec2 ndc = pixelPosition / (0.5 * u_lineResolution);
@@ -2847,12 +3265,9 @@ void main() {
2847
3265
  if (u_lineDashSize > 0.0) {
2848
3266
  float period = u_lineDashSize + max(u_lineGapSize, 0.0);
2849
3267
  float phase = mod(v_lineDistance + u_lineDashOffset, period);
2850
- // Discard the gap portion of each dash period.
2851
3268
  if (phase > u_lineDashSize) discard;
2852
3269
  }
2853
3270
  if (u_lineRoundCaps > 0.5) {
2854
- // Round caps: reject fragments outside the unit half-disc at each end. The
2855
- // corner's x component already spans -1..1 across the stroke width.
2856
3271
  float acrossStroke = v_lineCorner.x;
2857
3272
  if (abs(acrossStroke) > 1.0) discard;
2858
3273
  }