@aura3d/engine 1.4.4 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (487) hide show
  1. package/README.md +65 -30
  2. package/dist/animation/Retargeting.d.ts +12 -0
  3. package/dist/animation/Retargeting.d.ts.map +1 -0
  4. package/dist/animation/Retargeting.js +19 -0
  5. package/dist/animation/Retargeting.js.map +1 -0
  6. package/dist/assets/GLTFLoader.d.ts +4 -0
  7. package/dist/assets/GLTFLoader.d.ts.map +1 -1
  8. package/dist/assets/GLTFLoader.js +39 -4
  9. package/dist/assets/GLTFLoader.js.map +1 -1
  10. package/dist/assets/GLTFRenderResources.d.ts +16 -0
  11. package/dist/assets/GLTFRenderResources.d.ts.map +1 -1
  12. package/dist/assets/GLTFRenderResources.js +42 -2
  13. package/dist/assets/GLTFRenderResources.js.map +1 -1
  14. package/dist/assets/asset-corpus/ProductionGLTFRenderPipeline.d.ts +6 -0
  15. package/dist/assets/asset-corpus/ProductionGLTFRenderPipeline.d.ts.map +1 -1
  16. package/dist/assets/asset-corpus/ProductionGLTFRenderPipeline.js +2 -1
  17. package/dist/assets/asset-corpus/ProductionGLTFRenderPipeline.js.map +1 -1
  18. package/dist/assets/index.d.ts +0 -1
  19. package/dist/assets/index.d.ts.map +1 -1
  20. package/dist/assets/index.js +0 -1
  21. package/dist/assets/index.js.map +1 -1
  22. package/dist/assets/loaders/index.d.ts +0 -1
  23. package/dist/assets/loaders/index.d.ts.map +1 -1
  24. package/dist/assets/loaders/index.js +0 -1
  25. package/dist/assets/loaders/index.js.map +1 -1
  26. package/dist/aura3d-cli/asset-intent.d.ts +148 -0
  27. package/dist/aura3d-cli/asset-intent.d.ts.map +1 -0
  28. package/dist/aura3d-cli/asset-intent.js +145 -0
  29. package/dist/aura3d-cli/asset-intent.js.map +1 -0
  30. package/dist/aura3d-cli/asset-role-admission.d.ts +174 -0
  31. package/dist/aura3d-cli/asset-role-admission.d.ts.map +1 -0
  32. package/dist/aura3d-cli/asset-role-admission.js +381 -0
  33. package/dist/aura3d-cli/asset-role-admission.js.map +1 -0
  34. package/dist/aura3d-cli/asset-screening-effects.d.ts +95 -0
  35. package/dist/aura3d-cli/asset-screening-effects.d.ts.map +1 -0
  36. package/dist/aura3d-cli/asset-screening-effects.js +321 -0
  37. package/dist/aura3d-cli/asset-screening-effects.js.map +1 -0
  38. package/dist/aura3d-cli/asset-screening-pipeline.d.ts +132 -0
  39. package/dist/aura3d-cli/asset-screening-pipeline.d.ts.map +1 -0
  40. package/dist/aura3d-cli/asset-screening-pipeline.js +277 -0
  41. package/dist/aura3d-cli/asset-screening-pipeline.js.map +1 -0
  42. package/dist/aura3d-cli/cartoon-asset-profiles.d.ts +15 -0
  43. package/dist/aura3d-cli/cartoon-asset-profiles.d.ts.map +1 -0
  44. package/dist/aura3d-cli/cartoon-asset-profiles.js +46 -0
  45. package/dist/aura3d-cli/cartoon-asset-profiles.js.map +1 -0
  46. package/dist/aura3d-cli/cli-help.js +1 -1
  47. package/dist/aura3d-cli/cli.js +16 -2
  48. package/dist/aura3d-cli/cli.js.map +1 -1
  49. package/dist/aura3d-cli/index.d.ts +8 -0
  50. package/dist/aura3d-cli/index.d.ts.map +1 -1
  51. package/dist/aura3d-cli/index.js +390 -17
  52. package/dist/aura3d-cli/index.js.map +1 -1
  53. package/dist/aura3d-cli/pull-bridge/search.d.ts +4 -0
  54. package/dist/aura3d-cli/pull-bridge/search.d.ts.map +1 -1
  55. package/dist/aura3d-cli/pull-bridge/search.js +22 -0
  56. package/dist/aura3d-cli/pull-bridge/search.js.map +1 -1
  57. package/dist/aura3d-cli/pull-bridge.d.ts +20 -5
  58. package/dist/aura3d-cli/pull-bridge.d.ts.map +1 -1
  59. package/dist/aura3d-cli/pull-bridge.js +38 -1
  60. package/dist/aura3d-cli/pull-bridge.js.map +1 -1
  61. package/dist/controls/ControlState.d.ts +1 -1
  62. package/dist/controls/ControlState.d.ts.map +1 -1
  63. package/dist/controls/DragControls.d.ts +21 -1
  64. package/dist/controls/DragControls.d.ts.map +1 -1
  65. package/dist/controls/DragControls.js +24 -2
  66. package/dist/controls/DragControls.js.map +1 -1
  67. package/dist/controls/FirstPersonControls.d.ts +10 -1
  68. package/dist/controls/FirstPersonControls.d.ts.map +1 -1
  69. package/dist/controls/FirstPersonControls.js +18 -2
  70. package/dist/controls/FirstPersonControls.js.map +1 -1
  71. package/dist/controls/FlyControls.d.ts +42 -0
  72. package/dist/controls/FlyControls.d.ts.map +1 -1
  73. package/dist/controls/FlyControls.js +106 -3
  74. package/dist/controls/FlyControls.js.map +1 -1
  75. package/dist/controls/MapControls.d.ts +13 -0
  76. package/dist/controls/MapControls.d.ts.map +1 -1
  77. package/dist/controls/MapControls.js +40 -2
  78. package/dist/controls/MapControls.js.map +1 -1
  79. package/dist/controls/OrbitControls.d.ts +54 -0
  80. package/dist/controls/OrbitControls.d.ts.map +1 -1
  81. package/dist/controls/OrbitControls.js +133 -0
  82. package/dist/controls/OrbitControls.js.map +1 -1
  83. package/dist/controls/PointerLockControls.d.ts +12 -5
  84. package/dist/controls/PointerLockControls.d.ts.map +1 -1
  85. package/dist/controls/PointerLockControls.js +30 -11
  86. package/dist/controls/PointerLockControls.js.map +1 -1
  87. package/dist/controls/SelectionManager.d.ts +22 -1
  88. package/dist/controls/SelectionManager.d.ts.map +1 -1
  89. package/dist/controls/SelectionManager.js +64 -6
  90. package/dist/controls/SelectionManager.js.map +1 -1
  91. package/dist/controls/TransformControls.d.ts +135 -1
  92. package/dist/controls/TransformControls.d.ts.map +1 -1
  93. package/dist/controls/TransformControls.js +534 -10
  94. package/dist/controls/TransformControls.js.map +1 -1
  95. package/dist/controls/index.d.ts +7 -2
  96. package/dist/controls/index.d.ts.map +1 -1
  97. package/dist/controls/index.js +1 -1
  98. package/dist/controls/index.js.map +1 -1
  99. package/dist/create-aura3d/index.js +1 -1
  100. package/dist/create-aura3d/showcase-spec-game-geometry-extractor.d.ts +2 -0
  101. package/dist/create-aura3d/showcase-spec-game-geometry-extractor.d.ts.map +1 -1
  102. package/dist/create-aura3d/showcase-spec-game-geometry-extractor.js +781 -46
  103. package/dist/create-aura3d/showcase-spec-game-geometry-extractor.js.map +1 -1
  104. package/dist/create-aura3d/showcase-spec-game-template-evidence.d.ts.map +1 -1
  105. package/dist/create-aura3d/showcase-spec-game-template-evidence.js +24 -4
  106. package/dist/create-aura3d/showcase-spec-game-template-evidence.js.map +1 -1
  107. package/dist/create-aura3d/showcase-spec-platformer-artifacts.d.ts.map +1 -1
  108. package/dist/create-aura3d/showcase-spec-platformer-artifacts.js +28 -17
  109. package/dist/create-aura3d/showcase-spec-platformer-artifacts.js.map +1 -1
  110. package/dist/create-aura3d/showcase-spec-racing-artifacts.d.ts.map +1 -1
  111. package/dist/create-aura3d/showcase-spec-racing-artifacts.js +10 -5
  112. package/dist/create-aura3d/showcase-spec-racing-artifacts.js.map +1 -1
  113. package/dist/create-aura3d/showcase-spec-replacement-candidates.js +26 -0
  114. package/dist/create-aura3d/showcase-spec-replacement-candidates.js.map +1 -1
  115. package/dist/ecs/components/MeshComponent.d.ts +5 -2
  116. package/dist/ecs/components/MeshComponent.d.ts.map +1 -1
  117. package/dist/ecs/components/MeshComponent.js.map +1 -1
  118. package/dist/editor-runtime/CartoonSceneEditor.d.ts +111 -0
  119. package/dist/editor-runtime/CartoonSceneEditor.d.ts.map +1 -0
  120. package/dist/editor-runtime/CartoonSceneEditor.js +285 -0
  121. package/dist/editor-runtime/CartoonSceneEditor.js.map +1 -0
  122. package/dist/editor-runtime/InteractiveTransformGizmo.d.ts +142 -0
  123. package/dist/editor-runtime/InteractiveTransformGizmo.d.ts.map +1 -0
  124. package/dist/editor-runtime/InteractiveTransformGizmo.js +468 -0
  125. package/dist/editor-runtime/InteractiveTransformGizmo.js.map +1 -0
  126. package/dist/editor-runtime/index.d.ts +2 -0
  127. package/dist/editor-runtime/index.d.ts.map +1 -1
  128. package/dist/editor-runtime/index.js +1 -0
  129. package/dist/editor-runtime/index.js.map +1 -1
  130. package/dist/engine/agent-api/CartoonAssetManifest.d.ts +50 -0
  131. package/dist/engine/agent-api/CartoonAssetManifest.d.ts.map +1 -0
  132. package/dist/engine/agent-api/CartoonAssetManifest.js +65 -0
  133. package/dist/engine/agent-api/CartoonAssetManifest.js.map +1 -0
  134. package/dist/engine/agent-api/CartoonDirector.d.ts +120 -0
  135. package/dist/engine/agent-api/CartoonDirector.d.ts.map +1 -0
  136. package/dist/engine/agent-api/CartoonDirector.js +432 -0
  137. package/dist/engine/agent-api/CartoonDirector.js.map +1 -0
  138. package/dist/engine/agent-api/CartoonEpisodePackage.d.ts +51 -0
  139. package/dist/engine/agent-api/CartoonEpisodePackage.d.ts.map +1 -0
  140. package/dist/engine/agent-api/CartoonEpisodePackage.js +79 -0
  141. package/dist/engine/agent-api/CartoonEpisodePackage.js.map +1 -0
  142. package/dist/engine/agent-api/CartoonMotionQuality.d.ts +83 -0
  143. package/dist/engine/agent-api/CartoonMotionQuality.d.ts.map +1 -0
  144. package/dist/engine/agent-api/CartoonMotionQuality.js +128 -0
  145. package/dist/engine/agent-api/CartoonMotionQuality.js.map +1 -0
  146. package/dist/engine/agent-api/CartoonPerformance.d.ts +150 -0
  147. package/dist/engine/agent-api/CartoonPerformance.d.ts.map +1 -0
  148. package/dist/engine/agent-api/CartoonPerformance.js +345 -0
  149. package/dist/engine/agent-api/CartoonPerformance.js.map +1 -0
  150. package/dist/engine/agent-api/CartoonRenderQueue.d.ts +143 -0
  151. package/dist/engine/agent-api/CartoonRenderQueue.d.ts.map +1 -0
  152. package/dist/engine/agent-api/CartoonRenderQueue.js +410 -0
  153. package/dist/engine/agent-api/CartoonRenderQueue.js.map +1 -0
  154. package/dist/engine/agent-api/CartoonRouteProof.d.ts +112 -0
  155. package/dist/engine/agent-api/CartoonRouteProof.d.ts.map +1 -0
  156. package/dist/engine/agent-api/CartoonRouteProof.js +127 -0
  157. package/dist/engine/agent-api/CartoonRouteProof.js.map +1 -0
  158. package/dist/engine/agent-api/GameEvidence.d.ts +1 -0
  159. package/dist/engine/agent-api/GameEvidence.d.ts.map +1 -1
  160. package/dist/engine/agent-api/GameEvidence.js +1 -0
  161. package/dist/engine/agent-api/GameEvidence.js.map +1 -1
  162. package/dist/engine/agent-api/GameGenreKits.d.ts +13 -0
  163. package/dist/engine/agent-api/GameGenreKits.d.ts.map +1 -1
  164. package/dist/engine/agent-api/GameGenreKits.js +57 -11
  165. package/dist/engine/agent-api/GameGenreKits.js.map +1 -1
  166. package/dist/engine/agent-api/GameRuntime.d.ts +1 -0
  167. package/dist/engine/agent-api/GameRuntime.d.ts.map +1 -1
  168. package/dist/engine/agent-api/GameRuntime.js +1 -0
  169. package/dist/engine/agent-api/GameRuntime.js.map +1 -1
  170. package/dist/engine/agent-api/GameSceneGeometryBindings.d.ts +12 -0
  171. package/dist/engine/agent-api/GameSceneGeometryBindings.d.ts.map +1 -1
  172. package/dist/engine/agent-api/GameSceneGeometryBindings.js +2 -0
  173. package/dist/engine/agent-api/GameSceneGeometryBindings.js.map +1 -1
  174. package/dist/engine/agent-api/LayeredSceneComposition.d.ts +295 -0
  175. package/dist/engine/agent-api/LayeredSceneComposition.d.ts.map +1 -0
  176. package/dist/engine/agent-api/LayeredSceneComposition.js +387 -0
  177. package/dist/engine/agent-api/LayeredSceneComposition.js.map +1 -0
  178. package/dist/engine/agent-api/SceneGroundingUtils.d.ts +62 -0
  179. package/dist/engine/agent-api/SceneGroundingUtils.d.ts.map +1 -1
  180. package/dist/engine/agent-api/SceneGroundingUtils.js +37 -0
  181. package/dist/engine/agent-api/SceneGroundingUtils.js.map +1 -1
  182. package/dist/engine/agent-api/SubjectFramingUtils.d.ts +127 -0
  183. package/dist/engine/agent-api/SubjectFramingUtils.d.ts.map +1 -0
  184. package/dist/engine/agent-api/SubjectFramingUtils.js +126 -0
  185. package/dist/engine/agent-api/SubjectFramingUtils.js.map +1 -0
  186. package/dist/engine/agent-api/TouchControlBinding.d.ts +81 -0
  187. package/dist/engine/agent-api/TouchControlBinding.d.ts.map +1 -0
  188. package/dist/engine/agent-api/TouchControlBinding.js +116 -0
  189. package/dist/engine/agent-api/TouchControlBinding.js.map +1 -0
  190. package/dist/engine/agent-api/index.d.ts +39 -0
  191. package/dist/engine/agent-api/index.d.ts.map +1 -1
  192. package/dist/engine/agent-api/index.js +546 -74
  193. package/dist/engine/agent-api/index.js.map +1 -1
  194. package/dist/engine/agent-api/product-viewer-runtime.d.ts.map +1 -1
  195. package/dist/engine/production-runtime/GameRenderPreset.d.ts +32 -0
  196. package/dist/engine/production-runtime/GameRenderPreset.d.ts.map +1 -1
  197. package/dist/engine/production-runtime/GameRenderPreset.js +37 -2
  198. package/dist/engine/production-runtime/GameRenderPreset.js.map +1 -1
  199. package/dist/engine/production-runtime/TypedGLBActor.d.ts +8 -0
  200. package/dist/engine/production-runtime/TypedGLBActor.d.ts.map +1 -1
  201. package/dist/engine/production-runtime/TypedGLBActor.js +2 -1
  202. package/dist/engine/production-runtime/TypedGLBActor.js.map +1 -1
  203. package/dist/input/controls/PointerLockControls.d.ts +1 -1
  204. package/dist/input/controls/PointerLockControls.d.ts.map +1 -1
  205. package/dist/input/controls/PointerLockControls.js +12 -0
  206. package/dist/input/controls/PointerLockControls.js.map +1 -1
  207. package/dist/physics/Collider.d.ts +1 -1
  208. package/dist/physics/Collider.d.ts.map +1 -1
  209. package/dist/physics/Collider.js +2 -2
  210. package/dist/physics/Collider.js.map +1 -1
  211. package/dist/physics/CollisionEvents.d.ts +1 -0
  212. package/dist/physics/CollisionEvents.d.ts.map +1 -1
  213. package/dist/physics/CollisionEvents.js.map +1 -1
  214. package/dist/physics/NarrowPhase.d.ts +11 -0
  215. package/dist/physics/NarrowPhase.d.ts.map +1 -0
  216. package/dist/physics/NarrowPhase.js +565 -0
  217. package/dist/physics/NarrowPhase.js.map +1 -0
  218. package/dist/physics/PhysicsDebugDraw.js +19 -1
  219. package/dist/physics/PhysicsDebugDraw.js.map +1 -1
  220. package/dist/physics/PhysicsWorld.d.ts +43 -0
  221. package/dist/physics/PhysicsWorld.d.ts.map +1 -1
  222. package/dist/physics/PhysicsWorld.js +305 -30
  223. package/dist/physics/PhysicsWorld.js.map +1 -1
  224. package/dist/physics/RigidBody.d.ts +8 -1
  225. package/dist/physics/RigidBody.d.ts.map +1 -1
  226. package/dist/physics/RigidBody.js +32 -5
  227. package/dist/physics/RigidBody.js.map +1 -1
  228. package/dist/physics/Shape.d.ts +16 -2
  229. package/dist/physics/Shape.d.ts.map +1 -1
  230. package/dist/physics/Shape.js +93 -31
  231. package/dist/physics/Shape.js.map +1 -1
  232. package/dist/physics/TimeOfImpact.d.ts +15 -0
  233. package/dist/physics/TimeOfImpact.d.ts.map +1 -0
  234. package/dist/physics/TimeOfImpact.js +81 -0
  235. package/dist/physics/TimeOfImpact.js.map +1 -0
  236. package/dist/physics/index.d.ts +2 -0
  237. package/dist/physics/index.d.ts.map +1 -1
  238. package/dist/physics/index.js +2 -0
  239. package/dist/physics/index.js.map +1 -1
  240. package/dist/rendering/CascadedShadowMaps.d.ts +9 -1
  241. package/dist/rendering/CascadedShadowMaps.d.ts.map +1 -1
  242. package/dist/rendering/CascadedShadowMaps.js +68 -0
  243. package/dist/rendering/CascadedShadowMaps.js.map +1 -1
  244. package/dist/rendering/ClusteredForwardLighting.d.ts +28 -0
  245. package/dist/rendering/ClusteredForwardLighting.d.ts.map +1 -0
  246. package/dist/rendering/ClusteredForwardLighting.js +156 -0
  247. package/dist/rendering/ClusteredForwardLighting.js.map +1 -0
  248. package/dist/rendering/DepthPass.d.ts +14 -1
  249. package/dist/rendering/DepthPass.d.ts.map +1 -1
  250. package/dist/rendering/DepthPass.js +19 -4
  251. package/dist/rendering/DepthPass.js.map +1 -1
  252. package/dist/rendering/EnvironmentMapResources.d.ts +59 -1
  253. package/dist/rendering/EnvironmentMapResources.d.ts.map +1 -1
  254. package/dist/rendering/EnvironmentMapResources.js +140 -100
  255. package/dist/rendering/EnvironmentMapResources.js.map +1 -1
  256. package/dist/rendering/EnvironmentPlatform.d.ts +3 -2
  257. package/dist/rendering/EnvironmentPlatform.d.ts.map +1 -1
  258. package/dist/rendering/EnvironmentPlatform.js +99 -44
  259. package/dist/rendering/EnvironmentPlatform.js.map +1 -1
  260. package/dist/rendering/ForwardPass.d.ts +37 -0
  261. package/dist/rendering/ForwardPass.d.ts.map +1 -1
  262. package/dist/rendering/ForwardPass.js +234 -7
  263. package/dist/rendering/ForwardPass.js.map +1 -1
  264. package/dist/rendering/Geometry.d.ts +19 -0
  265. package/dist/rendering/Geometry.d.ts.map +1 -1
  266. package/dist/rendering/Geometry.js +64 -0
  267. package/dist/rendering/Geometry.js.map +1 -1
  268. package/dist/rendering/IBL.d.ts.map +1 -1
  269. package/dist/rendering/IBL.js +0 -1
  270. package/dist/rendering/IBL.js.map +1 -1
  271. package/dist/rendering/LightCollector.d.ts +5 -1
  272. package/dist/rendering/LightCollector.d.ts.map +1 -1
  273. package/dist/rendering/LightCollector.js +16 -3
  274. package/dist/rendering/LightCollector.js.map +1 -1
  275. package/dist/rendering/LightUniforms.d.ts +26 -1
  276. package/dist/rendering/LightUniforms.d.ts.map +1 -1
  277. package/dist/rendering/LightUniforms.js +140 -8
  278. package/dist/rendering/LightUniforms.js.map +1 -1
  279. package/dist/rendering/LightingRig.d.ts +77 -0
  280. package/dist/rendering/LightingRig.d.ts.map +1 -1
  281. package/dist/rendering/LightingRig.js +100 -11
  282. package/dist/rendering/LightingRig.js.map +1 -1
  283. package/dist/rendering/MeshConsolidation.d.ts +63 -0
  284. package/dist/rendering/MeshConsolidation.d.ts.map +1 -0
  285. package/dist/rendering/MeshConsolidation.js +251 -0
  286. package/dist/rendering/MeshConsolidation.js.map +1 -0
  287. package/dist/rendering/PMREM.d.ts +13 -1
  288. package/dist/rendering/PMREM.d.ts.map +1 -1
  289. package/dist/rendering/PMREM.js +18 -4
  290. package/dist/rendering/PMREM.js.map +1 -1
  291. package/dist/rendering/PostProcessPass.d.ts +24 -2
  292. package/dist/rendering/PostProcessPass.d.ts.map +1 -1
  293. package/dist/rendering/PostProcessPass.js +136 -18
  294. package/dist/rendering/PostProcessPass.js.map +1 -1
  295. package/dist/rendering/PrimitiveSubmissionAudit.d.ts +195 -0
  296. package/dist/rendering/PrimitiveSubmissionAudit.d.ts.map +1 -0
  297. package/dist/rendering/PrimitiveSubmissionAudit.js +280 -0
  298. package/dist/rendering/PrimitiveSubmissionAudit.js.map +1 -0
  299. package/dist/rendering/ProductTurntableFixtures.d.ts.map +1 -1
  300. package/dist/rendering/ProductTurntableFixtures.js +93 -13
  301. package/dist/rendering/ProductTurntableFixtures.js.map +1 -1
  302. package/dist/rendering/ReflectionProbe.d.ts +59 -0
  303. package/dist/rendering/ReflectionProbe.d.ts.map +1 -1
  304. package/dist/rendering/ReflectionProbe.js +224 -0
  305. package/dist/rendering/ReflectionProbe.js.map +1 -1
  306. package/dist/rendering/ReflectionSurfaces.d.ts +4 -2
  307. package/dist/rendering/ReflectionSurfaces.d.ts.map +1 -1
  308. package/dist/rendering/ReflectionSurfaces.js +13 -8
  309. package/dist/rendering/ReflectionSurfaces.js.map +1 -1
  310. package/dist/rendering/RenderBackend.d.ts +1 -1
  311. package/dist/rendering/RenderBackend.d.ts.map +1 -1
  312. package/dist/rendering/RenderBackend.js +5 -1
  313. package/dist/rendering/RenderBackend.js.map +1 -1
  314. package/dist/rendering/RenderDevice.d.ts +13 -2
  315. package/dist/rendering/RenderDevice.d.ts.map +1 -1
  316. package/dist/rendering/RenderDevice.js +5 -2
  317. package/dist/rendering/RenderDevice.js.map +1 -1
  318. package/dist/rendering/Renderer.d.ts +41 -1
  319. package/dist/rendering/Renderer.d.ts.map +1 -1
  320. package/dist/rendering/Renderer.js +406 -61
  321. package/dist/rendering/Renderer.js.map +1 -1
  322. package/dist/rendering/RendererPostprocessPlan.d.ts +10 -3
  323. package/dist/rendering/RendererPostprocessPlan.d.ts.map +1 -1
  324. package/dist/rendering/RendererPostprocessPlan.js +25 -5
  325. package/dist/rendering/RendererPostprocessPlan.js.map +1 -1
  326. package/dist/rendering/RendererVisualPipelineReport.d.ts +3 -2
  327. package/dist/rendering/RendererVisualPipelineReport.d.ts.map +1 -1
  328. package/dist/rendering/RendererVisualPipelineReport.js +2 -0
  329. package/dist/rendering/RendererVisualPipelineReport.js.map +1 -1
  330. package/dist/rendering/ScreenSpaceLineMaterial.d.ts +34 -0
  331. package/dist/rendering/ScreenSpaceLineMaterial.d.ts.map +1 -0
  332. package/dist/rendering/ScreenSpaceLineMaterial.js +93 -0
  333. package/dist/rendering/ScreenSpaceLineMaterial.js.map +1 -0
  334. package/dist/rendering/ShaderChunks.d.ts +7 -0
  335. package/dist/rendering/ShaderChunks.d.ts.map +1 -1
  336. package/dist/rendering/ShaderChunks.js +137 -0
  337. package/dist/rendering/ShaderChunks.js.map +1 -1
  338. package/dist/rendering/ShaderLibrary.d.ts +17 -0
  339. package/dist/rendering/ShaderLibrary.d.ts.map +1 -1
  340. package/dist/rendering/ShaderLibrary.js +940 -98
  341. package/dist/rendering/ShaderLibrary.js.map +1 -1
  342. package/dist/rendering/ShadowPass.d.ts +10 -0
  343. package/dist/rendering/ShadowPass.d.ts.map +1 -1
  344. package/dist/rendering/ShadowPass.js +8 -1
  345. package/dist/rendering/ShadowPass.js.map +1 -1
  346. package/dist/rendering/SkinnedLitMaterial.d.ts +6 -0
  347. package/dist/rendering/SkinnedLitMaterial.d.ts.map +1 -1
  348. package/dist/rendering/SkinnedLitMaterial.js +22 -8
  349. package/dist/rendering/SkinnedLitMaterial.js.map +1 -1
  350. package/dist/rendering/SkinnedUnlitMaterial.d.ts +8 -0
  351. package/dist/rendering/SkinnedUnlitMaterial.d.ts.map +1 -1
  352. package/dist/rendering/SkinnedUnlitMaterial.js +26 -9
  353. package/dist/rendering/SkinnedUnlitMaterial.js.map +1 -1
  354. package/dist/rendering/SpecularPrefilter.d.ts +42 -0
  355. package/dist/rendering/SpecularPrefilter.d.ts.map +1 -0
  356. package/dist/rendering/SpecularPrefilter.js +480 -0
  357. package/dist/rendering/SpecularPrefilter.js.map +1 -0
  358. package/dist/rendering/TerrainFixtures.d.ts +24 -0
  359. package/dist/rendering/TerrainFixtures.d.ts.map +1 -1
  360. package/dist/rendering/TerrainFixtures.js +96 -0
  361. package/dist/rendering/TerrainFixtures.js.map +1 -1
  362. package/dist/rendering/Texture.d.ts +7 -0
  363. package/dist/rendering/Texture.d.ts.map +1 -1
  364. package/dist/rendering/Texture.js +9 -0
  365. package/dist/rendering/Texture.js.map +1 -1
  366. package/dist/rendering/VertexFormat.d.ts +34 -1
  367. package/dist/rendering/VertexFormat.d.ts.map +1 -1
  368. package/dist/rendering/VertexFormat.js +61 -0
  369. package/dist/rendering/VertexFormat.js.map +1 -1
  370. package/dist/rendering/WebGL2Device.d.ts +48 -0
  371. package/dist/rendering/WebGL2Device.d.ts.map +1 -1
  372. package/dist/rendering/WebGL2Device.js +1298 -44
  373. package/dist/rendering/WebGL2Device.js.map +1 -1
  374. package/dist/rendering/WebGPUDevice.d.ts +7 -2
  375. package/dist/rendering/WebGPUDevice.d.ts.map +1 -1
  376. package/dist/rendering/WebGPUDevice.js +111 -17
  377. package/dist/rendering/WebGPUDevice.js.map +1 -1
  378. package/dist/rendering/WebGPUSkinningLimits.d.ts +14 -0
  379. package/dist/rendering/WebGPUSkinningLimits.d.ts.map +1 -0
  380. package/dist/rendering/WebGPUSkinningLimits.js +14 -0
  381. package/dist/rendering/WebGPUSkinningLimits.js.map +1 -0
  382. package/dist/rendering/cartoon/CartoonMaterialStyle.d.ts +19 -0
  383. package/dist/rendering/cartoon/CartoonMaterialStyle.d.ts.map +1 -0
  384. package/dist/rendering/cartoon/CartoonMaterialStyle.js +13 -0
  385. package/dist/rendering/cartoon/CartoonMaterialStyle.js.map +1 -0
  386. package/dist/rendering/cartoon/CartoonRenderPreset.d.ts +36 -0
  387. package/dist/rendering/cartoon/CartoonRenderPreset.d.ts.map +1 -0
  388. package/dist/rendering/cartoon/CartoonRenderPreset.js +23 -0
  389. package/dist/rendering/cartoon/CartoonRenderPreset.js.map +1 -0
  390. package/dist/rendering/cartoon/CartoonToonMaterial.d.ts +130 -0
  391. package/dist/rendering/cartoon/CartoonToonMaterial.d.ts.map +1 -0
  392. package/dist/rendering/cartoon/CartoonToonMaterial.js +303 -0
  393. package/dist/rendering/cartoon/CartoonToonMaterial.js.map +1 -0
  394. package/dist/rendering/cartoon/CartoonVisualQuality.d.ts +28 -0
  395. package/dist/rendering/cartoon/CartoonVisualQuality.d.ts.map +1 -0
  396. package/dist/rendering/cartoon/CartoonVisualQuality.js +39 -0
  397. package/dist/rendering/cartoon/CartoonVisualQuality.js.map +1 -0
  398. package/dist/rendering/cartoon/applyCartoonRenderPreset.d.ts +79 -0
  399. package/dist/rendering/cartoon/applyCartoonRenderPreset.d.ts.map +1 -0
  400. package/dist/rendering/cartoon/applyCartoonRenderPreset.js +88 -0
  401. package/dist/rendering/cartoon/applyCartoonRenderPreset.js.map +1 -0
  402. package/dist/rendering/cartoon/index.d.ts +6 -0
  403. package/dist/rendering/cartoon/index.d.ts.map +1 -0
  404. package/dist/rendering/cartoon/index.js +6 -0
  405. package/dist/rendering/cartoon/index.js.map +1 -0
  406. package/dist/rendering/index.d.ts +26 -15
  407. package/dist/rendering/index.d.ts.map +1 -1
  408. package/dist/rendering/index.js +15 -9
  409. package/dist/rendering/index.js.map +1 -1
  410. package/dist/rendering/postprocess/NativeLdrEffectLuts.d.ts +93 -0
  411. package/dist/rendering/postprocess/NativeLdrEffectLuts.d.ts.map +1 -0
  412. package/dist/rendering/postprocess/NativeLdrEffectLuts.js +202 -0
  413. package/dist/rendering/postprocess/NativeLdrEffectLuts.js.map +1 -0
  414. package/dist/rendering/production-runtime/PBRHDRPipeline.d.ts.map +1 -1
  415. package/dist/rendering/production-runtime/PBRHDRPipeline.js +3 -0
  416. package/dist/rendering/production-runtime/PBRHDRPipeline.js.map +1 -1
  417. package/dist/rendering/production-runtime/ProductionRendererTypes.d.ts +1 -0
  418. package/dist/rendering/production-runtime/ProductionRendererTypes.d.ts.map +1 -1
  419. package/dist/rendering/production-runtime/ProductionRendererTypes.js.map +1 -1
  420. package/dist/rendering/production-runtime/ProductionWebGL2Renderer.d.ts.map +1 -1
  421. package/dist/rendering/production-runtime/ProductionWebGL2Renderer.js +5 -3
  422. package/dist/rendering/production-runtime/ProductionWebGL2Renderer.js.map +1 -1
  423. package/dist/rendering/production-runtime/ProductionWebGPURenderer.d.ts.map +1 -1
  424. package/dist/rendering/production-runtime/ProductionWebGPURenderer.js +9 -5
  425. package/dist/rendering/production-runtime/ProductionWebGPURenderer.js.map +1 -1
  426. package/dist/rendering/production-runtime/TransmissionBackdropCapture.d.ts +5 -0
  427. package/dist/rendering/production-runtime/TransmissionBackdropCapture.d.ts.map +1 -1
  428. package/dist/rendering/production-runtime/TransmissionBackdropCapture.js +40 -0
  429. package/dist/rendering/production-runtime/TransmissionBackdropCapture.js.map +1 -1
  430. package/dist/rendering/production-runtime/environment/HDRLoader.d.ts +6 -0
  431. package/dist/rendering/production-runtime/environment/HDRLoader.d.ts.map +1 -1
  432. package/dist/rendering/production-runtime/environment/HDRLoader.js +19 -0
  433. package/dist/rendering/production-runtime/environment/HDRLoader.js.map +1 -1
  434. package/dist/rendering/production-runtime/index.d.ts +3 -1
  435. package/dist/rendering/production-runtime/index.d.ts.map +1 -1
  436. package/dist/rendering/production-runtime/index.js +1 -0
  437. package/dist/rendering/production-runtime/index.js.map +1 -1
  438. package/dist/scene/Light.d.ts +1 -1
  439. package/dist/scene/Light.d.ts.map +1 -1
  440. package/dist/scene/RectAreaLight.d.ts +22 -0
  441. package/dist/scene/RectAreaLight.d.ts.map +1 -0
  442. package/dist/scene/RectAreaLight.js +46 -0
  443. package/dist/scene/RectAreaLight.js.map +1 -0
  444. package/dist/scene/Scene.d.ts +6 -1
  445. package/dist/scene/Scene.d.ts.map +1 -1
  446. package/dist/scene/Scene.js +8 -1
  447. package/dist/scene/Scene.js.map +1 -1
  448. package/dist/scene/SceneSerializer.d.ts +3 -1
  449. package/dist/scene/SceneSerializer.d.ts.map +1 -1
  450. package/dist/scene/SceneSerializer.js +14 -7
  451. package/dist/scene/SceneSerializer.js.map +1 -1
  452. package/dist/scene/index.d.ts +1 -0
  453. package/dist/scene/index.d.ts.map +1 -1
  454. package/dist/scene/index.js +1 -0
  455. package/dist/scene/index.js.map +1 -1
  456. package/dist/scripting/CartoonNodeCategories.d.ts +3 -0
  457. package/dist/scripting/CartoonNodeCategories.d.ts.map +1 -0
  458. package/dist/scripting/CartoonNodeCategories.js +9 -0
  459. package/dist/scripting/CartoonNodeCategories.js.map +1 -0
  460. package/dist/scripting/CartoonVisualNodes.d.ts +3 -0
  461. package/dist/scripting/CartoonVisualNodes.d.ts.map +1 -0
  462. package/dist/scripting/CartoonVisualNodes.js +41 -0
  463. package/dist/scripting/CartoonVisualNodes.js.map +1 -0
  464. package/package.json +124 -113
  465. package/templates/cinematic-scene/package.json +1 -1
  466. package/templates/mini-game/package.json +1 -1
  467. package/templates/product-viewer/package.json +1 -1
  468. package/templates/production-asset-inspector/README.md +5 -0
  469. package/templates/production-asset-inspector/asset-manifest.json +9 -0
  470. package/templates/production-asset-inspector/index.html +2 -0
  471. package/templates/production-asset-inspector/package.json +17 -0
  472. package/templates/production-asset-inspector/src/main.ts +16 -0
  473. package/templates/production-material-studio/README.md +5 -0
  474. package/templates/production-material-studio/asset-manifest.json +9 -0
  475. package/templates/production-material-studio/index.html +2 -0
  476. package/templates/production-material-studio/package.json +17 -0
  477. package/templates/production-material-studio/src/main.ts +16 -0
  478. package/templates/production-product-configurator/README.md +5 -0
  479. package/templates/production-product-configurator/asset-manifest.json +9 -0
  480. package/templates/production-product-configurator/index.html +2 -0
  481. package/templates/production-product-configurator/package.json +17 -0
  482. package/templates/production-product-configurator/src/main.ts +16 -0
  483. package/templates/production-product-viewer/README.md +7 -0
  484. package/templates/production-product-viewer/asset-manifest.json +8 -0
  485. package/templates/production-product-viewer/index.html +26 -0
  486. package/templates/production-product-viewer/package.json +17 -0
  487. package/templates/production-product-viewer/src/main.ts +70 -0
