@aura3d/rendering 2.0.4 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/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/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/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/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 +13 -0
- package/dist/PostProcessPass.d.ts.map +1 -1
- package/dist/PostProcessPass.js +15 -4
- package/dist/PostProcessPass.js.map +1 -1
- package/dist/ReflectionSurfaces.d.ts +14 -0
- package/dist/ReflectionSurfaces.d.ts.map +1 -1
- package/dist/ReflectionSurfaces.js +93 -0
- package/dist/ReflectionSurfaces.js.map +1 -1
- package/dist/RenderDevice.d.ts +87 -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.js +3 -1
- package/dist/Renderer.js.map +1 -1
- package/dist/RendererPostprocessPlan.d.ts +48 -0
- package/dist/RendererPostprocessPlan.d.ts.map +1 -1
- package/dist/RendererPostprocessPlan.js +101 -11
- 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/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 +478 -34
- package/dist/ShaderLibrary.js.map +1 -1
- package/dist/ShaderLibraryCore.d.ts.map +1 -1
- package/dist/ShaderLibraryCore.js +70 -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/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 +1 -0
- package/dist/TexturedPBRMaterial.d.ts.map +1 -1
- package/dist/TexturedPBRMaterial.js +23 -2
- 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 +58 -0
- package/dist/WebGL2Device.d.ts.map +1 -1
- package/dist/WebGL2Device.js +536 -17
- package/dist/WebGL2Device.js.map +1 -1
- package/dist/WebGPUDevice.d.ts +50 -0
- package/dist/WebGPUDevice.d.ts.map +1 -1
- package/dist/WebGPUDevice.js +504 -10
- package/dist/WebGPUDevice.js.map +1 -1
- 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 +186 -0
- package/dist/effects/GPUParticleBackend.d.ts.map +1 -1
- package/dist/effects/GPUParticleBackend.js +692 -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/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/index.d.ts +57 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -10
- package/dist/index.js.map +1 -1
- package/dist/materials/MaterialExtensions.js +3 -3
- package/dist/materials/MaterialExtensions.js.map +1 -1
- package/dist/postprocess/NativeBloomPyramid.d.ts +28 -0
- package/dist/postprocess/NativeBloomPyramid.d.ts.map +1 -0
- package/dist/postprocess/NativeBloomPyramid.js +54 -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/ProductionWebGPURenderer.d.ts +52 -0
- package/dist/production-runtime/ProductionWebGPURenderer.d.ts.map +1 -1
- package/dist/production-runtime/ProductionWebGPURenderer.js +101 -0
- package/dist/production-runtime/ProductionWebGPURenderer.js.map +1 -1
- package/dist/production-runtime/index.d.ts +5 -4
- package/dist/production-runtime/index.d.ts.map +1 -1
- package/dist/production-runtime/index.js +3 -2
- package/dist/production-runtime/index.js.map +1 -1
- package/dist/production-runtime/passes/DepthPrepass.d.ts +26 -1
- package/dist/production-runtime/passes/DepthPrepass.d.ts.map +1 -1
- package/dist/production-runtime/passes/DepthPrepass.js +49 -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 +24 -1
- package/dist/production-runtime/passes/OpaquePass.d.ts.map +1 -1
- package/dist/production-runtime/passes/OpaquePass.js +41 -2
- package/dist/production-runtime/passes/OpaquePass.js.map +1 -1
- package/dist/production-runtime/passes/ShadowPass.d.ts +24 -1
- package/dist/production-runtime/passes/ShadowPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/ShadowPass.js +43 -2
- package/dist/production-runtime/passes/ShadowPass.js.map +1 -1
- package/dist/production-runtime/passes/SkyboxPass.d.ts +21 -1
- package/dist/production-runtime/passes/SkyboxPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/SkyboxPass.js +39 -2
- package/dist/production-runtime/passes/SkyboxPass.js.map +1 -1
- package/dist/production-runtime/passes/ToneMappingPass.d.ts +29 -1
- package/dist/production-runtime/passes/ToneMappingPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/ToneMappingPass.js +53 -2
- package/dist/production-runtime/passes/ToneMappingPass.js.map +1 -1
- package/dist/production-runtime/passes/TransparentPass.d.ts +24 -1
- package/dist/production-runtime/passes/TransparentPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/TransparentPass.js +42 -2
- package/dist/production-runtime/passes/TransparentPass.js.map +1 -1
- 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 +78 -0
- package/dist/webgpu/WebGPUPostShaders.d.ts.map +1 -0
- package/dist/webgpu/WebGPUPostShaders.js +262 -0
- package/dist/webgpu/WebGPUPostShaders.js.map +1 -0
- package/package.json +3 -3
package/dist/WebGL2Device.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { RenderDeviceError, } from "./RenderDevice.js";
|
|
1
|
+
import { RenderDeviceError, resolveGpuTargetOwner, spreadGpuTargetInventory, } from "./RenderDevice.js";
|
|
2
2
|
import { Texture, isCompressedTextureFormat, isFloatColorTextureFormat } from "./Texture.js";
|
|
3
3
|
import { isTextureBinding, TextureBinding } from "./TextureBinding.js";
|
|
4
4
|
import {} from "./VertexFormat.js";
|
|
5
5
|
import { WebGL2StateCache } from "./WebGL2StateCache.js";
|
|
6
6
|
import { BLOOM_BRIGHT_LUT_HEIGHT, BLOOM_BRIGHT_LUT_WIDTH, BLOOM_COMPOSITE_LUT_SIZE, OUTLINE_BLEND_LUT_WIDTH, OUTLINE_LIMB_RADIX, createOutlineBlendLut, createOutlineGradientBound, createBloomBrightThresholdLut, createBloomCompositeLut } from "./postprocess/NativeLdrEffectLuts.js";
|
|
7
|
+
import { normalizeBloomQualityPreset, resolveBloomPyramidPlan, } from "./postprocess/NativeBloomPyramid.js";
|
|
7
8
|
const WEBGL_CUBE_FACES = ["px", "nx", "py", "ny", "pz", "nz"];
|
|
8
9
|
class WebGL2Buffer {
|
|
9
10
|
id;
|
|
@@ -125,19 +126,26 @@ export class WebGL2Device {
|
|
|
125
126
|
bloomBrightExtractProgram = null;
|
|
126
127
|
bloomBlurProgram = null;
|
|
127
128
|
bloomCompositeProgram = null;
|
|
129
|
+
bloomDownsampleProgram = null;
|
|
130
|
+
bloomAccumulateProgram = null;
|
|
131
|
+
bloomPyramidResources = null;
|
|
128
132
|
outlineProgram = null;
|
|
129
133
|
ssaoProgram = null;
|
|
130
134
|
ssrProgram = null;
|
|
131
135
|
depthOfFieldProgram = null;
|
|
132
136
|
motionBlurProgram = null;
|
|
133
137
|
motionBlurVelocityTexture = null;
|
|
138
|
+
motionBlurVelocitySize = null;
|
|
134
139
|
taaProgram = null;
|
|
135
140
|
taaHistoryTexture = null;
|
|
141
|
+
taaHistorySize = null;
|
|
136
142
|
bloomPingPongResources = null;
|
|
137
143
|
bloomBrightLutTexture = null;
|
|
138
144
|
bloomBrightLutThreshold = null;
|
|
139
145
|
bloomCompositeLutTexture = null;
|
|
140
146
|
bloomCompositeLutIntensity = null;
|
|
147
|
+
bloomCompositeLutShoulder = null;
|
|
148
|
+
lastBloomDiagnostics = null;
|
|
141
149
|
outlineBlendLutTexture = null;
|
|
142
150
|
outlineBlendLutKey = null;
|
|
143
151
|
depthReadbackProgram = null;
|
|
@@ -645,6 +653,10 @@ export class WebGL2Device {
|
|
|
645
653
|
pass: depthOfFieldPass ? "depth-of-field" : ssaoPass ? "ssao" : "ssr"
|
|
646
654
|
});
|
|
647
655
|
}
|
|
656
|
+
// Raw GL depth is nonlinear (0.1/1000 defaults park the play area past
|
|
657
|
+
// 0.97), which blinded every depth-gated native pass. SSR and DOF compare
|
|
658
|
+
// linearized depth; SSAO keeps its legacy response (documented partial).
|
|
659
|
+
const depthRange = normalizeLdrDepthRange(options.depthRange);
|
|
648
660
|
const outputTarget = options.outputTarget;
|
|
649
661
|
if (outputTarget && (!(outputTarget instanceof WebGL2RenderTarget) || !this.renderTargets.has(outputTarget) || outputTarget.disposed)) {
|
|
650
662
|
throw new RenderDeviceError("Output render target is not a live WebGL2 resource owned by this device", "INVALID_RESOURCE", {
|
|
@@ -702,8 +714,43 @@ export class WebGL2Device {
|
|
|
702
714
|
let ldrSourceHandle = source.colorHandle;
|
|
703
715
|
let temporarySourceIndex = -1;
|
|
704
716
|
if (bloomOptions && bloomResources) {
|
|
705
|
-
|
|
706
|
-
|
|
717
|
+
if (bloomOptions.quality === "performance") {
|
|
718
|
+
ldrSourceHandle = this.executeNativeBloomPasses(ldrSourceHandle, bloomResources, bloomOptions, vertexArray, sourceIsHdr);
|
|
719
|
+
temporarySourceIndex = 1;
|
|
720
|
+
this.recordBloomDiagnostics(bloomOptions, source.width, source.height, sourceIsHdr, 1);
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
const plan = resolveBloomPyramidPlan(source.width, source.height, bloomOptions.quality, sourceIsHdr);
|
|
724
|
+
const pyramid = this.ensureBloomPyramidResources(plan, sourceIsHdr);
|
|
725
|
+
const pyramidResult = this.executeNativeBloomPyramidPasses(ldrSourceHandle, source.width, source.height, pyramid, {
|
|
726
|
+
textureA: bloomResources.textures[0],
|
|
727
|
+
framebufferA: bloomResources.framebuffers[0],
|
|
728
|
+
textureB: bloomResources.textures[1],
|
|
729
|
+
framebufferB: bloomResources.framebuffers[1]
|
|
730
|
+
}, bloomOptions, vertexArray, sourceIsHdr);
|
|
731
|
+
// The pyramid sum lands in ping-pong texture A; composite it with the
|
|
732
|
+
// source through the proven single-scale composite program.
|
|
733
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, bloomResources.framebuffers[1]);
|
|
734
|
+
this.gl.viewport(0, 0, source.width, source.height);
|
|
735
|
+
const pyramidComposite = this.ensureBloomCompositeProgram();
|
|
736
|
+
this.gl.useProgram(pyramidComposite);
|
|
737
|
+
this.gl.bindVertexArray(vertexArray);
|
|
738
|
+
this.bindFullscreenTexture(0, ldrSourceHandle);
|
|
739
|
+
this.bindFullscreenTexture(1, pyramidResult);
|
|
740
|
+
if (this.bloomCompositeLutTexture)
|
|
741
|
+
this.bindFullscreenTexture(2, this.bloomCompositeLutTexture);
|
|
742
|
+
this.gl.uniform1i(this.gl.getUniformLocation(pyramidComposite, "u_source"), 0);
|
|
743
|
+
this.gl.uniform1i(this.gl.getUniformLocation(pyramidComposite, "u_blurred"), 1);
|
|
744
|
+
this.gl.uniform1i(this.gl.getUniformLocation(pyramidComposite, "u_compositeLut"), 2);
|
|
745
|
+
this.gl.uniform1i(this.gl.getUniformLocation(pyramidComposite, "u_hdr"), sourceIsHdr ? 1 : 0);
|
|
746
|
+
this.gl.uniform1f(this.gl.getUniformLocation(pyramidComposite, "u_intensity"), bloomOptions.intensity);
|
|
747
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
748
|
+
ldrSourceHandle = bloomResources.textures[1];
|
|
749
|
+
// The composited result lives in texture B, so subsequent passes
|
|
750
|
+
// must write texture A (index 0) to avoid sampling a bound target.
|
|
751
|
+
temporarySourceIndex = 0;
|
|
752
|
+
this.recordBloomDiagnostics(bloomOptions, source.width, source.height, sourceIsHdr, plan.mipCount);
|
|
753
|
+
}
|
|
707
754
|
}
|
|
708
755
|
if ((depthOfFieldOptions || motionBlurOptions || ssaoOptions || ssrOptions || taaOptions || outlineOptions) && bloomResources) {
|
|
709
756
|
if (tonePass || colorPass) {
|
|
@@ -714,7 +761,7 @@ export class WebGL2Device {
|
|
|
714
761
|
}
|
|
715
762
|
if (depthOfFieldOptions && source.depthTextureHandle) {
|
|
716
763
|
const depthOfFieldTargetIndex = temporarySourceIndex === 0 ? 1 : 0;
|
|
717
|
-
ldrSourceHandle = this.executeNativeDepthOfFieldPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, depthOfFieldOptions, vertexArray, depthOfFieldTargetIndex);
|
|
764
|
+
ldrSourceHandle = this.executeNativeDepthOfFieldPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, depthOfFieldOptions, vertexArray, depthOfFieldTargetIndex, depthRange);
|
|
718
765
|
temporarySourceIndex = depthOfFieldTargetIndex;
|
|
719
766
|
}
|
|
720
767
|
if (motionBlurOptions) {
|
|
@@ -729,7 +776,7 @@ export class WebGL2Device {
|
|
|
729
776
|
}
|
|
730
777
|
if (ssrOptions && source.depthTextureHandle) {
|
|
731
778
|
const ssrTargetIndex = temporarySourceIndex === 0 ? 1 : 0;
|
|
732
|
-
ldrSourceHandle = this.executeNativeSsrPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, ssrOptions, vertexArray, ssrTargetIndex);
|
|
779
|
+
ldrSourceHandle = this.executeNativeSsrPass(ldrSourceHandle, source.depthTextureHandle, bloomResources, ssrOptions, vertexArray, ssrTargetIndex, depthRange);
|
|
733
780
|
temporarySourceIndex = ssrTargetIndex;
|
|
734
781
|
}
|
|
735
782
|
if (taaOptions) {
|
|
@@ -1023,6 +1070,7 @@ export class WebGL2Device {
|
|
|
1023
1070
|
const stateCacheStats = this.stateCache.stats();
|
|
1024
1071
|
const bufferBytes = [...this.buffers].filter((buffer) => !buffer.disposed).reduce((total, buffer) => total + buffer.byteLength, 0);
|
|
1025
1072
|
const textureBytes = liveTextureList.reduce((total, texture) => total + texture.byteLength, 0);
|
|
1073
|
+
const gpuTargetEntries = this.describeGpuTargetInventory(liveRenderTargets);
|
|
1026
1074
|
return {
|
|
1027
1075
|
drawCalls: this.drawCalls,
|
|
1028
1076
|
buffers: [...this.buffers].filter((buffer) => !buffer.disposed).length,
|
|
@@ -1041,6 +1089,7 @@ export class WebGL2Device {
|
|
|
1041
1089
|
nativeShadowMapBindings: this.nativeShadowMapBindings,
|
|
1042
1090
|
shadowRenderTargetsAllocated: this.shadowRenderTargetsAllocated,
|
|
1043
1091
|
nativeInstancedSubmissions: this.nativeInstancedSubmissions,
|
|
1092
|
+
bloom: this.lastBloomDiagnostics,
|
|
1044
1093
|
samplerAnisotropyUploads: this.samplerAnisotropyUploadCount,
|
|
1045
1094
|
maxTextureAnisotropy: this.maxTextureAnisotropy,
|
|
1046
1095
|
stateCacheIssued: stateCacheStats.issued,
|
|
@@ -1054,6 +1103,7 @@ export class WebGL2Device {
|
|
|
1054
1103
|
disposedShaders: [...this.shaders].filter((shader) => shader.disposed).length,
|
|
1055
1104
|
disposedRenderTargets: [...this.renderTargets].filter((target) => target.disposed).length,
|
|
1056
1105
|
disposedTextures: [...this.renderTargets].filter((target) => target.colorTexture.disposed).length + this.releasedTextureHandles,
|
|
1106
|
+
...spreadGpuTargetInventory(gpuTargetEntries),
|
|
1057
1107
|
lastError: this.lastError,
|
|
1058
1108
|
contextLost: this.contextLost
|
|
1059
1109
|
};
|
|
@@ -1116,6 +1166,14 @@ export class WebGL2Device {
|
|
|
1116
1166
|
this.gl.deleteProgram(this.bloomCompositeProgram);
|
|
1117
1167
|
this.bloomCompositeProgram = null;
|
|
1118
1168
|
}
|
|
1169
|
+
if (this.bloomDownsampleProgram) {
|
|
1170
|
+
this.gl.deleteProgram(this.bloomDownsampleProgram);
|
|
1171
|
+
this.bloomDownsampleProgram = null;
|
|
1172
|
+
}
|
|
1173
|
+
if (this.bloomAccumulateProgram) {
|
|
1174
|
+
this.gl.deleteProgram(this.bloomAccumulateProgram);
|
|
1175
|
+
this.bloomAccumulateProgram = null;
|
|
1176
|
+
}
|
|
1119
1177
|
if (this.outlineProgram) {
|
|
1120
1178
|
this.gl.deleteProgram(this.outlineProgram);
|
|
1121
1179
|
this.outlineProgram = null;
|
|
@@ -1139,6 +1197,7 @@ export class WebGL2Device {
|
|
|
1139
1197
|
if (this.motionBlurVelocityTexture) {
|
|
1140
1198
|
this.gl.deleteTexture(this.motionBlurVelocityTexture);
|
|
1141
1199
|
this.motionBlurVelocityTexture = null;
|
|
1200
|
+
this.motionBlurVelocitySize = null;
|
|
1142
1201
|
}
|
|
1143
1202
|
if (this.taaProgram) {
|
|
1144
1203
|
this.gl.deleteProgram(this.taaProgram);
|
|
@@ -1147,8 +1206,10 @@ export class WebGL2Device {
|
|
|
1147
1206
|
if (this.taaHistoryTexture) {
|
|
1148
1207
|
this.gl.deleteTexture(this.taaHistoryTexture);
|
|
1149
1208
|
this.taaHistoryTexture = null;
|
|
1209
|
+
this.taaHistorySize = null;
|
|
1150
1210
|
}
|
|
1151
1211
|
this.disposeBloomPingPongResources();
|
|
1212
|
+
this.disposeBloomPyramidResources();
|
|
1152
1213
|
if (this.bloomBrightLutTexture) {
|
|
1153
1214
|
this.gl.deleteTexture(this.bloomBrightLutTexture);
|
|
1154
1215
|
this.bloomBrightLutTexture = null;
|
|
@@ -1158,6 +1219,7 @@ export class WebGL2Device {
|
|
|
1158
1219
|
this.gl.deleteTexture(this.bloomCompositeLutTexture);
|
|
1159
1220
|
this.bloomCompositeLutTexture = null;
|
|
1160
1221
|
this.bloomCompositeLutIntensity = null;
|
|
1222
|
+
this.bloomCompositeLutShoulder = null;
|
|
1161
1223
|
}
|
|
1162
1224
|
if (this.outlineBlendLutTexture) {
|
|
1163
1225
|
this.gl.deleteTexture(this.outlineBlendLutTexture);
|
|
@@ -1283,6 +1345,7 @@ export class WebGL2Device {
|
|
|
1283
1345
|
return current;
|
|
1284
1346
|
}
|
|
1285
1347
|
this.disposeBloomPingPongResources();
|
|
1348
|
+
this.disposeBloomPyramidResources();
|
|
1286
1349
|
const textureUnit0 = this.captureTextureUnit0();
|
|
1287
1350
|
const previousFramebuffer = this.gl.getParameter(this.gl.FRAMEBUFFER_BINDING);
|
|
1288
1351
|
const textures = [];
|
|
@@ -1347,7 +1410,8 @@ export class WebGL2Device {
|
|
|
1347
1410
|
}
|
|
1348
1411
|
ensureBloomLutTextures(options) {
|
|
1349
1412
|
const updateBright = this.bloomBrightLutThreshold !== options.threshold;
|
|
1350
|
-
const updateComposite = this.bloomCompositeLutIntensity !== options.intensity
|
|
1413
|
+
const updateComposite = this.bloomCompositeLutIntensity !== options.intensity ||
|
|
1414
|
+
this.bloomCompositeLutShoulder !== options.shoulder;
|
|
1351
1415
|
if (this.bloomBrightLutTexture && this.bloomCompositeLutTexture && !updateBright && !updateComposite) {
|
|
1352
1416
|
return;
|
|
1353
1417
|
}
|
|
@@ -1366,10 +1430,11 @@ export class WebGL2Device {
|
|
|
1366
1430
|
this.bloomCompositeLutTexture = this.createNativeBloomDataTexture();
|
|
1367
1431
|
}
|
|
1368
1432
|
if (updateComposite) {
|
|
1369
|
-
const pixels = createBloomCompositeLut(options.intensity);
|
|
1433
|
+
const pixels = createBloomCompositeLut(options.intensity, { shoulder: options.shoulder });
|
|
1370
1434
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.bloomCompositeLutTexture);
|
|
1371
1435
|
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
1436
|
this.bloomCompositeLutIntensity = options.intensity;
|
|
1437
|
+
this.bloomCompositeLutShoulder = options.shoulder;
|
|
1373
1438
|
}
|
|
1374
1439
|
}
|
|
1375
1440
|
finally {
|
|
@@ -1414,6 +1479,7 @@ export class WebGL2Device {
|
|
|
1414
1479
|
if (!this.motionBlurVelocityTexture) {
|
|
1415
1480
|
this.motionBlurVelocityTexture = this.createNativeBloomDataTexture();
|
|
1416
1481
|
}
|
|
1482
|
+
this.motionBlurVelocitySize = { width, height };
|
|
1417
1483
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.motionBlurVelocityTexture);
|
|
1418
1484
|
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RG32F, width, height, 0, this.gl.RG, this.gl.FLOAT, velocity);
|
|
1419
1485
|
}
|
|
@@ -1428,6 +1494,7 @@ export class WebGL2Device {
|
|
|
1428
1494
|
if (!this.taaHistoryTexture) {
|
|
1429
1495
|
this.taaHistoryTexture = this.createNativeBloomDataTexture();
|
|
1430
1496
|
}
|
|
1497
|
+
this.taaHistorySize = { width, height };
|
|
1431
1498
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.taaHistoryTexture);
|
|
1432
1499
|
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA8, width, height, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, history);
|
|
1433
1500
|
}
|
|
@@ -1487,6 +1554,7 @@ export class WebGL2Device {
|
|
|
1487
1554
|
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_brightLut"), 1);
|
|
1488
1555
|
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_hdr"), hdr ? 1 : 0);
|
|
1489
1556
|
this.gl.uniform1f(this.gl.getUniformLocation(brightProgram, "u_threshold"), options.threshold);
|
|
1557
|
+
this.gl.uniform1f(this.gl.getUniformLocation(brightProgram, "u_softKnee"), options.softKnee);
|
|
1490
1558
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1491
1559
|
const blurProgram = this.ensureBloomBlurProgram();
|
|
1492
1560
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebufferB);
|
|
@@ -1517,6 +1585,140 @@ export class WebGL2Device {
|
|
|
1517
1585
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1518
1586
|
return textureB;
|
|
1519
1587
|
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Balanced/cinematic bloom: bright-extract once, then a half-resolution mip
|
|
1590
|
+
* chain where every level is downsampled, separably blurred, and accumulated
|
|
1591
|
+
* with its energy-preserving weight. Returns a full-resolution bloom texture
|
|
1592
|
+
* holding the weighted sum, ready for the existing composite program.
|
|
1593
|
+
*/
|
|
1594
|
+
executeNativeBloomPyramidPasses(sourceTexture, sourceWidth, sourceHeight, resources, fullResStaging, options, vertexArray, hdr) {
|
|
1595
|
+
const plan = resources.plan;
|
|
1596
|
+
const mipCount = plan.mips.length;
|
|
1597
|
+
const downsampleProgram = this.ensureBloomDownsampleProgram();
|
|
1598
|
+
const brightProgram = this.ensureBloomBrightExtractProgram();
|
|
1599
|
+
const blurProgram = this.ensureBloomBlurProgram();
|
|
1600
|
+
const accumulateProgram = this.ensureBloomAccumulateProgram();
|
|
1601
|
+
const brightLut = this.bloomBrightLutTexture;
|
|
1602
|
+
const mipTexture = (level, slot) => resources.textures[level * 2 + slot];
|
|
1603
|
+
const mipFramebuffer = (level, slot) => resources.framebuffers[level * 2 + slot];
|
|
1604
|
+
const mipSize = (level) => plan.mips[level];
|
|
1605
|
+
// Blurred result settles in slot 1 for level 0, slot 0 for levels >= 1.
|
|
1606
|
+
const blurredSlot = (level) => (level === 0 ? 1 : 0);
|
|
1607
|
+
const drawDownsample = (fromTexture, fromWidth, fromHeight, toFramebuffer, toWidth, toHeight) => {
|
|
1608
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, toFramebuffer);
|
|
1609
|
+
this.gl.viewport(0, 0, toWidth, toHeight);
|
|
1610
|
+
this.gl.useProgram(downsampleProgram);
|
|
1611
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1612
|
+
this.bindFullscreenTexture(0, fromTexture);
|
|
1613
|
+
this.gl.uniform1i(this.gl.getUniformLocation(downsampleProgram, "u_source"), 0);
|
|
1614
|
+
this.gl.uniform2f(this.gl.getUniformLocation(downsampleProgram, "u_texelSize"), 1 / fromWidth, 1 / fromHeight);
|
|
1615
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1616
|
+
};
|
|
1617
|
+
const drawBrightExtract = (fromTexture, toFramebuffer, size) => {
|
|
1618
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, toFramebuffer);
|
|
1619
|
+
this.gl.viewport(0, 0, size.width, size.height);
|
|
1620
|
+
this.gl.useProgram(brightProgram);
|
|
1621
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1622
|
+
this.bindFullscreenTexture(0, fromTexture);
|
|
1623
|
+
if (brightLut)
|
|
1624
|
+
this.bindFullscreenTexture(1, brightLut);
|
|
1625
|
+
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_source"), 0);
|
|
1626
|
+
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_brightLut"), 1);
|
|
1627
|
+
this.gl.uniform1i(this.gl.getUniformLocation(brightProgram, "u_hdr"), hdr ? 1 : 0);
|
|
1628
|
+
this.gl.uniform1f(this.gl.getUniformLocation(brightProgram, "u_threshold"), options.threshold);
|
|
1629
|
+
this.gl.uniform1f(this.gl.getUniformLocation(brightProgram, "u_softKnee"), options.softKnee);
|
|
1630
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1631
|
+
};
|
|
1632
|
+
const drawSeparableBlur = (readTexture, tmpTexture, tmpFramebuffer, outFramebuffer, size) => {
|
|
1633
|
+
// Horizontal into the temp target, vertical back into the output target.
|
|
1634
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, tmpFramebuffer);
|
|
1635
|
+
this.gl.viewport(0, 0, size.width, size.height);
|
|
1636
|
+
this.gl.useProgram(blurProgram);
|
|
1637
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1638
|
+
this.bindFullscreenTexture(0, readTexture);
|
|
1639
|
+
this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_source"), 0);
|
|
1640
|
+
this.gl.uniform2i(this.gl.getUniformLocation(blurProgram, "u_size"), size.width, size.height);
|
|
1641
|
+
this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_radius"), options.radius);
|
|
1642
|
+
this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_horizontal"), 1);
|
|
1643
|
+
this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_hdr"), hdr ? 1 : 0);
|
|
1644
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1645
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, outFramebuffer);
|
|
1646
|
+
this.bindFullscreenTexture(0, tmpTexture);
|
|
1647
|
+
this.gl.uniform1i(this.gl.getUniformLocation(blurProgram, "u_horizontal"), 0);
|
|
1648
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1649
|
+
};
|
|
1650
|
+
// Level 0: downsample, bright-extract into slot 1, blur H into slot 0 and
|
|
1651
|
+
// V back into slot 1. Blurred result settles in slot 1.
|
|
1652
|
+
const level0 = mipSize(0);
|
|
1653
|
+
drawDownsample(sourceTexture, sourceWidth, sourceHeight, mipFramebuffer(0, 0), level0.width, level0.height);
|
|
1654
|
+
drawBrightExtract(mipTexture(0, 0), mipFramebuffer(0, 1), level0);
|
|
1655
|
+
drawSeparableBlur(mipTexture(0, 1), mipTexture(0, 0), mipFramebuffer(0, 0), mipFramebuffer(0, 1), level0);
|
|
1656
|
+
for (let level = 1; level < mipCount; level += 1) {
|
|
1657
|
+
const size = mipSize(level);
|
|
1658
|
+
const previous = mipSize(level - 1);
|
|
1659
|
+
drawDownsample(mipTexture(level - 1, blurredSlot(level - 1)), previous.width, previous.height, mipFramebuffer(level, 0), size.width, size.height);
|
|
1660
|
+
// H into slot 1, V back into slot 0: blurred result settles in slot 0.
|
|
1661
|
+
drawSeparableBlur(mipTexture(level, 0), mipTexture(level, 1), mipFramebuffer(level, 1), mipFramebuffer(level, 0), size);
|
|
1662
|
+
}
|
|
1663
|
+
// Accumulate smallest-to-largest, ping-ponging between the two
|
|
1664
|
+
// accumulators (a pass must never sample its own bound target).
|
|
1665
|
+
const weights = plan.weights;
|
|
1666
|
+
let readAccumulator = { texture: resources.accumulatorTextureA, framebuffer: resources.accumulatorFramebufferA };
|
|
1667
|
+
let writeAccumulator = { texture: resources.accumulatorTextureB, framebuffer: resources.accumulatorFramebufferB };
|
|
1668
|
+
// Clear both accumulators; the seed reads the cleared sibling, never the
|
|
1669
|
+
// bound target (feedback loop).
|
|
1670
|
+
for (const acc of [readAccumulator, writeAccumulator]) {
|
|
1671
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, acc.framebuffer);
|
|
1672
|
+
this.gl.viewport(0, 0, level0.width, level0.height);
|
|
1673
|
+
this.gl.clearColor(0, 0, 0, 0);
|
|
1674
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
1675
|
+
}
|
|
1676
|
+
// Seed: smallest mip onto the cleared sibling accumulator.
|
|
1677
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, writeAccumulator.framebuffer);
|
|
1678
|
+
this.gl.viewport(0, 0, level0.width, level0.height);
|
|
1679
|
+
this.gl.useProgram(accumulateProgram);
|
|
1680
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1681
|
+
this.bindFullscreenTexture(0, readAccumulator.texture);
|
|
1682
|
+
this.bindFullscreenTexture(1, mipTexture(mipCount - 1, blurredSlot(mipCount - 1)));
|
|
1683
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_base"), 0);
|
|
1684
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_bloom"), 1);
|
|
1685
|
+
this.gl.uniform1f(this.gl.getUniformLocation(accumulateProgram, "u_weight"), weights[mipCount - 1]);
|
|
1686
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1687
|
+
for (let level = mipCount - 2; level >= 0; level -= 1) {
|
|
1688
|
+
const nextRead = writeAccumulator;
|
|
1689
|
+
const nextWrite = readAccumulator;
|
|
1690
|
+
readAccumulator = nextRead;
|
|
1691
|
+
writeAccumulator = nextWrite;
|
|
1692
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, writeAccumulator.framebuffer);
|
|
1693
|
+
this.gl.viewport(0, 0, level0.width, level0.height);
|
|
1694
|
+
this.gl.useProgram(accumulateProgram);
|
|
1695
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1696
|
+
this.bindFullscreenTexture(0, readAccumulator.texture);
|
|
1697
|
+
this.bindFullscreenTexture(1, mipTexture(level, blurredSlot(level)));
|
|
1698
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_base"), 0);
|
|
1699
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_bloom"), 1);
|
|
1700
|
+
this.gl.uniform1f(this.gl.getUniformLocation(accumulateProgram, "u_weight"), weights[level]);
|
|
1701
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1702
|
+
}
|
|
1703
|
+
// Upsample the weighted sum onto full-resolution staging target A, using
|
|
1704
|
+
// the cleared staging target B as the zero base (never sample A while
|
|
1705
|
+
// rendering into it).
|
|
1706
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, fullResStaging.framebufferB);
|
|
1707
|
+
this.gl.viewport(0, 0, sourceWidth, sourceHeight);
|
|
1708
|
+
this.gl.clearColor(0, 0, 0, 0);
|
|
1709
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
1710
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, fullResStaging.framebufferA);
|
|
1711
|
+
this.gl.viewport(0, 0, sourceWidth, sourceHeight);
|
|
1712
|
+
this.gl.useProgram(accumulateProgram);
|
|
1713
|
+
this.gl.bindVertexArray(vertexArray);
|
|
1714
|
+
this.bindFullscreenTexture(0, fullResStaging.textureB);
|
|
1715
|
+
this.bindFullscreenTexture(1, writeAccumulator.texture);
|
|
1716
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_base"), 0);
|
|
1717
|
+
this.gl.uniform1i(this.gl.getUniformLocation(accumulateProgram, "u_bloom"), 1);
|
|
1718
|
+
this.gl.uniform1f(this.gl.getUniformLocation(accumulateProgram, "u_weight"), 1);
|
|
1719
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1720
|
+
return fullResStaging.textureA;
|
|
1721
|
+
}
|
|
1520
1722
|
executeNativeOutlinePasses(sourceTexture, resources, options, vertexArray, targetIndex) {
|
|
1521
1723
|
const blendLut = this.outlineBlendLutTexture;
|
|
1522
1724
|
if (!blendLut) {
|
|
@@ -1559,7 +1761,7 @@ export class WebGL2Device {
|
|
|
1559
1761
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1560
1762
|
return targetTexture;
|
|
1561
1763
|
}
|
|
1562
|
-
executeNativeSsrPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex) {
|
|
1764
|
+
executeNativeSsrPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex, depthRange) {
|
|
1563
1765
|
const program = this.ensureSsrProgram();
|
|
1564
1766
|
const targetTexture = resources.textures[targetIndex];
|
|
1565
1767
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
|
|
@@ -1573,10 +1775,12 @@ export class WebGL2Device {
|
|
|
1573
1775
|
this.gl.uniform2i(this.gl.getUniformLocation(program, "u_size"), resources.width, resources.height);
|
|
1574
1776
|
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_intensity"), options.intensity);
|
|
1575
1777
|
this.gl.uniform1i(this.gl.getUniformLocation(program, "u_maxDistance"), options.maxDistance);
|
|
1778
|
+
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_depthNear"), depthRange.near);
|
|
1779
|
+
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_depthFar"), depthRange.far);
|
|
1576
1780
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1577
1781
|
return targetTexture;
|
|
1578
1782
|
}
|
|
1579
|
-
executeNativeDepthOfFieldPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex) {
|
|
1783
|
+
executeNativeDepthOfFieldPass(sourceTexture, depthTexture, resources, options, vertexArray, targetIndex, depthRange) {
|
|
1580
1784
|
const program = this.ensureDepthOfFieldProgram();
|
|
1581
1785
|
const targetTexture = resources.textures[targetIndex];
|
|
1582
1786
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, resources.framebuffers[targetIndex]);
|
|
@@ -1591,6 +1795,8 @@ export class WebGL2Device {
|
|
|
1591
1795
|
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_focusDepth"), options.focusDepth);
|
|
1592
1796
|
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_focusRange"), options.focusRange);
|
|
1593
1797
|
this.gl.uniform1i(this.gl.getUniformLocation(program, "u_maxRadius"), options.maxRadius);
|
|
1798
|
+
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_depthNear"), depthRange.near);
|
|
1799
|
+
this.gl.uniform1f(this.gl.getUniformLocation(program, "u_depthFar"), depthRange.far);
|
|
1594
1800
|
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
|
1595
1801
|
return targetTexture;
|
|
1596
1802
|
}
|
|
@@ -1648,6 +1854,223 @@ export class WebGL2Device {
|
|
|
1648
1854
|
this.gl.deleteFramebuffer(framebuffer);
|
|
1649
1855
|
this.bloomPingPongResources = null;
|
|
1650
1856
|
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Diagnostics for the most recent native bloom execution (muse3jsparity-PRD
|
|
1859
|
+
* A1). Null when no native bloom pass has run yet on this device.
|
|
1860
|
+
*/
|
|
1861
|
+
getBloomDiagnostics() {
|
|
1862
|
+
return this.lastBloomDiagnostics;
|
|
1863
|
+
}
|
|
1864
|
+
recordBloomDiagnostics(options, sourceWidth, sourceHeight, hdr, mipCount) {
|
|
1865
|
+
const plan = resolveBloomPyramidPlan(sourceWidth, sourceHeight, options.quality, hdr);
|
|
1866
|
+
const pingPongBytes = sourceWidth * sourceHeight * 4 * (hdr ? 2 : 1) * 2;
|
|
1867
|
+
const bytesPerChannel = plan.halfFloat ? 2 : 1;
|
|
1868
|
+
const level0 = plan.mips[0];
|
|
1869
|
+
const accumulatorBytes = level0.width * level0.height * 4 * bytesPerChannel;
|
|
1870
|
+
this.lastBloomDiagnostics = {
|
|
1871
|
+
quality: options.quality,
|
|
1872
|
+
mipCount,
|
|
1873
|
+
targetCount: options.quality === "performance" ? 2 : plan.mips.length * 2 + 1,
|
|
1874
|
+
targetBytes: (options.quality === "performance" ? pingPongBytes : plan.targetBytes + accumulatorBytes + pingPongBytes),
|
|
1875
|
+
compositeGain: options.intensity,
|
|
1876
|
+
threshold: options.threshold,
|
|
1877
|
+
intensity: options.intensity,
|
|
1878
|
+
softKnee: options.softKnee,
|
|
1879
|
+
shoulder: options.shoulder,
|
|
1880
|
+
halfFloat: hdr || plan.halfFloat,
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* PART U: every live GPU allocation with bytes + lane owner — registry
|
|
1885
|
+
* render targets (B1 shadow maps arrive labeled, B4 captures arrive as
|
|
1886
|
+
* `-mirror-target`/`-scene-color-target`) plus the raw-handle residents the
|
|
1887
|
+
* registry set cannot see: the A1 bloom pyramid (two targets per mip plus
|
|
1888
|
+
* two level-0 accumulators), the legacy bloom ping-pong pair, the A1/A3
|
|
1889
|
+
* lookup textures, and the motion-blur/taa history textures. The
|
|
1890
|
+
* depth-readback scratch pair is intentionally absent: it is allocated and
|
|
1891
|
+
* deleted inside one synchronous call, so it is never live at a diagnostics
|
|
1892
|
+
* boundary. A5 volumetric light, C4 decals, and the G1 SDF atlas own no GPU
|
|
1893
|
+
* target (forward uniforms + CPU kernel, CPU geometry, CPU atlas data), so
|
|
1894
|
+
* no entry is emitted for them; a target appearing under their owner would
|
|
1895
|
+
* be the signal that the lane grew one.
|
|
1896
|
+
*/
|
|
1897
|
+
describeGpuTargetInventory(liveRenderTargets) {
|
|
1898
|
+
const entries = liveRenderTargets.map((target) => ({
|
|
1899
|
+
label: target.label,
|
|
1900
|
+
kind: "render-target",
|
|
1901
|
+
bytes: target.colorTexture.byteLength + (target.depthTexture?.byteLength ?? 0),
|
|
1902
|
+
owner: resolveGpuTargetOwner(target.label)
|
|
1903
|
+
}));
|
|
1904
|
+
const pyramid = this.bloomPyramidResources;
|
|
1905
|
+
if (pyramid) {
|
|
1906
|
+
const bytesPerPixel = pyramid.hdr ? 8 : 4;
|
|
1907
|
+
pyramid.plan.mips.forEach((mip, index) => {
|
|
1908
|
+
for (const side of ["a", "b"]) {
|
|
1909
|
+
entries.push({
|
|
1910
|
+
label: `bloom-pyramid-mip${index}${side}`,
|
|
1911
|
+
kind: "pyramid-mip",
|
|
1912
|
+
bytes: mip.width * mip.height * bytesPerPixel,
|
|
1913
|
+
owner: "a1-bloom"
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
const level0 = pyramid.plan.mips[0];
|
|
1918
|
+
if (level0) {
|
|
1919
|
+
for (const side of ["a", "b"]) {
|
|
1920
|
+
entries.push({
|
|
1921
|
+
label: `bloom-pyramid-accumulator-${side}`,
|
|
1922
|
+
kind: "pyramid-accumulator",
|
|
1923
|
+
bytes: level0.width * level0.height * bytesPerPixel,
|
|
1924
|
+
owner: "a1-bloom"
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
const pingPong = this.bloomPingPongResources;
|
|
1930
|
+
if (pingPong) {
|
|
1931
|
+
const bytesPerPixel = pingPong.hdr ? 8 : 4;
|
|
1932
|
+
for (const side of ["a", "b"]) {
|
|
1933
|
+
entries.push({
|
|
1934
|
+
label: `bloom-ping-pong-${side}`,
|
|
1935
|
+
kind: "ping-pong",
|
|
1936
|
+
bytes: pingPong.width * pingPong.height * bytesPerPixel,
|
|
1937
|
+
owner: "a1-bloom"
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
if (this.bloomBrightLutTexture) {
|
|
1942
|
+
entries.push({
|
|
1943
|
+
label: "bloom-bright-lut",
|
|
1944
|
+
kind: "lut",
|
|
1945
|
+
bytes: BLOOM_BRIGHT_LUT_WIDTH * BLOOM_BRIGHT_LUT_HEIGHT * 4,
|
|
1946
|
+
owner: "a1-bloom"
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
if (this.bloomCompositeLutTexture) {
|
|
1950
|
+
entries.push({
|
|
1951
|
+
label: "bloom-composite-lut",
|
|
1952
|
+
kind: "lut",
|
|
1953
|
+
bytes: BLOOM_COMPOSITE_LUT_SIZE * BLOOM_COMPOSITE_LUT_SIZE * 4,
|
|
1954
|
+
owner: "a1-bloom"
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
if (this.outlineBlendLutTexture) {
|
|
1958
|
+
entries.push({ label: "outline-blend-lut", kind: "lut", bytes: OUTLINE_BLEND_LUT_WIDTH * 4, owner: "post" });
|
|
1959
|
+
}
|
|
1960
|
+
if (this.motionBlurVelocityTexture && this.motionBlurVelocitySize) {
|
|
1961
|
+
entries.push({
|
|
1962
|
+
label: "motion-blur-velocity",
|
|
1963
|
+
kind: "history",
|
|
1964
|
+
bytes: this.motionBlurVelocitySize.width * this.motionBlurVelocitySize.height * 8,
|
|
1965
|
+
owner: "post"
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
if (this.taaHistoryTexture && this.taaHistorySize) {
|
|
1969
|
+
entries.push({
|
|
1970
|
+
label: "taa-history",
|
|
1971
|
+
kind: "history",
|
|
1972
|
+
bytes: this.taaHistorySize.width * this.taaHistorySize.height * 4,
|
|
1973
|
+
owner: "post"
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
return entries;
|
|
1977
|
+
}
|
|
1978
|
+
disposeBloomPyramidResources() {
|
|
1979
|
+
if (!this.bloomPyramidResources)
|
|
1980
|
+
return;
|
|
1981
|
+
for (const texture of this.bloomPyramidResources.textures)
|
|
1982
|
+
this.gl.deleteTexture(texture);
|
|
1983
|
+
for (const framebuffer of this.bloomPyramidResources.framebuffers)
|
|
1984
|
+
this.gl.deleteFramebuffer(framebuffer);
|
|
1985
|
+
this.gl.deleteTexture(this.bloomPyramidResources.accumulatorTextureA);
|
|
1986
|
+
this.gl.deleteFramebuffer(this.bloomPyramidResources.accumulatorFramebufferA);
|
|
1987
|
+
this.gl.deleteTexture(this.bloomPyramidResources.accumulatorTextureB);
|
|
1988
|
+
this.gl.deleteFramebuffer(this.bloomPyramidResources.accumulatorFramebufferB);
|
|
1989
|
+
this.bloomPyramidResources = null;
|
|
1990
|
+
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Mip-chain targets for the balanced/cinematic bloom pyramid. Each level is a
|
|
1993
|
+
* single texture+framebuffer at the planned size with linear filtering so the
|
|
1994
|
+
* downsample and accumulate passes can sample across levels. Half-float backs
|
|
1995
|
+
* the chain when the source is HDR or the cinematic preset requests it.
|
|
1996
|
+
*/
|
|
1997
|
+
ensureBloomPyramidResources(plan, hdr) {
|
|
1998
|
+
const current = this.bloomPyramidResources;
|
|
1999
|
+
if (current && current.plan.quality === plan.quality && current.hdr === (hdr || plan.halfFloat) &&
|
|
2000
|
+
current.plan.mips.length === plan.mips.length &&
|
|
2001
|
+
current.plan.mips.every((mip, index) => mip.width === plan.mips[index].width && mip.height === plan.mips[index].height)) {
|
|
2002
|
+
return current;
|
|
2003
|
+
}
|
|
2004
|
+
this.disposeBloomPyramidResources();
|
|
2005
|
+
const textureUnit0 = this.captureTextureUnit0();
|
|
2006
|
+
const previousFramebuffer = this.gl.getParameter(this.gl.FRAMEBUFFER_BINDING);
|
|
2007
|
+
const textures = [];
|
|
2008
|
+
const framebuffers = [];
|
|
2009
|
+
const useHalfFloat = hdr || plan.halfFloat;
|
|
2010
|
+
const allocateTarget = (width, height, label) => {
|
|
2011
|
+
const texture = this.gl.createTexture();
|
|
2012
|
+
const framebuffer = this.gl.createFramebuffer();
|
|
2013
|
+
if (!texture || !framebuffer) {
|
|
2014
|
+
if (texture)
|
|
2015
|
+
this.gl.deleteTexture(texture);
|
|
2016
|
+
if (framebuffer)
|
|
2017
|
+
this.gl.deleteFramebuffer(framebuffer);
|
|
2018
|
+
throw new RenderDeviceError(`Failed to allocate WebGL2 bloom pyramid ${label}`, "WEBGL_ALLOCATION_FAILED", { width, height });
|
|
2019
|
+
}
|
|
2020
|
+
textures.push(texture);
|
|
2021
|
+
framebuffers.push(framebuffer);
|
|
2022
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
|
|
2023
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
|
2024
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
2025
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
|
2026
|
+
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
|
|
2027
|
+
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);
|
|
2028
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
|
|
2029
|
+
this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, texture, 0);
|
|
2030
|
+
const status = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER);
|
|
2031
|
+
if (status !== this.gl.FRAMEBUFFER_COMPLETE) {
|
|
2032
|
+
throw new RenderDeviceError("WebGL2 bloom pyramid framebuffer status is invalid", "FRAMEBUFFER_INVALID", { width, height, status });
|
|
2033
|
+
}
|
|
2034
|
+
return { texture, framebuffer };
|
|
2035
|
+
};
|
|
2036
|
+
try {
|
|
2037
|
+
// Two ping-pong targets per mip (separable blur needs distinct read/write
|
|
2038
|
+
// targets at the same size), plus two level-0-sized accumulators: the
|
|
2039
|
+
// weighted sum ping-pongs between them so no pass ever samples the
|
|
2040
|
+
// texture of its own bound framebuffer (feedback loop).
|
|
2041
|
+
for (const mip of plan.mips) {
|
|
2042
|
+
allocateTarget(mip.width, mip.height, "mip");
|
|
2043
|
+
allocateTarget(mip.width, mip.height, "mip");
|
|
2044
|
+
}
|
|
2045
|
+
const level0 = plan.mips[0];
|
|
2046
|
+
const accumulatorA = allocateTarget(level0.width, level0.height, "accumulator-a");
|
|
2047
|
+
const accumulatorB = allocateTarget(level0.width, level0.height, "accumulator-b");
|
|
2048
|
+
const finalResources = {
|
|
2049
|
+
plan,
|
|
2050
|
+
hdr: useHalfFloat,
|
|
2051
|
+
textures,
|
|
2052
|
+
framebuffers,
|
|
2053
|
+
accumulatorTextureA: accumulatorA.texture,
|
|
2054
|
+
accumulatorFramebufferA: accumulatorA.framebuffer,
|
|
2055
|
+
accumulatorTextureB: accumulatorB.texture,
|
|
2056
|
+
accumulatorFramebufferB: accumulatorB.framebuffer
|
|
2057
|
+
};
|
|
2058
|
+
this.bloomPyramidResources = finalResources;
|
|
2059
|
+
return finalResources;
|
|
2060
|
+
}
|
|
2061
|
+
catch (error) {
|
|
2062
|
+
for (const texture of textures)
|
|
2063
|
+
this.gl.deleteTexture(texture);
|
|
2064
|
+
for (const framebuffer of framebuffers)
|
|
2065
|
+
this.gl.deleteFramebuffer(framebuffer);
|
|
2066
|
+
throw error;
|
|
2067
|
+
}
|
|
2068
|
+
finally {
|
|
2069
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, previousFramebuffer);
|
|
2070
|
+
this.restoreTextureUnit0(textureUnit0);
|
|
2071
|
+
this.stateCache.invalidate();
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
1651
2074
|
captureTextureUnit0() {
|
|
1652
2075
|
const activeTexture = this.gl.getParameter(this.gl.ACTIVE_TEXTURE);
|
|
1653
2076
|
this.gl.activeTexture(this.gl.TEXTURE0);
|
|
@@ -1856,6 +2279,7 @@ uniform sampler2D u_source;
|
|
|
1856
2279
|
uniform sampler2D u_brightLut;
|
|
1857
2280
|
uniform int u_hdr;
|
|
1858
2281
|
uniform float u_threshold;
|
|
2282
|
+
uniform float u_softKnee;
|
|
1859
2283
|
out vec4 outColor;
|
|
1860
2284
|
|
|
1861
2285
|
uvec4 byteTexel(sampler2D source, ivec2 coordinate) {
|
|
@@ -1867,7 +2291,12 @@ void main() {
|
|
|
1867
2291
|
if (u_hdr == 1) {
|
|
1868
2292
|
vec4 source = texelFetch(u_source, pixel, 0);
|
|
1869
2293
|
float luma = dot(source.rgb, vec3(0.2126, 0.7152, 0.0722));
|
|
1870
|
-
|
|
2294
|
+
float kneeLow = max(u_threshold - u_softKnee, 0.0);
|
|
2295
|
+
float kneeHigh = min(u_threshold + u_softKnee, 1.0);
|
|
2296
|
+
float weight = u_softKnee <= 0.0
|
|
2297
|
+
? (luma >= u_threshold ? 1.0 : 0.0)
|
|
2298
|
+
: smoothstep(kneeLow, kneeHigh, luma);
|
|
2299
|
+
outColor = source * weight;
|
|
1871
2300
|
return;
|
|
1872
2301
|
}
|
|
1873
2302
|
uvec4 source = byteTexel(u_source, pixel);
|
|
@@ -1985,6 +2414,43 @@ void main() {
|
|
|
1985
2414
|
`, "webgl2-bloom-composite");
|
|
1986
2415
|
return this.bloomCompositeProgram;
|
|
1987
2416
|
}
|
|
2417
|
+
ensureBloomDownsampleProgram() {
|
|
2418
|
+
if (this.bloomDownsampleProgram)
|
|
2419
|
+
return this.bloomDownsampleProgram;
|
|
2420
|
+
this.bloomDownsampleProgram = this.createFullscreenProgram(`#version 300 es
|
|
2421
|
+
precision highp float;
|
|
2422
|
+
uniform sampler2D u_source;
|
|
2423
|
+
uniform vec2 u_texelSize;
|
|
2424
|
+
out vec4 outColor;
|
|
2425
|
+
void main() {
|
|
2426
|
+
vec2 uv = (gl_FragCoord.xy - 0.5) * u_texelSize;
|
|
2427
|
+
vec2 e = u_texelSize * 0.5;
|
|
2428
|
+
vec4 sum = texture(u_source, uv - e) + texture(u_source, uv + vec2(e.x, -e.y))
|
|
2429
|
+
+ texture(u_source, uv + vec2(-e.x, e.y)) + texture(u_source, uv + e);
|
|
2430
|
+
outColor = sum * 0.25;
|
|
2431
|
+
}
|
|
2432
|
+
`, "webgl2-bloom-downsample");
|
|
2433
|
+
return this.bloomDownsampleProgram;
|
|
2434
|
+
}
|
|
2435
|
+
ensureBloomAccumulateProgram() {
|
|
2436
|
+
if (this.bloomAccumulateProgram)
|
|
2437
|
+
return this.bloomAccumulateProgram;
|
|
2438
|
+
this.bloomAccumulateProgram = this.createFullscreenProgram(`#version 300 es
|
|
2439
|
+
precision highp float;
|
|
2440
|
+
uniform sampler2D u_base;
|
|
2441
|
+
uniform sampler2D u_bloom;
|
|
2442
|
+
uniform float u_weight;
|
|
2443
|
+
out vec4 outColor;
|
|
2444
|
+
void main() {
|
|
2445
|
+
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
|
2446
|
+
vec4 base = texelFetch(u_base, pixel, 0);
|
|
2447
|
+
vec2 uv = (vec2(pixel) + 0.5) / vec2(textureSize(u_base, 0));
|
|
2448
|
+
vec3 bloom = texture(u_bloom, uv).rgb;
|
|
2449
|
+
outColor = vec4(base.rgb + bloom * u_weight, base.a);
|
|
2450
|
+
}
|
|
2451
|
+
`, "webgl2-bloom-accumulate");
|
|
2452
|
+
return this.bloomAccumulateProgram;
|
|
2453
|
+
}
|
|
1988
2454
|
ensureOutlineProgram() {
|
|
1989
2455
|
if (this.outlineProgram)
|
|
1990
2456
|
return this.outlineProgram;
|
|
@@ -2127,13 +2593,24 @@ uniform sampler2D u_depth;
|
|
|
2127
2593
|
uniform ivec2 u_size;
|
|
2128
2594
|
uniform float u_intensity;
|
|
2129
2595
|
uniform int u_maxDistance;
|
|
2596
|
+
uniform float u_depthNear;
|
|
2597
|
+
uniform float u_depthFar;
|
|
2130
2598
|
out vec4 outColor;
|
|
2131
2599
|
|
|
2600
|
+
// Raw GL depth is nonlinear (0.1/1000 defaults park the play area past 0.97),
|
|
2601
|
+
// so every depth-gated comparison below runs on linearized depth. The CPU
|
|
2602
|
+
// byte kernel keeps fixture-unit semantics; the divergence is documented at
|
|
2603
|
+
// normalizeLdrDepthRange.
|
|
2604
|
+
float a3dSsrLinearDepth(float depth) {
|
|
2605
|
+
float viewZ = u_depthNear * u_depthFar / max(u_depthFar - depth * (u_depthFar - u_depthNear), 0.0001);
|
|
2606
|
+
return clamp((viewZ - u_depthNear) / max(u_depthFar - u_depthNear, 0.0001), 0.0, 1.0);
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2132
2609
|
void main() {
|
|
2133
2610
|
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
|
2134
2611
|
vec4 target = texelFetch(u_source, pixel, 0);
|
|
2135
|
-
float
|
|
2136
|
-
int rayDistance = max(1, int(round((1.0 -
|
|
2612
|
+
float linearDepth = a3dSsrLinearDepth(texelFetch(u_depth, pixel, 0).r);
|
|
2613
|
+
int rayDistance = max(1, int(round((1.0 - linearDepth) * float(u_maxDistance))));
|
|
2137
2614
|
int direction = pixel.x < u_size.x / 2 ? 1 : -1;
|
|
2138
2615
|
ivec2 samplePixel = ivec2(
|
|
2139
2616
|
clamp(pixel.x + direction * rayDistance, 0, u_size.x - 1),
|
|
@@ -2141,11 +2618,11 @@ void main() {
|
|
|
2141
2618
|
);
|
|
2142
2619
|
vec3 reflected = texelFetch(u_source, samplePixel, 0).rgb;
|
|
2143
2620
|
float sourceLuma = (reflected.r + reflected.g + reflected.b) / 3.0;
|
|
2144
|
-
if (sourceLuma < 0.18 ||
|
|
2621
|
+
if (sourceLuma < 0.18 || linearDepth > 0.995) {
|
|
2145
2622
|
outColor = target;
|
|
2146
2623
|
return;
|
|
2147
2624
|
}
|
|
2148
|
-
float boost = sourceLuma * u_intensity * (1.0 -
|
|
2625
|
+
float boost = sourceLuma * u_intensity * (1.0 - linearDepth);
|
|
2149
2626
|
outColor = vec4(clamp(target.rgb + reflected * boost, 0.0, 1.0), target.a);
|
|
2150
2627
|
}
|
|
2151
2628
|
`, "webgl2-ssr");
|
|
@@ -2163,13 +2640,26 @@ uniform ivec2 u_size;
|
|
|
2163
2640
|
uniform float u_focusDepth;
|
|
2164
2641
|
uniform float u_focusRange;
|
|
2165
2642
|
uniform int u_maxRadius;
|
|
2643
|
+
uniform float u_depthNear;
|
|
2644
|
+
uniform float u_depthFar;
|
|
2166
2645
|
out vec4 outColor;
|
|
2167
2646
|
|
|
2647
|
+
// Same linearization contract as the SSR program, except focus is authored
|
|
2648
|
+
// directly as a linear-distance fraction (0 = near plane, 1 = far plane):
|
|
2649
|
+
// buffer-unit focus values are unusable once depth is linearized, because the
|
|
2650
|
+
// projection curve parks the whole play area inside a sliver of buffer units.
|
|
2651
|
+
// The CPU byte kernel keeps buffer-unit semantics; the divergence is
|
|
2652
|
+
// documented at normalizeLdrDepthRange.
|
|
2653
|
+
float a3dDofLinearDepth(float depth) {
|
|
2654
|
+
float viewZ = u_depthNear * u_depthFar / max(u_depthFar - depth * (u_depthFar - u_depthNear), 0.0001);
|
|
2655
|
+
return clamp((viewZ - u_depthNear) / max(u_depthFar - u_depthNear, 0.0001), 0.0, 1.0);
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2168
2658
|
void main() {
|
|
2169
2659
|
ivec2 pixel = ivec2(gl_FragCoord.xy);
|
|
2170
2660
|
vec4 source = texelFetch(u_source, pixel, 0);
|
|
2171
|
-
float
|
|
2172
|
-
float normalizedBlur = max(0.0, abs(
|
|
2661
|
+
float linearDepth = a3dDofLinearDepth(texelFetch(u_depth, pixel, 0).r);
|
|
2662
|
+
float normalizedBlur = max(0.0, abs(linearDepth - u_focusDepth) - u_focusRange)
|
|
2173
2663
|
/ max(u_focusRange, 0.001) * float(u_maxRadius);
|
|
2174
2664
|
int radius = min(u_maxRadius, int(round(normalizedBlur)));
|
|
2175
2665
|
if (radius == 0) {
|
|
@@ -3248,7 +3738,22 @@ function normalizeNativeBloomOptions(options) {
|
|
|
3248
3738
|
if (!Number.isInteger(radius) || radius < 0 || radius > 16) {
|
|
3249
3739
|
throw new RenderDeviceError("Bloom radius must be an integer in [0, 16].", "INVALID_POSTPROCESS_OPTIONS", { radius });
|
|
3250
3740
|
}
|
|
3251
|
-
|
|
3741
|
+
const softKnee = bloomNumberOption(options, "softKnee", 0);
|
|
3742
|
+
if (!(softKnee >= 0) || softKnee > 0.5) {
|
|
3743
|
+
throw new RenderDeviceError("Bloom softKnee must be finite and in [0, 0.5].", "INVALID_POSTPROCESS_OPTIONS", { softKnee });
|
|
3744
|
+
}
|
|
3745
|
+
const shoulder = bloomNumberOption(options, "shoulder", 0);
|
|
3746
|
+
if (!(shoulder >= 0) || shoulder > 1) {
|
|
3747
|
+
throw new RenderDeviceError("Bloom shoulder must be finite and in [0, 1].", "INVALID_POSTPROCESS_OPTIONS", { shoulder });
|
|
3748
|
+
}
|
|
3749
|
+
let quality;
|
|
3750
|
+
try {
|
|
3751
|
+
quality = normalizeBloomQualityPreset(options.quality);
|
|
3752
|
+
}
|
|
3753
|
+
catch {
|
|
3754
|
+
throw new RenderDeviceError("Bloom quality must be one of performance|balanced|cinematic.", "INVALID_POSTPROCESS_OPTIONS", { quality: options.quality });
|
|
3755
|
+
}
|
|
3756
|
+
return { threshold, intensity, radius, quality, softKnee, shoulder };
|
|
3252
3757
|
}
|
|
3253
3758
|
function normalizeNativeOutlineOptions(options) {
|
|
3254
3759
|
const width = outlineNumberOption(options, "width", 1);
|
|
@@ -3293,6 +3798,20 @@ function normalizeNativeSsaoOptions(options) {
|
|
|
3293
3798
|
}
|
|
3294
3799
|
return { radius, intensity, bias };
|
|
3295
3800
|
}
|
|
3801
|
+
/**
|
|
3802
|
+
* Camera depth range for linearizing sampleable GL depth in the native
|
|
3803
|
+
* SSR/depth-of-field programs (muse3jsparity-PRD A3). Missing or invalid
|
|
3804
|
+
* ranges fall back to the engine perspective defaults (0.1/1000) rather than
|
|
3805
|
+
* failing the frame: the range is a calibration hint, and every engine camera
|
|
3806
|
+
* builder defaults to exactly 0.1/1000. The CPU byte kernels intentionally
|
|
3807
|
+
* keep fixture-unit depth semantics (deterministic reference); the native
|
|
3808
|
+
* programs document their GL-depth contract in-shader.
|
|
3809
|
+
*/
|
|
3810
|
+
function normalizeLdrDepthRange(range) {
|
|
3811
|
+
const near = range && Number.isFinite(range.near) && range.near > 0 ? range.near : 0.1;
|
|
3812
|
+
const far = range && Number.isFinite(range.far) && range.far > near ? range.far : 1000;
|
|
3813
|
+
return { near, far };
|
|
3814
|
+
}
|
|
3296
3815
|
function normalizeNativeSsrOptions(options) {
|
|
3297
3816
|
const intensity = ssrNumberOption(options, "intensity", 0.32);
|
|
3298
3817
|
const maxDistance = ssrNumberOption(options, "maxDistance", 16);
|