@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.
- package/README.md +65 -30
- package/dist/animation/Retargeting.d.ts +12 -0
- package/dist/animation/Retargeting.d.ts.map +1 -0
- package/dist/animation/Retargeting.js +19 -0
- package/dist/animation/Retargeting.js.map +1 -0
- package/dist/assets/GLTFLoader.d.ts +4 -0
- package/dist/assets/GLTFLoader.d.ts.map +1 -1
- package/dist/assets/GLTFLoader.js +39 -4
- package/dist/assets/GLTFLoader.js.map +1 -1
- package/dist/assets/GLTFRenderResources.d.ts +16 -0
- package/dist/assets/GLTFRenderResources.d.ts.map +1 -1
- package/dist/assets/GLTFRenderResources.js +42 -2
- package/dist/assets/GLTFRenderResources.js.map +1 -1
- package/dist/assets/asset-corpus/ProductionGLTFRenderPipeline.d.ts +6 -0
- package/dist/assets/asset-corpus/ProductionGLTFRenderPipeline.d.ts.map +1 -1
- package/dist/assets/asset-corpus/ProductionGLTFRenderPipeline.js +2 -1
- package/dist/assets/asset-corpus/ProductionGLTFRenderPipeline.js.map +1 -1
- package/dist/assets/index.d.ts +0 -1
- package/dist/assets/index.d.ts.map +1 -1
- package/dist/assets/index.js +0 -1
- package/dist/assets/index.js.map +1 -1
- package/dist/assets/loaders/index.d.ts +0 -1
- package/dist/assets/loaders/index.d.ts.map +1 -1
- package/dist/assets/loaders/index.js +0 -1
- package/dist/assets/loaders/index.js.map +1 -1
- package/dist/aura3d-cli/asset-intent.d.ts +148 -0
- package/dist/aura3d-cli/asset-intent.d.ts.map +1 -0
- package/dist/aura3d-cli/asset-intent.js +145 -0
- package/dist/aura3d-cli/asset-intent.js.map +1 -0
- package/dist/aura3d-cli/asset-role-admission.d.ts +174 -0
- package/dist/aura3d-cli/asset-role-admission.d.ts.map +1 -0
- package/dist/aura3d-cli/asset-role-admission.js +381 -0
- package/dist/aura3d-cli/asset-role-admission.js.map +1 -0
- package/dist/aura3d-cli/asset-screening-effects.d.ts +95 -0
- package/dist/aura3d-cli/asset-screening-effects.d.ts.map +1 -0
- package/dist/aura3d-cli/asset-screening-effects.js +321 -0
- package/dist/aura3d-cli/asset-screening-effects.js.map +1 -0
- package/dist/aura3d-cli/asset-screening-pipeline.d.ts +132 -0
- package/dist/aura3d-cli/asset-screening-pipeline.d.ts.map +1 -0
- package/dist/aura3d-cli/asset-screening-pipeline.js +277 -0
- package/dist/aura3d-cli/asset-screening-pipeline.js.map +1 -0
- package/dist/aura3d-cli/cartoon-asset-profiles.d.ts +15 -0
- package/dist/aura3d-cli/cartoon-asset-profiles.d.ts.map +1 -0
- package/dist/aura3d-cli/cartoon-asset-profiles.js +46 -0
- package/dist/aura3d-cli/cartoon-asset-profiles.js.map +1 -0
- package/dist/aura3d-cli/cli-help.js +1 -1
- package/dist/aura3d-cli/cli.js +16 -2
- package/dist/aura3d-cli/cli.js.map +1 -1
- package/dist/aura3d-cli/index.d.ts +8 -0
- package/dist/aura3d-cli/index.d.ts.map +1 -1
- package/dist/aura3d-cli/index.js +390 -17
- package/dist/aura3d-cli/index.js.map +1 -1
- package/dist/aura3d-cli/pull-bridge/search.d.ts +4 -0
- package/dist/aura3d-cli/pull-bridge/search.d.ts.map +1 -1
- package/dist/aura3d-cli/pull-bridge/search.js +22 -0
- package/dist/aura3d-cli/pull-bridge/search.js.map +1 -1
- package/dist/aura3d-cli/pull-bridge.d.ts +20 -5
- package/dist/aura3d-cli/pull-bridge.d.ts.map +1 -1
- package/dist/aura3d-cli/pull-bridge.js +38 -1
- package/dist/aura3d-cli/pull-bridge.js.map +1 -1
- package/dist/controls/ControlState.d.ts +1 -1
- package/dist/controls/ControlState.d.ts.map +1 -1
- package/dist/controls/DragControls.d.ts +21 -1
- package/dist/controls/DragControls.d.ts.map +1 -1
- package/dist/controls/DragControls.js +24 -2
- package/dist/controls/DragControls.js.map +1 -1
- package/dist/controls/FirstPersonControls.d.ts +10 -1
- package/dist/controls/FirstPersonControls.d.ts.map +1 -1
- package/dist/controls/FirstPersonControls.js +18 -2
- package/dist/controls/FirstPersonControls.js.map +1 -1
- package/dist/controls/FlyControls.d.ts +42 -0
- package/dist/controls/FlyControls.d.ts.map +1 -1
- package/dist/controls/FlyControls.js +106 -3
- package/dist/controls/FlyControls.js.map +1 -1
- package/dist/controls/MapControls.d.ts +13 -0
- package/dist/controls/MapControls.d.ts.map +1 -1
- package/dist/controls/MapControls.js +40 -2
- package/dist/controls/MapControls.js.map +1 -1
- package/dist/controls/OrbitControls.d.ts +54 -0
- package/dist/controls/OrbitControls.d.ts.map +1 -1
- package/dist/controls/OrbitControls.js +133 -0
- package/dist/controls/OrbitControls.js.map +1 -1
- package/dist/controls/PointerLockControls.d.ts +12 -5
- package/dist/controls/PointerLockControls.d.ts.map +1 -1
- package/dist/controls/PointerLockControls.js +30 -11
- package/dist/controls/PointerLockControls.js.map +1 -1
- package/dist/controls/SelectionManager.d.ts +22 -1
- package/dist/controls/SelectionManager.d.ts.map +1 -1
- package/dist/controls/SelectionManager.js +64 -6
- package/dist/controls/SelectionManager.js.map +1 -1
- package/dist/controls/TransformControls.d.ts +135 -1
- package/dist/controls/TransformControls.d.ts.map +1 -1
- package/dist/controls/TransformControls.js +534 -10
- package/dist/controls/TransformControls.js.map +1 -1
- package/dist/controls/index.d.ts +7 -2
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +1 -1
- package/dist/controls/index.js.map +1 -1
- package/dist/create-aura3d/index.js +1 -1
- package/dist/create-aura3d/showcase-spec-game-geometry-extractor.d.ts +2 -0
- package/dist/create-aura3d/showcase-spec-game-geometry-extractor.d.ts.map +1 -1
- package/dist/create-aura3d/showcase-spec-game-geometry-extractor.js +781 -46
- package/dist/create-aura3d/showcase-spec-game-geometry-extractor.js.map +1 -1
- package/dist/create-aura3d/showcase-spec-game-template-evidence.d.ts.map +1 -1
- package/dist/create-aura3d/showcase-spec-game-template-evidence.js +24 -4
- package/dist/create-aura3d/showcase-spec-game-template-evidence.js.map +1 -1
- package/dist/create-aura3d/showcase-spec-platformer-artifacts.d.ts.map +1 -1
- package/dist/create-aura3d/showcase-spec-platformer-artifacts.js +28 -17
- package/dist/create-aura3d/showcase-spec-platformer-artifacts.js.map +1 -1
- package/dist/create-aura3d/showcase-spec-racing-artifacts.d.ts.map +1 -1
- package/dist/create-aura3d/showcase-spec-racing-artifacts.js +10 -5
- package/dist/create-aura3d/showcase-spec-racing-artifacts.js.map +1 -1
- package/dist/create-aura3d/showcase-spec-replacement-candidates.js +26 -0
- package/dist/create-aura3d/showcase-spec-replacement-candidates.js.map +1 -1
- package/dist/ecs/components/MeshComponent.d.ts +5 -2
- package/dist/ecs/components/MeshComponent.d.ts.map +1 -1
- package/dist/ecs/components/MeshComponent.js.map +1 -1
- package/dist/editor-runtime/CartoonSceneEditor.d.ts +111 -0
- package/dist/editor-runtime/CartoonSceneEditor.d.ts.map +1 -0
- package/dist/editor-runtime/CartoonSceneEditor.js +285 -0
- package/dist/editor-runtime/CartoonSceneEditor.js.map +1 -0
- package/dist/editor-runtime/InteractiveTransformGizmo.d.ts +142 -0
- package/dist/editor-runtime/InteractiveTransformGizmo.d.ts.map +1 -0
- package/dist/editor-runtime/InteractiveTransformGizmo.js +468 -0
- package/dist/editor-runtime/InteractiveTransformGizmo.js.map +1 -0
- package/dist/editor-runtime/index.d.ts +2 -0
- package/dist/editor-runtime/index.d.ts.map +1 -1
- package/dist/editor-runtime/index.js +1 -0
- package/dist/editor-runtime/index.js.map +1 -1
- package/dist/engine/agent-api/CartoonAssetManifest.d.ts +50 -0
- package/dist/engine/agent-api/CartoonAssetManifest.d.ts.map +1 -0
- package/dist/engine/agent-api/CartoonAssetManifest.js +65 -0
- package/dist/engine/agent-api/CartoonAssetManifest.js.map +1 -0
- package/dist/engine/agent-api/CartoonDirector.d.ts +120 -0
- package/dist/engine/agent-api/CartoonDirector.d.ts.map +1 -0
- package/dist/engine/agent-api/CartoonDirector.js +432 -0
- package/dist/engine/agent-api/CartoonDirector.js.map +1 -0
- package/dist/engine/agent-api/CartoonEpisodePackage.d.ts +51 -0
- package/dist/engine/agent-api/CartoonEpisodePackage.d.ts.map +1 -0
- package/dist/engine/agent-api/CartoonEpisodePackage.js +79 -0
- package/dist/engine/agent-api/CartoonEpisodePackage.js.map +1 -0
- package/dist/engine/agent-api/CartoonMotionQuality.d.ts +83 -0
- package/dist/engine/agent-api/CartoonMotionQuality.d.ts.map +1 -0
- package/dist/engine/agent-api/CartoonMotionQuality.js +128 -0
- package/dist/engine/agent-api/CartoonMotionQuality.js.map +1 -0
- package/dist/engine/agent-api/CartoonPerformance.d.ts +150 -0
- package/dist/engine/agent-api/CartoonPerformance.d.ts.map +1 -0
- package/dist/engine/agent-api/CartoonPerformance.js +345 -0
- package/dist/engine/agent-api/CartoonPerformance.js.map +1 -0
- package/dist/engine/agent-api/CartoonRenderQueue.d.ts +143 -0
- package/dist/engine/agent-api/CartoonRenderQueue.d.ts.map +1 -0
- package/dist/engine/agent-api/CartoonRenderQueue.js +410 -0
- package/dist/engine/agent-api/CartoonRenderQueue.js.map +1 -0
- package/dist/engine/agent-api/CartoonRouteProof.d.ts +112 -0
- package/dist/engine/agent-api/CartoonRouteProof.d.ts.map +1 -0
- package/dist/engine/agent-api/CartoonRouteProof.js +127 -0
- package/dist/engine/agent-api/CartoonRouteProof.js.map +1 -0
- package/dist/engine/agent-api/GameEvidence.d.ts +1 -0
- package/dist/engine/agent-api/GameEvidence.d.ts.map +1 -1
- package/dist/engine/agent-api/GameEvidence.js +1 -0
- package/dist/engine/agent-api/GameEvidence.js.map +1 -1
- package/dist/engine/agent-api/GameGenreKits.d.ts +13 -0
- package/dist/engine/agent-api/GameGenreKits.d.ts.map +1 -1
- package/dist/engine/agent-api/GameGenreKits.js +57 -11
- package/dist/engine/agent-api/GameGenreKits.js.map +1 -1
- package/dist/engine/agent-api/GameRuntime.d.ts +1 -0
- package/dist/engine/agent-api/GameRuntime.d.ts.map +1 -1
- package/dist/engine/agent-api/GameRuntime.js +1 -0
- package/dist/engine/agent-api/GameRuntime.js.map +1 -1
- package/dist/engine/agent-api/GameSceneGeometryBindings.d.ts +12 -0
- package/dist/engine/agent-api/GameSceneGeometryBindings.d.ts.map +1 -1
- package/dist/engine/agent-api/GameSceneGeometryBindings.js +2 -0
- package/dist/engine/agent-api/GameSceneGeometryBindings.js.map +1 -1
- package/dist/engine/agent-api/LayeredSceneComposition.d.ts +295 -0
- package/dist/engine/agent-api/LayeredSceneComposition.d.ts.map +1 -0
- package/dist/engine/agent-api/LayeredSceneComposition.js +387 -0
- package/dist/engine/agent-api/LayeredSceneComposition.js.map +1 -0
- package/dist/engine/agent-api/SceneGroundingUtils.d.ts +62 -0
- package/dist/engine/agent-api/SceneGroundingUtils.d.ts.map +1 -1
- package/dist/engine/agent-api/SceneGroundingUtils.js +37 -0
- package/dist/engine/agent-api/SceneGroundingUtils.js.map +1 -1
- package/dist/engine/agent-api/SubjectFramingUtils.d.ts +127 -0
- package/dist/engine/agent-api/SubjectFramingUtils.d.ts.map +1 -0
- package/dist/engine/agent-api/SubjectFramingUtils.js +126 -0
- package/dist/engine/agent-api/SubjectFramingUtils.js.map +1 -0
- package/dist/engine/agent-api/TouchControlBinding.d.ts +81 -0
- package/dist/engine/agent-api/TouchControlBinding.d.ts.map +1 -0
- package/dist/engine/agent-api/TouchControlBinding.js +116 -0
- package/dist/engine/agent-api/TouchControlBinding.js.map +1 -0
- package/dist/engine/agent-api/index.d.ts +39 -0
- package/dist/engine/agent-api/index.d.ts.map +1 -1
- package/dist/engine/agent-api/index.js +546 -74
- package/dist/engine/agent-api/index.js.map +1 -1
- package/dist/engine/agent-api/product-viewer-runtime.d.ts.map +1 -1
- package/dist/engine/production-runtime/GameRenderPreset.d.ts +32 -0
- package/dist/engine/production-runtime/GameRenderPreset.d.ts.map +1 -1
- package/dist/engine/production-runtime/GameRenderPreset.js +37 -2
- package/dist/engine/production-runtime/GameRenderPreset.js.map +1 -1
- package/dist/engine/production-runtime/TypedGLBActor.d.ts +8 -0
- package/dist/engine/production-runtime/TypedGLBActor.d.ts.map +1 -1
- package/dist/engine/production-runtime/TypedGLBActor.js +2 -1
- package/dist/engine/production-runtime/TypedGLBActor.js.map +1 -1
- package/dist/input/controls/PointerLockControls.d.ts +1 -1
- package/dist/input/controls/PointerLockControls.d.ts.map +1 -1
- package/dist/input/controls/PointerLockControls.js +12 -0
- package/dist/input/controls/PointerLockControls.js.map +1 -1
- package/dist/physics/Collider.d.ts +1 -1
- package/dist/physics/Collider.d.ts.map +1 -1
- package/dist/physics/Collider.js +2 -2
- package/dist/physics/Collider.js.map +1 -1
- package/dist/physics/CollisionEvents.d.ts +1 -0
- package/dist/physics/CollisionEvents.d.ts.map +1 -1
- package/dist/physics/CollisionEvents.js.map +1 -1
- package/dist/physics/NarrowPhase.d.ts +11 -0
- package/dist/physics/NarrowPhase.d.ts.map +1 -0
- package/dist/physics/NarrowPhase.js +565 -0
- package/dist/physics/NarrowPhase.js.map +1 -0
- package/dist/physics/PhysicsDebugDraw.js +19 -1
- package/dist/physics/PhysicsDebugDraw.js.map +1 -1
- package/dist/physics/PhysicsWorld.d.ts +43 -0
- package/dist/physics/PhysicsWorld.d.ts.map +1 -1
- package/dist/physics/PhysicsWorld.js +305 -30
- package/dist/physics/PhysicsWorld.js.map +1 -1
- package/dist/physics/RigidBody.d.ts +8 -1
- package/dist/physics/RigidBody.d.ts.map +1 -1
- package/dist/physics/RigidBody.js +32 -5
- package/dist/physics/RigidBody.js.map +1 -1
- package/dist/physics/Shape.d.ts +16 -2
- package/dist/physics/Shape.d.ts.map +1 -1
- package/dist/physics/Shape.js +93 -31
- package/dist/physics/Shape.js.map +1 -1
- package/dist/physics/TimeOfImpact.d.ts +15 -0
- package/dist/physics/TimeOfImpact.d.ts.map +1 -0
- package/dist/physics/TimeOfImpact.js +81 -0
- package/dist/physics/TimeOfImpact.js.map +1 -0
- package/dist/physics/index.d.ts +2 -0
- package/dist/physics/index.d.ts.map +1 -1
- package/dist/physics/index.js +2 -0
- package/dist/physics/index.js.map +1 -1
- package/dist/rendering/CascadedShadowMaps.d.ts +9 -1
- package/dist/rendering/CascadedShadowMaps.d.ts.map +1 -1
- package/dist/rendering/CascadedShadowMaps.js +68 -0
- package/dist/rendering/CascadedShadowMaps.js.map +1 -1
- package/dist/rendering/ClusteredForwardLighting.d.ts +28 -0
- package/dist/rendering/ClusteredForwardLighting.d.ts.map +1 -0
- package/dist/rendering/ClusteredForwardLighting.js +156 -0
- package/dist/rendering/ClusteredForwardLighting.js.map +1 -0
- package/dist/rendering/DepthPass.d.ts +14 -1
- package/dist/rendering/DepthPass.d.ts.map +1 -1
- package/dist/rendering/DepthPass.js +19 -4
- package/dist/rendering/DepthPass.js.map +1 -1
- package/dist/rendering/EnvironmentMapResources.d.ts +59 -1
- package/dist/rendering/EnvironmentMapResources.d.ts.map +1 -1
- package/dist/rendering/EnvironmentMapResources.js +140 -100
- package/dist/rendering/EnvironmentMapResources.js.map +1 -1
- package/dist/rendering/EnvironmentPlatform.d.ts +3 -2
- package/dist/rendering/EnvironmentPlatform.d.ts.map +1 -1
- package/dist/rendering/EnvironmentPlatform.js +99 -44
- package/dist/rendering/EnvironmentPlatform.js.map +1 -1
- package/dist/rendering/ForwardPass.d.ts +37 -0
- package/dist/rendering/ForwardPass.d.ts.map +1 -1
- package/dist/rendering/ForwardPass.js +234 -7
- package/dist/rendering/ForwardPass.js.map +1 -1
- package/dist/rendering/Geometry.d.ts +19 -0
- package/dist/rendering/Geometry.d.ts.map +1 -1
- package/dist/rendering/Geometry.js +64 -0
- package/dist/rendering/Geometry.js.map +1 -1
- package/dist/rendering/IBL.d.ts.map +1 -1
- package/dist/rendering/IBL.js +0 -1
- package/dist/rendering/IBL.js.map +1 -1
- package/dist/rendering/LightCollector.d.ts +5 -1
- package/dist/rendering/LightCollector.d.ts.map +1 -1
- package/dist/rendering/LightCollector.js +16 -3
- package/dist/rendering/LightCollector.js.map +1 -1
- package/dist/rendering/LightUniforms.d.ts +26 -1
- package/dist/rendering/LightUniforms.d.ts.map +1 -1
- package/dist/rendering/LightUniforms.js +140 -8
- package/dist/rendering/LightUniforms.js.map +1 -1
- package/dist/rendering/LightingRig.d.ts +77 -0
- package/dist/rendering/LightingRig.d.ts.map +1 -1
- package/dist/rendering/LightingRig.js +100 -11
- package/dist/rendering/LightingRig.js.map +1 -1
- package/dist/rendering/MeshConsolidation.d.ts +63 -0
- package/dist/rendering/MeshConsolidation.d.ts.map +1 -0
- package/dist/rendering/MeshConsolidation.js +251 -0
- package/dist/rendering/MeshConsolidation.js.map +1 -0
- package/dist/rendering/PMREM.d.ts +13 -1
- package/dist/rendering/PMREM.d.ts.map +1 -1
- package/dist/rendering/PMREM.js +18 -4
- package/dist/rendering/PMREM.js.map +1 -1
- package/dist/rendering/PostProcessPass.d.ts +24 -2
- package/dist/rendering/PostProcessPass.d.ts.map +1 -1
- package/dist/rendering/PostProcessPass.js +136 -18
- package/dist/rendering/PostProcessPass.js.map +1 -1
- package/dist/rendering/PrimitiveSubmissionAudit.d.ts +195 -0
- package/dist/rendering/PrimitiveSubmissionAudit.d.ts.map +1 -0
- package/dist/rendering/PrimitiveSubmissionAudit.js +280 -0
- package/dist/rendering/PrimitiveSubmissionAudit.js.map +1 -0
- package/dist/rendering/ProductTurntableFixtures.d.ts.map +1 -1
- package/dist/rendering/ProductTurntableFixtures.js +93 -13
- package/dist/rendering/ProductTurntableFixtures.js.map +1 -1
- package/dist/rendering/ReflectionProbe.d.ts +59 -0
- package/dist/rendering/ReflectionProbe.d.ts.map +1 -1
- package/dist/rendering/ReflectionProbe.js +224 -0
- package/dist/rendering/ReflectionProbe.js.map +1 -1
- package/dist/rendering/ReflectionSurfaces.d.ts +4 -2
- package/dist/rendering/ReflectionSurfaces.d.ts.map +1 -1
- package/dist/rendering/ReflectionSurfaces.js +13 -8
- package/dist/rendering/ReflectionSurfaces.js.map +1 -1
- package/dist/rendering/RenderBackend.d.ts +1 -1
- package/dist/rendering/RenderBackend.d.ts.map +1 -1
- package/dist/rendering/RenderBackend.js +5 -1
- package/dist/rendering/RenderBackend.js.map +1 -1
- package/dist/rendering/RenderDevice.d.ts +13 -2
- package/dist/rendering/RenderDevice.d.ts.map +1 -1
- package/dist/rendering/RenderDevice.js +5 -2
- package/dist/rendering/RenderDevice.js.map +1 -1
- package/dist/rendering/Renderer.d.ts +41 -1
- package/dist/rendering/Renderer.d.ts.map +1 -1
- package/dist/rendering/Renderer.js +406 -61
- package/dist/rendering/Renderer.js.map +1 -1
- package/dist/rendering/RendererPostprocessPlan.d.ts +10 -3
- package/dist/rendering/RendererPostprocessPlan.d.ts.map +1 -1
- package/dist/rendering/RendererPostprocessPlan.js +25 -5
- package/dist/rendering/RendererPostprocessPlan.js.map +1 -1
- package/dist/rendering/RendererVisualPipelineReport.d.ts +3 -2
- package/dist/rendering/RendererVisualPipelineReport.d.ts.map +1 -1
- package/dist/rendering/RendererVisualPipelineReport.js +2 -0
- package/dist/rendering/RendererVisualPipelineReport.js.map +1 -1
- package/dist/rendering/ScreenSpaceLineMaterial.d.ts +34 -0
- package/dist/rendering/ScreenSpaceLineMaterial.d.ts.map +1 -0
- package/dist/rendering/ScreenSpaceLineMaterial.js +93 -0
- package/dist/rendering/ScreenSpaceLineMaterial.js.map +1 -0
- package/dist/rendering/ShaderChunks.d.ts +7 -0
- package/dist/rendering/ShaderChunks.d.ts.map +1 -1
- package/dist/rendering/ShaderChunks.js +137 -0
- package/dist/rendering/ShaderChunks.js.map +1 -1
- package/dist/rendering/ShaderLibrary.d.ts +17 -0
- package/dist/rendering/ShaderLibrary.d.ts.map +1 -1
- package/dist/rendering/ShaderLibrary.js +940 -98
- package/dist/rendering/ShaderLibrary.js.map +1 -1
- package/dist/rendering/ShadowPass.d.ts +10 -0
- package/dist/rendering/ShadowPass.d.ts.map +1 -1
- package/dist/rendering/ShadowPass.js +8 -1
- package/dist/rendering/ShadowPass.js.map +1 -1
- package/dist/rendering/SkinnedLitMaterial.d.ts +6 -0
- package/dist/rendering/SkinnedLitMaterial.d.ts.map +1 -1
- package/dist/rendering/SkinnedLitMaterial.js +22 -8
- package/dist/rendering/SkinnedLitMaterial.js.map +1 -1
- package/dist/rendering/SkinnedUnlitMaterial.d.ts +8 -0
- package/dist/rendering/SkinnedUnlitMaterial.d.ts.map +1 -1
- package/dist/rendering/SkinnedUnlitMaterial.js +26 -9
- package/dist/rendering/SkinnedUnlitMaterial.js.map +1 -1
- package/dist/rendering/SpecularPrefilter.d.ts +42 -0
- package/dist/rendering/SpecularPrefilter.d.ts.map +1 -0
- package/dist/rendering/SpecularPrefilter.js +480 -0
- package/dist/rendering/SpecularPrefilter.js.map +1 -0
- package/dist/rendering/TerrainFixtures.d.ts +24 -0
- package/dist/rendering/TerrainFixtures.d.ts.map +1 -1
- package/dist/rendering/TerrainFixtures.js +96 -0
- package/dist/rendering/TerrainFixtures.js.map +1 -1
- package/dist/rendering/Texture.d.ts +7 -0
- package/dist/rendering/Texture.d.ts.map +1 -1
- package/dist/rendering/Texture.js +9 -0
- package/dist/rendering/Texture.js.map +1 -1
- package/dist/rendering/VertexFormat.d.ts +34 -1
- package/dist/rendering/VertexFormat.d.ts.map +1 -1
- package/dist/rendering/VertexFormat.js +61 -0
- package/dist/rendering/VertexFormat.js.map +1 -1
- package/dist/rendering/WebGL2Device.d.ts +48 -0
- package/dist/rendering/WebGL2Device.d.ts.map +1 -1
- package/dist/rendering/WebGL2Device.js +1298 -44
- package/dist/rendering/WebGL2Device.js.map +1 -1
- package/dist/rendering/WebGPUDevice.d.ts +7 -2
- package/dist/rendering/WebGPUDevice.d.ts.map +1 -1
- package/dist/rendering/WebGPUDevice.js +111 -17
- package/dist/rendering/WebGPUDevice.js.map +1 -1
- package/dist/rendering/WebGPUSkinningLimits.d.ts +14 -0
- package/dist/rendering/WebGPUSkinningLimits.d.ts.map +1 -0
- package/dist/rendering/WebGPUSkinningLimits.js +14 -0
- package/dist/rendering/WebGPUSkinningLimits.js.map +1 -0
- package/dist/rendering/cartoon/CartoonMaterialStyle.d.ts +19 -0
- package/dist/rendering/cartoon/CartoonMaterialStyle.d.ts.map +1 -0
- package/dist/rendering/cartoon/CartoonMaterialStyle.js +13 -0
- package/dist/rendering/cartoon/CartoonMaterialStyle.js.map +1 -0
- package/dist/rendering/cartoon/CartoonRenderPreset.d.ts +36 -0
- package/dist/rendering/cartoon/CartoonRenderPreset.d.ts.map +1 -0
- package/dist/rendering/cartoon/CartoonRenderPreset.js +23 -0
- package/dist/rendering/cartoon/CartoonRenderPreset.js.map +1 -0
- package/dist/rendering/cartoon/CartoonToonMaterial.d.ts +130 -0
- package/dist/rendering/cartoon/CartoonToonMaterial.d.ts.map +1 -0
- package/dist/rendering/cartoon/CartoonToonMaterial.js +303 -0
- package/dist/rendering/cartoon/CartoonToonMaterial.js.map +1 -0
- package/dist/rendering/cartoon/CartoonVisualQuality.d.ts +28 -0
- package/dist/rendering/cartoon/CartoonVisualQuality.d.ts.map +1 -0
- package/dist/rendering/cartoon/CartoonVisualQuality.js +39 -0
- package/dist/rendering/cartoon/CartoonVisualQuality.js.map +1 -0
- package/dist/rendering/cartoon/applyCartoonRenderPreset.d.ts +79 -0
- package/dist/rendering/cartoon/applyCartoonRenderPreset.d.ts.map +1 -0
- package/dist/rendering/cartoon/applyCartoonRenderPreset.js +88 -0
- package/dist/rendering/cartoon/applyCartoonRenderPreset.js.map +1 -0
- package/dist/rendering/cartoon/index.d.ts +6 -0
- package/dist/rendering/cartoon/index.d.ts.map +1 -0
- package/dist/rendering/cartoon/index.js +6 -0
- package/dist/rendering/cartoon/index.js.map +1 -0
- package/dist/rendering/index.d.ts +26 -15
- package/dist/rendering/index.d.ts.map +1 -1
- package/dist/rendering/index.js +15 -9
- package/dist/rendering/index.js.map +1 -1
- package/dist/rendering/postprocess/NativeLdrEffectLuts.d.ts +93 -0
- package/dist/rendering/postprocess/NativeLdrEffectLuts.d.ts.map +1 -0
- package/dist/rendering/postprocess/NativeLdrEffectLuts.js +202 -0
- package/dist/rendering/postprocess/NativeLdrEffectLuts.js.map +1 -0
- package/dist/rendering/production-runtime/PBRHDRPipeline.d.ts.map +1 -1
- package/dist/rendering/production-runtime/PBRHDRPipeline.js +3 -0
- package/dist/rendering/production-runtime/PBRHDRPipeline.js.map +1 -1
- package/dist/rendering/production-runtime/ProductionRendererTypes.d.ts +1 -0
- package/dist/rendering/production-runtime/ProductionRendererTypes.d.ts.map +1 -1
- package/dist/rendering/production-runtime/ProductionRendererTypes.js.map +1 -1
- package/dist/rendering/production-runtime/ProductionWebGL2Renderer.d.ts.map +1 -1
- package/dist/rendering/production-runtime/ProductionWebGL2Renderer.js +5 -3
- package/dist/rendering/production-runtime/ProductionWebGL2Renderer.js.map +1 -1
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.d.ts.map +1 -1
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.js +9 -5
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.js.map +1 -1
- package/dist/rendering/production-runtime/TransmissionBackdropCapture.d.ts +5 -0
- package/dist/rendering/production-runtime/TransmissionBackdropCapture.d.ts.map +1 -1
- package/dist/rendering/production-runtime/TransmissionBackdropCapture.js +40 -0
- package/dist/rendering/production-runtime/TransmissionBackdropCapture.js.map +1 -1
- package/dist/rendering/production-runtime/environment/HDRLoader.d.ts +6 -0
- package/dist/rendering/production-runtime/environment/HDRLoader.d.ts.map +1 -1
- package/dist/rendering/production-runtime/environment/HDRLoader.js +19 -0
- package/dist/rendering/production-runtime/environment/HDRLoader.js.map +1 -1
- package/dist/rendering/production-runtime/index.d.ts +3 -1
- package/dist/rendering/production-runtime/index.d.ts.map +1 -1
- package/dist/rendering/production-runtime/index.js +1 -0
- package/dist/rendering/production-runtime/index.js.map +1 -1
- package/dist/scene/Light.d.ts +1 -1
- package/dist/scene/Light.d.ts.map +1 -1
- package/dist/scene/RectAreaLight.d.ts +22 -0
- package/dist/scene/RectAreaLight.d.ts.map +1 -0
- package/dist/scene/RectAreaLight.js +46 -0
- package/dist/scene/RectAreaLight.js.map +1 -0
- package/dist/scene/Scene.d.ts +6 -1
- package/dist/scene/Scene.d.ts.map +1 -1
- package/dist/scene/Scene.js +8 -1
- package/dist/scene/Scene.js.map +1 -1
- package/dist/scene/SceneSerializer.d.ts +3 -1
- package/dist/scene/SceneSerializer.d.ts.map +1 -1
- package/dist/scene/SceneSerializer.js +14 -7
- package/dist/scene/SceneSerializer.js.map +1 -1
- package/dist/scene/index.d.ts +1 -0
- package/dist/scene/index.d.ts.map +1 -1
- package/dist/scene/index.js +1 -0
- package/dist/scene/index.js.map +1 -1
- package/dist/scripting/CartoonNodeCategories.d.ts +3 -0
- package/dist/scripting/CartoonNodeCategories.d.ts.map +1 -0
- package/dist/scripting/CartoonNodeCategories.js +9 -0
- package/dist/scripting/CartoonNodeCategories.js.map +1 -0
- package/dist/scripting/CartoonVisualNodes.d.ts +3 -0
- package/dist/scripting/CartoonVisualNodes.d.ts.map +1 -0
- package/dist/scripting/CartoonVisualNodes.js +41 -0
- package/dist/scripting/CartoonVisualNodes.js.map +1 -0
- package/package.json +124 -113
- package/templates/cinematic-scene/package.json +1 -1
- package/templates/mini-game/package.json +1 -1
- package/templates/product-viewer/package.json +1 -1
- package/templates/production-asset-inspector/README.md +5 -0
- package/templates/production-asset-inspector/asset-manifest.json +9 -0
- package/templates/production-asset-inspector/index.html +2 -0
- package/templates/production-asset-inspector/package.json +17 -0
- package/templates/production-asset-inspector/src/main.ts +16 -0
- package/templates/production-material-studio/README.md +5 -0
- package/templates/production-material-studio/asset-manifest.json +9 -0
- package/templates/production-material-studio/index.html +2 -0
- package/templates/production-material-studio/package.json +17 -0
- package/templates/production-material-studio/src/main.ts +16 -0
- package/templates/production-product-configurator/README.md +5 -0
- package/templates/production-product-configurator/asset-manifest.json +9 -0
- package/templates/production-product-configurator/index.html +2 -0
- package/templates/production-product-configurator/package.json +17 -0
- package/templates/production-product-configurator/src/main.ts +16 -0
- package/templates/production-product-viewer/README.md +7 -0
- package/templates/production-product-viewer/asset-manifest.json +8 -0
- package/templates/production-product-viewer/index.html +26 -0
- package/templates/production-product-viewer/package.json +17 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
310
|
-
this.gl.
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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", {
|
|
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,
|
|
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.
|
|
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
|
|
486
|
-
const
|
|
487
|
-
const
|
|
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
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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
|
|
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
|
-
|
|
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;
|