@@ -1,8 +1,9 @@
1
1
  import { RenderDeviceError, } from "./RenderDevice.js";
2
- import { Texture, isCompressedTextureFormat } from "./Texture.js";
2
+ import { Texture, isCompressedTextureFormat, isFloatColorTextureFormat } from "./Texture.js";
3
3
  import { TextureBinding } from "./TextureBinding.js";
4
4
  import {} from "./VertexFormat.js";
5
5
  import { WebGL2StateCache } from "./WebGL2StateCache.js";
6
+ import { BLOOM_BRIGHT_LUT_HEIGHT, BLOOM_BRIGHT_LUT_WIDTH, BLOOM_COMPOSITE_LUT_SIZE, OUTLINE_BLEND_LUT_WIDTH, OUTLINE_LIMB_RADIX, createOutlineBlendLut, createOutlineGradientBound, createBloomBrightThresholdLut, createBloomCompositeLut } from "./postprocess/NativeLdrEffectLuts.js";
6
7
  const WEBGL_CUBE_FACES = ["px", "nx", "py", "ny", "pz", "nz"];
7
8
  class WebGL2Buffer {
8
9
  id;
@@ -61,9 +62,13 @@ class WebGL2RenderTarget {
61
62
  colorHandle;
62
63
  depthHandle;
63
64
  depthTextureHandle;
65
+ sampleCount;
66
+ drawFramebuffer;
67
+ multisampleColorHandle;
64
68
  gl;
65
69
  disposed = false;
66
- constructor(id, width, height, label, colorTexture, depthTexture, framebuffer, colorHandle, depthHandle, depthTextureHandle, gl) {
70
+ needsResolve = false;
71
+ constructor(id, width, height, label, colorTexture, depthTexture, framebuffer, colorHandle, depthHandle, depthTextureHandle, sampleCount, drawFramebuffer, multisampleColorHandle, gl) {
67
72
  this.id = id;
68
73
  this.width = width;
69
74
  this.height = height;
@@ -74,14 +79,21 @@ class WebGL2RenderTarget {
74
79
  this.colorHandle = colorHandle;
75
80
  this.depthHandle = depthHandle;
76
81
  this.depthTextureHandle = depthTextureHandle;
82
+ this.sampleCount = sampleCount;
83
+ this.drawFramebuffer = drawFramebuffer;
84
+ this.multisampleColorHandle = multisampleColorHandle;
77
85
  this.gl = gl;
78
86
  }
79
87
  dispose() {
80
88
  if (!this.disposed) {
81
89
  this.gl.deleteFramebuffer(this.framebuffer);
90
+ if (this.drawFramebuffer !== this.framebuffer)
91
+ this.gl.deleteFramebuffer(this.drawFramebuffer);
82
92
  this.gl.deleteTexture(this.colorHandle);
83
93
  if (this.depthHandle)
84
94
  this.gl.deleteRenderbuffer(this.depthHandle);
95
+ if (this.multisampleColorHandle)
96
+ this.gl.deleteRenderbuffer(this.multisampleColorHandle);
85
97
  if (this.depthTextureHandle)
86
98
  this.gl.deleteTexture(this.depthTextureHandle);
87
99
  this.colorTexture.dispose();
@@ -110,6 +122,24 @@ export class WebGL2Device {
110
122
  samplerObjectCache = new Map();
111
123
  presentationProgram = null;
112
124
  ldrPostprocessProgram = null;
125
+ bloomBrightExtractProgram = null;
126
+ bloomBlurProgram = null;
127
+ bloomCompositeProgram = null;
128
+ outlineProgram = null;
129
+ ssaoProgram = null;
130
+ ssrProgram = null;
131
+ depthOfFieldProgram = null;
132
+ motionBlurProgram = null;
133
+ motionBlurVelocityTexture = null;
134
+ taaProgram = null;
135
+ taaHistoryTexture = null;
136
+ bloomPingPongResources = null;
137
+ bloomBrightLutTexture = null;
138
+ bloomBrightLutThreshold = null;
139
+ bloomCompositeLutTexture = null;
140
+ bloomCompositeLutIntensity = null;
141
+ outlineBlendLutTexture = null;
142
+ outlineBlendLutKey = null;
113
143
  depthReadbackProgram = null;
114
144
  uniformLocationCache = new WeakMap();
115
145
  textureSamplerParameterCache = new WeakMap();
@@ -131,6 +161,7 @@ export class WebGL2Device {
131
161
  releasedTextureHandles = 0;
132
162
  nativeEnvironmentBindings = 0;
133
163
  nativeShadowMapBindings = 0;
164
+ shadowRenderTargetsAllocated = 0;
134
165
  lastError = null;
135
166
  frameActive = false;
136
167
  viewportWidth = 0;
@@ -150,7 +181,17 @@ export class WebGL2Device {
150
181
  if (!gl) {
151
182
  throw new RenderDeviceError("WebGL2 is not available for the provided canvas", "WEBGL2_UNAVAILABLE");
152
183
  }
153
- return new WebGL2Device(gl, options.canvas, options.errorCheckMode ?? "strict");
184
+ // Default to frame-level error checking.
185
+ //
186
+ // `strict` calls `gl.getError()` after every uniform upload, vertex-format
187
+ // bind, and draw. `gl.getError()` forces a synchronous CPU/GPU sync, so on a
188
+ // scene with a few dozen draws and many uniforms per draw it dominates frame
189
+ // time: profiling Aura Clash attributed ~93% of frame time to `getError`,
190
+ // holding the route at ~11 FPS on an Apple M4 Max. Frame-level checking still
191
+ // reports real WebGL errors (read once in `endFrame` and surfaced through
192
+ // `lastError`); callers that need per-operation attribution can still opt into
193
+ // `strict` explicitly.
194
+ return new WebGL2Device(gl, options.canvas, options.errorCheckMode ?? "frame");
154
195
  }
155
196
  constructor(gl, canvas, errorCheckMode) {
156
197
  this.errorCheckMode = errorCheckMode;
@@ -296,18 +337,42 @@ export class WebGL2Device {
296
337
  label: descriptor.label
297
338
  });
298
339
  }
340
+ const requestedSampleCount = descriptor.sampleCount ?? 1;
341
+ const maxSamples = Number(this.gl.getParameter(this.gl.MAX_SAMPLES) ?? 1);
342
+ if (!Number.isInteger(requestedSampleCount) || requestedSampleCount < 1 || requestedSampleCount > maxSamples) {
343
+ throw new RenderDeviceError("WebGL2 render-target sampleCount must be an integer supported by MAX_SAMPLES.", "INVALID_RENDER_TARGET_SAMPLE_COUNT", {
344
+ sampleCount: requestedSampleCount,
345
+ maxSamples,
346
+ label: descriptor.label
347
+ });
348
+ }
349
+ const sampleCount = requestedSampleCount;
299
350
  const colorHandle = this.gl.createTexture();
300
351
  const framebuffer = this.gl.createFramebuffer();
352
+ const drawFramebuffer = sampleCount > 1 ? this.gl.createFramebuffer() : framebuffer;
353
+ const multisampleColorHandle = sampleCount > 1 ? this.gl.createRenderbuffer() : null;
301
354
  const depthMode = descriptor.depth === "texture" ? "texture" : descriptor.depth === false ? "none" : "renderbuffer";
302
355
  const depthHandle = depthMode === "renderbuffer" ? this.gl.createRenderbuffer() : null;
356
+ const multisampleDepthHandle = sampleCount > 1 && depthMode !== "none"
357
+ ? (depthHandle ?? this.gl.createRenderbuffer())
358
+ : depthHandle;
303
359
  const depthTextureHandle = depthMode === "texture" ? this.gl.createTexture() : null;
304
- if (!colorHandle || !framebuffer || (depthMode === "renderbuffer" && !depthHandle) || (depthMode === "texture" && !depthTextureHandle)) {
360
+ if (!colorHandle
361
+ || !framebuffer
362
+ || !drawFramebuffer
363
+ || (sampleCount > 1 && !multisampleColorHandle)
364
+ || ((depthMode === "renderbuffer" || (sampleCount > 1 && depthMode !== "none")) && !multisampleDepthHandle)
365
+ || (depthMode === "texture" && !depthTextureHandle)) {
305
366
  if (colorHandle)
306
367
  this.gl.deleteTexture(colorHandle);
307
368
  if (framebuffer)
308
369
  this.gl.deleteFramebuffer(framebuffer);
309
- if (depthHandle)
310
- this.gl.deleteRenderbuffer(depthHandle);
370
+ if (drawFramebuffer && drawFramebuffer !== framebuffer)
371
+ this.gl.deleteFramebuffer(drawFramebuffer);
372
+ if (multisampleColorHandle)
373
+ this.gl.deleteRenderbuffer(multisampleColorHandle);
374
+ if (multisampleDepthHandle)
375
+ this.gl.deleteRenderbuffer(multisampleDepthHandle);
311
376
  if (depthTextureHandle)
312
377
  this.gl.deleteTexture(depthTextureHandle);
313
378
  throw new RenderDeviceError("Failed to allocate WebGL render target", "WEBGL_ALLOCATION_FAILED", {
@@ -331,7 +396,7 @@ export class WebGL2Device {
331
396
  this.gl.texImage2D(this.gl.TEXTURE_2D, 0, textureFormat.internalFormat, descriptor.width, descriptor.height, 0, this.gl.RGBA, textureFormat.type, null);
332
397
  this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
333
398
  this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, colorHandle, 0);
334
- if (depthHandle) {
399
+ if (depthHandle && sampleCount === 1) {
335
400
  this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, depthHandle);
336
401
  this.gl.renderbufferStorage(this.gl.RENDERBUFFER, this.gl.DEPTH_COMPONENT16, descriptor.width, descriptor.height);
337
402
  this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.RENDERBUFFER, depthHandle);
@@ -345,25 +410,48 @@ export class WebGL2Device {
345
410
  this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.DEPTH_COMPONENT24, descriptor.width, descriptor.height, 0, this.gl.DEPTH_COMPONENT, this.gl.UNSIGNED_INT, null);
346
411
  this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.TEXTURE_2D, depthTextureHandle, 0);
347
412
  }
348
- const status = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER);
413
+ const resolveStatus = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER);
414
+ let drawStatus = resolveStatus;
415
+ if (sampleCount > 1 && multisampleColorHandle) {
416
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, drawFramebuffer);
417
+ this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, multisampleColorHandle);
418
+ const multisampleColorFormat = format === "rgba8" ? this.gl.RGBA8 : textureFormat.internalFormat;
419
+ this.gl.renderbufferStorageMultisample(this.gl.RENDERBUFFER, sampleCount, multisampleColorFormat, descriptor.width, descriptor.height);
420
+ this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.RENDERBUFFER, multisampleColorHandle);
421
+ if (multisampleDepthHandle) {
422
+ this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, multisampleDepthHandle);
423
+ this.gl.renderbufferStorageMultisample(this.gl.RENDERBUFFER, sampleCount, this.gl.DEPTH_COMPONENT24, descriptor.width, descriptor.height);
424
+ this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.RENDERBUFFER, multisampleDepthHandle);
425
+ }
426
+ drawStatus = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER);
427
+ }
349
428
  this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, previousRenderbuffer);
