@aura3d/rendering 2.0.4 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AtmosphereWetness.d.ts +61 -0
- package/dist/AtmosphereWetness.d.ts.map +1 -0
- package/dist/AtmosphereWetness.js +99 -0
- package/dist/AtmosphereWetness.js.map +1 -0
- package/dist/CascadedShadowMaps.d.ts.map +1 -1
- package/dist/CascadedShadowMaps.js +3 -1
- package/dist/CascadedShadowMaps.js.map +1 -1
- package/dist/ClusteredForwardLighting.d.ts +25 -1
- package/dist/ClusteredForwardLighting.d.ts.map +1 -1
- package/dist/ClusteredForwardLighting.js +80 -3
- package/dist/ClusteredForwardLighting.js.map +1 -1
- package/dist/DayNightSky.d.ts +63 -0
- package/dist/DayNightSky.d.ts.map +1 -0
- package/dist/DayNightSky.js +156 -0
- package/dist/DayNightSky.js.map +1 -0
- package/dist/EnvironmentPresetPack.d.ts +30 -0
- package/dist/EnvironmentPresetPack.d.ts.map +1 -0
- package/dist/EnvironmentPresetPack.js +49 -0
- package/dist/EnvironmentPresetPack.js.map +1 -0
- package/dist/ExtensionScalarAtlas.d.ts +18 -0
- package/dist/ExtensionScalarAtlas.d.ts.map +1 -0
- package/dist/ExtensionScalarAtlas.js +107 -0
- package/dist/ExtensionScalarAtlas.js.map +1 -0
- package/dist/ForwardPass.d.ts +65 -0
- package/dist/ForwardPass.d.ts.map +1 -1
- package/dist/ForwardPass.js +196 -2
- package/dist/ForwardPass.js.map +1 -1
- package/dist/InstancedPBRMaterial.d.ts +1 -0
- package/dist/InstancedPBRMaterial.d.ts.map +1 -1
- package/dist/InstancedPBRMaterial.js +3 -0
- package/dist/InstancedPBRMaterial.js.map +1 -1
- package/dist/InstancingDiagnostics.d.ts +69 -0
- package/dist/InstancingDiagnostics.d.ts.map +1 -0
- package/dist/InstancingDiagnostics.js +98 -0
- package/dist/InstancingDiagnostics.js.map +1 -0
- package/dist/LeanWebGL2Device.d.ts +265 -0
- package/dist/LeanWebGL2Device.d.ts.map +1 -0
- package/dist/LeanWebGL2Device.js +4001 -0
- package/dist/LeanWebGL2Device.js.map +1 -0
- package/dist/LightingRig.d.ts +19 -1
- package/dist/LightingRig.d.ts.map +1 -1
- package/dist/LightingRig.js +61 -1
- package/dist/LightingRig.js.map +1 -1
- package/dist/MaterialBinding.js +1 -1
- package/dist/MaterialBinding.js.map +1 -1
- package/dist/MeshConsolidation.d.ts +12 -0
- package/dist/MeshConsolidation.d.ts.map +1 -1
- package/dist/MeshConsolidation.js +28 -0
- package/dist/MeshConsolidation.js.map +1 -1
- package/dist/MorphTargetPlan.d.ts +29 -2
- package/dist/MorphTargetPlan.d.ts.map +1 -1
- package/dist/MorphTargetPlan.js +23 -4
- package/dist/MorphTargetPlan.js.map +1 -1
- package/dist/NormalMappedPBRMaterial.d.ts.map +1 -1
- package/dist/NormalMappedPBRMaterial.js +22 -2
- package/dist/NormalMappedPBRMaterial.js.map +1 -1
- package/dist/OceanSurface.d.ts +75 -0
- package/dist/OceanSurface.d.ts.map +1 -1
- package/dist/OceanSurface.js +211 -0
- package/dist/OceanSurface.js.map +1 -1
- package/dist/PBRMaterial.d.ts +1 -0
- package/dist/PBRMaterial.d.ts.map +1 -1
- package/dist/PBRMaterial.js +23 -2
- package/dist/PBRMaterial.js.map +1 -1
- package/dist/PbrReference.d.ts +48 -0
- package/dist/PbrReference.d.ts.map +1 -1
- package/dist/PbrReference.js +126 -0
- package/dist/PbrReference.js.map +1 -1
- package/dist/PlanarReflection.d.ts +221 -0
- package/dist/PlanarReflection.d.ts.map +1 -0
- package/dist/PlanarReflection.js +541 -0
- package/dist/PlanarReflection.js.map +1 -0
- package/dist/PostProcessPass.d.ts +32 -0
- package/dist/PostProcessPass.d.ts.map +1 -1
- package/dist/PostProcessPass.js +79 -36
- package/dist/PostProcessPass.js.map +1 -1
- package/dist/ProjectionMath.d.ts +3 -0
- package/dist/ProjectionMath.d.ts.map +1 -0
- package/dist/ProjectionMath.js +26 -0
- package/dist/ProjectionMath.js.map +1 -0
- package/dist/ReflectionSurfaces.d.ts +17 -0
- package/dist/ReflectionSurfaces.d.ts.map +1 -1
- package/dist/ReflectionSurfaces.js +103 -1
- package/dist/ReflectionSurfaces.js.map +1 -1
- package/dist/RenderDevice.d.ts +92 -0
- package/dist/RenderDevice.d.ts.map +1 -1
- package/dist/RenderDevice.js +85 -0
- package/dist/RenderDevice.js.map +1 -1
- package/dist/Renderer.d.ts +9 -0
- package/dist/Renderer.d.ts.map +1 -1
- package/dist/Renderer.js +97 -9
- package/dist/Renderer.js.map +1 -1
- package/dist/RendererPostprocessPlan.d.ts +64 -3
- package/dist/RendererPostprocessPlan.d.ts.map +1 -1
- package/dist/RendererPostprocessPlan.js +116 -12
- package/dist/RendererPostprocessPlan.js.map +1 -1
- package/dist/RendererVisualPipelineReport.d.ts.map +1 -1
- package/dist/RendererVisualPipelineReport.js +3 -1
- package/dist/RendererVisualPipelineReport.js.map +1 -1
- package/dist/Sampler.d.ts +29 -0
- package/dist/Sampler.d.ts.map +1 -1
- package/dist/Sampler.js +47 -0
- package/dist/Sampler.js.map +1 -1
- package/dist/ScreenSpaceReflectionPass.d.ts +43 -0
- package/dist/ScreenSpaceReflectionPass.d.ts.map +1 -0
- package/dist/ScreenSpaceReflectionPass.js +58 -0
- package/dist/ScreenSpaceReflectionPass.js.map +1 -0
- package/dist/SdfText.d.ts +250 -0
- package/dist/SdfText.d.ts.map +1 -0
- package/dist/SdfText.js +551 -0
- package/dist/SdfText.js.map +1 -0
- package/dist/ShaderChunks.d.ts.map +1 -1
- package/dist/ShaderChunks.js +30 -12
- package/dist/ShaderChunks.js.map +1 -1
- package/dist/ShaderLibrary.d.ts.map +1 -1
- package/dist/ShaderLibrary.js +601 -186
- package/dist/ShaderLibrary.js.map +1 -1
- package/dist/ShaderLibraryCore.d.ts +3 -0
- package/dist/ShaderLibraryCore.d.ts.map +1 -1
- package/dist/ShaderLibraryCore.js +73 -12
- package/dist/ShaderLibraryCore.js.map +1 -1
- package/dist/ShadowMap.d.ts +24 -0
- package/dist/ShadowMap.d.ts.map +1 -1
- package/dist/ShadowMap.js +43 -0
- package/dist/ShadowMap.js.map +1 -1
- package/dist/SkinnedLitMaterial.d.ts.map +1 -1
- package/dist/SkinnedLitMaterial.js +2 -0
- package/dist/SkinnedLitMaterial.js.map +1 -1
- package/dist/SkinningBounds.d.ts +10 -0
- package/dist/SkinningBounds.d.ts.map +1 -1
- package/dist/SkinningBounds.js +27 -0
- package/dist/SkinningBounds.js.map +1 -1
- package/dist/SpriteFlipbook.d.ts +60 -0
- package/dist/SpriteFlipbook.d.ts.map +1 -0
- package/dist/SpriteFlipbook.js +128 -0
- package/dist/SpriteFlipbook.js.map +1 -0
- package/dist/TemporalHistory.d.ts +37 -0
- package/dist/TemporalHistory.d.ts.map +1 -0
- package/dist/TemporalHistory.js +135 -0
- package/dist/TemporalHistory.js.map +1 -0
- package/dist/TemporalMath.d.ts +3 -0
- package/dist/TemporalMath.d.ts.map +1 -0
- package/dist/TemporalMath.js +9 -0
- package/dist/TemporalMath.js.map +1 -0
- package/dist/TerrainTiles.d.ts +120 -0
- package/dist/TerrainTiles.d.ts.map +1 -0
- package/dist/TerrainTiles.js +221 -0
- package/dist/TerrainTiles.js.map +1 -0
- package/dist/TexturedPBRMaterial.d.ts +4 -0
- package/dist/TexturedPBRMaterial.d.ts.map +1 -1
- package/dist/TexturedPBRMaterial.js +88 -5
- package/dist/TexturedPBRMaterial.js.map +1 -1
- package/dist/TexturedUnlitMaterial.d.ts.map +1 -1
- package/dist/TexturedUnlitMaterial.js +20 -2
- package/dist/TexturedUnlitMaterial.js.map +1 -1
- package/dist/UnlitMaterial.d.ts.map +1 -1
- package/dist/UnlitMaterial.js +20 -2
- package/dist/UnlitMaterial.js.map +1 -1
- package/dist/VertexFormat.d.ts +6 -0
- package/dist/VertexFormat.d.ts.map +1 -1
- package/dist/VertexFormat.js +12 -0
- package/dist/VertexFormat.js.map +1 -1
- package/dist/VolumetricFog.d.ts +55 -0
- package/dist/VolumetricFog.d.ts.map +1 -0
- package/dist/VolumetricFog.js +91 -0
- package/dist/VolumetricFog.js.map +1 -0
- package/dist/WaterSurface.d.ts +77 -0
- package/dist/WaterSurface.d.ts.map +1 -0
- package/dist/WaterSurface.js +133 -0
- package/dist/WaterSurface.js.map +1 -0
- package/dist/WebGL2Device.d.ts +73 -0
- package/dist/WebGL2Device.d.ts.map +1 -1
- package/dist/WebGL2Device.js +836 -79
- package/dist/WebGL2Device.js.map +1 -1
- package/dist/WebGPUDevice.d.ts +62 -1
- package/dist/WebGPUDevice.d.ts.map +1 -1
- package/dist/WebGPUDevice.js +844 -51
- package/dist/WebGPUDevice.js.map +1 -1
- package/dist/WebGPUExtensionAtlas.d.ts +6 -0
- package/dist/WebGPUExtensionAtlas.d.ts.map +1 -0
- package/dist/WebGPUExtensionAtlas.js +225 -0
- package/dist/WebGPUExtensionAtlas.js.map +1 -0
- package/dist/WebGPUSkinningLimits.d.ts +40 -0
- package/dist/WebGPUSkinningLimits.d.ts.map +1 -1
- package/dist/WebGPUSkinningLimits.js +32 -0
- package/dist/WebGPUSkinningLimits.js.map +1 -1
- package/dist/effects/CollisionModule.d.ts +9 -0
- package/dist/effects/CollisionModule.d.ts.map +1 -1
- package/dist/effects/CollisionModule.js +10 -0
- package/dist/effects/CollisionModule.js.map +1 -1
- package/dist/effects/ColorModule.d.ts +7 -0
- package/dist/effects/ColorModule.d.ts.map +1 -1
- package/dist/effects/ColorModule.js +23 -0
- package/dist/effects/ColorModule.js.map +1 -1
- package/dist/effects/ForceModule.d.ts +35 -0
- package/dist/effects/ForceModule.d.ts.map +1 -1
- package/dist/effects/ForceModule.js +49 -0
- package/dist/effects/ForceModule.js.map +1 -1
- package/dist/effects/GPUParticleBackend.d.ts +188 -0
- package/dist/effects/GPUParticleBackend.d.ts.map +1 -1
- package/dist/effects/GPUParticleBackend.js +710 -1
- package/dist/effects/GPUParticleBackend.js.map +1 -1
- package/dist/effects/HeightfieldModule.d.ts +58 -0
- package/dist/effects/HeightfieldModule.d.ts.map +1 -0
- package/dist/effects/HeightfieldModule.js +129 -0
- package/dist/effects/HeightfieldModule.js.map +1 -0
- package/dist/effects/LightingModule.d.ts +34 -0
- package/dist/effects/LightingModule.d.ts.map +1 -0
- package/dist/effects/LightingModule.js +54 -0
- package/dist/effects/LightingModule.js.map +1 -0
- package/dist/effects/ParticleDiagnostics.d.ts +8 -0
- package/dist/effects/ParticleDiagnostics.d.ts.map +1 -1
- package/dist/effects/ParticleDiagnostics.js +3 -0
- package/dist/effects/ParticleDiagnostics.js.map +1 -1
- package/dist/effects/ParticleModule.d.ts +8 -0
- package/dist/effects/ParticleModule.d.ts.map +1 -1
- package/dist/effects/ParticleModule.js.map +1 -1
- package/dist/effects/ParticleRenderer.d.ts +20 -0
- package/dist/effects/ParticleRenderer.d.ts.map +1 -1
- package/dist/effects/ParticleRenderer.js +57 -7
- package/dist/effects/ParticleRenderer.js.map +1 -1
- package/dist/effects/ParticleSystem.d.ts +45 -1
- package/dist/effects/ParticleSystem.d.ts.map +1 -1
- package/dist/effects/ParticleSystem.js +373 -4
- package/dist/effects/ParticleSystem.js.map +1 -1
- package/dist/effects/ResidentGPUParticleRenderer.d.ts +158 -0
- package/dist/effects/ResidentGPUParticleRenderer.d.ts.map +1 -0
- package/dist/effects/ResidentGPUParticleRenderer.js +576 -0
- package/dist/effects/ResidentGPUParticleRenderer.js.map +1 -0
- package/dist/effects/SizeModule.d.ts +4 -0
- package/dist/effects/SizeModule.d.ts.map +1 -1
- package/dist/effects/SizeModule.js +16 -0
- package/dist/effects/SizeModule.js.map +1 -1
- package/dist/effects/SubEmitterModule.d.ts +61 -0
- package/dist/effects/SubEmitterModule.d.ts.map +1 -0
- package/dist/effects/SubEmitterModule.js +119 -0
- package/dist/effects/SubEmitterModule.js.map +1 -0
- package/dist/effects/TrailModule.d.ts +38 -0
- package/dist/effects/TrailModule.d.ts.map +1 -1
- package/dist/effects/TrailModule.js +95 -2
- package/dist/effects/TrailModule.js.map +1 -1
- package/dist/effects/TurbulenceModule.d.ts +43 -0
- package/dist/effects/TurbulenceModule.d.ts.map +1 -0
- package/dist/effects/TurbulenceModule.js +130 -0
- package/dist/effects/TurbulenceModule.js.map +1 -0
- package/dist/effects/index.d.ts +4 -0
- package/dist/effects/index.d.ts.map +1 -1
- package/dist/effects/index.js +4 -0
- package/dist/effects/index.js.map +1 -1
- package/dist/extension-scalar-atlas.d.ts +3 -0
- package/dist/extension-scalar-atlas.d.ts.map +1 -0
- package/dist/extension-scalar-atlas.js +2 -0
- package/dist/extension-scalar-atlas.js.map +1 -0
- package/dist/index.d.ts +58 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -10
- package/dist/index.js.map +1 -1
- package/dist/lean/LeanProductionRenderer.d.ts.map +1 -1
- package/dist/lean/LeanProductionRenderer.js +2 -2
- package/dist/lean/LeanProductionRenderer.js.map +1 -1
- package/dist/lean-core-runtime.d.ts +13 -0
- package/dist/lean-core-runtime.d.ts.map +1 -0
- package/dist/lean-core-runtime.js +10 -0
- package/dist/lean-core-runtime.js.map +1 -0
- package/dist/materials/MaterialExtensions.js +3 -3
- package/dist/materials/MaterialExtensions.js.map +1 -1
- package/dist/postprocess/NativeBloomPyramid.d.ts +44 -0
- package/dist/postprocess/NativeBloomPyramid.d.ts.map +1 -0
- package/dist/postprocess/NativeBloomPyramid.js +84 -0
- package/dist/postprocess/NativeBloomPyramid.js.map +1 -0
- package/dist/postprocess/NativeLdrEffectLuts.d.ts +10 -1
- package/dist/postprocess/NativeLdrEffectLuts.d.ts.map +1 -1
- package/dist/postprocess/NativeLdrEffectLuts.js +19 -2
- package/dist/postprocess/NativeLdrEffectLuts.js.map +1 -1
- package/dist/postprocess/index.d.ts +2 -0
- package/dist/postprocess/index.d.ts.map +1 -1
- package/dist/postprocess/index.js +1 -0
- package/dist/postprocess/index.js.map +1 -1
- package/dist/production-runtime/PBRHDRPipeline.d.ts +22 -0
- package/dist/production-runtime/PBRHDRPipeline.d.ts.map +1 -1
- package/dist/production-runtime/PBRHDRPipeline.js +125 -0
- package/dist/production-runtime/PBRHDRPipeline.js.map +1 -1
- package/dist/production-runtime/ProductionRendererTypes.d.ts +1 -0
- package/dist/production-runtime/ProductionRendererTypes.d.ts.map +1 -1
- package/dist/production-runtime/ProductionRendererTypes.js.map +1 -1
- package/dist/production-runtime/ProductionRuntimeRenderer.d.ts +3 -0
- package/dist/production-runtime/ProductionRuntimeRenderer.d.ts.map +1 -1
- package/dist/production-runtime/ProductionRuntimeRenderer.js +8 -1
- package/dist/production-runtime/ProductionRuntimeRenderer.js.map +1 -1
- package/dist/production-runtime/ProductionWebGL2Renderer.d.ts +4 -0
- package/dist/production-runtime/ProductionWebGL2Renderer.d.ts.map +1 -1
- package/dist/production-runtime/ProductionWebGL2Renderer.js +15 -0
- package/dist/production-runtime/ProductionWebGL2Renderer.js.map +1 -1
- package/dist/production-runtime/ProductionWebGPURenderer.d.ts +58 -0
- package/dist/production-runtime/ProductionWebGPURenderer.d.ts.map +1 -1
- package/dist/production-runtime/ProductionWebGPURenderer.js +127 -0
- package/dist/production-runtime/ProductionWebGPURenderer.js.map +1 -1
- package/dist/production-runtime/framegraph/FrameGraph.d.ts +4 -2
- package/dist/production-runtime/framegraph/FrameGraph.d.ts.map +1 -1
- package/dist/production-runtime/framegraph/FrameGraph.js +62 -4
- package/dist/production-runtime/framegraph/FrameGraph.js.map +1 -1
- package/dist/production-runtime/framegraph/NativeFrameGraphBindings.d.ts +32 -0
- package/dist/production-runtime/framegraph/NativeFrameGraphBindings.d.ts.map +1 -0
- package/dist/production-runtime/framegraph/NativeFrameGraphBindings.js +111 -0
- package/dist/production-runtime/framegraph/NativeFrameGraphBindings.js.map +1 -0
- package/dist/production-runtime/framegraph/RenderPass.d.ts +23 -0
- package/dist/production-runtime/framegraph/RenderPass.d.ts.map +1 -1
- package/dist/production-runtime/framegraph/RenderPass.js +87 -1
- package/dist/production-runtime/framegraph/RenderPass.js.map +1 -1
- package/dist/production-runtime/index.d.ts +7 -4
- package/dist/production-runtime/index.d.ts.map +1 -1
- package/dist/production-runtime/index.js +4 -2
- package/dist/production-runtime/index.js.map +1 -1
- package/dist/production-runtime/passes/DepthPrepass.d.ts +18 -1
- package/dist/production-runtime/passes/DepthPrepass.d.ts.map +1 -1
- package/dist/production-runtime/passes/DepthPrepass.js +41 -2
- package/dist/production-runtime/passes/DepthPrepass.js.map +1 -1
- package/dist/production-runtime/passes/FramegraphTopology.d.ts +13 -0
- package/dist/production-runtime/passes/FramegraphTopology.d.ts.map +1 -0
- package/dist/production-runtime/passes/FramegraphTopology.js +59 -0
- package/dist/production-runtime/passes/FramegraphTopology.js.map +1 -0
- package/dist/production-runtime/passes/OpaquePass.d.ts +19 -1
- package/dist/production-runtime/passes/OpaquePass.d.ts.map +1 -1
- package/dist/production-runtime/passes/OpaquePass.js +43 -2
- package/dist/production-runtime/passes/OpaquePass.js.map +1 -1
- package/dist/production-runtime/passes/ShadowPass.d.ts +19 -1
- package/dist/production-runtime/passes/ShadowPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/ShadowPass.js +45 -2
- package/dist/production-runtime/passes/ShadowPass.js.map +1 -1
- package/dist/production-runtime/passes/SkyboxPass.d.ts +17 -1
- package/dist/production-runtime/passes/SkyboxPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/SkyboxPass.js +41 -2
- package/dist/production-runtime/passes/SkyboxPass.js.map +1 -1
- package/dist/production-runtime/passes/ToneMappingPass.d.ts +26 -1
- package/dist/production-runtime/passes/ToneMappingPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/ToneMappingPass.js +62 -2
- package/dist/production-runtime/passes/ToneMappingPass.js.map +1 -1
- package/dist/production-runtime/passes/TransparentPass.d.ts +19 -1
- package/dist/production-runtime/passes/TransparentPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/TransparentPass.js +47 -2
- package/dist/production-runtime/passes/TransparentPass.js.map +1 -1
- package/dist/reflection-surfaces.d.ts +5 -0
- package/dist/reflection-surfaces.d.ts.map +1 -0
- package/dist/reflection-surfaces.js +3 -0
- package/dist/reflection-surfaces.js.map +1 -0
- package/dist/shadows/CascadeHysteresis.d.ts +67 -0
- package/dist/shadows/CascadeHysteresis.d.ts.map +1 -0
- package/dist/shadows/CascadeHysteresis.js +104 -0
- package/dist/shadows/CascadeHysteresis.js.map +1 -0
- package/dist/shadows/ContactShadows.d.ts +52 -0
- package/dist/shadows/ContactShadows.d.ts.map +1 -1
- package/dist/shadows/ContactShadows.js +122 -0
- package/dist/shadows/ContactShadows.js.map +1 -1
- package/dist/shadows/SpotShadowMaps.d.ts +77 -0
- package/dist/shadows/SpotShadowMaps.d.ts.map +1 -0
- package/dist/shadows/SpotShadowMaps.js +106 -0
- package/dist/shadows/SpotShadowMaps.js.map +1 -0
- package/dist/webgpu/WebGPUPostProcess.d.ts +2 -0
- package/dist/webgpu/WebGPUPostProcess.d.ts.map +1 -1
- package/dist/webgpu/WebGPUPostProcess.js +1 -0
- package/dist/webgpu/WebGPUPostProcess.js.map +1 -1
- package/dist/webgpu/WebGPUPostShaders.d.ts +77 -0
- package/dist/webgpu/WebGPUPostShaders.d.ts.map +1 -0
- package/dist/webgpu/WebGPUPostShaders.js +261 -0
- package/dist/webgpu/WebGPUPostShaders.js.map +1 -0
- package/dist/webgpu/WebGPUTemporal.d.ts +17 -0
- package/dist/webgpu/WebGPUTemporal.d.ts.map +1 -0
- package/dist/webgpu/WebGPUTemporal.js +64 -0
- package/dist/webgpu/WebGPUTemporal.js.map +1 -0
- package/dist/webgpu.d.ts +6 -0
- package/dist/webgpu.d.ts.map +1 -1
- package/dist/webgpu.js +4 -0
- package/dist/webgpu.js.map +1 -1
- package/package.json +15 -3
package/dist/WebGL2Device.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { temporalAccumulationWeight } from "./TemporalMath.js";
|
|
2
|
+
import { invertSsrProjection } from "./ProjectionMath.js";
|
|
3
|
+
import { RenderDeviceError, resolveGpuTargetOwner, spreadGpuTargetInventory, } from "./RenderDevice.js";
|
|
2
4
|
import { Texture, isCompressedTextureFormat, isFloatColorTextureFormat } from "./Texture.js";
|
|
3
5
|
import { isTextureBinding, TextureBinding } from "./TextureBinding.js";
|
|
4
6
|
import {} from "./VertexFormat.js";
|
|
5
7
|
import { WebGL2StateCache } from "./WebGL2StateCache.js";
|
|
6
8
|
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";
|
|
9
|
+
import { normalizeBloomQualityPreset, resolveBloomPyramidBlurRadii, resolveBloomPyramidPlan, resolveBloomPyramidResponseGain, } from "./postprocess/NativeBloomPyramid.js";
|
|
7
10
|
const WEBGL_CUBE_FACES = ["px", "nx", "py", "ny", "pz", "nz"];
|
|
8
11
|
class WebGL2Buffer {
|
|
9
12
|
id;
|
|
@@ -111,6 +114,8 @@ export class WebGL2Device {
|
|
|
111
114
|
gl;
|
|
112
115
|
nextId = 1;
|
|
113
116
|
drawCalls = 0;
|
|
117
|
+
nativeTemporalPasses = 0;
|
|
118
|
+
nativeTemporalBindings = 0;
|
|
114
119
|
buffers = new Set();
|
|
115
120
|
shaders = new Set();
|
|
116
121
|
renderTargets = new Set();
|
|
@@ -125,19 +130,27 @@ export class WebGL2Device {
|
|
|
125
130
|
bloomBrightExtractProgram = null;
|
|
126
131
|
bloomBlurProgram = null;
|
|
127
132
|
bloomCompositeProgram = null;
|
|
133
|
+
bloomDownsampleProgram = null;
|
|
134
|
+
bloomAccumulateProgram = null;
|
|
135
|
+
bloomPyramidResources = null;
|
|
128
136
|
outlineProgram = null;
|
|
129
137
|
ssaoProgram = null;
|
|
130
138
|
ssrProgram = null;
|
|
131
139
|
depthOfFieldProgram = null;
|
|
132
140
|
motionBlurProgram = null;
|
|
133
141
|
motionBlurVelocityTexture = null;
|
|
142
|
+
motionBlurVelocitySize = null;
|
|
134
143
|
taaProgram = null;
|
|
144
|
+
taaPresentationProgram = null;
|
|
135
145
|
taaHistoryTexture = null;
|
|
146
|
+
taaHistorySize = null;
|
|
136
147
|
bloomPingPongResources = null;
|
|
137
148
|
bloomBrightLutTexture = null;
|
|
138
149
|
bloomBrightLutThreshold = null;
|
|
139
150
|
bloomCompositeLutTexture = null;
|
|
140
151
|
bloomCompositeLutIntensity = null;
|
|
152
|
+
bloomCompositeLutShoulder = null;
|
|
153
|
+
lastBloomDiagnostics = null;
|
|
141
154
|
outlineBlendLutTexture = null;
|
|
142
155
|
outlineBlendLutKey = null;
|
|
143
156
|
depthReadbackProgram = null;
|
|
@@ -508,7 +521,9 @@ export class WebGL2Device {
|
|
|
508
521
|
this.renderTargets.add(target);
|
|
509
522
|
// Shadow passes label their depth target after the shadow map texture, so the
|
|
510
523
|
// label is the device-side signal that a shadow depth target was allocated.
|
|
511
|
-
|
|
524
|
+
// Cascaded maps use the established renderer-csm-cascade-* labels.
|
|
525
|
+
// Count their actual allocation too, including the first shadow frame.
|
|
526
|
+
if (/shadow|(?:^|-)csm(?:-|$)/i.test(descriptor.label ?? ""))
|
|
512
527
|
this.shadowRenderTargetsAllocated += 1;
|
|
513
528
|
this.textures.set(target.colorTexture, colorHandle);
|
|
514
529
|
this.textureUploadModes.set(target.colorTexture, "rgba8");
|
|
@@ -610,6 +625,29 @@ export class WebGL2Device {
|
|
|
610
625
|
});
|
|
611
626
|
}
|
|
612
627
|
}
|
|
628
|
+
/** Executes the same native SSR kernel used by postprocessing into an owned target. */
|
|
629
|
+
executeReflectionSurfaceSsr(source, normalMask, output, options) {
|
|
630
|
+
this.assertAlive();
|
|
631
|
+
for (const target of [source, normalMask, output]) {
|
|
632
|
+
if (!(target instanceof WebGL2RenderTarget) || !this.renderTargets.has(target) || target.disposed) {
|
|
633
|
+
throw new RenderDeviceError("SSR inputs must be live targets owned by this device", "INVALID_RESOURCE");
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const color = source, normals = normalMask, destination = output;
|
|
637
|
+
if (!color.depthTextureHandle || color === destination || normals === destination
|
|
638
|
+
|| color.width !== normals.width || color.height !== normals.height) {
|
|
639
|
+
throw new RenderDeviceError("SSR requires sampleable scene depth, matching normal-mask input, and a separate output", "INVALID_RESOURCE");
|
|
640
|
+
}
|
|
641
|
+
this.resolveMultisampleTarget(color);
|
|
642
|
+
this.resolveMultisampleTarget(normals);
|
|
643
|
+
const state = this.prepareFullscreenPresentation(destination.framebuffer, destination.width, destination.height);
|
|
644
|
+
try {
|
|
645
|
+
this.drawSsrKernel(color.colorHandle, color.depthTextureHandle, destination.width, destination.height, { ...options, normalMask: normals.colorHandle }, this.ensurePresentationVertexArray(), { near: 0.1, far: 1000 });
|
|
646
|
+
}
|
|
647
|
+
finally {
|
|
648
|
+
this.restoreFullscreenPresentationState(state, destination.width, destination.height);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
613
651
|
presentLdrPostprocess(source, options) {
|
|
614
652
|
this.assertAlive();
|
|
615
653
|
if (!(source instanceof WebGL2RenderTarget) || !this.renderTargets.has(source) || source.disposed) {
|
|
@@ -645,6 +683,10 @@ export class WebGL2Device {
|
|
|
645
683
|
pass: depthOfFieldPass ? "depth-of-field" : ssaoPass ? "ssao" : "ssr"
|
|
646
684
|
});
|
|
647
685
|
}
|
|
686
|
+
// Raw GL depth is nonlinear (0.1/1000 defaults park the play area past
|
|
687
|
+
// 0.97), which blinded every depth-gated native pass. SSR and DOF compare
|
|
688
|
+
// linearized depth; SSAO keeps its legacy response (documented partial).
|
|
689
|
+
const depthRange = normalizeLdrDepthRange(options.depthRange);
|
|
648
690
|
const outputTarget = options.outputTarget;
|
|
649
691
|
if (outputTarget && (!(outputTarget instanceof WebGL2RenderTarget) || !this.renderTargets.has(outputTarget) || outputTarget.disposed)) {
|
|
650
692
|
throw new RenderDeviceError("Output render target is not a live WebGL2 resource owned by this device", "INVALID_RESOURCE", {
|
|
@@ -689,10 +731,10 @@ export class WebGL2Device {
|
|
|
689
731
|
if (outlineOptions) {
|
|
690
732
|
this.ensureOutlineBlendLutTexture(outlineOptions);
|
|
691
733
|
}
|
|
692
|
-
if (motionBlurOptions) {
|
|
734
|
+
if (motionBlurOptions?.velocity) {
|
|
693
735
|
this.ensureMotionBlurVelocityTexture(source.width, source.height, motionBlurOptions.velocity);
|
|
694
736
|
}
|
|
695
|
-
if (taaOptions) {
|
|
737
|
+
if (taaOptions?.history) {
|
|
696
738
|
this.ensureTaaHistoryTexture(source.width, source.height, taaOptions.history);
|
|
697
739
|
}
|
|
698
740
|
const outputWidth = webglOutputTarget?.width ?? (this.viewportWidth || this.gl.drawingBufferWidth);
|
|
@@ -702,8 +744,43 @@ export class WebGL2Device {
|
|
|
702
744
|
let ldrSourceHandle = source.colorHandle;
|
|
703
745
|
let temporarySourceIndex = -1;
|
|
704
746
|
if (bloomOptions && bloomResources) {
|
|
705
|
-
|
|
706
|
-
|
|
747
|
+
if (bloomOptions.quality === "performance") {
|
|
748
|
+
ldrSourceHandle = this.executeNativeBloomPasses(ldrSourceHandle, bloomResources, bloomOptions, vertexArray, sourceIsHdr);
|
|
749
|
+
temporarySourceIndex = 1;
|
|
750
|
+
this.recordBloomDiagnostics(bloomOptions, source.width, source.height, sourceIsHdr, 1);
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
const plan = resolveBloomPyramidPlan(source.width, source.height, bloomOptions.quality, sourceIsHdr);
|
|
754
|
+
const pyramid = this.ensureBloomPyramidResources(plan, sourceIsHdr);
|
|
755
|
+
const pyramidResult = this.executeNativeBloomPyramidPasses(ldrSourceHandle, source.width, source.height, pyramid, {
|
|
756
|
+
textureA: bloomResources.textures[0],
|
|
757
|
+
framebufferA: bloomResources.framebuffers[0],
|
|
758
|
+
textureB: bloomResources.textures[1],
|
|
759
|
+
framebufferB: bloomResources.framebuffers[1]
|
|
760
|
+
}, bloomOptions, vertexArray, sourceIsHdr);
|
|
761
|
+
// The pyramid sum lands in ping-pong texture A; composite it with the
|
|
762
|
+
// source through the proven single-scale composite program.
|
|
763
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, bloomResources.framebuffers[1]);
|
|
764
|
+
this.gl.viewport(0, 0, source.width, source.height);
|
|
765
|
+
const pyramidComposite = this.ensureBloomCompositeProgram();
|
|
766
|
+
this.gl.useProgram(pyramidComposite);
|
|
767
|
+
this.gl.bindVertexArray(vertexArray);
|
|
768
|
+
this.bindFullscreenTexture(0, ldrSourceHandle);
|
|
769
|
+
this.bindFullscreenTexture(1, pyramidResult);
|
|
770
|
+
if (this.bloomCompositeLutTexture)
|
|
771
|
+
this.bindFullscreenTexture(2, this.bloomCompositeLutTexture);
|
|
772
|
+
this.gl.uniform1i(this.gl.getUniformLocation(pyramidComposite, "u_source"), 0);
|
|
773
|
+
this.gl.uniform1i(this.gl.getUniformLocation(pyramidComposite, "u_blurred"), 1);
|
|
774
|
+
this.gl.uniform1i(this.gl.getUniformLocation(pyramidComposite, "u_compositeLut"), 2);
|
|
775
|
+
this.gl.uniform1i(this.gl.getUniformLocation(pyramidComposite, "u_hdr"), sourceIsHdr ? 1 : 0);
|
|
776
|
+
this.gl.uniform1f(this.gl.getUniformLocation(pyramidComposite, "u_intensity"), bloomOptions.intensity * resolveBloomPyramidResponseGain(plan));
|
|
777
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
778
|
+
ldrSourceHandle = bloomResources.textures[1];
|
|
779
|
+
// The composited result lives in texture B, so subsequent passes
|
|
780
|
+
// must write texture A (index 0) to avoid sampling a bound target.
|
|
781
|
+
temporarySourceIndex = 0;
|
|
782
|
+
this.recordBloomDiagnostics(bloomOptions, source.width, source.height, sourceIsHdr, plan.mipCount);
|
|
783
|
+
}
|
|
707
784
|
}
|
|
708
785
|
if ((depthOfFieldOptions || motionBlurOptions || ssaoOptions || ssrOptions || taaOptions || outlineOptions) && bloomResources) {
|
|
709
786
|
if (tonePass || colorPass) {
|
|
@@ -714,7 +791,7 @@ export class WebGL2Device {
|
|
|
714
791
|
}
|
|
715
792
|
if (depthOfFieldOptions && source.depthTextureHandle) {
|
|
716
793
|
const depthOfFieldTargetIndex = temporarySourceIndex === 0 ? 1 : 0;
|
|
717
|
-
ldrSourceHandle = this.executeNativeDepthOfFieldPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, depthOfFieldOptions, vertexArray, depthOfFieldTargetIndex);
|
|
794
|
+
ldrSourceHandle = this.executeNativeDepthOfFieldPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, depthOfFieldOptions, vertexArray, depthOfFieldTargetIndex, depthRange);
|
|
718
795
|
temporarySourceIndex = depthOfFieldTargetIndex;
|
|
719
796
|
}
|
|
720
797
|
if (motionBlurOptions) {
|
|
@@ -729,7 +806,7 @@ export class WebGL2Device {
|
|
|
729
806
|
}
|
|
730
807
|
if (ssrOptions && source.depthTextureHandle) {
|
|
731
808
|
const ssrTargetIndex = temporarySourceIndex === 0 ? 1 : 0;
|
|
732
|
-
ldrSourceHandle = this.executeNativeSsrPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, ssrOptions, vertexArray, ssrTargetIndex);
|
|
809
|
+
ldrSourceHandle = this.executeNativeSsrPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, ssrOptions, vertexArray, ssrTargetIndex, depthRange);
|
|
733
810
|
temporarySourceIndex = ssrTargetIndex;
|
|
734
811
|
}
|
|
735
812
|
if (taaOptions) {
|
|
@@ -994,6 +1071,8 @@ export class WebGL2Device {
|
|
|
994
1071
|
["actualViewportHeight", viewport[3] ?? 0],
|
|
995
1072
|
["renderTarget", this.activeRenderTarget?.label ?? null],
|
|
996
1073
|
["drawCalls", this.drawCalls],
|
|
1074
|
+
["nativeTemporalPasses", this.nativeTemporalPasses],
|
|
1075
|
+
["nativeTemporalBindings", this.nativeTemporalBindings],
|
|
997
1076
|
["shaderProgramCreates", this.shaderProgramCreateCount],
|
|
998
1077
|
["uniformLocationLookups", this.uniformLocationLookupCount],
|
|
999
1078
|
["bufferUpdates", this.bufferUpdateCount],
|
|
@@ -1023,6 +1102,7 @@ export class WebGL2Device {
|
|
|
1023
1102
|
const stateCacheStats = this.stateCache.stats();
|
|
1024
1103
|
const bufferBytes = [...this.buffers].filter((buffer) => !buffer.disposed).reduce((total, buffer) => total + buffer.byteLength, 0);
|
|
1025
1104
|
const textureBytes = liveTextureList.reduce((total, texture) => total + texture.byteLength, 0);
|
|
1105
|
+
const gpuTargetEntries = this.describeGpuTargetInventory(liveRenderTargets);
|
|
1026
1106
|
return {
|
|
1027
1107
|
drawCalls: this.drawCalls,
|
|
1028
1108
|
buffers: [...this.buffers].filter((buffer) => !buffer.disposed).length,
|
|
@@ -1041,6 +1121,9 @@ export class WebGL2Device {
|
|
|
1041
1121
|
nativeShadowMapBindings: this.nativeShadowMapBindings,
|
|
1042
1122
|
shadowRenderTargetsAllocated: this.shadowRenderTargetsAllocated,
|
|
1043
1123
|
nativeInstancedSubmissions: this.nativeInstancedSubmissions,
|
|
1124
|
+
bloom: this.lastBloomDiagnostics,
|
|
1125
|
+
nativeTemporalPasses: this.nativeTemporalPasses,
|
|
1126
|
+
nativeTemporalBindings: this.nativeTemporalBindings,
|
|
1044
1127
|
samplerAnisotropyUploads: this.samplerAnisotropyUploadCount,
|
|
1045
1128
|
maxTextureAnisotropy: this.maxTextureAnisotropy,
|
|
1046
1129
|
stateCacheIssued: stateCacheStats.issued,
|
|
@@ -1054,6 +1137,7 @@ export class WebGL2Device {
|
|
|
1054
1137
|
disposedShaders: [...this.shaders].filter((shader) => shader.disposed).length,
|
|
1055
1138
|
disposedRenderTargets: [...this.renderTargets].filter((target) => target.disposed).length,
|
|
1056
1139
|
disposedTextures: [...this.renderTargets].filter((target) => target.colorTexture.disposed).length + this.releasedTextureHandles,
|
|
1140
|
+
...spreadGpuTargetInventory(gpuTargetEntries),
|
|
1057
1141
|
lastError: this.lastError,
|
|
1058
1142
|
contextLost: this.contextLost
|
|
1059
1143
|
};
|
|
@@ -1116,6 +1200,14 @@ export class WebGL2Device {
|
|
|
1116
1200
|
this.gl.deleteProgram(this.bloomCompositeProgram);
|
|
1117
1201
|
this.bloomCompositeProgram = null;
|
|
1118
1202
|
}
|
|
1203
|
+
if (this.bloomDownsampleProgram) {
|
|
1204
|
+
this.gl.deleteProgram(this.bloomDownsampleProgram);
|
|
1205
|
+
this.bloomDownsampleProgram = null;
|
|
1206
|
+
}
|
|
1207
|
+
if (this.bloomAccumulateProgram) {
|
|
1208
|
+
this.gl.deleteProgram(this.bloomAccumulateProgram);
|
|
1209
|
+
this.bloomAccumulateProgram = null;
|
|
1210
|
+
}
|
|
1119
1211
|
if (this.outlineProgram) {
|
|
1120
1212
|
this.gl.deleteProgram(this.outlineProgram);
|
|
1121
1213
|
this.outlineProgram = null;
|
|
@@ -1139,16 +1231,23 @@ export class WebGL2Device {
|
|
|
1139
1231
|
if (this.motionBlurVelocityTexture) {
|
|
1140
1232
|
this.gl.deleteTexture(this.motionBlurVelocityTexture);
|
|
1141
1233
|
this.motionBlurVelocityTexture = null;
|
|
1234
|
+
this.motionBlurVelocitySize = null;
|
|
1142
1235
|
}
|
|
1143
1236
|
if (this.taaProgram) {
|
|
1144
1237
|
this.gl.deleteProgram(this.taaProgram);
|
|
1145
1238
|
this.taaProgram = null;
|
|
1146
1239
|
}
|
|
1240
|
+
if (this.taaPresentationProgram) {
|
|
1241
|
+
this.gl.deleteProgram(this.taaPresentationProgram);
|
|
1242
|
+
this.taaPresentationProgram = null;
|
|
1243
|
+
}
|
|
1147
1244
|
if (this.taaHistoryTexture) {
|
|
1148
1245
|
this.gl.deleteTexture(this.taaHistoryTexture);
|
|
1149
1246
|
this.taaHistoryTexture = null;
|
|
1247
|
+
this.taaHistorySize = null;
|
|
1150
1248
|
}
|
|
1151
1249
|
this.disposeBloomPingPongResources();
|
|
1250
|
+
this.disposeBloomPyramidResources();
|
|
1152
1251
|
if (this.bloomBrightLutTexture) {
|
|
1153
1252
|
this.gl.deleteTexture(this.bloomBrightLutTexture);
|
|
1154
1253
|
this.bloomBrightLutTexture = null;
|
|
@@ -1158,6 +1257,7 @@ export class WebGL2Device {
|
|
|
1158
1257
|
this.gl.deleteTexture(this.bloomCompositeLutTexture);
|
|
1159
1258
|
this.bloomCompositeLutTexture = null;
|
|
1160
1259
|
this.bloomCompositeLutIntensity = null;
|
|
1260
|
+
this.bloomCompositeLutShoulder = null;
|
|
1161
1261
|
}
|
|
1162
1262
|
if (this.outlineBlendLutTexture) {
|
|
1163
1263
|
this.gl.deleteTexture(this.outlineBlendLutTexture);
|
|
@@ -1283,6 +1383,7 @@ export class WebGL2Device {
|
|
|
1283
1383
|
return current;
|
|
1284
1384
|
}
|
|
1285
1385
|
this.disposeBloomPingPongResources();
|
|
1386
|
+
this.disposeBloomPyramidResources();
|
|
1286
1387
|
const textureUnit0 = this.captureTextureUnit0();
|
|
1287
1388
|
const previousFramebuffer = this.gl.getParameter(this.gl.FRAMEBUFFER_BINDING);
|
|
1288
1389
|
const textures = [];
|
|
@@ -1347,7 +1448,8 @@ export class WebGL2Device {
|
|
|
1347
1448
|
}
|
|
1348
1449
|
ensureBloomLutTextures(options) {
|
|
1349
1450
|
const updateBright = this.bloomBrightLutThreshold !== options.threshold;
|
|
1350
|
-
const updateComposite = this.bloomCompositeLutIntensity !== options.intensity
|
|
1451
|
+
const updateComposite = this.bloomCompositeLutIntensity !== options.intensity ||
|
|
1452
|
+
this.bloomCompositeLutShoulder !== options.shoulder;
|
|
1351
1453
|
if (this.bloomBrightLutTexture && this.bloomCompositeLutTexture && !updateBright && !updateComposite) {
|
|
1352
1454
|
return;
|
|
1353
1455
|
}
|
|
@@ -1366,10 +1468,11 @@ export class WebGL2Device {
|
|
|
1366
1468
|
this.bloomCompositeLutTexture = this.createNativeBloomDataTexture();
|
|
1367
1469
|
}
|
|
1368
1470
|
if (updateComposite) {
|
|
1369
|
-
const pixels = createBloomCompositeLut(options.intensity);
|
|
1471
|
+
const pixels = createBloomCompositeLut(options.intensity, { shoulder: options.shoulder });
|
|
1370
1472
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.bloomCompositeLutTexture);
|
|
1371
1473
|
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);
|
|
1372
1474
|
this.bloomCompositeLutIntensity = options.intensity;
|
|
1475
|
+
this.bloomCompositeLutShoulder = options.shoulder;
|
|
1373
1476
|
}
|
|
1374
1477
|
}
|
|
1375
1478
|
finally {
|
|
@@ -1414,6 +1517,7 @@ export class WebGL2Device {
|
|
|
1414
1517
|
if (!this.motionBlurVelocityTexture) {
|
|
1415
1518
|
this.motionBlurVelocityTexture = this.createNativeBloomDataTexture();
|
|
1416
1519
|
}
|
|
1520
|
+
this.motionBlurVelocitySize = { width, height };
|
|
1417
1521
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.motionBlurVelocityTexture);
|
|
1418
1522
|
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RG32F, width, height, 0, this.gl.RG, this.gl.FLOAT, velocity);
|
|
1419
1523
|
}
|
|
@@ -1428,6 +1532,7 @@ export class WebGL2Device {
|
|
|
1428
1532
|
if (!this.taaHistoryTexture) {
|
|
1429
1533
|
this.taaHistoryTexture = this.createNativeBloomDataTexture();
|
|
1430
1534
|
}
|
|
1535
|
+
this.taaHistorySize = { width, height };
|
|
1431
1536
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.taaHistoryTexture);
|
|
1432
1537
|
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA8, width, height, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, history);
|
|
1433
1538
|
}
|
|
@@ -1487,6 +1592,7 @@ export class WebGL2Device {
|
|
|
1487
1592
|
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_brightLut"), 1);
|
|
1488
1593
|
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_hdr"), hdr ? 1 : 0);
|
|
1489
1594
|
this.gl.uniform1f(this.gl.getUniformLocation(brightProgram, "u_threshold"), options.threshold);
|
|
1595
|
+
this.gl.uniform1f(this.gl.getUniformLocation(brightProgram, "u_softKnee"), options.softKnee);
|
|
1490
1596
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1491
1597
|
const blurProgram = this.ensureBloomBlurProgram();
|
|
1492
1598
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebufferB);
|
|
@@ -1517,6 +1623,158 @@ export class WebGL2Device {
|
|
|
1517
1623
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1518
1624
|
return textureB;
|
|
1519
1625
|
}
|
|
1626
|
+
/**
|
|
1627
|
+
* Balanced/cinematic bloom: bright-extract once, then a half-resolution mip
|
|
1628
|
+
* chain where every level is downsampled, separably blurred, and accumulated
|
|
1629
|
+
* with its energy-preserving weight. Returns a full-resolution bloom texture
|
|
1630
|
+
* holding the weighted sum, ready for the existing composite program.
|
|
1631
|
+
*/
|
|
1632
|
+
executeNativeBloomPyramidPasses(sourceTexture, sourceWidth, sourceHeight, resources, fullResStaging, options, vertexArray, hdr) {
|
|
1633
|
+
const plan = resources.plan;
|
|
1634
|
+
const mipCount = plan.mips.length;
|
|
1635
|
+
const blurRadii = resolveBloomPyramidBlurRadii(plan, options.radius);
|
|
1636
|
+
const downsampleProgram = this.ensureBloomDownsampleProgram();
|
|
1637
|
+
const brightProgram = this.ensureBloomBrightExtractProgram();
|
|
1638
|
+
const blurProgram = this.ensureBloomBlurProgram();
|
|
1639
|
+
const accumulateProgram = this.ensureBloomAccumulateProgram();
|
|
1640
|
+
const brightLut = this.bloomBrightLutTexture;
|
|
1641
|
+
const mipTexture = (level, slot) => resources.textures[level * 2 + slot];
|
|
1642
|
+
const mipFramebuffer = (level, slot) => resources.framebuffers[level * 2 + slot];
|
|
1643
|
+
const mipSize = (level) => plan.mips[level];
|
|
1644
|
+
// Every level settles in slot 0 after the vertical blur.
|
|
1645
|
+
const blurredSlot = (_level) => 0;
|
|
1646
|
+
const drawDownsample = (fromTexture, fromWidth, fromHeight, toFramebuffer, toWidth, toHeight) => {
|
|
1647
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, toFramebuffer);
|
|
1648
|
+
this.gl.viewport(0, 0, toWidth, toHeight);
|
|
1649
|
+
this.gl.useProgram(downsampleProgram);
|
|
1650
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1651
|
+
this.bindFullscreenTexture(0, fromTexture);
|
|
1652
|
+
this.gl.uniform1i(this.gl.getUniformLocation(downsampleProgram, "u_source"), 0);
|
|
1653
|
+
this.gl.uniform2f(this.gl.getUniformLocation(downsampleProgram, "u_texelSize"), 1 / fromWidth, 1 / fromHeight);
|
|
1654
|
+
this.gl.uniform2f(this.gl.getUniformLocation(downsampleProgram, "u_targetSize"), toWidth, toHeight);
|
|
1655
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1656
|
+
};
|
|
1657
|
+
const drawBrightExtract = (fromTexture, toFramebuffer, size) => {
|
|
1658
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, toFramebuffer);
|
|
1659
|
+
this.gl.viewport(0, 0, size.width, size.height);
|
|
1660
|
+
this.gl.useProgram(brightProgram);
|
|
1661
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1662
|
+
this.bindFullscreenTexture(0, fromTexture);
|
|
1663
|
+
if (brightLut)
|
|
1664
|
+
this.bindFullscreenTexture(1, brightLut);
|
|
1665
|
+
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_source"), 0);
|
|
1666
|
+
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_brightLut"), 1);
|
|
1667
|
+
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_hdr"), hdr ? 1 : 0);
|
|
1668
|
+
this.gl.uniform1f(this.gl.getUniformLocation(brightProgram, "u_threshold"), options.threshold);
|
|
1669
|
+
this.gl.uniform1f(this.gl.getUniformLocation(brightProgram, "u_softKnee"), options.softKnee);
|
|
1670
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1671
|
+
};
|
|
1672
|
+
const drawSeparableBlur = (readTexture, tmpTexture, tmpFramebuffer, outFramebuffer, size, radius) => {
|
|
1673
|
+
// Horizontal into the temp target, vertical back into the output target.
|
|
1674
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, tmpFramebuffer);
|
|
1675
|
+
this.gl.viewport(0, 0, size.width, size.height);
|
|
1676
|
+
this.gl.useProgram(blurProgram);
|
|
1677
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1678
|
+
this.bindFullscreenTexture(0, readTexture);
|
|
1679
|
+
this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_source"), 0);
|
|
1680
|
+
this.gl.uniform2i(this.gl.getUniformLocation(blurProgram, "u_size"), size.width, size.height);
|
|
1681
|
+
this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_radius"), radius);
|
|
1682
|
+
this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_horizontal"), 1);
|
|
1683
|
+
this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_hdr"), hdr ? 1 : 0);
|
|
1684
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1685
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, outFramebuffer);
|
|
1686
|
+
this.bindFullscreenTexture(0, tmpTexture);
|
|
1687
|
+
this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_horizontal"), 0);
|
|
1688
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1689
|
+
};
|
|
1690
|
+
// Extract highlights at the source resolution before the first downsample.
|
|
1691
|
+
// Downsampling the complete scene first averaged narrow emissive sources
|
|
1692
|
+
// below the authored threshold, so balanced/cinematic bloom could execute
|
|
1693
|
+
// all native passes yet remain pixel-identical to disabled output.
|
|
1694
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, fullResStaging.framebufferA);
|
|
1695
|
+
this.gl.viewport(0, 0, sourceWidth, sourceHeight);
|
|
1696
|
+
this.gl.useProgram(brightProgram);
|
|
1697
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1698
|
+
this.bindFullscreenTexture(0, sourceTexture);
|
|
1699
|
+
if (brightLut)
|
|
1700
|
+
this.bindFullscreenTexture(1, brightLut);
|
|
1701
|
+
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_source"), 0);
|
|
1702
|
+
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_brightLut"), 1);
|
|
1703
|
+
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_hdr"), hdr ? 1 : 0);
|
|
1704
|
+
this.gl.uniform1f(this.gl.getUniformLocation(brightProgram, "u_threshold"), options.threshold);
|
|
1705
|
+
this.gl.uniform1f(this.gl.getUniformLocation(brightProgram, "u_softKnee"), options.softKnee);
|
|
1706
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1707
|
+
// Level 0 downsamples the extracted highlight field, then blurs H into
|
|
1708
|
+
// slot 1 and V back into slot 0. The result therefore settles in slot 0.
|
|
1709
|
+
const level0 = mipSize(0);
|
|
1710
|
+
drawDownsample(fullResStaging.textureA, sourceWidth, sourceHeight, mipFramebuffer(0, 0), level0.width, level0.height);
|
|
1711
|
+
drawSeparableBlur(mipTexture(0, 0), mipTexture(0, 1), mipFramebuffer(0, 1), mipFramebuffer(0, 0), level0, blurRadii[0]);
|
|
1712
|
+
for (let level = 1; level < mipCount; level += 1) {
|
|
1713
|
+
const size = mipSize(level);
|
|
1714
|
+
const previous = mipSize(level - 1);
|
|
1715
|
+
drawDownsample(mipTexture(level - 1, blurredSlot(level - 1)), previous.width, previous.height, mipFramebuffer(level, 0), size.width, size.height);
|
|
1716
|
+
// H into slot 1, V back into slot 0: blurred result settles in slot 0.
|
|
1717
|
+
drawSeparableBlur(mipTexture(level, 0), mipTexture(level, 1), mipFramebuffer(level, 1), mipFramebuffer(level, 0), size, blurRadii[level]);
|
|
1718
|
+
}
|
|
1719
|
+
// Accumulate smallest-to-largest, ping-ponging between the two
|
|
1720
|
+
// accumulators (a pass must never sample its own bound target).
|
|
1721
|
+
const weights = plan.weights;
|
|
1722
|
+
let readAccumulator = { texture: resources.accumulatorTextureA, framebuffer: resources.accumulatorFramebufferA };
|
|
1723
|
+
let writeAccumulator = { texture: resources.accumulatorTextureB, framebuffer: resources.accumulatorFramebufferB };
|
|
1724
|
+
// Clear both accumulators; the seed reads the cleared sibling, never the
|
|
1725
|
+
// bound target (feedback loop).
|
|
1726
|
+
for (const acc of [readAccumulator, writeAccumulator]) {
|
|
1727
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, acc.framebuffer);
|
|
1728
|
+
this.gl.viewport(0, 0, level0.width, level0.height);
|
|
1729
|
+
this.gl.clearColor(0, 0, 0, 0);
|
|
1730
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
1731
|
+
}
|
|
1732
|
+
// Seed: smallest mip onto the cleared sibling accumulator.
|
|
1733
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, writeAccumulator.framebuffer);
|
|
1734
|
+
this.gl.viewport(0, 0, level0.width, level0.height);
|
|
1735
|
+
this.gl.useProgram(accumulateProgram);
|
|
1736
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1737
|
+
this.bindFullscreenTexture(0, readAccumulator.texture);
|
|
1738
|
+
this.bindFullscreenTexture(1, mipTexture(mipCount - 1, blurredSlot(mipCount - 1)));
|
|
1739
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_base"), 0);
|
|
1740
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_bloom"), 1);
|
|
1741
|
+
this.gl.uniform1f(this.gl.getUniformLocation(accumulateProgram, "u_weight"), weights[mipCount - 1]);
|
|
1742
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1743
|
+
for (let level = mipCount - 2; level >= 0; level -= 1) {
|
|
1744
|
+
const nextRead = writeAccumulator;
|
|
1745
|
+
const nextWrite = readAccumulator;
|
|
1746
|
+
readAccumulator = nextRead;
|
|
1747
|
+
writeAccumulator = nextWrite;
|
|
1748
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, writeAccumulator.framebuffer);
|
|
1749
|
+
this.gl.viewport(0, 0, level0.width, level0.height);
|
|
1750
|
+
this.gl.useProgram(accumulateProgram);
|
|
1751
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1752
|
+
this.bindFullscreenTexture(0, readAccumulator.texture);
|
|
1753
|
+
this.bindFullscreenTexture(1, mipTexture(level, blurredSlot(level)));
|
|
1754
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_base"), 0);
|
|
1755
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_bloom"), 1);
|
|
1756
|
+
this.gl.uniform1f(this.gl.getUniformLocation(accumulateProgram, "u_weight"), weights[level]);
|
|
1757
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1758
|
+
}
|
|
1759
|
+
// Upsample the weighted sum onto full-resolution staging target A, using
|
|
1760
|
+
// the cleared staging target B as the zero base (never sample A while
|
|
1761
|
+
// rendering into it).
|
|
1762
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, fullResStaging.framebufferB);
|
|
1763
|
+
this.gl.viewport(0, 0, sourceWidth, sourceHeight);
|
|
1764
|
+
this.gl.clearColor(0, 0, 0, 0);
|
|
1765
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
1766
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, fullResStaging.framebufferA);
|
|
1767
|
+
this.gl.viewport(0, 0, sourceWidth, sourceHeight);
|
|
1768
|
+
this.gl.useProgram(accumulateProgram);
|
|
1769
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1770
|
+
this.bindFullscreenTexture(0, fullResStaging.textureB);
|
|
1771
|
+
this.bindFullscreenTexture(1, writeAccumulator.texture);
|
|
1772
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_base"), 0);
|
|
1773
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_bloom"), 1);
|
|
1774
|
+
this.gl.uniform1f(this.gl.getUniformLocation(accumulateProgram, "u_weight"), 1);
|
|
1775
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1776
|
+
return fullResStaging.textureA;
|
|
1777
|
+
}
|
|
1520
1778
|
executeNativeOutlinePasses(sourceTexture, resources, options, vertexArray, targetIndex) {
|
|
1521
1779
|
const blendLut = this.outlineBlendLutTexture;
|
|
1522
1780
|
if (!blendLut) {
|
|
@@ -1559,24 +1817,38 @@ export class WebGL2Device {
|
|
|
1559
1817
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1560
1818
|
return targetTexture;
|
|
1561
1819
|
}
|
|
1562
|
-
executeNativeSsrPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex) {
|
|
1563
|
-
const program = this.ensureSsrProgram();
|
|
1820
|
+
executeNativeSsrPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex, depthRange) {
|
|
1564
1821
|
const targetTexture = resources.textures[targetIndex];
|
|
1565
1822
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
|
|
1566
|
-
this.
|
|
1823
|
+
this.drawSsrKernel(sourceTexture, depthTexture, resources.width, resources.height, options, vertexArray, depthRange);
|
|
1824
|
+
return targetTexture;
|
|
1825
|
+
}
|
|
1826
|
+
drawSsrKernel(sourceTexture, depthTexture, width, height, options, vertexArray, depthRange) {
|
|
1827
|
+
const program = this.ensureSsrProgram();
|
|
1828
|
+
this.gl.viewport(0, 0, width, height);
|
|
1567
1829
|
this.gl.useProgram(program);
|
|
1568
1830
|
this.gl.bindVertexArray(vertexArray);
|
|
1569
1831
|
this.bindFullscreenTexture(0, sourceTexture);
|
|
1570
1832
|
this.bindFullscreenTexture(1, depthTexture);
|
|
1833
|
+
this.bindFullscreenTexture(2, options.normalMask ?? sourceTexture);
|
|
1571
1834
|
this.gl.uniform1i(this.gl.getUniformLocation(program, "u_source"), 0);
|
|
1572
1835
|
this.gl.uniform1i(this.gl.getUniformLocation(program, "u_depth"), 1);
|
|
1573
|
-
this.gl.
|
|
1836
|
+
this.gl.uniform1i(this.gl.getUniformLocation(program, "u_normalMask"), 2);
|
|
1837
|
+
this.gl.uniform1i(this.gl.getUniformLocation(program, "u_hasNormalMask"), options.normalMask ? 1 : 0);
|
|
1838
|
+
this.gl.uniform2i(this.gl.getUniformLocation(program, "u_size"), width, height);
|
|
1574
1839
|
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_intensity"), options.intensity);
|
|
1575
|
-
this.gl.
|
|
1840
|
+
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_maxDistance"), options.maxDistance);
|
|
1841
|
+
this.gl.uniform1i(this.gl.getUniformLocation(program, "u_maxSteps"), options.maxSteps ?? 32);
|
|
1842
|
+
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_thickness"), options.thickness ?? 0.2);
|
|
1843
|
+
if (options.projection && options.inverseProjection) {
|
|
1844
|
+
this.gl.uniformMatrix4fv(this.gl.getUniformLocation(program, "u_projection"), false, options.projection);
|
|
1845
|
+
this.gl.uniformMatrix4fv(this.gl.getUniformLocation(program, "u_inverseProjection"), false, options.inverseProjection);
|
|
1846
|
+
}
|
|
1847
|
+
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_depthNear"), depthRange.near);
|
|
1848
|
+
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_depthFar"), depthRange.far);
|
|
1576
1849
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1577
|
-
return targetTexture;
|
|
1578
1850
|
}
|
|
1579
|
-
executeNativeDepthOfFieldPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex) {
|
|
1851
|
+
executeNativeDepthOfFieldPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex, depthRange) {
|
|
1580
1852
|
const program = this.ensureDepthOfFieldProgram();
|
|
1581
1853
|
const targetTexture = resources.textures[targetIndex];
|
|
1582
1854
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
|
|
@@ -1591,11 +1863,13 @@ export class WebGL2Device {
|
|
|
1591
1863
|
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_focusDepth"), options.focusDepth);
|
|
1592
1864
|
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_focusRange"), options.focusRange);
|
|
1593
1865
|
this.gl.uniform1i(this.gl.getUniformLocation(program, "u_maxRadius"), options.maxRadius);
|
|
1866
|
+
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_depthNear"), depthRange.near);
|
|
1867
|
+
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_depthFar"), depthRange.far);
|
|
1594
1868
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1595
1869
|
return targetTexture;
|
|
1596
1870
|
}
|
|
1597
1871
|
executeNativeMotionBlurPass(sourceTexture, resources, options, vertexArray, targetIndex) {
|
|
1598
|
-
const velocityTexture = this.motionBlurVelocityTexture;
|
|
1872
|
+
const velocityTexture = options.temporal ? this.requireTemporalTarget(options.temporal.velocity, resources.width, resources.height).colorHandle : this.motionBlurVelocityTexture;
|
|
1599
1873
|
if (!velocityTexture) {
|
|
1600
1874
|
throw new RenderDeviceError("WebGL2 motion-blur velocity texture was not initialized", "WEBGL_ALLOCATION_FAILED");
|
|
1601
1875
|
}
|
|
@@ -1612,28 +1886,66 @@ export class WebGL2Device {
|
|
|
1612
1886
|
this.gl.uniform2i(this.gl.getUniformLocation(program, "u_size"), resources.width, resources.height);
|
|
1613
1887
|
this.gl.uniform1i(this.gl.getUniformLocation(program, "u_samples"), options.samples);
|
|
1614
1888
|
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_scale"), options.scale);
|
|
1889
|
+
this.gl.uniform1i(this.gl.getUniformLocation(program, "u_velocityUv"), options.temporal ? 1 : 0);
|
|
1890
|
+
if (options.temporal) {
|
|
1891
|
+
this.nativeTemporalPasses++;
|
|
1892
|
+
this.nativeTemporalBindings++;
|
|
1893
|
+
}
|
|
1615
1894
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1616
1895
|
return targetTexture;
|
|
1617
1896
|
}
|
|
1618
1897
|
executeNativeTaaPass(sourceTexture, resources, options, vertexArray, targetIndex) {
|
|
1619
|
-
const
|
|
1898
|
+
const temporal = options.temporal;
|
|
1899
|
+
const historyTarget = temporal ? this.requireTemporalTarget(temporal.history, resources.width, resources.height) : undefined;
|
|
1900
|
+
const historyTexture = historyTarget?.colorHandle ?? this.taaHistoryTexture;
|
|
1620
1901
|
if (!historyTexture) {
|
|
1621
1902
|
throw new RenderDeviceError("WebGL2 TAA history texture was not initialized", "WEBGL_ALLOCATION_FAILED");
|
|
1622
1903
|
}
|
|
1623
|
-
const
|
|
1904
|
+
const accumulationProgram = this.ensureTaaProgram();
|
|
1624
1905
|
const targetTexture = resources.textures[targetIndex];
|
|
1625
|
-
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
|
|
1626
1906
|
this.gl.viewport(0, 0, resources.width, resources.height);
|
|
1627
|
-
this.gl.useProgram(
|
|
1907
|
+
this.gl.useProgram(accumulationProgram);
|
|
1628
1908
|
this.gl.bindVertexArray(vertexArray);
|
|
1629
1909
|
this.bindFullscreenTexture(0, sourceTexture);
|
|
1630
1910
|
this.bindFullscreenTexture(1, historyTexture);
|
|
1631
|
-
this.gl.uniform1i(this.gl.getUniformLocation(
|
|
1632
|
-
this.gl.uniform1i(this.gl.getUniformLocation(
|
|
1633
|
-
this.gl.uniform1f(this.gl.getUniformLocation(
|
|
1634
|
-
this.gl.
|
|
1911
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulationProgram, "u_source"), 0);
|
|
1912
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulationProgram, "u_history"), 1);
|
|
1913
|
+
this.gl.uniform1f(this.gl.getUniformLocation(accumulationProgram, "u_blend"), temporal && !temporal.historyValid ? 0 : temporalAccumulationWeight(options.blend, temporal?.historyFrames));
|
|
1914
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulationProgram, "u_temporal"), temporal ? 1 : 0);
|
|
1915
|
+
if (temporal) {
|
|
1916
|
+
const velocity = this.requireTemporalTarget(temporal.velocity, resources.width, resources.height);
|
|
1917
|
+
const destination = this.requireTemporalTarget(temporal.historyOutput, resources.width, resources.height);
|
|
1918
|
+
if (destination === historyTarget || destination === velocity || historyTarget === velocity)
|
|
1919
|
+
throw new RenderDeviceError("Temporal targets must not alias", "TEMPORAL_TARGET_ALIAS");
|
|
1920
|
+
this.bindFullscreenTexture(2, velocity.colorHandle);
|
|
1921
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulationProgram, "u_velocity"), 2);
|
|
1922
|
+
// Resolve exactly once into the next history target. Presentation reads
|
|
1923
|
+
// this immutable result through a separate program, so display tuning
|
|
1924
|
+
// cannot alter the accumulated history shader or its stored bytes.
|
|
1925
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, destination.framebuffer);
|
|
1926
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1927
|
+
const presentationProgram = this.ensureTaaPresentationProgram();
|
|
1928
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
|
|
1929
|
+
this.gl.useProgram(presentationProgram);
|
|
1930
|
+
this.bindFullscreenTexture(0, sourceTexture);
|
|
1931
|
+
this.bindFullscreenTexture(1, destination.colorHandle);
|
|
1932
|
+
this.gl.uniform1i(this.gl.getUniformLocation(presentationProgram, "u_source"), 0);
|
|
1933
|
+
this.gl.uniform1i(this.gl.getUniformLocation(presentationProgram, "u_accumulated"), 1);
|
|
1934
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1935
|
+
this.nativeTemporalPasses++;
|
|
1936
|
+
this.nativeTemporalBindings += 3;
|
|
1937
|
+
}
|
|
1938
|
+
else {
|
|
1939
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
|
|
1940
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1941
|
+
}
|
|
1635
1942
|
return targetTexture;
|
|
1636
1943
|
}
|
|
1944
|
+
requireTemporalTarget(target, width, height) {
|
|
1945
|
+
if (!(target instanceof WebGL2RenderTarget) || !this.renderTargets.has(target) || target.disposed || target.width !== width || target.height !== height || target.colorTexture.format !== "rgba16f")
|
|
1946
|
+
throw new RenderDeviceError("Temporal input must be a live same-device same-size RGBA16F target", "TEMPORAL_TARGET_INVALID");
|
|
1947
|
+
return target;
|
|
1948
|
+
}
|
|
1637
1949
|
bindFullscreenTexture(unit, texture) {
|
|
1638
1950
|
this.gl.activeTexture(this.gl.TEXTURE0 + unit);
|
|
1639
1951
|
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
|
|
@@ -1648,6 +1960,223 @@ export class WebGL2Device {
|
|
|
1648
1960
|
this.gl.deleteFramebuffer(framebuffer);
|
|
1649
1961
|
this.bloomPingPongResources = null;
|
|
1650
1962
|
}
|
|
1963
|
+
/**
|
|
1964
|
+
* Diagnostics for the most recent native bloom execution (muse3jsparity-PRD
|
|
1965
|
+
* A1). Null when no native bloom pass has run yet on this device.
|
|
1966
|
+
*/
|
|
1967
|
+
getBloomDiagnostics() {
|
|
1968
|
+
return this.lastBloomDiagnostics;
|
|
1969
|
+
}
|
|
1970
|
+
recordBloomDiagnostics(options, sourceWidth, sourceHeight, hdr, mipCount) {
|
|
1971
|
+
const plan = resolveBloomPyramidPlan(sourceWidth, sourceHeight, options.quality, hdr);
|
|
1972
|
+
const pingPongBytes = sourceWidth * sourceHeight * 4 * (hdr ? 2 : 1) * 2;
|
|
1973
|
+
const bytesPerChannel = plan.halfFloat ? 2 : 1;
|
|
1974
|
+
const level0 = plan.mips[0];
|
|
1975
|
+
const accumulatorBytes = level0.width * level0.height * 4 * bytesPerChannel;
|
|
1976
|
+
this.lastBloomDiagnostics = {
|
|
1977
|
+
quality: options.quality,
|
|
1978
|
+
mipCount,
|
|
1979
|
+
targetCount: options.quality === "performance" ? 2 : plan.mips.length * 2 + 1,
|
|
1980
|
+
targetBytes: (options.quality === "performance" ? pingPongBytes : plan.targetBytes + accumulatorBytes + pingPongBytes),
|
|
1981
|
+
compositeGain: options.intensity * resolveBloomPyramidResponseGain(plan),
|
|
1982
|
+
threshold: options.threshold,
|
|
1983
|
+
intensity: options.intensity,
|
|
1984
|
+
softKnee: options.softKnee,
|
|
1985
|
+
shoulder: options.shoulder,
|
|
1986
|
+
halfFloat: hdr || plan.halfFloat,
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
/**
|
|
1990
|
+
* PART U: every live GPU allocation with bytes + lane owner — registry
|
|
1991
|
+
* render targets (B1 shadow maps arrive labeled, B4 captures arrive as
|
|
1992
|
+
* `-mirror-target`/`-scene-color-target`) plus the raw-handle residents the
|
|
1993
|
+
* registry set cannot see: the A1 bloom pyramid (two targets per mip plus
|
|
1994
|
+
* two level-0 accumulators), the legacy bloom ping-pong pair, the A1/A3
|
|
1995
|
+
* lookup textures, and the motion-blur/taa history textures. The
|
|
1996
|
+
* depth-readback scratch pair is intentionally absent: it is allocated and
|
|
1997
|
+
* deleted inside one synchronous call, so it is never live at a diagnostics
|
|
1998
|
+
* boundary. A5 volumetric light, C4 decals, and the G1 SDF atlas own no GPU
|
|
1999
|
+
* target (forward uniforms + CPU kernel, CPU geometry, CPU atlas data), so
|
|
2000
|
+
* no entry is emitted for them; a target appearing under their owner would
|
|
2001
|
+
* be the signal that the lane grew one.
|
|
2002
|
+
*/
|
|
2003
|
+
describeGpuTargetInventory(liveRenderTargets) {
|
|
2004
|
+
const entries = liveRenderTargets.map((target) => ({
|
|
2005
|
+
label: target.label,
|
|
2006
|
+
kind: "render-target",
|
|
2007
|
+
bytes: target.colorTexture.byteLength + (target.depthTexture?.byteLength ?? 0),
|
|
2008
|
+
owner: resolveGpuTargetOwner(target.label)
|
|
2009
|
+
}));
|
|
2010
|
+
const pyramid = this.bloomPyramidResources;
|
|
2011
|
+
if (pyramid) {
|
|
2012
|
+
const bytesPerPixel = pyramid.hdr ? 8 : 4;
|
|
2013
|
+
pyramid.plan.mips.forEach((mip, index) => {
|
|
2014
|
+
for (const side of ["a", "b"]) {
|
|
2015
|
+
entries.push({
|
|
2016
|
+
label: `bloom-pyramid-mip${index}${side}`,
|
|
2017
|
+
kind: "pyramid-mip",
|
|
2018
|
+
bytes: mip.width * mip.height * bytesPerPixel,
|
|
2019
|
+
owner: "a1-bloom"
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
});
|
|
2023
|
+
const level0 = pyramid.plan.mips[0];
|
|
2024
|
+
if (level0) {
|
|
2025
|
+
for (const side of ["a", "b"]) {
|
|
2026
|
+
entries.push({
|
|
2027
|
+
label: `bloom-pyramid-accumulator-${side}`,
|
|
2028
|
+
kind: "pyramid-accumulator",
|
|
2029
|
+
bytes: level0.width * level0.height * bytesPerPixel,
|
|
2030
|
+
owner: "a1-bloom"
|
|
2031
|
+
});
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
const pingPong = this.bloomPingPongResources;
|
|
2036
|
+
if (pingPong) {
|
|
2037
|
+
const bytesPerPixel = pingPong.hdr ? 8 : 4;
|
|
2038
|
+
for (const side of ["a", "b"]) {
|
|
2039
|
+
entries.push({
|
|
2040
|
+
label: `bloom-ping-pong-${side}`,
|
|
2041
|
+
kind: "ping-pong",
|
|
2042
|
+
bytes: pingPong.width * pingPong.height * bytesPerPixel,
|
|
2043
|
+
owner: "a1-bloom"
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
if (this.bloomBrightLutTexture) {
|
|
2048
|
+
entries.push({
|
|
2049
|
+
label: "bloom-bright-lut",
|
|
2050
|
+
kind: "lut",
|
|
2051
|
+
bytes: BLOOM_BRIGHT_LUT_WIDTH * BLOOM_BRIGHT_LUT_HEIGHT * 4,
|
|
2052
|
+
owner: "a1-bloom"
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
if (this.bloomCompositeLutTexture) {
|
|
2056
|
+
entries.push({
|
|
2057
|
+
label: "bloom-composite-lut",
|
|
2058
|
+
kind: "lut",
|
|
2059
|
+
bytes: BLOOM_COMPOSITE_LUT_SIZE * BLOOM_COMPOSITE_LUT_SIZE * 4,
|
|
2060
|
+
owner: "a1-bloom"
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
if (this.outlineBlendLutTexture) {
|
|
2064
|
+
entries.push({ label: "outline-blend-lut", kind: "lut", bytes: OUTLINE_BLEND_LUT_WIDTH * 4, owner: "post" });
|
|
2065
|
+
}
|
|
2066
|
+
if (this.motionBlurVelocityTexture && this.motionBlurVelocitySize) {
|
|
2067
|
+
entries.push({
|
|
2068
|
+
label: "motion-blur-velocity",
|
|
2069
|
+
kind: "history",
|
|
2070
|
+
bytes: this.motionBlurVelocitySize.width * this.motionBlurVelocitySize.height * 8,
|
|
2071
|
+
owner: "post"
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
if (this.taaHistoryTexture && this.taaHistorySize) {
|
|
2075
|
+
entries.push({
|
|
2076
|
+
label: "taa-history",
|
|
2077
|
+
kind: "history",
|
|
2078
|
+
bytes: this.taaHistorySize.width * this.taaHistorySize.height * 4,
|
|
2079
|
+
owner: "post"
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
return entries;
|
|
2083
|
+
}
|
|
2084
|
+
disposeBloomPyramidResources() {
|
|
2085
|
+
if (!this.bloomPyramidResources)
|
|
2086
|
+
return;
|
|
2087
|
+
for (const texture of this.bloomPyramidResources.textures)
|
|
2088
|
+
this.gl.deleteTexture(texture);
|
|
2089
|
+
for (const framebuffer of this.bloomPyramidResources.framebuffers)
|
|
2090
|
+
this.gl.deleteFramebuffer(framebuffer);
|
|
2091
|
+
this.gl.deleteTexture(this.bloomPyramidResources.accumulatorTextureA);
|
|
2092
|
+
this.gl.deleteFramebuffer(this.bloomPyramidResources.accumulatorFramebufferA);
|
|
2093
|
+
this.gl.deleteTexture(this.bloomPyramidResources.accumulatorTextureB);
|
|
2094
|
+
this.gl.deleteFramebuffer(this.bloomPyramidResources.accumulatorFramebufferB);
|
|
2095
|
+
this.bloomPyramidResources = null;
|
|
2096
|
+
}
|
|
2097
|
+
/**
|
|
2098
|
+
* Mip-chain targets for the balanced/cinematic bloom pyramid. Each level is a
|
|
2099
|
+
* single texture+framebuffer at the planned size with linear filtering so the
|
|
2100
|
+
* downsample and accumulate passes can sample across levels. Half-float backs
|
|
2101
|
+
* the chain when the source is HDR or the cinematic preset requests it.
|
|
2102
|
+
*/
|
|
2103
|
+
ensureBloomPyramidResources(plan, hdr) {
|
|
2104
|
+
const current = this.bloomPyramidResources;
|
|
2105
|
+
if (current && current.plan.quality === plan.quality && current.hdr === (hdr || plan.halfFloat) &&
|
|
2106
|
+
current.plan.mips.length === plan.mips.length &&
|
|
2107
|
+
current.plan.mips.every((mip, index) => mip.width === plan.mips[index].width && mip.height === plan.mips[index].height)) {
|
|
2108
|
+
return current;
|
|
2109
|
+
}
|
|
2110
|
+
this.disposeBloomPyramidResources();
|
|
2111
|
+
const textureUnit0 = this.captureTextureUnit0();
|
|
2112
|
+
const previousFramebuffer = this.gl.getParameter(this.gl.FRAMEBUFFER_BINDING);
|
|
2113
|
+
const textures = [];
|
|
2114
|
+
const framebuffers = [];
|
|
2115
|
+
const useHalfFloat = hdr || plan.halfFloat;
|
|
2116
|
+
const allocateTarget = (width, height, label) => {
|
|
2117
|
+
const texture = this.gl.createTexture();
|
|
2118
|
+
const framebuffer = this.gl.createFramebuffer();
|
|
2119
|
+
if (!texture || !framebuffer) {
|
|
2120
|
+
if (texture)
|
|
2121
|
+
this.gl.deleteTexture(texture);
|
|
2122
|
+
if (framebuffer)
|
|
2123
|
+
this.gl.deleteFramebuffer(framebuffer);
|
|
2124
|
+
throw new RenderDeviceError(`Failed to allocate WebGL2 bloom pyramid ${label}`, "WEBGL_ALLOCATION_FAILED", { width, height });
|
|
2125
|
+
}
|
|
2126
|
+
textures.push(texture);
|
|
2127
|
+
framebuffers.push(framebuffer);
|
|
2128
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
|
|
2129
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
|
2130
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
2131
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
|
2132
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
|
|
2133
|
+
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, useHalfFloat ? this.gl.RGBA16F : this.gl.RGBA8, width, height, 0, this.gl.RGBA, useHalfFloat ? this.gl.HALF_FLOAT : this.gl.UNSIGNED_BYTE, null);
|
|
2134
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
|
|
2135
|
+
this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, texture, 0);
|
|
2136
|
+
const status = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER);
|
|
2137
|
+
if (status !== this.gl.FRAMEBUFFER_COMPLETE) {
|
|
2138
|
+
throw new RenderDeviceError("WebGL2 bloom pyramid framebuffer status is invalid", "FRAMEBUFFER_INVALID", { width, height, status });
|
|
2139
|
+
}
|
|
2140
|
+
return { texture, framebuffer };
|
|
2141
|
+
};
|
|
2142
|
+
try {
|
|
2143
|
+
// Two ping-pong targets per mip (separable blur needs distinct read/write
|
|
2144
|
+
// targets at the same size), plus two level-0-sized accumulators: the
|
|
2145
|
+
// weighted sum ping-pongs between them so no pass ever samples the
|
|
2146
|
+
// texture of its own bound framebuffer (feedback loop).
|
|
2147
|
+
for (const mip of plan.mips) {
|
|
2148
|
+
allocateTarget(mip.width, mip.height, "mip");
|
|
2149
|
+
allocateTarget(mip.width, mip.height, "mip");
|
|
2150
|
+
}
|
|
2151
|
+
const level0 = plan.mips[0];
|
|
2152
|
+
const accumulatorA = allocateTarget(level0.width, level0.height, "accumulator-a");
|
|
2153
|
+
const accumulatorB = allocateTarget(level0.width, level0.height, "accumulator-b");
|
|
2154
|
+
const finalResources = {
|
|
2155
|
+
plan,
|
|
2156
|
+
hdr: useHalfFloat,
|
|
2157
|
+
textures,
|
|
2158
|
+
framebuffers,
|
|
2159
|
+
accumulatorTextureA: accumulatorA.texture,
|
|
2160
|
+
accumulatorFramebufferA: accumulatorA.framebuffer,
|
|
2161
|
+
accumulatorTextureB: accumulatorB.texture,
|
|
2162
|
+
accumulatorFramebufferB: accumulatorB.framebuffer
|
|
2163
|
+
};
|
|
2164
|
+
this.bloomPyramidResources = finalResources;
|
|
2165
|
+
return finalResources;
|
|
2166
|
+
}
|
|
2167
|
+
catch (error) {
|
|
2168
|
+
for (const texture of textures)
|
|
2169
|
+
this.gl.deleteTexture(texture);
|
|
2170
|
+
for (const framebuffer of framebuffers)
|
|
2171
|
+
this.gl.deleteFramebuffer(framebuffer);
|
|
2172
|
+
throw error;
|
|
2173
|
+
}
|
|
2174
|
+
finally {
|
|
2175
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, previousFramebuffer);
|
|
2176
|
+
this.restoreTextureUnit0(textureUnit0);
|
|
2177
|
+
this.stateCache.invalidate();
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
1651
2180
|
captureTextureUnit0() {
|
|
1652
2181
|
const activeTexture = this.gl.getParameter(this.gl.ACTIVE_TEXTURE);
|
|
1653
2182
|
this.gl.activeTexture(this.gl.TEXTURE0);
|
|
@@ -1856,6 +2385,7 @@ uniform sampler2D u_source;
|
|
|
1856
2385
|
uniform sampler2D u_brightLut;
|
|
1857
2386
|
uniform int u_hdr;
|
|
1858
2387
|
uniform float u_threshold;
|
|
2388
|
+
uniform float u_softKnee;
|
|
1859
2389
|
out vec4 outColor;
|
|
1860
2390
|
|
|
1861
2391
|
uvec4 byteTexel(sampler2D source, ivec2 coordinate) {
|
|
@@ -1867,7 +2397,12 @@ void main() {
|
|
|
1867
2397
|
if (u_hdr == 1) {
|
|
1868
2398
|
vec4 source = texelFetch(u_source, pixel, 0);
|
|
1869
2399
|
float luma = dot(source.rgb, vec3(0.2126, 0.7152, 0.0722));
|
|
1870
|
-
|
|
2400
|
+
float kneeLow = max(u_threshold - u_softKnee, 0.0);
|
|
2401
|
+
float kneeHigh = min(u_threshold + u_softKnee, 1.0);
|
|
2402
|
+
float weight = u_softKnee <= 0.0
|
|
2403
|
+
? (luma >= u_threshold ? 1.0 : 0.0)
|
|
2404
|
+
: smoothstep(kneeLow, kneeHigh, luma);
|
|
2405
|
+
outColor = source * weight;
|
|
1871
2406
|
return;
|
|
1872
2407
|
}
|
|
1873
2408
|
uvec4 source = byteTexel(u_source, pixel);
|
|
@@ -1907,38 +2442,25 @@ uniform int u_horizontal;
|
|
|
1907
2442
|
uniform int u_hdr;
|
|
1908
2443
|
out vec4 outColor;
|
|
1909
2444
|
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
return uvec4(texelFetch(u_source, bounded, 0) * 255.0 + 0.5);
|
|
2445
|
+
vec4 sourcePixel(ivec2 coordinate) {
|
|
2446
|
+
return texelFetch(u_source, clamp(coordinate, ivec2(0), u_size - ivec2(1)), 0);
|
|
1913
2447
|
}
|
|
1914
2448
|
|
|
1915
2449
|
void main() {
|
|
1916
2450
|
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
for (int offset = -16; offset <= 16; offset += 1) {
|
|
1929
|
-
if (abs(offset) > u_radius) {
|
|
1930
|
-
continue;
|
|
1931
|
-
}
|
|
1932
|
-
ivec2 sampleCoordinate = pixel + (
|
|
1933
|
-
u_horizontal == 1
|
|
1934
|
-
? ivec2(offset, 0)
|
|
1935
|
-
: ivec2(0, offset)
|
|
1936
|
-
);
|
|
1937
|
-
sum += sourceByte(sampleCoordinate);
|
|
2451
|
+
float sigma = max(float(u_radius) / 3.0, 0.5);
|
|
2452
|
+
float coefficient = 0.39894 / sigma;
|
|
2453
|
+
vec4 sum = sourcePixel(pixel) * coefficient;
|
|
2454
|
+
float weightSum = coefficient;
|
|
2455
|
+
for (int offset = 1; offset <= 16; offset += 1) {
|
|
2456
|
+
if (offset > u_radius) continue;
|
|
2457
|
+
float distance = float(offset);
|
|
2458
|
+
float weight = coefficient * exp(-0.5 * distance * distance / (sigma * sigma));
|
|
2459
|
+
ivec2 delta = u_horizontal == 1 ? ivec2(offset, 0) : ivec2(0, offset);
|
|
2460
|
+
sum += (sourcePixel(pixel + delta) + sourcePixel(pixel - delta)) * weight;
|
|
2461
|
+
weightSum += 2.0 * weight;
|
|
1938
2462
|
}
|
|
1939
|
-
|
|
1940
|
-
uvec4 rounded = (sum * 2u + uvec4(kernelSize)) / uvec4(kernelSize * 2u);
|
|
1941
|
-
outColor = vec4(rounded) / 255.0;
|
|
2463
|
+
outColor = sum / max(weightSum, 0.000001);
|
|
1942
2464
|
}
|
|
1943
2465
|
`, "webgl2-bloom-blur");
|
|
1944
2466
|
return this.bloomBlurProgram;
|
|
@@ -1985,6 +2507,48 @@ void main() {
|
|
|
1985
2507
|
`, "webgl2-bloom-composite");
|
|
1986
2508
|
return this.bloomCompositeProgram;
|
|
1987
2509
|
}
|
|
2510
|
+
ensureBloomDownsampleProgram() {
|
|
2511
|
+
if (this.bloomDownsampleProgram)
|
|
2512
|
+
return this.bloomDownsampleProgram;
|
|
2513
|
+
this.bloomDownsampleProgram = this.createFullscreenProgram(`#version 300 es
|
|
2514
|
+
precision highp float;
|
|
2515
|
+
uniform sampler2D u_source;
|
|
2516
|
+
uniform vec2 u_texelSize;
|
|
2517
|
+
uniform vec2 u_targetSize;
|
|
2518
|
+
out vec4 outColor;
|
|
2519
|
+
void main() {
|
|
2520
|
+
// gl_FragCoord is expressed in destination pixels. Normalize by the
|
|
2521
|
+
// destination size so every downsample covers the complete source image;
|
|
2522
|
+
// multiplying by source texel size cropped a half-size target to the
|
|
2523
|
+
// lower-left quarter of the preceding level.
|
|
2524
|
+
vec2 uv = gl_FragCoord.xy / u_targetSize;
|
|
2525
|
+
vec2 e = u_texelSize * 0.5;
|
|
2526
|
+
vec4 sum = texture(u_source, uv - e) + texture(u_source, uv + vec2(e.x, -e.y))
|
|
2527
|
+
+ texture(u_source, uv + vec2(-e.x, e.y)) + texture(u_source, uv + e);
|
|
2528
|
+
outColor = sum * 0.25;
|
|
2529
|
+
}
|
|
2530
|
+
`, "webgl2-bloom-downsample");
|
|
2531
|
+
return this.bloomDownsampleProgram;
|
|
2532
|
+
}
|
|
2533
|
+
ensureBloomAccumulateProgram() {
|
|
2534
|
+
if (this.bloomAccumulateProgram)
|
|
2535
|
+
return this.bloomAccumulateProgram;
|
|
2536
|
+
this.bloomAccumulateProgram = this.createFullscreenProgram(`#version 300 es
|
|
2537
|
+
precision highp float;
|
|
2538
|
+
uniform sampler2D u_base;
|
|
2539
|
+
uniform sampler2D u_bloom;
|
|
2540
|
+
uniform float u_weight;
|
|
2541
|
+
out vec4 outColor;
|
|
2542
|
+
void main() {
|
|
2543
|
+
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
|
2544
|
+
vec4 base = texelFetch(u_base, pixel, 0);
|
|
2545
|
+
vec2 uv = (vec2(pixel) + 0.5) / vec2(textureSize(u_base, 0));
|
|
2546
|
+
vec3 bloom = texture(u_bloom, uv).rgb;
|
|
2547
|
+
outColor = vec4(base.rgb + bloom * u_weight, base.a);
|
|
2548
|
+
}
|
|
2549
|
+
`, "webgl2-bloom-accumulate");
|
|
2550
|
+
return this.bloomAccumulateProgram;
|
|
2551
|
+
}
|
|
1988
2552
|
ensureOutlineProgram() {
|
|
1989
2553
|
if (this.outlineProgram)
|
|
1990
2554
|
return this.outlineProgram;
|
|
@@ -2124,29 +2688,73 @@ precision highp float;
|
|
|
2124
2688
|
precision highp int;
|
|
2125
2689
|
uniform sampler2D u_source;
|
|
2126
2690
|
uniform sampler2D u_depth;
|
|
2691
|
+
uniform sampler2D u_normalMask;
|
|
2127
2692
|
uniform ivec2 u_size;
|
|
2128
2693
|
uniform float u_intensity;
|
|
2129
|
-
uniform
|
|
2694
|
+
uniform float u_maxDistance;
|
|
2695
|
+
uniform int u_maxSteps;
|
|
2696
|
+
uniform float u_thickness;
|
|
2697
|
+
uniform int u_hasNormalMask;
|
|
2698
|
+
uniform mat4 u_projection;
|
|
2699
|
+
uniform mat4 u_inverseProjection;
|
|
2130
2700
|
out vec4 outColor;
|
|
2131
|
-
|
|
2701
|
+
vec3 positionAtDepth(vec2 uv, float depth) {
|
|
2702
|
+
vec4 p = u_inverseProjection * vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
|
|
2703
|
+
return p.xyz / p.w;
|
|
2704
|
+
}
|
|
2705
|
+
vec3 positionAt(vec2 uv) {
|
|
2706
|
+
return positionAtDepth(uv, texture(u_depth, uv).r);
|
|
2707
|
+
}
|
|
2708
|
+
vec3 viewDirection(vec2 uv) {
|
|
2709
|
+
return normalize(positionAtDepth(uv, 0.999) - positionAtDepth(uv, 0.0));
|
|
2710
|
+
}
|
|
2711
|
+
vec2 projectPosition(vec3 p) {
|
|
2712
|
+
vec4 clip = u_projection * vec4(p, 1.0);
|
|
2713
|
+
return clip.xy / clip.w * 0.5 + 0.5;
|
|
2714
|
+
}
|
|
2715
|
+
float depthDifference(vec3 p, vec2 uv) {
|
|
2716
|
+
return dot(p - positionAt(uv), viewDirection(uv));
|
|
2717
|
+
}
|
|
2132
2718
|
void main() {
|
|
2133
|
-
|
|
2134
|
-
vec4
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
);
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2719
|
+
vec2 uv = gl_FragCoord.xy / vec2(u_size);
|
|
2720
|
+
vec4 base = texture(u_source, uv);
|
|
2721
|
+
outColor = base;
|
|
2722
|
+
vec3 origin = positionAt(uv);
|
|
2723
|
+
vec4 normalMask = texture(u_normalMask, uv);
|
|
2724
|
+
vec3 normal = u_hasNormalMask == 1 ? normalize(normalMask.xyz * 2.0 - 1.0) : normalize(cross(dFdx(origin), dFdy(origin)));
|
|
2725
|
+
float roughness = u_hasNormalMask == 1 ? normalMask.a : 0.25;
|
|
2726
|
+
if (texture(u_depth, uv).r >= 0.999999 || (u_hasNormalMask == 1 && normalMask.a == 0.0) || roughness >= 1.0) return;
|
|
2727
|
+
vec3 ray = normalize(reflect(viewDirection(uv), normal));
|
|
2728
|
+
float stepSize = u_maxDistance / float(u_maxSteps);
|
|
2729
|
+
vec3 start = origin + normal * u_thickness;
|
|
2730
|
+
float previous = 0.0;
|
|
2731
|
+
for (int i = 1; i <= 64; ++i) {
|
|
2732
|
+
if (i > u_maxSteps) break;
|
|
2733
|
+
float distanceAlong = float(i) * stepSize;
|
|
2734
|
+
vec3 candidate = start + ray * distanceAlong;
|
|
2735
|
+
vec4 candidateClip = u_projection * vec4(candidate, 1.0);
|
|
2736
|
+
if (candidateClip.w <= 0.0 || abs(candidateClip.z) >= candidateClip.w) break;
|
|
2737
|
+
vec2 hitUV = projectPosition(candidate);
|
|
2738
|
+
if (any(lessThanEqual(hitUV, vec2(0.0))) || any(greaterThanEqual(hitUV, vec2(1.0)))) break;
|
|
2739
|
+
float difference = depthDifference(candidate, hitUV);
|
|
2740
|
+
if (difference >= 0.0 && previous < 0.0) {
|
|
2741
|
+
float lo = distanceAlong - stepSize, hi = distanceAlong;
|
|
2742
|
+
for (int j = 0; j < 5; ++j) {
|
|
2743
|
+
float mid = (lo + hi) * 0.5;
|
|
2744
|
+
vec3 point = start + ray * mid;
|
|
2745
|
+
if (depthDifference(point, projectPosition(point)) >= 0.0) hi = mid; else lo = mid;
|
|
2746
|
+
}
|
|
2747
|
+
candidate = start + ray * hi;
|
|
2748
|
+
hitUV = projectPosition(candidate);
|
|
2749
|
+
difference = depthDifference(candidate, hitUV);
|
|
2750
|
+
if (difference > u_thickness || texture(u_depth, hitUV).r >= 0.999999) break;
|
|
2751
|
+
float edge = smoothstep(0.0, 0.08, min(min(hitUV.x, hitUV.y), min(1.0 - hitUV.x, 1.0 - hitUV.y)));
|
|
2752
|
+
float response = clamp(u_intensity * edge * (1.0 - roughness) * (1.0 - hi / u_maxDistance), 0.0, 1.0);
|
|
2753
|
+
outColor = vec4(mix(base.rgb, texture(u_source, hitUV).rgb, response), base.a);
|
|
2754
|
+
break;
|
|
2755
|
+
}
|
|
2756
|
+
previous = difference;
|
|
2147
2757
|
}
|
|
2148
|
-
float boost = sourceLuma * u_intensity * (1.0 - depth);
|
|
2149
|
-
outColor = vec4(clamp(target.rgb + reflected * boost, 0.0, 1.0), target.a);
|
|
2150
2758
|
}
|
|
2151
2759
|
`, "webgl2-ssr");
|
|
2152
2760
|
return this.ssrProgram;
|
|
@@ -2163,13 +2771,26 @@ uniform ivec2 u_size;
|
|
|
2163
2771
|
uniform float u_focusDepth;
|
|
2164
2772
|
uniform float u_focusRange;
|
|
2165
2773
|
uniform int u_maxRadius;
|
|
2774
|
+
uniform float u_depthNear;
|
|
2775
|
+
uniform float u_depthFar;
|
|
2166
2776
|
out vec4 outColor;
|
|
2167
2777
|
|
|
2778
|
+
// Same linearization contract as the SSR program, except focus is authored
|
|
2779
|
+
// directly as a linear-distance fraction (0 = near plane, 1 = far plane):
|
|
2780
|
+
// buffer-unit focus values are unusable once depth is linearized, because the
|
|
2781
|
+
// projection curve parks the whole play area inside a sliver of buffer units.
|
|
2782
|
+
// The CPU byte kernel keeps buffer-unit semantics; the divergence is
|
|
2783
|
+
// documented at normalizeLdrDepthRange.
|
|
2784
|
+
float a3dDofLinearDepth(float depth) {
|
|
2785
|
+
float viewZ = u_depthNear * u_depthFar / max(u_depthFar - depth * (u_depthFar - u_depthNear), 0.0001);
|
|
2786
|
+
return clamp((viewZ - u_depthNear) / max(u_depthFar - u_depthNear, 0.0001), 0.0, 1.0);
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2168
2789
|
void main() {
|
|
2169
2790
|
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
|
2170
2791
|
vec4 source = texelFetch(u_source, pixel, 0);
|
|
2171
|
-
float
|
|
2172
|
-
float normalizedBlur = max(0.0, abs(
|
|
2792
|
+
float linearDepth = a3dDofLinearDepth(texelFetch(u_depth, pixel, 0).r);
|
|
2793
|
+
float normalizedBlur = max(0.0, abs(linearDepth - u_focusDepth) - u_focusRange)
|
|
2173
2794
|
/ max(u_focusRange, 0.001) * float(u_maxRadius);
|
|
2174
2795
|
int radius = min(u_maxRadius, int(round(normalizedBlur)));
|
|
2175
2796
|
if (radius == 0) {
|
|
@@ -2200,6 +2821,7 @@ precision highp float;
|
|
|
2200
2821
|
precision highp int;
|
|
2201
2822
|
uniform sampler2D u_source;
|
|
2202
2823
|
uniform sampler2D u_velocity;
|
|
2824
|
+
uniform bool u_velocityUv;
|
|
2203
2825
|
uniform ivec2 u_size;
|
|
2204
2826
|
uniform int u_samples;
|
|
2205
2827
|
uniform float u_scale;
|
|
@@ -2209,6 +2831,8 @@ void main() {
|
|
|
2209
2831
|
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
|
2210
2832
|
vec4 source = texelFetch(u_source, pixel, 0);
|
|
2211
2833
|
vec2 velocity = texelFetch(u_velocity, pixel, 0).rg * u_scale;
|
|
2834
|
+
if (u_velocityUv) velocity *= vec2(u_size);
|
|
2835
|
+
velocity = clamp(velocity, vec2(-64.0), vec2(64.0));
|
|
2212
2836
|
if (length(velocity) < 0.01) {
|
|
2213
2837
|
outColor = source;
|
|
2214
2838
|
return;
|
|
@@ -2233,18 +2857,108 @@ void main() {
|
|
|
2233
2857
|
precision highp float;
|
|
2234
2858
|
uniform sampler2D u_source;
|
|
2235
2859
|
uniform sampler2D u_history;
|
|
2860
|
+
uniform sampler2D u_velocity;
|
|
2236
2861
|
uniform float u_blend;
|
|
2862
|
+
uniform bool u_temporal;
|
|
2237
2863
|
out vec4 outColor;
|
|
2864
|
+
vec4 cubicWeights(float f) {
|
|
2865
|
+
float f2=f*f, f3=f2*f;
|
|
2866
|
+
return vec4(-0.5*f+f2-0.5*f3,1.0-2.5*f2+1.5*f3,0.5*f+2.0*f2-1.5*f3,-0.5*f2+0.5*f3);
|
|
2867
|
+
}
|
|
2868
|
+
vec4 historyAt(vec2 uv) {
|
|
2869
|
+
ivec2 size=textureSize(u_history,0);
|
|
2870
|
+
vec2 position=uv*vec2(size)-0.5;
|
|
2871
|
+
ivec2 base=ivec2(floor(position));
|
|
2872
|
+
vec4 wx=cubicWeights(fract(position.x)), wy=cubicWeights(fract(position.y));
|
|
2873
|
+
vec4 result=vec4(0.0);
|
|
2874
|
+
// Repeated bilinear history reconstruction diffuses a moving edge every
|
|
2875
|
+
// frame. Cubic reconstruction preserves it; the resolve's current-neighbor
|
|
2876
|
+
// clipping below bounds ringing before accumulation.
|
|
2877
|
+
for(int y=0;y<4;y++) for(int x=0;x<4;x++) {
|
|
2878
|
+
result+=texelFetch(u_history,clamp(base+ivec2(x-1,y-1),ivec2(0),size-1),0)*wx[x]*wy[y];
|
|
2879
|
+
}
|
|
2880
|
+
return result;
|
|
2881
|
+
}
|
|
2238
2882
|
|
|
2239
2883
|
void main() {
|
|
2240
2884
|
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
|
2885
|
+
ivec2 size = textureSize(u_source, 0);
|
|
2886
|
+
vec2 outputUV=(vec2(pixel)+0.5)/vec2(size);
|
|
2887
|
+
// Raster jitter supplies quadrature samples inside the output pixel.
|
|
2888
|
+
// Keep that sample on its pixel's accumulation grid; undoing the jitter
|
|
2889
|
+
// spatially filters it and defeats temporal coverage integration.
|
|
2890
|
+
vec2 sourceUV=outputUV;
|
|
2891
|
+
ivec2 sourcePixel=clamp(ivec2(floor(sourceUV*vec2(size))),ivec2(0),size-1);
|
|
2892
|
+
// Reconstruct current color on the stable output grid before accumulation.
|
|
2241
2893
|
vec4 source = texelFetch(u_source, pixel, 0);
|
|
2242
|
-
|
|
2243
|
-
|
|
2894
|
+
vec4 motion = u_temporal ? texelFetch(u_velocity, sourcePixel, 0) : vec4(0.0);
|
|
2895
|
+
// A currently uncovered sample may still carry integrated foreground
|
|
2896
|
+
// coverage in history. Track nearby foreground motion for that coverage,
|
|
2897
|
+
// but retain this pixel's own current/previous depth tags: copying RGBA
|
|
2898
|
+
// wholesale falsely labels background color as a foreground surface.
|
|
2899
|
+
if (u_temporal) {
|
|
2900
|
+
float nearestDepth=motion.b;
|
|
2901
|
+
for(int y=-1;y<=1;y++) for(int x=-1;x<=1;x++) {
|
|
2902
|
+
vec4 candidate=texelFetch(u_velocity,clamp(pixel+ivec2(x,y),ivec2(0),size-1),0);
|
|
2903
|
+
if(candidate.b<nearestDepth) {nearestDepth=candidate.b;motion.xy=candidate.xy;}
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
vec2 previousUV = outputUV-motion.xy;
|
|
2907
|
+
vec4 history = u_temporal ? historyAt(previousUV) : texture(u_history, previousUV);
|
|
2908
|
+
float weight = u_blend;
|
|
2909
|
+
if (u_temporal) {
|
|
2910
|
+
// Reprojected history incurs reconstruction error as the footprint moves
|
|
2911
|
+
// across the pixel grid. Reduce its confidence with displacement instead
|
|
2912
|
+
// of repeatedly giving a softened moving edge the stationary .9 weight.
|
|
2913
|
+
// Physical motion itself remains fully represented in previousUV.
|
|
2914
|
+
weight /= 1.0 + length(motion.xy * vec2(size));
|
|
2915
|
+
// Depth is categorical at a silhouette: bilinear alpha interpolation creates
|
|
2916
|
+
// nonexistent surfaces and rejects valid history on every jittered edge.
|
|
2917
|
+
// Test surface presence in the central history footprint. RGB remains
|
|
2918
|
+
// an integrated coverage value reconstructed from the wider cubic kernel.
|
|
2919
|
+
ivec2 previousBase=ivec2(floor(previousUV*vec2(size)-vec2(0.5)));
|
|
2920
|
+
float depthError=1.0;
|
|
2921
|
+
for(int y=0;y<2;y++) for(int x=0;x<2;x++) {
|
|
2922
|
+
float previousDepth=texelFetch(u_history,clamp(previousBase+ivec2(x,y),ivec2(0),size-1),0).a;
|
|
2923
|
+
depthError=min(depthError,abs(previousDepth-motion.a));
|
|
2924
|
+
}
|
|
2925
|
+
if (any(lessThan(previousUV,vec2(0.0))) || any(greaterThanEqual(previousUV,vec2(1.0))) || depthError>0.002) weight=0.0;
|
|
2926
|
+
vec3 lo=source.rgb, hi=source.rgb;
|
|
2927
|
+
for(int y=-1;y<=1;y++) for(int x=-1;x<=1;x++) { vec3 tap=texelFetch(u_source,clamp(pixel+ivec2(x,y),ivec2(0),size-1),0).rgb; lo=min(lo,tap); hi=max(hi,tap); }
|
|
2928
|
+
history.rgb=clamp(history.rgb,lo,hi);
|
|
2929
|
+
}
|
|
2930
|
+
vec3 resolved=mix(source.rgb,history.rgb,weight);
|
|
2931
|
+
outColor=vec4(resolved,u_temporal?motion.b:source.a);
|
|
2244
2932
|
}
|
|
2245
|
-
`, "webgl2-taa");
|
|
2933
|
+
`, "webgl2-taa-accumulation");
|
|
2246
2934
|
return this.taaProgram;
|
|
2247
2935
|
}
|
|
2936
|
+
ensureTaaPresentationProgram() {
|
|
2937
|
+
if (this.taaPresentationProgram)
|
|
2938
|
+
return this.taaPresentationProgram;
|
|
2939
|
+
this.taaPresentationProgram = this.createFullscreenProgram(`#version 300 es
|
|
2940
|
+
precision highp float;
|
|
2941
|
+
uniform sampler2D u_source;
|
|
2942
|
+
uniform sampler2D u_accumulated;
|
|
2943
|
+
out vec4 outColor;
|
|
2944
|
+
void main() {
|
|
2945
|
+
ivec2 pixel=ivec2(gl_FragCoord.xy);
|
|
2946
|
+
ivec2 size=textureSize(u_source,0);
|
|
2947
|
+
vec4 source=texelFetch(u_source,pixel,0);
|
|
2948
|
+
vec3 accumulated=texelFetch(u_accumulated,pixel,0).rgb;
|
|
2949
|
+
// Retained native pixels isolate the remaining spatial excess to the
|
|
2950
|
+
// horizontally moving silhouette. Apply a bounded three-tap unsharp resolve
|
|
2951
|
+
// to presentation only; accumulated history remains immutable.
|
|
2952
|
+
vec3 center=mix(source.rgb,accumulated,0.90);
|
|
2953
|
+
ivec2 leftPixel=clamp(pixel+ivec2(-1,0),ivec2(0),size-1);
|
|
2954
|
+
ivec2 rightPixel=clamp(pixel+ivec2(1,0),ivec2(0),size-1);
|
|
2955
|
+
vec3 left=mix(texelFetch(u_source,leftPixel,0).rgb,texelFetch(u_accumulated,leftPixel,0).rgb,0.90);
|
|
2956
|
+
vec3 right=mix(texelFetch(u_source,rightPixel,0).rgb,texelFetch(u_accumulated,rightPixel,0).rgb,0.90);
|
|
2957
|
+
outColor=vec4(clamp(center*1.04-(left+right)*0.02,0.0,1.0),source.a);
|
|
2958
|
+
}
|
|
2959
|
+
`, "webgl2-taa-presentation");
|
|
2960
|
+
return this.taaPresentationProgram;
|
|
2961
|
+
}
|
|
2248
2962
|
createFullscreenProgram(fragmentSource, label) {
|
|
2249
2963
|
const vertex = this.compileShader(this.gl.VERTEX_SHADER, `#version 300 es
|
|
2250
2964
|
precision highp float;
|
|
@@ -3248,7 +3962,22 @@ function normalizeNativeBloomOptions(options) {
|
|
|
3248
3962
|
if (!Number.isInteger(radius) || radius < 0 || radius > 16) {
|
|
3249
3963
|
throw new RenderDeviceError("Bloom radius must be an integer in [0, 16].", "INVALID_POSTPROCESS_OPTIONS", { radius });
|
|
3250
3964
|
}
|
|
3251
|
-
|
|
3965
|
+
const softKnee = bloomNumberOption(options, "softKnee", 0);
|
|
3966
|
+
if (!(softKnee >= 0) || softKnee > 0.5) {
|
|
3967
|
+
throw new RenderDeviceError("Bloom softKnee must be finite and in [0, 0.5].", "INVALID_POSTPROCESS_OPTIONS", { softKnee });
|
|
3968
|
+
}
|
|
3969
|
+
const shoulder = bloomNumberOption(options, "shoulder", 0);
|
|
3970
|
+
if (!(shoulder >= 0) || shoulder > 1) {
|
|
3971
|
+
throw new RenderDeviceError("Bloom shoulder must be finite and in [0, 1].", "INVALID_POSTPROCESS_OPTIONS", { shoulder });
|
|
3972
|
+
}
|
|
3973
|
+
let quality;
|
|
3974
|
+
try {
|
|
3975
|
+
quality = normalizeBloomQualityPreset(options.quality);
|
|
3976
|
+
}
|
|
3977
|
+
catch {
|
|
3978
|
+
throw new RenderDeviceError("Bloom quality must be one of performance|balanced|cinematic.", "INVALID_POSTPROCESS_OPTIONS", { quality: options.quality });
|
|
3979
|
+
}
|
|
3980
|
+
return { threshold, intensity, radius, quality, softKnee, shoulder };
|
|
3252
3981
|
}
|
|
3253
3982
|
function normalizeNativeOutlineOptions(options) {
|
|
3254
3983
|
const width = outlineNumberOption(options, "width", 1);
|
|
@@ -3293,6 +4022,20 @@ function normalizeNativeSsaoOptions(options) {
|
|
|
3293
4022
|
}
|
|
3294
4023
|
return { radius, intensity, bias };
|
|
3295
4024
|
}
|
|
4025
|
+
/**
|
|
4026
|
+
* Camera depth range for linearizing sampleable GL depth in the native
|
|
4027
|
+
* SSR/depth-of-field programs (muse3jsparity-PRD A3). Missing or invalid
|
|
4028
|
+
* ranges fall back to the engine perspective defaults (0.1/1000) rather than
|
|
4029
|
+
* failing the frame: the range is a calibration hint, and every engine camera
|
|
4030
|
+
* builder defaults to exactly 0.1/1000. The CPU byte kernels intentionally
|
|
4031
|
+
* keep fixture-unit depth semantics (deterministic reference); the native
|
|
4032
|
+
* programs document their GL-depth contract in-shader.
|
|
4033
|
+
*/
|
|
4034
|
+
function normalizeLdrDepthRange(range) {
|
|
4035
|
+
const near = range && Number.isFinite(range.near) && range.near > 0 ? range.near : 0.1;
|
|
4036
|
+
const far = range && Number.isFinite(range.far) && range.far > near ? range.far : 1000;
|
|
4037
|
+
return { near, far };
|
|
4038
|
+
}
|
|
3296
4039
|
function normalizeNativeSsrOptions(options) {
|
|
3297
4040
|
const intensity = ssrNumberOption(options, "intensity", 0.32);
|
|
3298
4041
|
const maxDistance = ssrNumberOption(options, "maxDistance", 16);
|
|
@@ -3302,7 +4045,11 @@ function normalizeNativeSsrOptions(options) {
|
|
|
3302
4045
|
if (!Number.isInteger(maxDistance) || maxDistance < 1 || maxDistance > 64) {
|
|
3303
4046
|
throw new RenderDeviceError("SSR maxDistance must be an integer in [1, 64].", "INVALID_POSTPROCESS_OPTIONS", { maxDistance });
|
|
3304
4047
|
}
|
|
3305
|
-
|
|
4048
|
+
const projection = options["projection"];
|
|
4049
|
+
if (!(projection instanceof Float32Array))
|
|
4050
|
+
throw new RenderDeviceError("Native SSR requires the actual frame projection or view-projection matrix.", "INVALID_POSTPROCESS_OPTIONS");
|
|
4051
|
+
const inverseProjection = invertSsrProjection(projection);
|
|
4052
|
+
return { intensity, maxDistance, projection, inverseProjection };
|
|
3306
4053
|
}
|
|
3307
4054
|
function normalizeNativeDepthOfFieldOptions(options) {
|
|
3308
4055
|
const focusDepth = depthOfFieldNumberOption(options, "focusDepth", 0.5);
|
|
@@ -3322,6 +4069,11 @@ function normalizeNativeDepthOfFieldOptions(options) {
|
|
|
3322
4069
|
function normalizeNativeMotionBlurOptions(options, width, height) {
|
|
3323
4070
|
const samples = motionBlurNumberOption(options, "samples", 5);
|
|
3324
4071
|
const scale = motionBlurNumberOption(options, "scale", 1);
|
|
4072
|
+
if (options.temporal) {
|
|
4073
|
+
if (!Number.isInteger(samples) || samples < 2 || samples > 16 || scale < 0 || scale > 8)
|
|
4074
|
+
throw new RenderDeviceError("Invalid motion blur samples/scale", "INVALID_POSTPROCESS_OPTIONS");
|
|
4075
|
+
return { samples, scale, temporal: options.temporal };
|
|
4076
|
+
}
|
|
3325
4077
|
const value = options.velocity;
|
|
3326
4078
|
if (!(value instanceof Float32Array) && !Array.isArray(value)) {
|
|
3327
4079
|
throw new RenderDeviceError("Motion blur requires a velocity Float32Array or number array.", "WEBGL_LDR_POSTPROCESS_VELOCITY_REQUIRED");
|
|
@@ -3343,6 +4095,11 @@ function normalizeNativeMotionBlurOptions(options, width, height) {
|
|
|
3343
4095
|
}
|
|
3344
4096
|
function normalizeNativeTaaOptions(options, width, height) {
|
|
3345
4097
|
const blend = taaNumberOption(options, "blend", 0.18);
|
|
4098
|
+
if (options.temporal) {
|
|
4099
|
+
if (blend < 0 || blend > .95)
|
|
4100
|
+
throw new RenderDeviceError("Invalid TAA blend", "INVALID_POSTPROCESS_OPTIONS");
|
|
4101
|
+
return { blend, temporal: options.temporal };
|
|
4102
|
+
}
|
|
3346
4103
|
const history = options.history;
|
|
3347
4104
|
if (!(history instanceof Uint8Array)) {
|
|
3348
4105
|
throw new RenderDeviceError("TAA requires a Uint8Array history buffer.", "WEBGL_LDR_POSTPROCESS_HISTORY_REQUIRED");
|