350
429
  this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, previousFramebuffer);
351
430
  this.gl.bindTexture(this.gl.TEXTURE_2D, previousTexture);
352
431
  this.gl.activeTexture(previousActiveTexture);
353
432
  this.stateCache.invalidate();
354
- if (status !== this.gl.FRAMEBUFFER_COMPLETE) {
433
+ if (resolveStatus !== this.gl.FRAMEBUFFER_COMPLETE || drawStatus !== this.gl.FRAMEBUFFER_COMPLETE) {
355
434
  this.gl.deleteTexture(colorHandle);
356
435
  this.gl.deleteFramebuffer(framebuffer);
357
- this.gl.deleteRenderbuffer(depthHandle);
436
+ if (drawFramebuffer !== framebuffer)
437
+ this.gl.deleteFramebuffer(drawFramebuffer);
438
+ if (multisampleColorHandle)
439
+ this.gl.deleteRenderbuffer(multisampleColorHandle);
440
+ if (multisampleDepthHandle)
441
+ this.gl.deleteRenderbuffer(multisampleDepthHandle);
358
442
  if (depthTextureHandle)
359
443
  this.gl.deleteTexture(depthTextureHandle);
360
- throw new RenderDeviceError("WebGL render target framebuffer status is invalid", "FRAMEBUFFER_INVALID", { status });
444
+ throw new RenderDeviceError("WebGL render target framebuffer status is invalid", "FRAMEBUFFER_INVALID", { resolveStatus, drawStatus });
361
445
  }
362
446
  const depthTexture = depthTextureHandle
363
447
  ? new Texture({ width: descriptor.width, height: descriptor.height, format: "depth24", label: `${descriptor.label ?? "render-target"}-depth` })
364
448
  : undefined;
365
- const target = new WebGL2RenderTarget(this.nextId++, descriptor.width, descriptor.height, descriptor.label ?? "render-target", new Texture({ width: descriptor.width, height: descriptor.height, format, label: descriptor.label ?? "render-target-color" }), depthTexture, framebuffer, colorHandle, depthHandle, depthTextureHandle, this.gl);
449
+ const target = new WebGL2RenderTarget(this.nextId++, descriptor.width, descriptor.height, descriptor.label ?? "render-target", new Texture({ width: descriptor.width, height: descriptor.height, format, label: descriptor.label ?? "render-target-color" }), depthTexture, framebuffer, colorHandle, multisampleDepthHandle, depthTextureHandle, sampleCount, drawFramebuffer, multisampleColorHandle, this.gl);
366
450
  this.renderTargets.add(target);
451
+ // Shadow passes label their depth target after the shadow map texture, so the
452
+ // label is the device-side signal that a shadow depth target was allocated.
453
+ if ((descriptor.label ?? "").toLowerCase().includes("shadow"))
454
+ this.shadowRenderTargetsAllocated += 1;
367
455
  this.textures.set(target.colorTexture, colorHandle);
368
456
  this.textureUploadModes.set(target.colorTexture, "rgba8");
369
457
  if (depthTexture && depthTextureHandle) {
@@ -374,6 +462,9 @@ export class WebGL2Device {
374
462
  }
375
463
  setRenderTarget(target) {
376
464
  this.assertAlive();
465
+ if (this.activeRenderTarget && this.activeRenderTarget !== target) {
466
+ this.resolveMultisampleTarget(this.activeRenderTarget);
467
+ }
377
468
  if (target === null) {
378
469
  this.activeRenderTarget = null;
379
470
  this.stateCache.bindFramebuffer(this.gl.FRAMEBUFFER, null, () => this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null));
@@ -386,9 +477,23 @@ export class WebGL2Device {
386
477
  });
387
478
  }
388
479
  this.activeRenderTarget = target;
389
- this.stateCache.bindFramebuffer(this.gl.FRAMEBUFFER, target.framebuffer, () => this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, target.framebuffer));
480
+ this.stateCache.bindFramebuffer(this.gl.FRAMEBUFFER, target.drawFramebuffer, () => this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, target.drawFramebuffer));
390
481
  this.stateCache.viewport(0, 0, target.width, target.height, () => this.gl.viewport(0, 0, target.width, target.height));
391
482
  }
483
+ resolveMultisampleTarget(target) {
484
+ if (target.sampleCount <= 1 || !target.needsResolve)
485
+ return;
486
+ const previousFramebuffer = this.gl.getParameter(this.gl.FRAMEBUFFER_BINDING);
487
+ this.gl.bindFramebuffer(this.gl.READ_FRAMEBUFFER, target.drawFramebuffer);
488
+ this.gl.bindFramebuffer(this.gl.DRAW_FRAMEBUFFER, target.framebuffer);
489
+ let mask = this.gl.COLOR_BUFFER_BIT;
490
+ if (target.depthTextureHandle)
491
+ mask |= this.gl.DEPTH_BUFFER_BIT;
492
+ this.gl.blitFramebuffer(0, 0, target.width, target.height, 0, 0, target.width, target.height, mask, this.gl.NEAREST);
493
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, previousFramebuffer);
494
+ target.needsResolve = false;
495
+ this.stateCache.invalidate();
496
+ }
392
497
  writeRenderTargetPixels(target, pixels) {
393
498
  this.assertAlive();
394
499
  if (!(target instanceof WebGL2RenderTarget) || !this.renderTargets.has(target) || target.disposed) {
@@ -413,6 +518,7 @@ export class WebGL2Device {
413
518
  try {
414
519
  this.gl.bindTexture(this.gl.TEXTURE_2D, target.colorHandle);
415
520
  this.gl.texSubImage2D(this.gl.TEXTURE_2D, 0, 0, 0, target.width, target.height, this.gl.RGBA, this.gl.UNSIGNED_BYTE, pixels);
521
+ target.needsResolve = false;
416
522
  }
417
523
  finally {
418
524
  this.restoreTextureUnit0(textureUnit0);
@@ -432,6 +538,7 @@ export class WebGL2Device {
432
538
  targetId: source.id
433
539
  });
434
540
  }
541
+ this.resolveMultisampleTarget(source);
435
542
  const outputWidth = this.viewportWidth || this.gl.drawingBufferWidth;
436
543
  const outputHeight = this.viewportHeight || this.gl.drawingBufferHeight;
437
544
  this.drawRenderTargetToBackbuffer(source, outputWidth, outputHeight);
@@ -452,13 +559,41 @@ export class WebGL2Device {
452
559
  targetId: source.id
453
560
  });
454
561
  }
562
+ this.resolveMultisampleTarget(source);
455
563
  const tonePass = options.passes.find((pass) => pass.name === "tone-mapping");
564
+ const bloomPass = options.passes.find((pass) => pass.name === "bloom");
565
+ const depthOfFieldPass = options.passes.find((pass) => pass.name === "depth-of-field");
566
+ const motionBlurPass = options.passes.find((pass) => pass.name === "motion-blur");
567
+ const ssaoPass = options.passes.find((pass) => pass.name === "ssao");
568
+ const ssrPass = options.passes.find((pass) => pass.name === "ssr");
569
+ const taaPass = options.passes.find((pass) => pass.name === "taa");
570
+ const outlinePass = options.passes.find((pass) => pass.name === "outline");
456
571
  if (source.colorTexture.format !== "rgba8" && !tonePass) {
457
572
  throw new RenderDeviceError("WebGL2 HDR postprocess presentation requires a tone-mapping pass before LDR output.", "WEBGL_LDR_POSTPROCESS_FORMAT_UNSUPPORTED", {
458
573
  targetId: source.id,
459
574
  format: source.colorTexture.format
460
575
  });
461
576
  }
577
+ if (source.colorTexture.format !== "rgba8" && bloomPass) {
578
+ throw new RenderDeviceError("WebGL2 native LUT bloom accepts rgba8 input; HDR bloom must use the renderer float-bloom path before tone mapping.", "WEBGL_LDR_POSTPROCESS_FORMAT_UNSUPPORTED", {
579
+ targetId: source.id,
580
+ format: source.colorTexture.format,
581
+ pass: "bloom"
582
+ });
583
+ }
584
+ if (source.colorTexture.format !== "rgba8" && (depthOfFieldPass || motionBlurPass || ssaoPass || ssrPass || taaPass || outlinePass) && !tonePass) {
585
+ throw new RenderDeviceError("WebGL2 byte-kernel postprocess requires rgba8 input or a preceding tone-mapping pass.", "WEBGL_LDR_POSTPROCESS_FORMAT_UNSUPPORTED", {
586
+ targetId: source.id,
587
+ format: source.colorTexture.format,
588
+ pass: depthOfFieldPass ? "depth-of-field" : motionBlurPass ? "motion-blur" : ssaoPass ? "ssao" : ssrPass ? "ssr" : taaPass ? "taa" : "outline"
589
+ });
590
+ }
591
+ if ((depthOfFieldPass || ssaoPass || ssrPass) && !source.depthTextureHandle) {
592
+ throw new RenderDeviceError("WebGL2 native depth postprocess requires a renderer-owned sampleable depth texture.", "WEBGL_LDR_POSTPROCESS_DEPTH_REQUIRED", {
593
+ targetId: source.id,
594
+ pass: depthOfFieldPass ? "depth-of-field" : ssaoPass ? "ssao" : "ssr"
595
+ });
596
+ }
462
597
  const outputTarget = options.outputTarget;
463
598
  if (outputTarget && (!(outputTarget instanceof WebGL2RenderTarget) || !this.renderTargets.has(outputTarget) || outputTarget.disposed)) {
464
599
  throw new RenderDeviceError("Output render target is not a live WebGL2 resource owned by this device", "INVALID_RESOURCE", {
@@ -482,41 +617,81 @@ export class WebGL2Device {
482
617
  const webglOutputTarget = outputTarget;
483
618
  const colorPass = options.passes.find((pass) => pass.name === "color-grade");
484
619
  const fxaaPass = options.passes.find((pass) => pass.name === "fxaa");
485
- const toneOptions = { ...(options.toneMappingDefaults ?? {}), ...(tonePass?.options ?? {}) };
486
- const colorOptions = colorPass?.options ?? {};
487
- const fxaaOptions = fxaaPass?.options ?? {};
620
+ const bloomOptions = bloomPass ? normalizeNativeBloomOptions(bloomPass.options) : undefined;
621
+ const depthOfFieldOptions = depthOfFieldPass ? normalizeNativeDepthOfFieldOptions(depthOfFieldPass.options) : undefined;
622
+ const motionBlurOptions = motionBlurPass
623
+ ? normalizeNativeMotionBlurOptions(motionBlurPass.options, source.width, source.height)
624
+ : undefined;
625
+ const ssaoOptions = ssaoPass ? normalizeNativeSsaoOptions(ssaoPass.options) : undefined;
626
+ const ssrOptions = ssrPass ? normalizeNativeSsrOptions(ssrPass.options) : undefined;
627
+ const taaOptions = taaPass ? normalizeNativeTaaOptions(taaPass.options, source.width, source.height) : undefined;
628
+ const outlineOptions = outlinePass ? normalizeNativeOutlineOptions(outlinePass.options) : undefined;
488
629
  const program = this.ensureLdrPostprocessProgram();
489
630
  const vertexArray = this.ensurePresentationVertexArray();
631
+ const bloomResources = bloomOptions || depthOfFieldOptions || motionBlurOptions || ssaoOptions || ssrOptions || taaOptions || outlineOptions
632
+ ? this.ensureBloomPingPongResources(source.width, source.height)
633
+ : undefined;
634
+ if (bloomOptions) {
635
+ this.ensureBloomLutTextures(bloomOptions);
636
+ }
637
+ if (outlineOptions) {
638
+ this.ensureOutlineBlendLutTexture(outlineOptions);
639
+ }
640
+ if (motionBlurOptions) {
641
+ this.ensureMotionBlurVelocityTexture(source.width, source.height, motionBlurOptions.velocity);
642
+ }
643
+ if (taaOptions) {
644
+ this.ensureTaaHistoryTexture(source.width, source.height, taaOptions.history);
645
+ }
490
646
  const outputWidth = webglOutputTarget?.width ?? (this.viewportWidth || this.gl.drawingBufferWidth);
491
647
  const outputHeight = webglOutputTarget?.height ?? (this.viewportHeight || this.gl.drawingBufferHeight);
492
648
  const previousState = this.prepareFullscreenPresentation(webglOutputTarget?.framebuffer ?? null, outputWidth, outputHeight);
493
649
  try {
494
- this.gl.useProgram(program);
495
- this.gl.bindVertexArray(vertexArray);
496
- this.gl.activeTexture(this.gl.TEXTURE0);
497
- this.gl.bindTexture(this.gl.TEXTURE_2D, source.colorHandle);
498
- this.gl.bindSampler(0, null);
499
- this.gl.uniform1i(this.gl.getUniformLocation(program, "u_source"), 0);
500
- this.gl.uniform2f(this.gl.getUniformLocation(program, "u_texelSize"), 1 / source.width, 1 / source.height);
501
- this.gl.uniform1i(this.gl.getUniformLocation(program, "u_hasToneMapping"), tonePass ? 1 : 0);
502
- this.gl.uniform1i(this.gl.getUniformLocation(program, "u_toneOperator"), toneMappingOperatorId(stringOption(toneOptions, "operator", "reinhard")));
503
- this.gl.uniform1i(this.gl.getUniformLocation(program, "u_inputColorSpace"), colorSpaceId(stringOption(toneOptions, "inputColorSpace", "linear")));
504
- this.gl.uniform1i(this.gl.getUniformLocation(program, "u_outputColorSpace"), colorSpaceId(stringOption(toneOptions, "outputColorSpace", "linear")));
505
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_exposure"), numberOption(toneOptions, "exposure", 1));
506
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_whitePoint"), Math.max(0.0001, numberOption(toneOptions, "whitePoint", 1)));
507
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_gamma"), Math.max(0.0001, numberOption(toneOptions, "gamma", 2.2)));
508
- this.gl.uniform1i(this.gl.getUniformLocation(program, "u_hasColorGrade"), colorPass ? 1 : 0);
509
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_contrast"), numberOption(colorOptions, "contrast", 1));
510
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_temperature"), numberOption(colorOptions, "temperature", 0));
511
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_tint"), numberOption(colorOptions, "tint", 0));
512
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_saturation"), numberOption(colorOptions, "saturation", 1));
513
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_vibrance"), numberOption(colorOptions, "vibrance", 0));
514
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_vignette"), numberOption(colorOptions, "vignette", 0));
515
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_sharpening"), numberOption(colorOptions, "sharpening", 0));
516
- this.gl.uniform1i(this.gl.getUniformLocation(program, "u_hasFxaa"), fxaaPass ? 1 : 0);
517
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_edgeThreshold"), numberOption(fxaaOptions, "edgeThreshold", 0.125));
518
- this.gl.uniform1f(this.gl.getUniformLocation(program, "u_subpixelBlend"), numberOption(fxaaOptions, "subpixelBlend", 0.75));
519
- this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
650
+ let ldrSourceHandle = source.colorHandle;
651
+ let temporarySourceIndex = -1;
652
+ if (bloomOptions && bloomResources) {
653
+ ldrSourceHandle = this.executeNativeBloomPasses(ldrSourceHandle, bloomResources, bloomOptions, vertexArray);
654
+ temporarySourceIndex = 1;
655
+ }
656
+ if ((depthOfFieldOptions || motionBlurOptions || ssaoOptions || ssrOptions || taaOptions || outlineOptions) && bloomResources) {
657
+ if (tonePass || colorPass) {
658
+ const baseTargetIndex = 0;
659
+ this.drawNativeLdrStage(ldrSourceHandle, bloomResources.framebuffers[baseTargetIndex], source.width, source.height, program, vertexArray, tonePass, colorPass, undefined, options.toneMappingDefaults);
660
+ ldrSourceHandle = bloomResources.textures[baseTargetIndex];
661
+ temporarySourceIndex = baseTargetIndex;
662
+ }
663
+ if (depthOfFieldOptions && source.depthTextureHandle) {
664
+ const depthOfFieldTargetIndex = temporarySourceIndex === 0 ? 1 : 0;
665
+ ldrSourceHandle = this.executeNativeDepthOfFieldPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, depthOfFieldOptions, vertexArray, depthOfFieldTargetIndex);
666
+ temporarySourceIndex = depthOfFieldTargetIndex;
667
+ }
668
+ if (motionBlurOptions) {
669
+ const motionBlurTargetIndex = temporarySourceIndex === 0 ? 1 : 0;
670
+ ldrSourceHandle = this.executeNativeMotionBlurPass(ldrSourceHandle, bloomResources, motionBlurOptions, vertexArray, motionBlurTargetIndex);
671
+ temporarySourceIndex = motionBlurTargetIndex;
672
+ }
673
+ if (ssaoOptions && source.depthTextureHandle) {
674
+ const ssaoTargetIndex = temporarySourceIndex === 0 ? 1 : 0;
675
+ ldrSourceHandle = this.executeNativeSsaoPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, ssaoOptions, vertexArray, ssaoTargetIndex);
676
+ temporarySourceIndex = ssaoTargetIndex;
677
+ }
678
+ if (ssrOptions && source.depthTextureHandle) {
679
+ const ssrTargetIndex = temporarySourceIndex === 0 ? 1 : 0;
680
+ ldrSourceHandle = this.executeNativeSsrPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, ssrOptions, vertexArray, ssrTargetIndex);
681
+ temporarySourceIndex = ssrTargetIndex;
682
+ }
683
+ if (taaOptions) {
684
+ const taaTargetIndex = temporarySourceIndex === 0 ? 1 : 0;
685
+ ldrSourceHandle = this.executeNativeTaaPass(ldrSourceHandle, bloomResources, taaOptions, vertexArray, taaTargetIndex);
686
+ temporarySourceIndex = taaTargetIndex;
687
+ }
688
+ }
689
+ if (outlineOptions && bloomResources) {
690
+ const outlineTargetIndex = temporarySourceIndex === 0 ? 1 : 0;
691
+ ldrSourceHandle = this.executeNativeOutlinePasses(ldrSourceHandle, bloomResources, outlineOptions, vertexArray, outlineTargetIndex);
692
+ temporarySourceIndex = outlineTargetIndex;
693
+ }
694
+ this.drawNativeLdrStage(ldrSourceHandle, webglOutputTarget?.framebuffer ?? null, outputWidth, outputHeight, program, vertexArray, depthOfFieldOptions || motionBlurOptions || ssaoOptions || ssrOptions || taaOptions || outlineOptions ? undefined : tonePass, depthOfFieldOptions || motionBlurOptions || ssaoOptions || ssrOptions || taaOptions || outlineOptions ? undefined : colorPass, fxaaPass, options.toneMappingDefaults);
520
695
  this.gl.flush();
521
696
  }
522
697
  finally {
@@ -548,8 +723,17 @@ export class WebGL2Device {
548
723
  boundsHeight
549
724
  });
550
725
  }
726
+ const readTarget = this.activeRenderTarget;
727
+ if (readTarget) {
728
+ this.resolveMultisampleTarget(readTarget);
729
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, readTarget.framebuffer);
730
+ }
551
731
  const pixels = new Uint8Array(width * height * 4);
552
732
  this.gl.readPixels(x, y, width, height, this.gl.RGBA, this.gl.UNSIGNED_BYTE, pixels);
733
+ if (readTarget && readTarget.sampleCount > 1) {
734
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, readTarget.drawFramebuffer);
735
+ this.stateCache.invalidate();
736
+ }
553
737
  return pixels;
554
738
  }
555
739
  readFloatPixels(x, y, width, height) {
@@ -569,8 +753,17 @@ export class WebGL2Device {
569
753
  boundsHeight
570
754
  });
571
755
  }
756
+ const readTarget = this.activeRenderTarget;
757
+ if (readTarget) {
758
+ this.resolveMultisampleTarget(readTarget);
759
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, readTarget.framebuffer);
760
+ }
572
761
  const pixels = new Float32Array(width * height * 4);
573
762
  this.gl.readPixels(x, y, width, height, this.gl.RGBA, this.gl.FLOAT, pixels);
763
+ if (readTarget && readTarget.sampleCount > 1) {
764
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, readTarget.drawFramebuffer);
765
+ this.stateCache.invalidate();
766
+ }
574
767
  return pixels;
575
768
  }
576
769
  readDepthPixels(x, y, width, height) {
@@ -594,6 +787,7 @@ export class WebGL2Device {
594
787
  boundsHeight: target.height
595
788
  });
596
789
  }
790
+ this.resolveMultisampleTarget(target);
597
791
  const encoded = this.copyDepthTextureToBytes(target, x, y, width, height);
598
792
  const pixels = new Float32Array(width * height);
599
793
  for (let index = 0; index < pixels.length; index += 1) {
@@ -633,6 +827,8 @@ export class WebGL2Device {
633
827
  this.stateCache.depthMask(true, () => this.gl.depthMask(true));
634
828
  this.gl.clearDepth(1);
635
829
  this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
830
+ if (this.activeRenderTarget?.sampleCount && this.activeRenderTarget.sampleCount > 1)
831
+ this.activeRenderTarget.needsResolve = true;
636
832
  }
637
833
  clearRenderTarget(color) {
638
834
  this.assertFrame();
@@ -640,6 +836,8 @@ export class WebGL2Device {
640
836
  this.stateCache.depthMask(true, () => this.gl.depthMask(true));
641
837
  this.gl.clearDepth(1);
642
838
  this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
839
+ if (this.activeRenderTarget?.sampleCount && this.activeRenderTarget.sampleCount > 1)
840
+ this.activeRenderTarget.needsResolve = true;
643
841
  }
644
842
  draw(command) {
645
843
  this.assertFrame();
@@ -707,6 +905,8 @@ export class WebGL2Device {
707
905
  }
708
906
  }
709
907
  this.drawCalls += 1;
908
+ if (this.activeRenderTarget?.sampleCount && this.activeRenderTarget.sampleCount > 1)
909
+ this.activeRenderTarget.needsResolve = true;
710
910
  if (this.errorCheckMode === "strict") {
711
911
  const drawError = this.readError();
712
912
  if (drawError) {
@@ -776,6 +976,7 @@ export class WebGL2Device {
776
976
  buffers: [...this.buffers].filter((buffer) => !buffer.disposed).length,
777
977
  shaders: [...this.shaders].filter((shader) => !shader.disposed).length,
778
978
  renderTargets: liveRenderTargets.length,
979
+ maxRenderTargetSampleCount: Math.max(1, ...liveRenderTargets.map((target) => target.sampleCount)),
779
980
  textures: liveTextures.size,
780
981
  bufferBytes,
781
982
  textureBytes,
@@ -786,6 +987,7 @@ export class WebGL2Device {
786
987
  textureFallbackBytes: fallbackTextures.reduce((total, texture) => total + texture.fallbackByteLength, 0),
787
988
  nativeEnvironmentBindings: this.nativeEnvironmentBindings,
788
989
  nativeShadowMapBindings: this.nativeShadowMapBindings,
990
+ shadowRenderTargetsAllocated: this.shadowRenderTargetsAllocated,
789
991
  nativeInstancedSubmissions: this.nativeInstancedSubmissions,
790
992
  samplerAnisotropyUploads: this.samplerAnisotropyUploadCount,
791
993
  maxTextureAnisotropy: this.maxTextureAnisotropy,
@@ -848,6 +1050,66 @@ export class WebGL2Device {
848
1050
  this.gl.deleteProgram(this.ldrPostprocessProgram);
849
1051
  this.ldrPostprocessProgram = null;
850
1052
  }
1053
+ if (this.bloomBrightExtractProgram) {
1054
+ this.gl.deleteProgram(this.bloomBrightExtractProgram);
1055
+ this.bloomBrightExtractProgram = null;
1056
+ }
1057
+ if (this.bloomBlurProgram) {
1058
+ this.gl.deleteProgram(this.bloomBlurProgram);
1059
+ this.bloomBlurProgram = null;
1060
+ }
1061
+ if (this.bloomCompositeProgram) {
1062
+ this.gl.deleteProgram(this.bloomCompositeProgram);
1063
+ this.bloomCompositeProgram = null;
1064
+ }
1065
+ if (this.outlineProgram) {
1066
+ this.gl.deleteProgram(this.outlineProgram);
1067
+ this.outlineProgram = null;
1068
+ }
1069
+ if (this.ssaoProgram) {
1070
+ this.gl.deleteProgram(this.ssaoProgram);
1071
+ this.ssaoProgram = null;
1072
+ }
1073
+ if (this.ssrProgram) {
1074
+ this.gl.deleteProgram(this.ssrProgram);
1075
+ this.ssrProgram = null;
1076
+ }
1077
+ if (this.depthOfFieldProgram) {
1078
+ this.gl.deleteProgram(this.depthOfFieldProgram);
1079
+ this.depthOfFieldProgram = null;
1080
+ }
1081
+ if (this.motionBlurProgram) {
1082
+ this.gl.deleteProgram(this.motionBlurProgram);
1083
+ this.motionBlurProgram = null;
1084
+ }
1085
+ if (this.motionBlurVelocityTexture) {
1086
+ this.gl.deleteTexture(this.motionBlurVelocityTexture);
1087
+ this.motionBlurVelocityTexture = null;
1088
+ }
1089
+ if (this.taaProgram) {
1090
+ this.gl.deleteProgram(this.taaProgram);
1091
+ this.taaProgram = null;
1092
+ }
1093
+ if (this.taaHistoryTexture) {
1094
+ this.gl.deleteTexture(this.taaHistoryTexture);
1095
+ this.taaHistoryTexture = null;
1096
+ }
1097
+ this.disposeBloomPingPongResources();
1098
+ if (this.bloomBrightLutTexture) {
1099
+ this.gl.deleteTexture(this.bloomBrightLutTexture);
1100
+ this.bloomBrightLutTexture = null;
1101
+ this.bloomBrightLutThreshold = null;
1102
+ }
1103
+ if (this.bloomCompositeLutTexture) {
1104
+ this.gl.deleteTexture(this.bloomCompositeLutTexture);
1105
+ this.bloomCompositeLutTexture = null;
1106
+ this.bloomCompositeLutIntensity = null;
1107
+ }
1108
+ if (this.outlineBlendLutTexture) {
1109
+ this.gl.deleteTexture(this.outlineBlendLutTexture);
1110
+ this.outlineBlendLutTexture = null;
1111
+ this.outlineBlendLutKey = null;
1112
+ }
851
1113
  if (this.presentationVertexArray) {
852
1114
  this.gl.deleteVertexArray(this.presentationVertexArray);
853
1115
  this.presentationVertexArray = null;
@@ -961,6 +1223,369 @@ export class WebGL2Device {
961
1223
  this.restoreFullscreenPresentationState(previousState, outputWidth, outputHeight);
962
1224
  }
963
1225
  }
1226
+ ensureBloomPingPongResources(width, height) {
1227
+ const current = this.bloomPingPongResources;
1228
+ if (current && current.width === width && current.height === height) {
1229
+ return current;
1230
+ }
1231
+ this.disposeBloomPingPongResources();
1232
+ const textureUnit0 = this.captureTextureUnit0();
1233
+ const previousFramebuffer = this.gl.getParameter(this.gl.FRAMEBUFFER_BINDING);
1234
+ const textures = [];
1235
+ const framebuffers = [];
1236
+ try {
1237
+ for (let index = 0; index < 2; index += 1) {
1238
+ const texture = this.gl.createTexture();
1239
+ const framebuffer = this.gl.createFramebuffer();
1240
+ if (!texture || !framebuffer) {
1241
+ if (texture)
1242
+ this.gl.deleteTexture(texture);
1243
+ if (framebuffer)
1244
+ this.gl.deleteFramebuffer(framebuffer);
1245
+ throw new RenderDeviceError("Failed to allocate WebGL2 bloom ping-pong resources", "WEBGL_ALLOCATION_FAILED", {
1246
+ width,
1247
+ height,
1248
+ index
1249
+ });
1250
+ }
1251
+ textures.push(texture);
1252
+ framebuffers.push(framebuffer);
1253
+ this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
1254
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
1255
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
1256
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
1257
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
1258
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA8, width, height, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null);
1259
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
1260
+ this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, texture, 0);
1261
+ const status = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER);
1262
+ if (status !== this.gl.FRAMEBUFFER_COMPLETE) {
1263
+ throw new RenderDeviceError("WebGL2 bloom ping-pong framebuffer status is invalid", "FRAMEBUFFER_INVALID", {
1264
+ width,
1265
+ height,
1266
+ index,
1267
+ status
1268
+ });
1269
+ }
1270
+ }
1271
+ const resources = {
1272
+ width,
1273
+ height,
1274
+ textures: [textures[0], textures[1]],
1275
+ framebuffers: [framebuffers[0], framebuffers[1]]
1276
+ };
1277
+ this.bloomPingPongResources = resources;
1278
+ return resources;
1279
+ }
1280
+ catch (error) {
1281
+ for (const texture of textures)
1282
+ this.gl.deleteTexture(texture);
1283
+ for (const framebuffer of framebuffers)
1284
+ this.gl.deleteFramebuffer(framebuffer);
1285
+ throw error;
1286
+ }
1287
+ finally {
1288
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, previousFramebuffer);
1289
+ this.restoreTextureUnit0(textureUnit0);
1290
+ this.stateCache.invalidate();
1291
+ }
1292
+ }
1293
+ ensureBloomLutTextures(options) {
1294
+ const updateBright = this.bloomBrightLutThreshold !== options.threshold;
1295
+ const updateComposite = this.bloomCompositeLutIntensity !== options.intensity;
1296
+ if (this.bloomBrightLutTexture && this.bloomCompositeLutTexture && !updateBright && !updateComposite) {
1297
+ return;
1298
+ }
1299
+ const textureUnit0 = this.captureTextureUnit0();
1300
+ try {
1301
+ if (!this.bloomBrightLutTexture) {
1302
+ this.bloomBrightLutTexture = this.createNativeBloomDataTexture();
1303
+ }
1304
+ if (updateBright) {
1305
+ const pixels = createBloomBrightThresholdLut(options.threshold);
1306
+ this.gl.bindTexture(this.gl.TEXTURE_2D, this.bloomBrightLutTexture);
1307
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA8, BLOOM_BRIGHT_LUT_WIDTH, BLOOM_BRIGHT_LUT_HEIGHT, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, pixels);
1308
+ this.bloomBrightLutThreshold = options.threshold;
1309
+ }
1310
+ if (!this.bloomCompositeLutTexture) {
1311
+ this.bloomCompositeLutTexture = this.createNativeBloomDataTexture();
1312
+ }
1313
+ if (updateComposite) {
1314
+ const pixels = createBloomCompositeLut(options.intensity);
1315
+ this.gl.bindTexture(this.gl.TEXTURE_2D, this.bloomCompositeLutTexture);
1316
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA8, BLOOM_COMPOSITE_LUT_SIZE, BLOOM_COMPOSITE_LUT_SIZE, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, pixels);
1317
+ this.bloomCompositeLutIntensity = options.intensity;
1318
+ }
1319
+ }
1320
+ finally {
1321
+ this.restoreTextureUnit0(textureUnit0);
1322
+ this.stateCache.invalidate();
1323
+ }
1324
+ }
1325
+ createNativeBloomDataTexture() {
1326
+ const texture = this.gl.createTexture();
1327
+ if (!texture) {
1328
+ throw new RenderDeviceError("Failed to allocate WebGL2 bloom lookup texture", "WEBGL_ALLOCATION_FAILED");
1329
+ }
1330
+ this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
1331
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
1332
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
1333
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
1334
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
1335
+ return texture;
1336
+ }
1337
+ ensureOutlineBlendLutTexture(options) {
1338
+ const combinedAlpha = options.opacity * (options.color[3] / 255);
1339
+ const key = `${options.color.join(",")}|${combinedAlpha}`;
1340
+ if (this.outlineBlendLutTexture && this.outlineBlendLutKey === key)
1341
+ return;
1342
+ const textureUnit0 = this.captureTextureUnit0();
1343
+ try {
1344
+ if (!this.outlineBlendLutTexture) {
1345
+ this.outlineBlendLutTexture = this.createNativeBloomDataTexture();
1346
+ }
1347
+ this.gl.bindTexture(this.gl.TEXTURE_2D, this.outlineBlendLutTexture);
1348
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA8, OUTLINE_BLEND_LUT_WIDTH, 1, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, createOutlineBlendLut(options.color, combinedAlpha));
1349
+ this.outlineBlendLutKey = key;
1350
+ }
1351
+ finally {
1352
+ this.restoreTextureUnit0(textureUnit0);
1353
+ this.stateCache.invalidate();
1354
+ }
1355
+ }
1356
+ ensureMotionBlurVelocityTexture(width, height, velocity) {
1357
+ const textureUnit0 = this.captureTextureUnit0();
1358
+ try {
1359
+ if (!this.motionBlurVelocityTexture) {
1360
+ this.motionBlurVelocityTexture = this.createNativeBloomDataTexture();
1361
+ }
1362
+ this.gl.bindTexture(this.gl.TEXTURE_2D, this.motionBlurVelocityTexture);
1363
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RG32F, width, height, 0, this.gl.RG, this.gl.FLOAT, velocity);
1364
+ }
1365
+ finally {
1366
+ this.restoreTextureUnit0(textureUnit0);
1367
+ this.stateCache.invalidate();
1368
+ }
1369
+ }
1370
+ ensureTaaHistoryTexture(width, height, history) {
1371
+ const textureUnit0 = this.captureTextureUnit0();
1372
+ try {
1373
+ if (!this.taaHistoryTexture) {
1374
+ this.taaHistoryTexture = this.createNativeBloomDataTexture();
1375
+ }
1376
+ this.gl.bindTexture(this.gl.TEXTURE_2D, this.taaHistoryTexture);
1377
+ this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA8, width, height, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, history);
1378
+ }
1379
+ finally {
1380
+ this.restoreTextureUnit0(textureUnit0);
1381
+ this.stateCache.invalidate();
1382
+ }
1383
+ }
1384
+ drawNativeLdrStage(sourceTexture, framebuffer, width, height, program, vertexArray, tonePass, colorPass, fxaaPass, toneMappingDefaults) {
1385
+ const toneOptions = { ...(toneMappingDefaults ?? {}), ...(tonePass?.options ?? {}) };
1386
+ const colorOptions = colorPass?.options ?? {};
1387
+ const fxaaOptions = fxaaPass?.options ?? {};
1388
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
1389
+ this.gl.viewport(0, 0, width, height);
1390
+ this.gl.useProgram(program);
1391
+ this.gl.bindVertexArray(vertexArray);
1392
+ this.bindFullscreenTexture(0, sourceTexture);
1393
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_source"), 0);
1394
+ this.gl.uniform2f(this.gl.getUniformLocation(program, "u_texelSize"), 1 / width, 1 / height);
1395
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_hasToneMapping"), tonePass ? 1 : 0);
1396
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_toneOperator"), toneMappingOperatorId(stringOption(toneOptions, "operator", "reinhard")));
1397
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_inputColorSpace"), colorSpaceId(stringOption(toneOptions, "inputColorSpace", "linear")));
1398
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_outputColorSpace"), colorSpaceId(stringOption(toneOptions, "outputColorSpace", "linear")));
1399
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_exposure"), numberOption(toneOptions, "exposure", 1));
1400
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_whitePoint"), Math.max(0.0001, numberOption(toneOptions, "whitePoint", 1)));
1401
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_gamma"), Math.max(0.0001, numberOption(toneOptions, "gamma", 2.2)));
1402
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_hasColorGrade"), colorPass ? 1 : 0);
1403
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_contrast"), numberOption(colorOptions, "contrast", 1));
1404
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_temperature"), numberOption(colorOptions, "temperature", 0));
1405
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_tint"), numberOption(colorOptions, "tint", 0));
1406
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_saturation"), numberOption(colorOptions, "saturation", 1));
1407
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_vibrance"), numberOption(colorOptions, "vibrance", 0));
1408
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_vignette"), numberOption(colorOptions, "vignette", 0));
1409
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_sharpening"), numberOption(colorOptions, "sharpening", 0));
1410
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_hasFxaa"), fxaaPass ? 1 : 0);
1411
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_edgeThreshold"), numberOption(fxaaOptions, "edgeThreshold", 0.125));
1412
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_subpixelBlend"), numberOption(fxaaOptions, "subpixelBlend", 0.75));
1413
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1414
+ }
1415
+ executeNativeBloomPasses(sourceTexture, resources, options, vertexArray) {
1416
+ const brightLut = this.bloomBrightLutTexture;
1417
+ const compositeLut = this.bloomCompositeLutTexture;
1418
+ if (!brightLut || !compositeLut) {
1419
+ throw new RenderDeviceError("WebGL2 bloom lookup textures were not initialized", "WEBGL_ALLOCATION_FAILED");
1420
+ }
1421
+ const [textureA, textureB] = resources.textures;
1422
+ const [framebufferA, framebufferB] = resources.framebuffers;
1423
+ const brightProgram = this.ensureBloomBrightExtractProgram();
1424
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebufferA);
1425
+ this.gl.viewport(0, 0, resources.width, resources.height);
1426
+ this.gl.useProgram(brightProgram);
1427
+ this.gl.bindVertexArray(vertexArray);
1428
+ this.bindFullscreenTexture(0, sourceTexture);
1429
+ this.bindFullscreenTexture(1, brightLut);
1430
+ this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_source"), 0);
1431
+ this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_brightLut"), 1);
1432
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1433
+ const blurProgram = this.ensureBloomBlurProgram();
1434
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebufferB);
1435
+ this.gl.useProgram(blurProgram);
1436
+ this.bindFullscreenTexture(0, textureA);
1437
+ this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_source"), 0);
1438
+ this.gl.uniform2i(this.gl.getUniformLocation(blurProgram, "u_size"), resources.width, resources.height);
1439
+ this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_radius"), options.radius);
1440
+ this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_horizontal"), 1);
1441
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1442
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebufferA);
1443
+ this.bindFullscreenTexture(0, textureB);
1444
+ this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_horizontal"), 0);
1445
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1446
+ const compositeProgram = this.ensureBloomCompositeProgram();
1447
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebufferB);
1448
+ this.gl.useProgram(compositeProgram);
1449
+ this.bindFullscreenTexture(0, sourceTexture);
1450
+ this.bindFullscreenTexture(1, textureA);
1451
+ this.bindFullscreenTexture(2, compositeLut);
1452
+ this.gl.uniform1i(this.gl.getUniformLocation(compositeProgram, "u_source"), 0);
1453
+ this.gl.uniform1i(this.gl.getUniformLocation(compositeProgram, "u_blurred"), 1);
1454
+ this.gl.uniform1i(this.gl.getUniformLocation(compositeProgram, "u_compositeLut"), 2);
1455
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1456
+ return textureB;
1457
+ }
1458
+ executeNativeOutlinePasses(sourceTexture, resources, options, vertexArray, targetIndex) {
1459
+ const blendLut = this.outlineBlendLutTexture;
1460
+ if (!blendLut) {
1461
+ throw new RenderDeviceError("WebGL2 outline lookup texture was not initialized", "WEBGL_ALLOCATION_FAILED");
1462
+ }
1463
+ const program = this.ensureOutlineProgram();
1464
+ const targetTexture = resources.textures[targetIndex];
1465
+ const targetFramebuffer = resources.framebuffers[targetIndex];
1466
+ const bound = createOutlineGradientBound(options.threshold);
1467
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, targetFramebuffer);
1468
+ this.gl.viewport(0, 0, resources.width, resources.height);
1469
+ this.gl.useProgram(program);
1470
+ this.gl.bindVertexArray(vertexArray);
1471
+ this.bindFullscreenTexture(0, sourceTexture);
1472
+ this.bindFullscreenTexture(1, blendLut);
1473
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_source"), 0);
1474
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_blendLut"), 1);
1475
+ this.gl.uniform2i(this.gl.getUniformLocation(program, "u_size"), resources.width, resources.height);
1476
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_width"), options.width);
1477
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_boundHigh"), bound.highWord);
1478
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_boundLow"), bound.lowWord);
1479
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1480
+ return targetTexture;
1481
+ }
1482
+ executeNativeSsaoPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex) {
1483
+ const program = this.ensureSsaoProgram();
1484
+ const targetTexture = resources.textures[targetIndex];
1485
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
1486
+ this.gl.viewport(0, 0, resources.width, resources.height);
1487
+ this.gl.useProgram(program);
1488
+ this.gl.bindVertexArray(vertexArray);
1489
+ this.bindFullscreenTexture(0, sourceTexture);
1490
+ this.bindFullscreenTexture(1, depthTexture);
1491
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_source"), 0);
1492
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_depth"), 1);
1493
+ this.gl.uniform2i(this.gl.getUniformLocation(program, "u_size"), resources.width, resources.height);
1494
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_radius"), options.radius);
1495
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_intensity"), options.intensity);
1496
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_bias"), options.bias);
1497
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1498
+ return targetTexture;
1499
+ }
1500
+ executeNativeSsrPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex) {
1501
+ const program = this.ensureSsrProgram();
1502
+ const targetTexture = resources.textures[targetIndex];
1503
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
1504
+ this.gl.viewport(0, 0, resources.width, resources.height);
1505
+ this.gl.useProgram(program);
1506
+ this.gl.bindVertexArray(vertexArray);
1507
+ this.bindFullscreenTexture(0, sourceTexture);
1508
+ this.bindFullscreenTexture(1, depthTexture);
1509
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_source"), 0);
1510
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_depth"), 1);
1511
+ this.gl.uniform2i(this.gl.getUniformLocation(program, "u_size"), resources.width, resources.height);
1512
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_intensity"), options.intensity);
1513
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_maxDistance"), options.maxDistance);
1514
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1515
+ return targetTexture;
1516
+ }
1517
+ executeNativeDepthOfFieldPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex) {
1518
+ const program = this.ensureDepthOfFieldProgram();
1519
+ const targetTexture = resources.textures[targetIndex];
1520
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
1521
+ this.gl.viewport(0, 0, resources.width, resources.height);
1522
+ this.gl.useProgram(program);
1523
+ this.gl.bindVertexArray(vertexArray);
1524
+ this.bindFullscreenTexture(0, sourceTexture);
1525
+ this.bindFullscreenTexture(1, depthTexture);
1526
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_source"), 0);
1527
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_depth"), 1);
1528
+ this.gl.uniform2i(this.gl.getUniformLocation(program, "u_size"), resources.width, resources.height);
1529
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_focusDepth"), options.focusDepth);
1530
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_focusRange"), options.focusRange);
1531
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_maxRadius"), options.maxRadius);
1532
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1533
+ return targetTexture;
1534
+ }
1535
+ executeNativeMotionBlurPass(sourceTexture, resources, options, vertexArray, targetIndex) {
1536
+ const velocityTexture = this.motionBlurVelocityTexture;
1537
+ if (!velocityTexture) {
1538
+ throw new RenderDeviceError("WebGL2 motion-blur velocity texture was not initialized", "WEBGL_ALLOCATION_FAILED");
1539
+ }
1540
+ const program = this.ensureMotionBlurProgram();
1541
+ const targetTexture = resources.textures[targetIndex];
1542
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
1543
+ this.gl.viewport(0, 0, resources.width, resources.height);
1544
+ this.gl.useProgram(program);
1545
+ this.gl.bindVertexArray(vertexArray);
1546
+ this.bindFullscreenTexture(0, sourceTexture);
1547
+ this.bindFullscreenTexture(1, velocityTexture);
1548
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_source"), 0);
1549
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_velocity"), 1);
1550
+ this.gl.uniform2i(this.gl.getUniformLocation(program, "u_size"), resources.width, resources.height);
1551
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_samples"), options.samples);
1552
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_scale"), options.scale);
1553
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1554
+ return targetTexture;
1555
+ }
1556
+ executeNativeTaaPass(sourceTexture, resources, options, vertexArray, targetIndex) {
1557
+ const historyTexture = this.taaHistoryTexture;
1558
+ if (!historyTexture) {
1559
+ throw new RenderDeviceError("WebGL2 TAA history texture was not initialized", "WEBGL_ALLOCATION_FAILED");
1560
+ }
1561
+ const program = this.ensureTaaProgram();
1562
+ const targetTexture = resources.textures[targetIndex];
1563
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
1564
+ this.gl.viewport(0, 0, resources.width, resources.height);
1565
+ this.gl.useProgram(program);
1566
+ this.gl.bindVertexArray(vertexArray);
1567
+ this.bindFullscreenTexture(0, sourceTexture);
1568
+ this.bindFullscreenTexture(1, historyTexture);
1569
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_source"), 0);
1570
+ this.gl.uniform1i(this.gl.getUniformLocation(program, "u_history"), 1);
1571
+ this.gl.uniform1f(this.gl.getUniformLocation(program, "u_blend"), options.blend);
1572
+ this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
1573
+ return targetTexture;
1574
+ }
1575
+ bindFullscreenTexture(unit, texture) {
1576
+ this.gl.activeTexture(this.gl.TEXTURE0 + unit);
1577
+ this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
1578
+ this.gl.bindSampler(unit, null);
1579
+ }
1580
+ disposeBloomPingPongResources() {
1581
+ if (!this.bloomPingPongResources)
1582
+ return;
1583
+ for (const texture of this.bloomPingPongResources.textures)
1584
+ this.gl.deleteTexture(texture);
1585
+ for (const framebuffer of this.bloomPingPongResources.framebuffers)
1586
+ this.gl.deleteFramebuffer(framebuffer);
1587
+ this.bloomPingPongResources = null;
1588
+ }
964
1589
  captureTextureUnit0() {
965
1590
  const activeTexture = this.gl.getParameter(this.gl.ACTIVE_TEXTURE);
966
1591
  this.gl.activeTexture(this.gl.TEXTURE0);
@@ -976,11 +1601,29 @@ export class WebGL2Device {
976
1601
  this.gl.bindSampler(0, snapshot.sampler);
977
1602
  this.gl.activeTexture(snapshot.activeTexture);
978
1603
  }
1604
+ captureTextureUnitBinding(unit) {
1605
+ this.gl.activeTexture(this.gl.TEXTURE0 + unit);
1606
+ return {
1607
+ texture2d: this.gl.getParameter(this.gl.TEXTURE_BINDING_2D),
1608
+ sampler: this.gl.getParameter(this.gl.SAMPLER_BINDING)
1609
+ };
1610
+ }
1611
+ restoreTextureUnitBinding(unit, snapshot) {
1612
+ this.gl.activeTexture(this.gl.TEXTURE0 + unit);
1613
+ this.gl.bindTexture(this.gl.TEXTURE_2D, snapshot.texture2d);
1614
+ this.gl.bindSampler(unit, snapshot.sampler);
1615
+ }
979
1616
  prepareFullscreenPresentation(framebuffer, outputWidth, outputHeight) {
1617
+ const textureUnit0 = this.captureTextureUnit0();
1618
+ const textureUnit1 = this.captureTextureUnitBinding(1);
1619
+ const textureUnit2 = this.captureTextureUnitBinding(2);
1620
+ this.gl.activeTexture(textureUnit0.activeTexture);
980
1621
  const snapshot = {
981
1622
  framebuffer: this.gl.getParameter(this.gl.FRAMEBUFFER_BINDING),
982
1623
  program: this.gl.getParameter(this.gl.CURRENT_PROGRAM),
983
- textureUnit0: this.captureTextureUnit0(),
1624
+ textureUnit0,
1625
+ textureUnit1,
1626
+ textureUnit2,
984
1627
  vertexArray: this.gl.getParameter(this.gl.VERTEX_ARRAY_BINDING),
985
1628
  viewport: this.gl.getParameter(this.gl.VIEWPORT),
986
1629
  colorMask: this.gl.getParameter(this.gl.COLOR_WRITEMASK),
@@ -1006,6 +1649,8 @@ export class WebGL2Device {
1006
1649
  }
1007
1650
  restoreFullscreenPresentationState(snapshot, fallbackWidth, fallbackHeight) {
1008
1651
  this.gl.bindVertexArray(snapshot.vertexArray);
1652
+ this.restoreTextureUnitBinding(2, snapshot.textureUnit2);
1653
+ this.restoreTextureUnitBinding(1, snapshot.textureUnit1);
1009
1654
  this.restoreTextureUnit0(snapshot.textureUnit0);
1010
1655
  this.gl.useProgram(snapshot.program);
1011
1656
  if (snapshot.depthTestEnabled)
@@ -1138,6 +1783,406 @@ void main() {
1138
1783
  this.presentationProgram = program;
1139
1784
  return program;
1140
1785
  }
1786
+ ensureBloomBrightExtractProgram() {
1787
+ if (this.bloomBrightExtractProgram) {
1788
+ return this.bloomBrightExtractProgram;
1789
+ }
1790
+ this.bloomBrightExtractProgram = this.createFullscreenProgram(`#version 300 es
1791
+ precision highp float;
1792
+ precision highp int;
1793
+ uniform sampler2D u_source;
1794
+ uniform sampler2D u_brightLut;
1795
+ out vec4 outColor;
1796
+
1797
+ uvec4 byteTexel(sampler2D source, ivec2 coordinate) {
1798
+ return uvec4(texelFetch(source, coordinate, 0) * 255.0 + 0.5);
1799
+ }
1800
+
1801
+ void main() {
1802
+ ivec2 pixel = ivec2(gl_FragCoord.xy);
1803
+ uvec4 source = byteTexel(u_source, pixel);
1804
+ uint colorIndex = (source.r << 16u) | (source.g << 8u) | source.b;
1805
+ uint byteIndex = colorIndex >> 3u;
1806
+ uint texelIndex = byteIndex >> 2u;
1807
+ ivec2 lutCoordinate = ivec2(
1808
+ int(texelIndex % ${BLOOM_BRIGHT_LUT_WIDTH}u),
1809
+ int(texelIndex / ${BLOOM_BRIGHT_LUT_WIDTH}u)
1810
+ );
1811
+ uvec4 packed = byteTexel(u_brightLut, lutCoordinate);
1812
+ uint component = byteIndex & 3u;
1813
+ uint packedByte = component == 0u
1814
+ ? packed.r
1815
+ : component == 1u
1816
+ ? packed.g
1817
+ : component == 2u
1818
+ ? packed.b
1819
+ : packed.a;
1820
+ bool bright = ((packedByte >> (colorIndex & 7u)) & 1u) == 1u;
1821
+ outColor = bright ? vec4(source) / 255.0 : vec4(0.0);
1822
+ }
1823
+ `, "webgl2-bloom-bright-extract");
1824
+ return this.bloomBrightExtractProgram;
1825
+ }
1826
+ ensureBloomBlurProgram() {
1827
+ if (this.bloomBlurProgram) {
1828
+ return this.bloomBlurProgram;
1829
+ }
1830
+ this.bloomBlurProgram = this.createFullscreenProgram(`#version 300 es
1831
+ precision highp float;
1832
+ precision highp int;
1833
+ uniform sampler2D u_source;
1834
+ uniform ivec2 u_size;
1835
+ uniform int u_radius;
1836
+ uniform int u_horizontal;
1837
+ out vec4 outColor;
1838
+
1839
+ uvec4 sourceByte(ivec2 coordinate) {
1840
+ ivec2 bounded = clamp(coordinate, ivec2(0), u_size - ivec2(1));
1841
+ return uvec4(texelFetch(u_source, bounded, 0) * 255.0 + 0.5);
1842
+ }
1843
+
1844
+ void main() {
1845
+ ivec2 pixel = ivec2(gl_FragCoord.xy);
1846
+ uvec4 sum = uvec4(0u);
1847
+ for (int offset = -16; offset <= 16; offset += 1) {
1848
+ if (abs(offset) > u_radius) {
1849
+ continue;
1850
+ }
1851
+ ivec2 sampleCoordinate = pixel + (
1852
+ u_horizontal == 1
1853
+ ? ivec2(offset, 0)
1854
+ : ivec2(0, offset)
1855
+ );
1856
+ sum += sourceByte(sampleCoordinate);
1857
+ }
1858
+ uint kernelSize = uint(u_radius * 2 + 1);
1859
+ uvec4 rounded = (sum * 2u + uvec4(kernelSize)) / uvec4(kernelSize * 2u);
1860
+ outColor = vec4(rounded) / 255.0;
1861
+ }
1862
+ `, "webgl2-bloom-blur");
1863
+ return this.bloomBlurProgram;
1864
+ }
1865
+ ensureBloomCompositeProgram() {
1866
+ if (this.bloomCompositeProgram) {
1867
+ return this.bloomCompositeProgram;
1868
+ }
1869
+ this.bloomCompositeProgram = this.createFullscreenProgram(`#version 300 es
1870
+ precision highp float;
1871
+ precision highp int;
1872
+ uniform sampler2D u_source;
1873
+ uniform sampler2D u_blurred;
1874
+ uniform sampler2D u_compositeLut;
1875
+ out vec4 outColor;
1876
+
1877
+ uvec4 byteTexel(sampler2D source, ivec2 coordinate) {
1878
+ return uvec4(texelFetch(source, coordinate, 0) * 255.0 + 0.5);
1879
+ }
1880
+
1881
+ float compositeChannel(uint source, uint blurred) {
1882
+ return texelFetch(u_compositeLut, ivec2(int(source), int(blurred)), 0).r;
1883
+ }
1884
+
1885
+ void main() {
1886
+ ivec2 pixel = ivec2(gl_FragCoord.xy);
1887
+ uvec4 source = byteTexel(u_source, pixel);
1888
+ uvec4 blurred = byteTexel(u_blurred, pixel);
1889
+ outColor = vec4(
1890
+ compositeChannel(source.r, blurred.r),
1891
+ compositeChannel(source.g, blurred.g),
1892
+ compositeChannel(source.b, blurred.b),
1893
+ float(source.a) / 255.0
1894
+ );
1895
+ }
1896
+ `, "webgl2-bloom-composite");
1897
+ return this.bloomCompositeProgram;
1898
+ }
1899
+ ensureOutlineProgram() {
1900
+ if (this.outlineProgram)
1901
+ return this.outlineProgram;
1902
+ this.outlineProgram = this.createFullscreenProgram(`#version 300 es
1903
+ precision highp float;
1904
+ precision highp int;
1905
+ uniform sampler2D u_source;
1906
+ uniform sampler2D u_blendLut;
1907
+ uniform ivec2 u_size;
1908
+ uniform int u_width;
1909
+ uniform float u_boundHigh;
1910
+ uniform float u_boundLow;
1911
+ out vec4 outColor;
1912
+
1913
+ uvec4 sourceByte(ivec2 coordinate) {
1914
+ ivec2 bounded = clamp(coordinate, ivec2(0), u_size - ivec2(1));
1915
+ return uvec4(texelFetch(u_source, bounded, 0) * 255.0 + 0.5);
1916
+ }
1917
+
1918
+ uint lumaNumerator(ivec2 coordinate) {
1919
+ uvec3 source = sourceByte(coordinate).rgb;
1920
+ return 2126u * source.r + 7152u * source.g + 722u * source.b;
1921
+ }
1922
+
1923
+ uvec2 squareWords(int signedValue) {
1924
+ uint value = uint(abs(signedValue));
1925
+ uint high = value >> 12u;
1926
+ uint low = value & 4095u;
1927
+ uint middle = 2u * high * low;
1928
+ uint lowWord = low * low + (middle & 4095u) * 4096u;
1929
+ uint highWord = high * high + (middle >> 12u);
1930
+ highWord += lowWord / ${OUTLINE_LIMB_RADIX}u;
1931
+ lowWord %= ${OUTLINE_LIMB_RADIX}u;
1932
+ return uvec2(highWord, lowWord);
1933
+ }
1934
+
1935
+ bool edgeAt(ivec2 pixel) {
1936
+ int nw = int(lumaNumerator(pixel + ivec2(-1, -1)));
1937
+ int n = int(lumaNumerator(pixel + ivec2(0, -1)));
1938
+ int ne = int(lumaNumerator(pixel + ivec2(1, -1)));
1939
+ int w = int(lumaNumerator(pixel + ivec2(-1, 0)));
1940
+ int e = int(lumaNumerator(pixel + ivec2(1, 0)));
1941
+ int sw = int(lumaNumerator(pixel + ivec2(-1, 1)));
1942
+ int s = int(lumaNumerator(pixel + ivec2(0, 1)));
1943
+ int se = int(lumaNumerator(pixel + ivec2(1, 1)));
1944
+ int gx = -nw - 2 * w - sw + ne + 2 * e + se;
1945
+ int gy = -nw - 2 * n - ne + sw + 2 * s + se;
1946
+ uvec2 x = squareWords(gx);
1947
+ uvec2 y = squareWords(gy);
1948
+ uint low = x.y + y.y;
1949
+ uint high = x.x + y.x;
1950
+ if (low >= ${OUTLINE_LIMB_RADIX}u) {
1951
+ low -= ${OUTLINE_LIMB_RADIX}u;
1952
+ high += 1u;
1953
+ }
1954
+ uint boundHigh = uint(u_boundHigh);
1955
+ uint boundLow = uint(u_boundLow);
1956
+ return high != boundHigh ? high > boundHigh : low >= boundLow;
1957
+ }
1958
+
1959
+ void main() {
1960
+ ivec2 pixel = ivec2(gl_FragCoord.xy);
1961
+ bool outlined = false;
1962
+ for (int offsetY = -6; offsetY <= 6 && !outlined; offsetY += 1) {
1963
+ for (int offsetX = -6; offsetX <= 6; offsetX += 1) {
1964
+ if (abs(offsetX) > u_width || abs(offsetY) > u_width) continue;
1965
+ if (offsetX * offsetX + offsetY * offsetY > u_width * u_width) continue;
1966
+ ivec2 candidate = clamp(pixel + ivec2(offsetX, offsetY), ivec2(0), u_size - ivec2(1));
1967
+ if (edgeAt(candidate)) {
1968
+ outlined = true;
1969
+ break;
1970
+ }
1971
+ }
1972
+ }
1973
+ uvec4 source = sourceByte(pixel);
1974
+ if (!outlined) {
1975
+ outColor = vec4(source) / 255.0;
1976
+ return;
1977
+ }
1978
+ outColor = vec4(
1979
+ texelFetch(u_blendLut, ivec2(int(source.r), 0), 0).r,
1980
+ texelFetch(u_blendLut, ivec2(int(source.g), 0), 0).g,
1981
+ texelFetch(u_blendLut, ivec2(int(source.b), 0), 0).b,
1982
+ float(source.a) / 255.0
1983
+ );
1984
+ }
1985
+ `, "webgl2-outline");
1986
+ return this.outlineProgram;
1987
+ }
1988
+ ensureSsaoProgram() {
1989
+ if (this.ssaoProgram)
1990
+ return this.ssaoProgram;
1991
+ this.ssaoProgram = this.createFullscreenProgram(`#version 300 es
1992
+ precision highp float;
1993
+ precision highp int;
1994
+ uniform sampler2D u_source;
1995
+ uniform sampler2D u_depth;
1996
+ uniform ivec2 u_size;
1997
+ uniform int u_radius;
1998
+ uniform float u_intensity;
1999
+ uniform float u_bias;
2000
+ out vec4 outColor;
2001
+
2002
+ float depthAt(ivec2 coordinate) {
2003
+ return texelFetch(u_depth, clamp(coordinate, ivec2(0), u_size - ivec2(1)), 0).r;
2004
+ }
2005
+
2006
+ void main() {
2007
+ ivec2 pixel = ivec2(gl_FragCoord.xy);
2008
+ float centerDepth = depthAt(pixel);
2009
+ float occlusion = 0.0;
2010
+ float samples = 0.0;
2011
+ for (int stepY = -1; stepY <= 1; stepY += 1) {
2012
+ for (int stepX = -1; stepX <= 1; stepX += 1) {
2013
+ if (stepX == 0 && stepY == 0) continue;
2014
+ float sampleDepth = depthAt(pixel + ivec2(stepX * u_radius, stepY * u_radius));
2015
+ occlusion += clamp(
2016
+ (centerDepth - sampleDepth - u_bias) / (0.14 + float(u_radius) * 0.04),
2017
+ 0.0,
2018
+ 1.0
2019
+ );
2020
+ samples += 1.0;
2021
+ }
2022
+ }
2023
+ float factor = max(0.18, 1.0 - (occlusion / max(1.0, samples)) * u_intensity * 0.72);
2024
+ vec4 source = texelFetch(u_source, pixel, 0);
2025
+ outColor = vec4(source.rgb * factor, source.a);
2026
+ }
2027
+ `, "webgl2-ssao");
2028
+ return this.ssaoProgram;
2029
+ }
2030
+ ensureSsrProgram() {
2031
+ if (this.ssrProgram)
2032
+ return this.ssrProgram;
2033
+ this.ssrProgram = this.createFullscreenProgram(`#version 300 es
2034
+ precision highp float;
2035
+ precision highp int;
2036
+ uniform sampler2D u_source;
2037
+ uniform sampler2D u_depth;
2038
+ uniform ivec2 u_size;
2039
+ uniform float u_intensity;
2040
+ uniform int u_maxDistance;
2041
+ out vec4 outColor;
2042
+
2043
+ void main() {
2044
+ ivec2 pixel = ivec2(gl_FragCoord.xy);
2045
+ vec4 target = texelFetch(u_source, pixel, 0);
2046
+ float depth = texelFetch(u_depth, pixel, 0).r;
2047
+ int rayDistance = max(1, int(round((1.0 - depth) * float(u_maxDistance))));
2048
+ int direction = pixel.x < u_size.x / 2 ? 1 : -1;
2049
+ ivec2 samplePixel = ivec2(
2050
+ clamp(pixel.x + direction * rayDistance, 0, u_size.x - 1),
2051
+ clamp(u_size.y - 1 - pixel.y, 0, u_size.y - 1)
2052
+ );
2053
+ vec3 reflected = texelFetch(u_source, samplePixel, 0).rgb;
2054
+ float sourceLuma = (reflected.r + reflected.g + reflected.b) / 3.0;
2055
+ if (sourceLuma < 0.18 || depth > 0.92) {
2056
+ outColor = target;
2057
+ return;
2058
+ }
2059
+ float boost = sourceLuma * u_intensity * (1.0 - depth);
2060
+ outColor = vec4(clamp(target.rgb + reflected * boost, 0.0, 1.0), target.a);
2061
+ }
2062
+ `, "webgl2-ssr");
2063
+ return this.ssrProgram;
2064
+ }
2065
+ ensureDepthOfFieldProgram() {
2066
+ if (this.depthOfFieldProgram)
2067
+ return this.depthOfFieldProgram;
2068
+ this.depthOfFieldProgram = this.createFullscreenProgram(`#version 300 es
2069
+ precision highp float;
2070
+ precision highp int;
2071
+ uniform sampler2D u_source;
2072
+ uniform sampler2D u_depth;
2073
+ uniform ivec2 u_size;
2074
+ uniform float u_focusDepth;
2075
+ uniform float u_focusRange;
2076
+ uniform int u_maxRadius;
2077
+ out vec4 outColor;
2078
+
2079
+ void main() {
2080
+ ivec2 pixel = ivec2(gl_FragCoord.xy);
2081
+ vec4 source = texelFetch(u_source, pixel, 0);
2082
+ float depth = texelFetch(u_depth, pixel, 0).r;
2083
+ float normalizedBlur = max(0.0, abs(depth - u_focusDepth) - u_focusRange)
2084
+ / max(u_focusRange, 0.001) * float(u_maxRadius);
2085
+ int radius = min(u_maxRadius, int(round(normalizedBlur)));
2086
+ if (radius == 0) {
2087
+ outColor = source;
2088
+ return;
2089
+ }
2090
+ vec3 sum = vec3(0.0);
2091
+ float samples = 0.0;
2092
+ for (int offsetY = -8; offsetY <= 8; offsetY += 1) {
2093
+ for (int offsetX = -8; offsetX <= 8; offsetX += 1) {
2094
+ if (abs(offsetX) > radius || abs(offsetY) > radius) continue;
2095
+ if (offsetX * offsetX + offsetY * offsetY > radius * radius) continue;
2096
+ ivec2 samplePixel = clamp(pixel + ivec2(offsetX, offsetY), ivec2(0), u_size - ivec2(1));
2097
+ sum += texelFetch(u_source, samplePixel, 0).rgb;
2098
+ samples += 1.0;
2099
+ }
2100
+ }
2101
+ outColor = vec4(sum / max(1.0, samples), source.a);
2102
+ }
2103
+ `, "webgl2-depth-of-field");
2104
+ return this.depthOfFieldProgram;
2105
+ }
2106
+ ensureMotionBlurProgram() {
2107
+ if (this.motionBlurProgram)
2108
+ return this.motionBlurProgram;
2109
+ this.motionBlurProgram = this.createFullscreenProgram(`#version 300 es
2110
+ precision highp float;
2111
+ precision highp int;
2112
+ uniform sampler2D u_source;
2113
+ uniform sampler2D u_velocity;
2114
+ uniform ivec2 u_size;
2115
+ uniform int u_samples;
2116
+ uniform float u_scale;
2117
+ out vec4 outColor;
2118
+
2119
+ void main() {
2120
+ ivec2 pixel = ivec2(gl_FragCoord.xy);
2121
+ vec4 source = texelFetch(u_source, pixel, 0);
2122
+ vec2 velocity = texelFetch(u_velocity, pixel, 0).rg * u_scale;
2123
+ if (length(velocity) < 0.01) {
2124
+ outColor = source;
2125
+ return;
2126
+ }
2127
+ vec3 sum = vec3(0.0);
2128
+ for (int sampleIndex = 0; sampleIndex < 16; sampleIndex += 1) {
2129
+ if (sampleIndex >= u_samples) continue;
2130
+ float t = float(sampleIndex) / float(u_samples - 1) - 0.5;
2131
+ ivec2 samplePixel = ivec2(floor(vec2(pixel) - velocity * t + vec2(0.5)));
2132
+ samplePixel = clamp(samplePixel, ivec2(0), u_size - ivec2(1));
2133
+ sum += texelFetch(u_source, samplePixel, 0).rgb;
2134
+ }
2135
+ outColor = vec4(sum / float(u_samples), source.a);
2136
+ }
2137
+ `, "webgl2-motion-blur");
2138
+ return this.motionBlurProgram;
2139
+ }
2140
+ ensureTaaProgram() {
2141
+ if (this.taaProgram)
2142
+ return this.taaProgram;
2143
+ this.taaProgram = this.createFullscreenProgram(`#version 300 es
2144
+ precision highp float;
2145
+ uniform sampler2D u_source;
2146
+ uniform sampler2D u_history;
2147
+ uniform float u_blend;
2148
+ out vec4 outColor;
2149
+
2150
+ void main() {
2151
+ ivec2 pixel = ivec2(gl_FragCoord.xy);
2152
+ vec4 source = texelFetch(u_source, pixel, 0);
2153
+ vec3 history = texelFetch(u_history, pixel, 0).rgb;
2154
+ outColor = vec4(mix(source.rgb, history, u_blend), source.a);
2155
+ }
2156
+ `, "webgl2-taa");
2157
+ return this.taaProgram;
2158
+ }
2159
+ createFullscreenProgram(fragmentSource, label) {
2160
+ const vertex = this.compileShader(this.gl.VERTEX_SHADER, `#version 300 es
2161
+ precision highp float;
2162
+ void main() {
2163
+ vec2 position = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
2164
+ gl_Position = vec4(position * 2.0 - 1.0, 0.0, 1.0);
2165
+ }
2166
+ `, label);
2167
+ const fragment = this.compileShader(this.gl.FRAGMENT_SHADER, fragmentSource, label);
2168
+ const program = this.gl.createProgram();
2169
+ if (!program) {
2170
+ this.gl.deleteShader(vertex);
2171
+ this.gl.deleteShader(fragment);
2172
+ throw new RenderDeviceError("Failed to allocate WebGL2 fullscreen shader program", "WEBGL_ALLOCATION_FAILED", { label });
2173
+ }
2174
+ this.gl.attachShader(program, vertex);
2175
+ this.gl.attachShader(program, fragment);
2176
+ this.gl.linkProgram(program);
2177
+ this.gl.deleteShader(vertex);
2178
+ this.gl.deleteShader(fragment);
2179
+ if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
2180
+ const log = this.gl.getProgramInfoLog(program) ?? "Unknown fullscreen shader link error";
2181
+ this.gl.deleteProgram(program);
2182
+ throw new RenderDeviceError("WebGL2 fullscreen shader link failed", "SHADER_LINK_FAILED", { label, log });
2183
+ }
2184
+ return program;
2185
+ }
1141
2186
  ensureLdrPostprocessProgram() {
1142
2187
  if (this.ldrPostprocessProgram) {
1143
2188
  return this.ldrPostprocessProgram;
@@ -1409,6 +2454,16 @@ void main() {
1409
2454
  else {
1410
2455
  throw new RenderDeviceError("Unsupported uniform value", "UNSUPPORTED_UNIFORM", { name, valueType: typeof value });
1411
2456
  }
2457
+ if (this.errorCheckMode === "strict") {
2458
+ const uniformError = this.readError();
2459
+ if (uniformError) {
2460
+ throw new RenderDeviceError(`WebGL2 uniform upload failed for ${name}: ${uniformError}`, "WEBGL_DRAW_FAILED", {
2461
+ name,
2462
+ stage: "uniform",
2463
+ error: uniformError
2464
+ });
2465
+ }
2466
+ }
1412
2467
  }
1413
2468
  }
1414
2469
  getUniformLocation(shader, name) {
@@ -1631,7 +2686,20 @@ void main() {
1631
2686
  else {
1632
2687
  const uploadFormat = this.textureUploadFormat(texture);
1633
2688
  this.gl.texImage2D(this.gl.TEXTURE_2D, 0, uploadFormat.internalFormat, texture.width, texture.height, 0, uploadFormat.format, uploadFormat.type, texture.data ? texturePixelUploadData(texture.data, texture.format) : null);
1634
- this.gl.generateMipmap(this.gl.TEXTURE_2D);
2689
+ // Float colour formats are not guaranteed mipmap-filterable in WebGL2:
2690
+ // RGBA32F/RGBA16F are only linear-filterable with OES_texture_float_linear, and
2691
+ // generateMipmap on a non-filterable format raises INVALID_OPERATION. Data
2692
+ // textures such as joint palettes are point-sampled with texelFetch and never
2693
+ // need mips, so skip generation and pin the level range instead.
2694
+ if (isFloatColorTextureFormat(texture.format)) {
2695
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_BASE_LEVEL, 0);
2696
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAX_LEVEL, 0);
2697
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
2698
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
2699
+ }
2700
+ else {
2701
+ this.gl.generateMipmap(this.gl.TEXTURE_2D);
2702
+ }
1635
2703
  }
1636
2704
  this.textureUploadModes.set(texture, texture.format);
1637
2705
  this.textures.set(texture, handle);
@@ -2056,6 +3124,192 @@ function numberOption(options, key, fallback) {
2056
3124
  const value = options[key];
2057
3125
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
2058
3126
  }
3127
+ function normalizeNativeBloomOptions(options) {
3128
+ const threshold = bloomNumberOption(options, "threshold", 0.75);
3129
+ const intensity = bloomNumberOption(options, "intensity", 0.35);
3130
+ const radius = bloomNumberOption(options, "radius", 1);
3131
+ if (threshold < 0 || threshold > 1) {
3132
+ throw new RenderDeviceError("Bloom threshold must be finite and in [0, 1].", "INVALID_POSTPROCESS_OPTIONS", { threshold });
3133
+ }
3134
+ if (intensity < 0) {
3135
+ throw new RenderDeviceError("Bloom intensity must be finite and non-negative.", "INVALID_POSTPROCESS_OPTIONS", { intensity });
3136
+ }
3137
+ if (!Number.isInteger(radius) || radius < 0 || radius > 16) {
3138
+ throw new RenderDeviceError("Bloom radius must be an integer in [0, 16].", "INVALID_POSTPROCESS_OPTIONS", { radius });
3139
+ }
3140
+ return { threshold, intensity, radius };
3141
+ }
3142
+ function normalizeNativeOutlineOptions(options) {
3143
+ const width = outlineNumberOption(options, "width", 1);
3144
+ const threshold = outlineNumberOption(options, "threshold", 0.22);
3145
+ const opacity = outlineNumberOption(options, "opacity", 0.85);
3146
+ const value = options.color;
3147
+ const sourceColor = value === undefined ? [255, 188, 64, 255] : value;
3148
+ if (!Array.isArray(sourceColor)
3149
+ || (sourceColor.length !== 3 && sourceColor.length !== 4)
3150
+ || sourceColor.some((channel) => typeof channel !== "number" || !Number.isFinite(channel) || channel < 0 || channel > 255)) {
3151
+ throw new RenderDeviceError("Outline color must contain three or four finite channels in [0, 255].", "INVALID_POSTPROCESS_OPTIONS", { color: value });
3152
+ }
3153
+ if (!Number.isInteger(width) || width < 1 || width > 6) {
3154
+ throw new RenderDeviceError("Outline width must be an integer in [1, 6].", "INVALID_POSTPROCESS_OPTIONS", { width });
3155
+ }
3156
+ if (threshold < 0 || threshold > 4) {
3157
+ throw new RenderDeviceError("Outline threshold must be finite and in [0, 4].", "INVALID_POSTPROCESS_OPTIONS", { threshold });
3158
+ }
3159
+ if (opacity < 0 || opacity > 1) {
3160
+ throw new RenderDeviceError("Outline opacity must be finite and in [0, 1].", "INVALID_POSTPROCESS_OPTIONS", { opacity });
3161
+ }
3162
+ const color = [
3163
+ sourceColor[0],
3164
+ sourceColor[1],
3165
+ sourceColor[2],
3166
+ (sourceColor[3] ?? 255)
3167
+ ];
3168
+ return { width, threshold, opacity, color };
3169
+ }
3170
+ function normalizeNativeSsaoOptions(options) {
3171
+ const radius = ssaoNumberOption(options, "radius", 2);
3172
+ const intensity = ssaoNumberOption(options, "intensity", 0.38);
3173
+ const bias = ssaoNumberOption(options, "bias", 0.015);
3174
+ if (!Number.isInteger(radius) || radius < 1 || radius > 8) {
3175
+ throw new RenderDeviceError("SSAO radius must be an integer in [1, 8].", "INVALID_POSTPROCESS_OPTIONS", { radius });
3176
+ }
3177
+ if (intensity < 0 || intensity > 2) {
3178
+ throw new RenderDeviceError("SSAO intensity must be finite and in [0, 2].", "INVALID_POSTPROCESS_OPTIONS", { intensity });
3179
+ }
3180
+ if (bias < 0 || bias > 0.25) {
3181
+ throw new RenderDeviceError("SSAO bias must be finite and in [0, 0.25].", "INVALID_POSTPROCESS_OPTIONS", { bias });
3182
+ }
3183
+ return { radius, intensity, bias };
3184
+ }
3185
+ function normalizeNativeSsrOptions(options) {
3186
+ const intensity = ssrNumberOption(options, "intensity", 0.32);
3187
+ const maxDistance = ssrNumberOption(options, "maxDistance", 16);
3188
+ if (intensity < 0 || intensity > 2) {
3189
+ throw new RenderDeviceError("SSR intensity must be finite and in [0, 2].", "INVALID_POSTPROCESS_OPTIONS", { intensity });
3190
+ }
3191
+ if (!Number.isInteger(maxDistance) || maxDistance < 1 || maxDistance > 64) {
3192
+ throw new RenderDeviceError("SSR maxDistance must be an integer in [1, 64].", "INVALID_POSTPROCESS_OPTIONS", { maxDistance });
3193
+ }
3194
+ return { intensity, maxDistance };
3195
+ }
3196
+ function normalizeNativeDepthOfFieldOptions(options) {
3197
+ const focusDepth = depthOfFieldNumberOption(options, "focusDepth", 0.5);
3198
+ const focusRange = depthOfFieldNumberOption(options, "focusRange", 0.12);
3199
+ const maxRadius = depthOfFieldNumberOption(options, "maxRadius", 2);
3200
+ if (focusDepth < 0 || focusDepth > 1) {
3201
+ throw new RenderDeviceError("Depth-of-field focusDepth must be finite and in [0, 1].", "INVALID_POSTPROCESS_OPTIONS", { focusDepth });
3202
+ }
3203
+ if (focusRange < 0.001 || focusRange > 1) {
3204
+ throw new RenderDeviceError("Depth-of-field focusRange must be finite and in [0.001, 1].", "INVALID_POSTPROCESS_OPTIONS", { focusRange });
3205
+ }
3206
+ if (!Number.isInteger(maxRadius) || maxRadius < 0 || maxRadius > 8) {
3207
+ throw new RenderDeviceError("Depth-of-field maxRadius must be an integer in [0, 8].", "INVALID_POSTPROCESS_OPTIONS", { maxRadius });
3208
+ }
3209
+ return { focusDepth, focusRange, maxRadius };
3210
+ }
3211
+ function normalizeNativeMotionBlurOptions(options, width, height) {
3212
+ const samples = motionBlurNumberOption(options, "samples", 5);
3213
+ const scale = motionBlurNumberOption(options, "scale", 1);
3214
+ const value = options.velocity;
3215
+ if (!(value instanceof Float32Array) && !Array.isArray(value)) {
3216
+ throw new RenderDeviceError("Motion blur requires a velocity Float32Array or number array.", "WEBGL_LDR_POSTPROCESS_VELOCITY_REQUIRED");
3217
+ }
3218
+ const velocity = value instanceof Float32Array ? value : new Float32Array(value);
3219
+ if (velocity.length !== width * height * 2) {
3220
+ throw new RenderDeviceError("Motion blur velocity must contain width * height * 2 float samples.", "INVALID_POSTPROCESS_OPTIONS", {
3221
+ expectedLength: width * height * 2,
3222
+ actualLength: velocity.length
3223
+ });
3224
+ }
3225
+ if (!Number.isInteger(samples) || samples < 2 || samples > 16) {
3226
+ throw new RenderDeviceError("Motion blur samples must be an integer in [2, 16].", "INVALID_POSTPROCESS_OPTIONS", { samples });
3227
+ }
3228
+ if (scale < 0 || scale > 8) {
3229
+ throw new RenderDeviceError("Motion blur scale must be finite and in [0, 8].", "INVALID_POSTPROCESS_OPTIONS", { scale });
3230
+ }
3231
+ return { samples, scale, velocity };
3232
+ }
3233
+ function normalizeNativeTaaOptions(options, width, height) {
3234
+ const blend = taaNumberOption(options, "blend", 0.18);
3235
+ const history = options.history;
3236
+ if (!(history instanceof Uint8Array)) {
3237
+ throw new RenderDeviceError("TAA requires a Uint8Array history buffer.", "WEBGL_LDR_POSTPROCESS_HISTORY_REQUIRED");
3238
+ }
3239
+ if (history.byteLength !== width * height * 4) {
3240
+ throw new RenderDeviceError("TAA history must contain width * height * 4 RGBA bytes.", "INVALID_POSTPROCESS_OPTIONS", {
3241
+ expectedLength: width * height * 4,
3242
+ actualLength: history.byteLength
3243
+ });
3244
+ }
3245
+ if (blend < 0 || blend > 0.95) {
3246
+ throw new RenderDeviceError("TAA blend must be finite and in [0, 0.95].", "INVALID_POSTPROCESS_OPTIONS", { blend });
3247
+ }
3248
+ return { blend, history };
3249
+ }
3250
+ function bloomNumberOption(options, key, fallback) {
3251
+ const value = options[key];
3252
+ if (value === undefined)
3253
+ return fallback;
3254
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3255
+ throw new RenderDeviceError(`Bloom ${key} must be a finite number.`, "INVALID_POSTPROCESS_OPTIONS", { key, value });
3256
+ }
3257
+ return value;
3258
+ }
3259
+ function outlineNumberOption(options, key, fallback) {
3260
+ const value = options[key];
3261
+ if (value === undefined)
3262
+ return fallback;
3263
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3264
+ throw new RenderDeviceError(`Outline ${key} must be a finite number.`, "INVALID_POSTPROCESS_OPTIONS", { key, value });
3265
+ }
3266
+ return value;
3267
+ }
3268
+ function ssaoNumberOption(options, key, fallback) {
3269
+ const value = options[key];
3270
+ if (value === undefined)
3271
+ return fallback;
3272
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3273
+ throw new RenderDeviceError(`SSAO ${key} must be a finite number.`, "INVALID_POSTPROCESS_OPTIONS", { key, value });
3274
+ }
3275
+ return value;
3276
+ }
3277
+ function ssrNumberOption(options, key, fallback) {
3278
+ const value = options[key];
3279
+ if (value === undefined)
3280
+ return fallback;
3281
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3282
+ throw new RenderDeviceError(`SSR ${key} must be a finite number.`, "INVALID_POSTPROCESS_OPTIONS", { key, value });
3283
+ }
3284
+ return value;
3285
+ }
3286
+ function depthOfFieldNumberOption(options, key, fallback) {
3287
+ const value = options[key];
3288
+ if (value === undefined)
3289
+ return fallback;
3290
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3291
+ throw new RenderDeviceError(`Depth-of-field ${key} must be a finite number.`, "INVALID_POSTPROCESS_OPTIONS", { key, value });
3292
+ }
3293
+ return value;
3294
+ }
3295
+ function motionBlurNumberOption(options, key, fallback) {
3296
+ const value = options[key];
3297
+ if (value === undefined)
3298
+ return fallback;
3299
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3300
+ throw new RenderDeviceError(`Motion blur ${key} must be a finite number.`, "INVALID_POSTPROCESS_OPTIONS", { key, value });
3301
+ }
3302
+ return value;
3303
+ }
3304
+ function taaNumberOption(options, key, fallback) {
3305
+ const value = options[key];
3306
+ if (value === undefined)
3307
+ return fallback;
3308
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3309
+ throw new RenderDeviceError(`TAA ${key} must be a finite number.`, "INVALID_POSTPROCESS_OPTIONS", { key, value });
3310
+ }
3311
+ return value;
3312
+ }
2059
3313
  function stringOption(options, key, fallback) {
2060
3314
  const value = options[key];
2061
3315
  return typeof value === "string" ? value : fallback;