@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/WebGPUDevice.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { RenderDeviceError, viewBytes } from "./RenderDevice.js";
|
|
1
|
+
import { RenderDeviceError, viewBytes, resolveGpuTargetOwner, spreadGpuTargetInventory } from "./RenderDevice.js";
|
|
2
2
|
import { MAX_WEBGPU_SKINNING_JOINTS } from "./WebGPUSkinningLimits.js";
|
|
3
|
+
import { defaultWebGPUBloomWeights, normalizeWebGPUColorGradeOptions, normalizeWebGPUBloomOptions, webgpuBlurFragment, webgpuBloomCompositeFragment, webgpuBrightExtractFragment, webgpuColorGradeFragment, webgpuFxaaFragment, WEBGPU_POST_VERTEX_WGSL } from "./webgpu/WebGPUPostShaders.js";
|
|
4
|
+
import { DEFAULT_DEPTH_SHADER_MARKER } from "./ShaderLibraryCore.js";
|
|
3
5
|
import { reflectShaderSources } from "./ShaderReflection.js";
|
|
4
6
|
import { Texture, bytesPerPixel, isCompressedTextureFormat } from "./Texture.js";
|
|
5
7
|
import { isTextureBinding, TextureBinding } from "./TextureBinding.js";
|
|
@@ -157,6 +159,22 @@ export class WebGPUDevice {
|
|
|
157
159
|
nativeMorphSubmissions = 0;
|
|
158
160
|
nativeEnvironmentBindings = 0;
|
|
159
161
|
nativeShadowMapBindings = 0;
|
|
162
|
+
nativeBloomPasses = 0;
|
|
163
|
+
nativeColorGradePasses = 0;
|
|
164
|
+
nativeFxaaPasses = 0;
|
|
165
|
+
webgpuPostPipelines = new Map();
|
|
166
|
+
webgpuPostLinearSampler = null;
|
|
167
|
+
lastWebGPUBloom = null;
|
|
168
|
+
/**
|
|
169
|
+
* Uniform buffers referenced by submitted-but-maybe-unexecuted post
|
|
170
|
+
* passes. Destroying them synchronously after submit races the GPU (the
|
|
171
|
+
* reads happen later), so they retire here and are reclaimed at the start
|
|
172
|
+
* of the next post run — after any intervening readback fence — or on
|
|
173
|
+
* dispose.
|
|
174
|
+
*/
|
|
175
|
+
webgpuPostRetiredUniformBuffers = [];
|
|
176
|
+
webgpuPostErrorScopePending = [];
|
|
177
|
+
webgpuPostErrors = [];
|
|
160
178
|
canvasSubmissions = 0;
|
|
161
179
|
buffers = new Set();
|
|
162
180
|
shaders = new Set();
|
|
@@ -800,6 +818,9 @@ export class WebGPUDevice {
|
|
|
800
818
|
["nativeMorphSubmissions", this.nativeMorphSubmissions],
|
|
801
819
|
["nativeEnvironmentBindings", this.nativeEnvironmentBindings],
|
|
802
820
|
["nativeShadowMapBindings", this.nativeShadowMapBindings],
|
|
821
|
+
["nativeBloomPasses", this.nativeBloomPasses],
|
|
822
|
+
["nativeColorGradePasses", this.nativeColorGradePasses],
|
|
823
|
+
["nativeFxaaPasses", this.nativeFxaaPasses],
|
|
803
824
|
["canvasSubmissions", this.canvasSubmissions]
|
|
804
825
|
]);
|
|
805
826
|
}
|
|
@@ -825,6 +846,12 @@ export class WebGPUDevice {
|
|
|
825
846
|
disposedShaders: [...this.shaders].filter((shader) => shader.disposed).length,
|
|
826
847
|
disposedRenderTargets: [...this.renderTargets].filter((target) => target.disposed).length,
|
|
827
848
|
disposedTextures: [...this.renderTargets].reduce((total, target) => total + (target.colorTexture.disposed ? 1 : 0) + (target.depthTexture?.disposed ? 1 : 0), 0),
|
|
849
|
+
...spreadGpuTargetInventory(liveRenderTargets.map((target) => ({
|
|
850
|
+
label: target.label,
|
|
851
|
+
kind: "render-target",
|
|
852
|
+
bytes: target.colorTexture.byteLength + (target.depthTexture?.byteLength ?? 0),
|
|
853
|
+
owner: resolveGpuTargetOwner(target.label)
|
|
854
|
+
}))),
|
|
828
855
|
nativeSubmissions: this.nativeSubmissions,
|
|
829
856
|
nativeRenderPipelinesCreated: this.nativeRenderPipelinesCreated,
|
|
830
857
|
nativeRenderPasses: this.nativeRenderPasses,
|
|
@@ -840,13 +867,310 @@ export class WebGPUDevice {
|
|
|
840
867
|
nativeMorphSubmissions: this.nativeMorphSubmissions,
|
|
841
868
|
nativeEnvironmentBindings: this.nativeEnvironmentBindings,
|
|
842
869
|
nativeShadowMapBindings: this.nativeShadowMapBindings,
|
|
870
|
+
nativeBloomPasses: this.nativeBloomPasses,
|
|
871
|
+
nativeColorGradePasses: this.nativeColorGradePasses,
|
|
872
|
+
nativeFxaaPasses: this.nativeFxaaPasses,
|
|
843
873
|
lastError: this.lastError,
|
|
844
874
|
contextLost: this.contextLost
|
|
845
875
|
};
|
|
846
876
|
}
|
|
877
|
+
/**
|
|
878
|
+
* muse3jsparity-PRD J2 — native WebGPU bloom pyramid.
|
|
879
|
+
*
|
|
880
|
+
* Renders bright-extract -> separable-blur mip pyramid -> composite over
|
|
881
|
+
* the source, all as real WGSL fullscreen passes on this device. Returns a
|
|
882
|
+
* fresh rgba8 target owned by the caller (dispose it); every intermediate
|
|
883
|
+
* is disposed before return. Throws fail-closed when the native device
|
|
884
|
+
* cannot run fullscreen passes.
|
|
885
|
+
*/
|
|
886
|
+
executeWebGPUBloom(source, options = {}) {
|
|
887
|
+
this.reclaimWebGPUPostUniformBuffers();
|
|
888
|
+
const normalized = normalizeWebGPUBloomOptions(options);
|
|
889
|
+
const src = this.requireRenderTarget(source);
|
|
890
|
+
const srcView = src.nativeView;
|
|
891
|
+
if (!srcView) {
|
|
892
|
+
throw new RenderDeviceError("WebGPU bloom source target has no native view", "NATIVE_POST_UNSUPPORTED", { label: src.label });
|
|
893
|
+
}
|
|
894
|
+
const width = src.width;
|
|
895
|
+
const height = src.height;
|
|
896
|
+
const mipFormat = normalized.halfFloat ? "rgba16f" : "rgba8";
|
|
897
|
+
const owned = [];
|
|
898
|
+
const track = (target) => {
|
|
899
|
+
owned.push(target);
|
|
900
|
+
return target;
|
|
901
|
+
};
|
|
902
|
+
try {
|
|
903
|
+
const bright = track(this.createRenderTarget({ width, height, format: mipFormat, label: "a3d-webgpu-bloom-bright" }));
|
|
904
|
+
this.runWebGPUFullscreenPass({
|
|
905
|
+
label: "webgpu-bloom-bright",
|
|
906
|
+
fragment: webgpuBrightExtractFragment(),
|
|
907
|
+
entryPoint: "fs_bright",
|
|
908
|
+
uniforms: new Float32Array([normalized.threshold, normalized.knee, normalized.strength, 0]),
|
|
909
|
+
views: [srcView],
|
|
910
|
+
target: bright
|
|
911
|
+
});
|
|
912
|
+
const mips = [];
|
|
913
|
+
let level = bright;
|
|
914
|
+
let levelWidth = width;
|
|
915
|
+
let levelHeight = height;
|
|
916
|
+
for (let mip = 0; mip < normalized.mipCount; mip += 1) {
|
|
917
|
+
levelWidth = Math.max(1, levelWidth >> 1);
|
|
918
|
+
levelHeight = Math.max(1, levelHeight >> 1);
|
|
919
|
+
const scratch = track(this.createRenderTarget({ width: levelWidth, height: levelHeight, format: mipFormat, label: `a3d-webgpu-bloom-mip${mip}-scratch` }));
|
|
920
|
+
const mipTarget = track(this.createRenderTarget({ width: levelWidth, height: levelHeight, format: mipFormat, label: `a3d-webgpu-bloom-mip${mip}` }));
|
|
921
|
+
const levelView = this.requireRenderTarget(level).nativeView;
|
|
922
|
+
const scratchView = this.requireRenderTarget(scratch).nativeView;
|
|
923
|
+
if (!levelView || !scratchView) {
|
|
924
|
+
throw new RenderDeviceError("WebGPU bloom pyramid target has no native view", "NATIVE_POST_UNSUPPORTED", { mip });
|
|
925
|
+
}
|
|
926
|
+
this.runWebGPUFullscreenPass({
|
|
927
|
+
label: `webgpu-bloom-mip${mip}-h`,
|
|
928
|
+
fragment: webgpuBlurFragment(),
|
|
929
|
+
entryPoint: "fs_blur",
|
|
930
|
+
uniforms: new Float32Array([1, 0, 1 / levelWidth, 1 / levelHeight]),
|
|
931
|
+
views: [levelView],
|
|
932
|
+
target: scratch
|
|
933
|
+
});
|
|
934
|
+
this.runWebGPUFullscreenPass({
|
|
935
|
+
label: `webgpu-bloom-mip${mip}-v`,
|
|
936
|
+
fragment: webgpuBlurFragment(),
|
|
937
|
+
entryPoint: "fs_blur",
|
|
938
|
+
uniforms: new Float32Array([0, 1, 1 / levelWidth, 1 / levelHeight]),
|
|
939
|
+
views: [scratchView],
|
|
940
|
+
target: mipTarget
|
|
941
|
+
});
|
|
942
|
+
mips.push(mipTarget);
|
|
943
|
+
level = mipTarget;
|
|
944
|
+
}
|
|
945
|
+
const weights = defaultWebGPUBloomWeights(normalized.mipCount);
|
|
946
|
+
const composite = track(this.createRenderTarget({ width, height, format: "rgba8", label: "a3d-webgpu-bloom-composite" }));
|
|
947
|
+
const mipViews = mips.map((mipTarget) => {
|
|
948
|
+
const view = this.requireRenderTarget(mipTarget).nativeView;
|
|
949
|
+
if (!view)
|
|
950
|
+
throw new RenderDeviceError("WebGPU bloom mip has no native view", "NATIVE_POST_UNSUPPORTED", {});
|
|
951
|
+
return view;
|
|
952
|
+
});
|
|
953
|
+
this.runWebGPUFullscreenPass({
|
|
954
|
+
label: "webgpu-bloom-composite",
|
|
955
|
+
fragment: webgpuBloomCompositeFragment({ weights, strength: normalized.strength }),
|
|
956
|
+
entryPoint: "fs_composite",
|
|
957
|
+
uniforms: new Float32Array([normalized.strength, 0, 0, 0]),
|
|
958
|
+
views: [srcView, ...mipViews],
|
|
959
|
+
target: composite
|
|
960
|
+
});
|
|
961
|
+
const bytesPerPixel = normalized.halfFloat ? 8 : 4;
|
|
962
|
+
const targetBytes = width * height * bytesPerPixel
|
|
963
|
+
+ mips.reduce((total, mipTarget) => total + mipTarget.width * mipTarget.height * bytesPerPixel, 0)
|
|
964
|
+
+ width * height * 4;
|
|
965
|
+
this.lastWebGPUBloom = {
|
|
966
|
+
mipCount: normalized.mipCount,
|
|
967
|
+
halfFloat: normalized.halfFloat,
|
|
968
|
+
targetBytes,
|
|
969
|
+
compositeGain: normalized.strength,
|
|
970
|
+
passes: 1 + normalized.mipCount * 2 + 1,
|
|
971
|
+
executionMode: "webgpu-native-post"
|
|
972
|
+
};
|
|
973
|
+
this.nativeBloomPasses += this.lastWebGPUBloom.passes;
|
|
974
|
+
for (const target of owned) {
|
|
975
|
+
if (target !== composite)
|
|
976
|
+
target.dispose();
|
|
977
|
+
}
|
|
978
|
+
return composite;
|
|
979
|
+
}
|
|
980
|
+
catch (error) {
|
|
981
|
+
for (const target of owned)
|
|
982
|
+
target.dispose();
|
|
983
|
+
throw error;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
/** Last native bloom run on this device (null before the first run). */
|
|
987
|
+
getWebGPUBloomDiagnostics() {
|
|
988
|
+
return this.lastWebGPUBloom;
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Drains GPU validation errors observed by the native post passes.
|
|
992
|
+
* Empty means every submitted post pass validated clean.
|
|
993
|
+
*/
|
|
994
|
+
async drainWebGPUPostErrors() {
|
|
995
|
+
const pending = this.webgpuPostErrorScopePending;
|
|
996
|
+
this.webgpuPostErrorScopePending = [];
|
|
997
|
+
await Promise.all(pending);
|
|
998
|
+
const errors = [...this.webgpuPostErrors];
|
|
999
|
+
this.webgpuPostErrors = [];
|
|
1000
|
+
return errors;
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* muse3jsparity-PRD J2 — native WebGPU color grade.
|
|
1004
|
+
* Exposure (stops) -> saturation around luma -> contrast around middle
|
|
1005
|
+
* gray, one WGSL fullscreen pass into a fresh caller-owned rgba8 target.
|
|
1006
|
+
*/
|
|
1007
|
+
executeWebGPUColorGrade(source, options = {}) {
|
|
1008
|
+
this.reclaimWebGPUPostUniformBuffers();
|
|
1009
|
+
const normalized = normalizeWebGPUColorGradeOptions(options);
|
|
1010
|
+
const src = this.requireRenderTarget(source);
|
|
1011
|
+
const srcView = src.nativeView;
|
|
1012
|
+
if (!srcView) {
|
|
1013
|
+
throw new RenderDeviceError("WebGPU color-grade source target has no native view", "NATIVE_POST_UNSUPPORTED", { label: src.label });
|
|
1014
|
+
}
|
|
1015
|
+
const out = this.createRenderTarget({ width: src.width, height: src.height, format: "rgba8", label: "a3d-webgpu-color-grade" });
|
|
1016
|
+
try {
|
|
1017
|
+
this.runWebGPUFullscreenPass({
|
|
1018
|
+
label: "webgpu-color-grade",
|
|
1019
|
+
fragment: webgpuColorGradeFragment(),
|
|
1020
|
+
entryPoint: "fs_grade",
|
|
1021
|
+
uniforms: new Float32Array([normalized.exposure, normalized.contrast, normalized.saturation, 0]),
|
|
1022
|
+
views: [srcView],
|
|
1023
|
+
target: out
|
|
1024
|
+
});
|
|
1025
|
+
this.nativeColorGradePasses += 1;
|
|
1026
|
+
return out;
|
|
1027
|
+
}
|
|
1028
|
+
catch (error) {
|
|
1029
|
+
out.dispose();
|
|
1030
|
+
throw error;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* muse3jsparity-PRD J2 — native WebGPU FXAA.
|
|
1035
|
+
* Compact FXAA 3.11 console core (luma neighborhood, one perpendicular
|
|
1036
|
+
* blend tap pair; flat neighborhoods pass through untouched). TAA is
|
|
1037
|
+
* intentionally out of scope: no velocity/history inputs exist on the
|
|
1038
|
+
* WebGPU path (same withheld doctrine as the root A3 nodes).
|
|
1039
|
+
*/
|
|
1040
|
+
executeWebGPUFxaa(source) {
|
|
1041
|
+
this.reclaimWebGPUPostUniformBuffers();
|
|
1042
|
+
const src = this.requireRenderTarget(source);
|
|
1043
|
+
const srcView = src.nativeView;
|
|
1044
|
+
if (!srcView) {
|
|
1045
|
+
throw new RenderDeviceError("WebGPU FXAA source target has no native view", "NATIVE_POST_UNSUPPORTED", { label: src.label });
|
|
1046
|
+
}
|
|
1047
|
+
const out = this.createRenderTarget({ width: src.width, height: src.height, format: "rgba8", label: "a3d-webgpu-fxaa" });
|
|
1048
|
+
try {
|
|
1049
|
+
this.runWebGPUFullscreenPass({
|
|
1050
|
+
label: "webgpu-fxaa",
|
|
1051
|
+
fragment: webgpuFxaaFragment(),
|
|
1052
|
+
entryPoint: "fs_fxaa",
|
|
1053
|
+
uniforms: new Float32Array([1 / src.width, 1 / src.height, 0, 0]),
|
|
1054
|
+
views: [srcView],
|
|
1055
|
+
target: out
|
|
1056
|
+
});
|
|
1057
|
+
this.nativeFxaaPasses += 1;
|
|
1058
|
+
return out;
|
|
1059
|
+
}
|
|
1060
|
+
catch (error) {
|
|
1061
|
+
out.dispose();
|
|
1062
|
+
throw error;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
runWebGPUFullscreenPass(args) {
|
|
1066
|
+
// No assertFrame here on purpose: these are target-to-target passes with
|
|
1067
|
+
// their own encoders, so they run both inside a Renderer frame (scene ->
|
|
1068
|
+
// post chaining) and after it closed (proof harnesses that render, then
|
|
1069
|
+
// post, then read back).
|
|
1070
|
+
this.assertAlive();
|
|
1071
|
+
const target = this.requireRenderTarget(args.target);
|
|
1072
|
+
const targetView = target.nativeView;
|
|
1073
|
+
if (!targetView) {
|
|
1074
|
+
throw new RenderDeviceError("WebGPU post target has no native view", "NATIVE_POST_UNSUPPORTED", { label: target.label });
|
|
1075
|
+
}
|
|
1076
|
+
if (!this.device.createShaderModule || !this.device.createRenderPipeline || !this.device.createCommandEncoder
|
|
1077
|
+
|| !this.device.createSampler || !this.device.createBuffer || !this.device.createBindGroup) {
|
|
1078
|
+
throw new RenderDeviceError("WebGPU fullscreen post requires shader/pipeline/sampler/buffer natives", "NATIVE_POST_UNSUPPORTED", { label: args.label });
|
|
1079
|
+
}
|
|
1080
|
+
const scopeDevice = this.device;
|
|
1081
|
+
const scopedErrors = Boolean(scopeDevice.pushErrorScope && scopeDevice.popErrorScope);
|
|
1082
|
+
if (scopedErrors) {
|
|
1083
|
+
// Bracket everything below (module/pipeline/bind-group creation plus
|
|
1084
|
+
// the submit) so per-pass validation failures land here labeled.
|
|
1085
|
+
scopeDevice.pushErrorScope?.("validation");
|
|
1086
|
+
}
|
|
1087
|
+
const format = webgpuTextureFormat(target.colorTexture.format);
|
|
1088
|
+
const cacheKey = `${args.label}:${args.entryPoint}:${format}`;
|
|
1089
|
+
let pipeline = this.webgpuPostPipelines.get(cacheKey);
|
|
1090
|
+
if (!pipeline) {
|
|
1091
|
+
const vertexModule = this.device.createShaderModule({ label: `${args.label}-vs`, code: WEBGPU_POST_VERTEX_WGSL });
|
|
1092
|
+
const fragmentModule = this.device.createShaderModule({ label: `${args.label}-fs`, code: args.fragment });
|
|
1093
|
+
pipeline = this.device.createRenderPipeline({
|
|
1094
|
+
label: `${args.label}-pipeline`,
|
|
1095
|
+
layout: "auto",
|
|
1096
|
+
vertex: { module: vertexModule, entryPoint: "vs_post" },
|
|
1097
|
+
fragment: { module: fragmentModule, entryPoint: args.entryPoint, targets: [{ format }] },
|
|
1098
|
+
primitive: { topology: "triangle-list" }
|
|
1099
|
+
});
|
|
1100
|
+
this.nativeRenderPipelinesCreated += 1;
|
|
1101
|
+
this.webgpuPostPipelines.set(cacheKey, pipeline);
|
|
1102
|
+
}
|
|
1103
|
+
if (!this.webgpuPostLinearSampler) {
|
|
1104
|
+
this.webgpuPostLinearSampler = this.device.createSampler({
|
|
1105
|
+
minFilter: "linear",
|
|
1106
|
+
magFilter: "linear",
|
|
1107
|
+
addressModeU: "clamp-to-edge",
|
|
1108
|
+
addressModeV: "clamp-to-edge"
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
const uniformBuffer = this.device.createBuffer({
|
|
1112
|
+
label: `${args.label}-uniforms`,
|
|
1113
|
+
size: args.uniforms.byteLength,
|
|
1114
|
+
usage: BUFFER_USAGE.UNIFORM | BUFFER_USAGE.COPY_DST
|
|
1115
|
+
});
|
|
1116
|
+
let submitted = false;
|
|
1117
|
+
try {
|
|
1118
|
+
this.device.queue.writeBuffer(uniformBuffer, 0, args.uniforms);
|
|
1119
|
+
const layout = pipeline.getBindGroupLayout?.(0);
|
|
1120
|
+
if (!layout)
|
|
1121
|
+
throw new RenderDeviceError("WebGPU post pipeline has no bind-group layout", "NATIVE_POST_UNSUPPORTED", { label: args.label });
|
|
1122
|
+
const group = this.device.createBindGroup({
|
|
1123
|
+
label: `${args.label}-bind`,
|
|
1124
|
+
layout,
|
|
1125
|
+
entries: [
|
|
1126
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
1127
|
+
{ binding: 1, resource: this.webgpuPostLinearSampler },
|
|
1128
|
+
...args.views.map((view, index) => ({ binding: 2 + index, resource: view }))
|
|
1129
|
+
]
|
|
1130
|
+
});
|
|
1131
|
+
const encoder = this.device.createCommandEncoder({ label: `${args.label}-encoder` });
|
|
1132
|
+
const pass = encoder.beginRenderPass({
|
|
1133
|
+
label: `${args.label}-pass`,
|
|
1134
|
+
colorAttachments: [{ view: targetView, loadOp: "load", storeOp: "store" }]
|
|
1135
|
+
});
|
|
1136
|
+
pass.setPipeline(pipeline);
|
|
1137
|
+
pass.setBindGroup?.(0, group);
|
|
1138
|
+
pass.draw(3, 1, 0);
|
|
1139
|
+
pass.end();
|
|
1140
|
+
this.device.queue.submit([encoder.finish()]);
|
|
1141
|
+
submitted = true;
|
|
1142
|
+
// The pass owns every pixel (fullscreen triangle), so a later
|
|
1143
|
+
// readPixelsAsync must NOT flush a stale pending clear over it.
|
|
1144
|
+
target.nativeNeedsClear = false;
|
|
1145
|
+
this.nativeRenderPasses += 1;
|
|
1146
|
+
// Retired, not destroyed: the submit only enqueues; the pass reads
|
|
1147
|
+
// these uniforms when the GPU gets to it.
|
|
1148
|
+
this.webgpuPostRetiredUniformBuffers.push(uniformBuffer);
|
|
1149
|
+
}
|
|
1150
|
+
catch (error) {
|
|
1151
|
+
if (!submitted)
|
|
1152
|
+
uniformBuffer.destroy();
|
|
1153
|
+
throw error;
|
|
1154
|
+
}
|
|
1155
|
+
finally {
|
|
1156
|
+
if (scopedErrors && scopeDevice.popErrorScope) {
|
|
1157
|
+
const pending = scopeDevice.popErrorScope().then((info) => {
|
|
1158
|
+
if (info?.message)
|
|
1159
|
+
this.webgpuPostErrors.push(`[${args.label}] ${info.message}`);
|
|
1160
|
+
}, () => undefined);
|
|
1161
|
+
this.webgpuPostErrorScopePending.push(pending);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
reclaimWebGPUPostUniformBuffers() {
|
|
1166
|
+
for (const buffer of this.webgpuPostRetiredUniformBuffers)
|
|
1167
|
+
buffer.destroy();
|
|
1168
|
+
this.webgpuPostRetiredUniformBuffers = [];
|
|
1169
|
+
}
|
|
847
1170
|
dispose() {
|
|
848
1171
|
if (this.disposed)
|
|
849
1172
|
return;
|
|
1173
|
+
this.reclaimWebGPUPostUniformBuffers();
|
|
850
1174
|
for (const buffer of this.buffers)
|
|
851
1175
|
buffer.dispose();
|
|
852
1176
|
for (const shader of this.shaders)
|
|
@@ -1098,7 +1422,9 @@ export class WebGPUDevice {
|
|
|
1098
1422
|
{ binding: 13, resource: materialTextures.occlusion.sampler },
|
|
1099
1423
|
{ binding: 14, resource: materialTextures.occlusion.view },
|
|
1100
1424
|
{ binding: 15, resource: materialTextures.clusterLightData.view },
|
|
1101
|
-
{ binding: 16, resource: materialTextures.clusterLightIndices.view }
|
|
1425
|
+
{ binding: 16, resource: materialTextures.clusterLightIndices.view },
|
|
1426
|
+
{ binding: 17, resource: materialTextures.spotShadow.sampler },
|
|
1427
|
+
{ binding: 18, resource: materialTextures.spotShadow.view }
|
|
1102
1428
|
] : [])
|
|
1103
1429
|
]
|
|
1104
1430
|
})
|
|
@@ -1184,6 +1510,8 @@ export class WebGPUDevice {
|
|
|
1184
1510
|
this.nativeEnvironmentBindings += 1;
|
|
1185
1511
|
if (materialTextures?.actualShadow)
|
|
1186
1512
|
this.nativeShadowMapBindings += 1;
|
|
1513
|
+
if (materialTextures?.actualSpotShadow)
|
|
1514
|
+
this.nativeShadowMapBindings += 1;
|
|
1187
1515
|
if (!this.activeRenderTarget)
|
|
1188
1516
|
this.canvasSubmissions += 1;
|
|
1189
1517
|
}
|
|
@@ -1221,6 +1549,20 @@ export class WebGPUDevice {
|
|
|
1221
1549
|
if (!binding?.texture || binding.texture.disposed || !binding.validate().ok || !hasNativeSampledTextureBinding(this.device))
|
|
1222
1550
|
return null;
|
|
1223
1551
|
const texture = binding.texture;
|
|
1552
|
+
// J2 zero-copy shadow path: a render target's native color texture is
|
|
1553
|
+
// created with TEXTURE_BINDING usage, so a shadow map rendered natively
|
|
1554
|
+
// (ShadowPass "encoded-color-depth") samples directly — no CPU readback
|
|
1555
|
+
// round-trip. Same-queue submission order guarantees the depth writes
|
|
1556
|
+
// are visible to the later lit pass.
|
|
1557
|
+
for (const target of this.renderTargets) {
|
|
1558
|
+
if (target.disposed || target.sampleCount !== 1 || target.colorTexture !== texture || !target.nativeView)
|
|
1559
|
+
continue;
|
|
1560
|
+
return {
|
|
1561
|
+
view: target.nativeView,
|
|
1562
|
+
sampler: this.device.createSampler(webgpuSamplerDescriptor(binding.sampler)),
|
|
1563
|
+
actual: true
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1224
1566
|
let resource = this.nativeSampledTextures.get(texture);
|
|
1225
1567
|
if (!resource) {
|
|
1226
1568
|
if (texture.source) {
|
|
@@ -1290,6 +1632,7 @@ export class WebGPUDevice {
|
|
|
1290
1632
|
const environmentBinding = uniformTextureBinding(command.uniforms, "u_environmentMapTexture");
|
|
1291
1633
|
const brdfBinding = uniformTextureBinding(command.uniforms, "u_environmentBrdfLutTexture");
|
|
1292
1634
|
const shadowBinding = uniformTextureBinding(command.uniforms, "u_shadowMapTexture");
|
|
1635
|
+
const spotShadowBinding = uniformTextureBinding(command.uniforms, "u_spotShadowMapTexture");
|
|
1293
1636
|
const normalBinding = uniformTextureBinding(command.uniforms, "u_normalTexture");
|
|
1294
1637
|
const metallicRoughnessBinding = uniformTextureBinding(command.uniforms, "u_metallicRoughnessTexture");
|
|
1295
1638
|
const occlusionBinding = uniformTextureBinding(command.uniforms, "u_occlusionTexture");
|
|
@@ -1299,18 +1642,20 @@ export class WebGPUDevice {
|
|
|
1299
1642
|
const environment = this.createNativeSampledTextureBinding(environmentBinding) ?? this.createNativeFallbackSampledTextureBinding("environment-srgb", [92, 116, 156, 255], "srgb");
|
|
1300
1643
|
const brdf = this.createNativeSampledTextureBinding(brdfBinding) ?? this.createNativeFallbackSampledTextureBinding("brdf-linear", [180, 180, 255, 255], "linear");
|
|
1301
1644
|
const shadow = this.createNativeSampledTextureBinding(shadowBinding) ?? this.createNativeFallbackSampledTextureBinding("shadow-lit-linear", [255, 255, 255, 255], "linear");
|
|
1645
|
+
const spotShadow = this.createNativeSampledTextureBinding(spotShadowBinding) ?? this.createNativeFallbackSampledTextureBinding("spot-shadow-lit-linear", [255, 255, 255, 255], "linear");
|
|
1302
1646
|
const normal = this.createNativeSampledTextureBinding(normalBinding) ?? this.createNativeFallbackSampledTextureBinding("flat-normal-linear", [128, 128, 255, 255], "linear");
|
|
1303
1647
|
const metallicRoughness = this.createNativeSampledTextureBinding(metallicRoughnessBinding) ?? this.createNativeFallbackSampledTextureBinding("metallic-roughness-linear", [255, 255, 255, 255], "linear");
|
|
1304
1648
|
const occlusion = this.createNativeSampledTextureBinding(occlusionBinding) ?? this.createNativeFallbackSampledTextureBinding("occlusion-linear", [255, 255, 255, 255], "linear");
|
|
1305
1649
|
const clusterLightData = this.createNativeSampledTextureBinding(clusterLightDataBinding) ?? this.createNativeFallbackSampledTextureBinding("cluster-light-data-linear", [0, 0, 0, 0], "linear");
|
|
1306
1650
|
const clusterLightIndices = this.createNativeSampledTextureBinding(clusterLightIndicesBinding) ?? this.createNativeFallbackSampledTextureBinding("cluster-light-indices-linear", [0, 0, 0, 0], "linear");
|
|
1307
|
-
if (!base || !environment || !brdf || !shadow || !normal || !metallicRoughness || !occlusion || !clusterLightData || !clusterLightIndices)
|
|
1651
|
+
if (!base || !environment || !brdf || !shadow || !spotShadow || !normal || !metallicRoughness || !occlusion || !clusterLightData || !clusterLightIndices)
|
|
1308
1652
|
return null;
|
|
1309
1653
|
return {
|
|
1310
1654
|
base,
|
|
1311
1655
|
environment,
|
|
1312
1656
|
brdf,
|
|
1313
1657
|
shadow,
|
|
1658
|
+
spotShadow,
|
|
1314
1659
|
normal,
|
|
1315
1660
|
metallicRoughness,
|
|
1316
1661
|
occlusion,
|
|
@@ -1320,6 +1665,7 @@ export class WebGPUDevice {
|
|
|
1320
1665
|
actualEnvironment: environmentBinding !== null && environment.actual,
|
|
1321
1666
|
actualBrdf: brdfBinding !== null && brdf.actual,
|
|
1322
1667
|
actualShadow: shadowBinding !== null && shadow.actual,
|
|
1668
|
+
actualSpotShadow: spotShadowBinding !== null && spotShadow.actual,
|
|
1323
1669
|
actualNormal: normalBinding !== null && normal.actual,
|
|
1324
1670
|
actualMetallicRoughness: metallicRoughnessBinding !== null && metallicRoughness.actual,
|
|
1325
1671
|
actualOcclusion: occlusionBinding !== null && occlusion.actual
|
|
@@ -1355,8 +1701,10 @@ export class WebGPUDevice {
|
|
|
1355
1701
|
createNativeDrawUniformBuffer(command) {
|
|
1356
1702
|
if (!this.device.createBindGroup)
|
|
1357
1703
|
return null;
|
|
1358
|
-
// 188 floats for the scalar/matrix fields + 96 joint matrices (16 floats each) appended at 188
|
|
1359
|
-
|
|
1704
|
+
// 188 floats for the scalar/matrix fields + 96 joint matrices (16 floats each) appended at 188
|
|
1705
|
+
// + J2 native shadow projections (312 floats at 1724: directional matrix/params/PCF, then spot)
|
|
1706
|
+
// + model matrix (16 floats at 2036) for instanced world composition.
|
|
1707
|
+
const data = new Float32Array(2052);
|
|
1360
1708
|
const baseColorBinding = uniformBaseColorTextureBinding(command.uniforms);
|
|
1361
1709
|
const environmentBinding = uniformTextureBinding(command.uniforms, "u_environmentMapTexture");
|
|
1362
1710
|
const normalBinding = uniformTextureBinding(command.uniforms, "u_normalTexture");
|
|
@@ -1408,6 +1756,37 @@ export class WebGPUDevice {
|
|
|
1408
1756
|
for (let index = 0; index < MAX_WEBGPU_SKINNING_JOINTS; index += 1) {
|
|
1409
1757
|
data.set(jointMatrices[index] ?? identityMatrix(), 188 + index * 16);
|
|
1410
1758
|
}
|
|
1759
|
+
// J2 native shadow projections (WGSL parity with a3dForwardShadowFactor /
|
|
1760
|
+
// a3dSpotShadowFactor). Identity matrices + zero enabled flags keep every
|
|
1761
|
+
// shadow factor at exactly 1.0 (lit) when the ForwardPass gates stay shut.
|
|
1762
|
+
data.set(uniformMat4(command.uniforms?.get("u_shadowMapMatrix")) ?? identityMatrix(), 1724);
|
|
1763
|
+
const shadowTexel = uniformVec2(command.uniforms?.get("u_shadowMapTexelSize")) ?? [1, 1];
|
|
1764
|
+
data.set([
|
|
1765
|
+
uniformNumber(command.uniforms?.get("u_shadowMapBias"), 0.001),
|
|
1766
|
+
uniformNumber(command.uniforms?.get("u_shadowMapSlopeBias"), 1),
|
|
1767
|
+
shadowTexel[0],
|
|
1768
|
+
shadowTexel[1]
|
|
1769
|
+
], 1740);
|
|
1770
|
+
data.set([uniformNumber(command.uniforms?.get("u_shadowPcfSampleCount"), 1), 0, 0, 0], 1744);
|
|
1771
|
+
setNativeShadowPcfSamples(data, command.uniforms?.get("u_shadowPcfSamples"), 1748);
|
|
1772
|
+
data.set(uniformMat4(command.uniforms?.get("u_spotShadowMatrix")) ?? identityMatrix(), 1876);
|
|
1773
|
+
const spotLightPosition = uniformVec3(command.uniforms?.get("u_spotShadowLightPosition")) ?? [0, 0, 0];
|
|
1774
|
+
data.set([spotLightPosition[0], spotLightPosition[1], spotLightPosition[2], uniformNumber(command.uniforms?.get("u_spotShadowRange"), 1)], 1892);
|
|
1775
|
+
const spotLightDirection = uniformVec3(command.uniforms?.get("u_spotShadowLightDirection")) ?? [0, -1, 0];
|
|
1776
|
+
data.set([spotLightDirection[0], spotLightDirection[1], spotLightDirection[2], 0], 1896);
|
|
1777
|
+
data.set([
|
|
1778
|
+
uniformNumber(command.uniforms?.get("u_spotShadowMapEnabled"), 0),
|
|
1779
|
+
uniformNumber(command.uniforms?.get("u_spotShadowStrength"), 0.65),
|
|
1780
|
+
uniformNumber(command.uniforms?.get("u_spotShadowBias"), 0.001),
|
|
1781
|
+
uniformNumber(command.uniforms?.get("u_spotShadowSlopeBias"), 1)
|
|
1782
|
+
], 1900);
|
|
1783
|
+
const spotTexel = uniformVec2(command.uniforms?.get("u_spotShadowTexelSize")) ?? [1, 1];
|
|
1784
|
+
const spotCone = uniformVec2(command.uniforms?.get("u_spotShadowCone")) ?? [Math.PI / 4, 0];
|
|
1785
|
+
data.set([spotTexel[0], spotTexel[1], uniformNumber(command.uniforms?.get("u_spotShadowPcfSampleCount"), 1), spotCone[0]], 1904);
|
|
1786
|
+
setNativeShadowPcfSamples(data, command.uniforms?.get("u_spotShadowPcfSamples"), 1908);
|
|
1787
|
+
// Model matrix for the instanced vertex world composition
|
|
1788
|
+
// (u_modelMatrix * instanceMatrix * position, GLSL parity).
|
|
1789
|
+
data.set(modelMatrix, 2036);
|
|
1411
1790
|
const morphDeltas = command.uniforms?.get("u_morphPositionDeltas");
|
|
1412
1791
|
const morphNumbers = morphDeltas instanceof Float32Array || Array.isArray(morphDeltas) ? Array.from(morphDeltas).slice(0, 32) : [];
|
|
1413
1792
|
if (morphNumbers.length > 0)
|
|
@@ -1806,6 +2185,15 @@ function uniformVec2(value) {
|
|
|
1806
2185
|
? [components[0], components[1]]
|
|
1807
2186
|
: null;
|
|
1808
2187
|
}
|
|
2188
|
+
function setNativeShadowPcfSamples(data, value, offset) {
|
|
2189
|
+
const numbers = value instanceof Float32Array || Array.isArray(value) ? Array.from(value) : [];
|
|
2190
|
+
for (let index = 0; index < 32; index += 1) {
|
|
2191
|
+
const quad = numbers.slice(index * 4, index * 4 + 4);
|
|
2192
|
+
// Default single center tap (weight 1) so an enabled shadow map without
|
|
2193
|
+
// an explicit kernel still occludes instead of dividing by zero weight.
|
|
2194
|
+
data.set(quad.length === 4 && quad.every(Number.isFinite) ? quad : [0, 0, 1, 0], offset + index * 4);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
1809
2197
|
function uniformMat4Array(value, count) {
|
|
1810
2198
|
const numbers = value instanceof Float32Array || Array.isArray(value) ? Array.from(value) : [];
|
|
1811
2199
|
return Array.from({ length: count }, (_, index) => {
|
|
@@ -2191,11 +2579,20 @@ function createNativeShaderSources(sources) {
|
|
|
2191
2579
|
return {
|
|
2192
2580
|
vertex: `// ${sources.marker}\nstruct DrawUniforms {\n modelViewProjection: mat4x4<f32>,\n color: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> u_draw: DrawUniforms;\n\n@vertex\nfn ${vertexEntry}(@location(0) position: vec3<f32>) -> VertexOutput {\n var output: VertexOutput;\n let clipPosition = u_draw.modelViewProjection * vec4<f32>(position, 1.0);
|
|
2193
2581
|
output.position = vec4<f32>(clipPosition.x, clipPosition.y, clipPosition.z * 0.5 + clipPosition.w * 0.5, clipPosition.w);\n return output;\n}\n`,
|
|
2194
|
-
fragment:
|
|
2582
|
+
fragment: sources.marker.includes(DEFAULT_DEPTH_SHADER_MARKER)
|
|
2583
|
+
? nativeDepthFragment(sources.marker, fragmentEntry)
|
|
2584
|
+
: `// ${sources.marker}\nstruct DrawUniforms {\n modelViewProjection: mat4x4<f32>,\n color: vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> u_draw: DrawUniforms;\n\n@fragment\nfn ${fragmentEntry}() -> @location(0) vec4<f32> {\n return u_draw.color;\n}\n`,
|
|
2195
2585
|
entryPoints: [vertexEntry, fragmentEntry],
|
|
2196
2586
|
uniformLayout: "generated-basic"
|
|
2197
2587
|
};
|
|
2198
2588
|
}
|
|
2589
|
+
function nativeDepthFragment(marker, fragmentEntry) {
|
|
2590
|
+
// J2 native depth encoding: the GLSL depth program writes gl_FragCoord.z
|
|
2591
|
+
// into an rgba8 shadow target ("encoded-color-depth"). The framebuffer
|
|
2592
|
+
// depth @builtin(position).z is already 0..1 in WebGPU, so packing it
|
|
2593
|
+
// samples identically to the GL path through the projective PCF reader.
|
|
2594
|
+
return `// ${marker}\nstruct DrawUniforms {\n modelViewProjection: mat4x4<f32>,\n color: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> u_draw: DrawUniforms;\n\n@fragment\nfn ${fragmentEntry}(input: VertexOutput) -> @location(0) vec4<f32> {\n return vec4<f32>(input.position.z, input.position.z, input.position.z, 1.0);\n}\n`;
|
|
2595
|
+
}
|
|
2199
2596
|
const PORTABLE_BINDINGS_MARKER = "/* @aura3d-bindings */";
|
|
2200
2597
|
function injectPortableBindings(source, bindings) {
|
|
2201
2598
|
if (!source.includes(PORTABLE_BINDINGS_MARKER)) {
|
|
@@ -2299,6 +2696,23 @@ function nativeUniformStruct() {
|
|
|
2299
2696
|
// 96-joint skinning palette (WebGL2 \`u_jointMatrices[96]\` parity). Appended at the struct end so
|
|
2300
2697
|
// the existing field offsets are unchanged.
|
|
2301
2698
|
joints: array<mat4x4<f32>, 96>,
|
|
2699
|
+
// J2 native shadow projections (float offsets 1724+, after the 96-joint
|
|
2700
|
+
// palette). Directional block mirrors GLSL \`a3dForwardShadowFactor\`;
|
|
2701
|
+
// spot block mirrors \`a3dSpotShadowFactor\` (cone/range pre-check inside
|
|
2702
|
+
// the helper). Appended here so no existing field offset moves.
|
|
2703
|
+
shadowMapMatrix: mat4x4<f32>,
|
|
2704
|
+
shadowParams0: vec4<f32>,
|
|
2705
|
+
shadowParams1: vec4<f32>,
|
|
2706
|
+
shadowPcf: array<vec4<f32>, 32>,
|
|
2707
|
+
spotShadowMatrix: mat4x4<f32>,
|
|
2708
|
+
spotShadowParams0: vec4<f32>,
|
|
2709
|
+
spotShadowParams1: vec4<f32>,
|
|
2710
|
+
spotShadowParams2: vec4<f32>,
|
|
2711
|
+
spotShadowParams3: vec4<f32>,
|
|
2712
|
+
spotShadowPcf: array<vec4<f32>, 32>,
|
|
2713
|
+
// Model matrix for GLSL-parity world composition on the instanced path
|
|
2714
|
+
// (\`u_modelMatrix * instanceMatrix * position\`; float offset 2036).
|
|
2715
|
+
modelMatrix: mat4x4<f32>,
|
|
2302
2716
|
};
|
|
2303
2717
|
|
|
2304
2718
|
@group(0) @binding(0) var<uniform> u_draw: DrawUniforms;
|
|
@@ -2330,6 +2744,8 @@ ${nativeUniformStruct()}
|
|
|
2330
2744
|
@group(0) @binding(14) var u_occlusionTexture: texture_2d<f32>;
|
|
2331
2745
|
@group(0) @binding(15) var u_clusterLightData: texture_2d<f32>;
|
|
2332
2746
|
@group(0) @binding(16) var u_clusterLightIndices: texture_2d<f32>;
|
|
2747
|
+
@group(0) @binding(17) var u_spotShadowSampler: sampler;
|
|
2748
|
+
@group(0) @binding(18) var u_spotShadowTexture: texture_2d<f32>;
|
|
2333
2749
|
|
|
2334
2750
|
struct VertexOutput {
|
|
2335
2751
|
@builtin(position) position: vec4<f32>,
|
|
@@ -2421,6 +2837,67 @@ fn perturbNormal(normalInput: vec3<f32>, tangentFrame: vec4<f32>, normalSample:
|
|
|
2421
2837
|
return normalize(tangent * mapped.x + bitangent * mapped.y + n * max(mapped.z, 0.001));
|
|
2422
2838
|
}
|
|
2423
2839
|
|
|
2840
|
+
fn a3dNativeShadowFactor(worldPosition: vec3<f32>, normal: vec3<f32>, lightDirection: vec3<f32>, useSpot: bool, shadowSampler: sampler, shadowTexture: texture_2d<f32>) -> f32 {
|
|
2841
|
+
var shadowMatrix = u_draw.shadowMapMatrix;
|
|
2842
|
+
var bias = u_draw.shadowParams0.x;
|
|
2843
|
+
var slopeBias = u_draw.shadowParams0.y;
|
|
2844
|
+
var texelSize = u_draw.shadowParams0.zw;
|
|
2845
|
+
var strength = u_draw.flags.z;
|
|
2846
|
+
var sampleCount = i32(u_draw.shadowParams1.x + 0.5);
|
|
2847
|
+
var receiverLightDirection = lightDirection;
|
|
2848
|
+
if (useSpot) {
|
|
2849
|
+
if (u_draw.spotShadowParams2.x < 0.5) { return 1.0; }
|
|
2850
|
+
let toFragment = worldPosition - u_draw.spotShadowParams0.xyz;
|
|
2851
|
+
let distanceToLight = max(length(toFragment), 0.0001);
|
|
2852
|
+
if (distanceToLight > u_draw.spotShadowParams0.w) { return 1.0; }
|
|
2853
|
+
let coneAxis = u_draw.spotShadowParams1.xyz / max(length(u_draw.spotShadowParams1.xyz), 0.0001);
|
|
2854
|
+
if (dot(toFragment / distanceToLight, coneAxis) < cos(u_draw.spotShadowParams3.w)) { return 1.0; }
|
|
2855
|
+
receiverLightDirection = coneAxis * -1.0;
|
|
2856
|
+
shadowMatrix = u_draw.spotShadowMatrix;
|
|
2857
|
+
bias = u_draw.spotShadowParams2.z;
|
|
2858
|
+
slopeBias = u_draw.spotShadowParams2.w;
|
|
2859
|
+
texelSize = u_draw.spotShadowParams3.xy;
|
|
2860
|
+
strength = u_draw.spotShadowParams2.y;
|
|
2861
|
+
sampleCount = i32(u_draw.spotShadowParams3.z + 0.5);
|
|
2862
|
+
}
|
|
2863
|
+
let lightPosition = shadowMatrix * vec4<f32>(worldPosition, 1.0);
|
|
2864
|
+
let projected = lightPosition.xyz / max(lightPosition.w, 0.0001);
|
|
2865
|
+
let uv = projected.xy * 0.5 + vec2<f32>(0.5, 0.5);
|
|
2866
|
+
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { return 1.0; }
|
|
2867
|
+
// Native render targets store framebuffer row 0 at the top while the CPU
|
|
2868
|
+
// projection math addresses texels GL-style (row 0 at the bottom), so the
|
|
2869
|
+
// sample V must be mirrored to land on the texel the projection means.
|
|
2870
|
+
// Display and readback paths already account for the row order, which is
|
|
2871
|
+
// why screenshots and readPixels match GL while direct sampling does not.
|
|
2872
|
+
let sampleUv = vec2<f32>(uv.x, 1.0 - uv.y);
|
|
2873
|
+
let receiverNormal = normalize(normal);
|
|
2874
|
+
let shadowLightDirection = receiverLightDirection / max(length(receiverLightDirection), 0.0001);
|
|
2875
|
+
let normalDotLight = clamp(abs(dot(receiverNormal, shadowLightDirection)), 0.0, 1.0);
|
|
2876
|
+
// Shared bias-table policy (GLSL parity): per-sample tangent-scaled slope
|
|
2877
|
+
// bias — a centre-only bias under-compensates outer PCF taps on sloped
|
|
2878
|
+
// receivers, so each tap scales by its own texel distance.
|
|
2879
|
+
let slopeTangent = min(sqrt(max(1.0 - normalDotLight * normalDotLight, 0.0)) / max(normalDotLight, 0.05), 8.0);
|
|
2880
|
+
let slopeTexelBias = slopeTangent * slopeBias * max(texelSize.x, texelSize.y);
|
|
2881
|
+
let projectedDepth = projected.z * 0.5 + 0.5;
|
|
2882
|
+
var shadowed = 0.0;
|
|
2883
|
+
var totalWeight = 0.0;
|
|
2884
|
+
let taps = clamp(sampleCount, 1, 32);
|
|
2885
|
+
for (var i = 0; i < 32; i = i + 1) {
|
|
2886
|
+
if (i >= taps) { break; }
|
|
2887
|
+
var sampleData = u_draw.shadowPcf[i];
|
|
2888
|
+
if (useSpot) { sampleData = u_draw.spotShadowPcf[i]; }
|
|
2889
|
+
let weight = max(sampleData.z, 0.0);
|
|
2890
|
+
let offset = sampleData.xy * texelSize;
|
|
2891
|
+
let storedDepth = textureSampleLevel(shadowTexture, shadowSampler, sampleUv + offset, 0.0).r;
|
|
2892
|
+
let sampleTexelDistance = max(1.0, length(sampleData.xy));
|
|
2893
|
+
let receiverDepth = projectedDepth - bias - slopeTexelBias * sampleTexelDistance;
|
|
2894
|
+
shadowed = shadowed + select(0.0, 1.0, receiverDepth > storedDepth) * weight;
|
|
2895
|
+
totalWeight = totalWeight + weight;
|
|
2896
|
+
}
|
|
2897
|
+
let occlusion = select(0.0, shadowed / totalWeight, totalWeight > 0.0);
|
|
2898
|
+
return mix(1.0, 1.0 - occlusion, clamp(strength, 0.0, 1.0));
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2424
2901
|
fn shadePbr(normalInput: vec3<f32>, tangentFrame: vec4<f32>, uv: vec2<f32>, worldPosition: vec3<f32>, fragmentPosition: vec2<f32>) -> vec4<f32> {
|
|
2425
2902
|
var normal = normalize(normalInput);
|
|
2426
2903
|
if (u_draw.morph0.x > 0.5) {
|
|
@@ -2484,11 +2961,14 @@ fn shadePbr(normalInput: vec3<f32>, tangentFrame: vec4<f32>, uv: vec2<f32>, worl
|
|
|
2484
2961
|
environmentSpecularContribution = environmentSpecular;
|
|
2485
2962
|
environment = environment + environmentDiffuse + environmentSpecular;
|
|
2486
2963
|
}
|
|
2964
|
+
// J2 native shadow projections: directional PCF mirrors GLSL
|
|
2965
|
+
// a3dForwardShadowFactor; spot PCF mirrors a3dSpotShadowFactor. Both
|
|
2966
|
+
// default to 1.0 (lit) when disabled or outside the shadow frustum.
|
|
2487
2967
|
var shadow = 1.0;
|
|
2488
2968
|
if (u_draw.flags.y > 0.5) {
|
|
2489
|
-
|
|
2490
|
-
shadow = mix(1.0, shadowDepth, clamp(u_draw.flags.z, 0.0, 1.0));
|
|
2969
|
+
shadow = a3dNativeShadowFactor(worldPosition, normal, lightDirection, false, u_shadowSampler, u_shadowTexture);
|
|
2491
2970
|
}
|
|
2971
|
+
shadow = shadow * a3dNativeShadowFactor(worldPosition, normal, lightDirection, true, u_spotShadowSampler, u_spotShadowTexture);
|
|
2492
2972
|
var clusteredDirect = vec3<f32>(0.0, 0.0, 0.0);
|
|
2493
2973
|
if (u_draw.materialFlags.w > 0.5) {
|
|
2494
2974
|
let indexDimensions = textureDimensions(u_clusterLightIndices);
|
|
@@ -2615,13 +3095,27 @@ fn instanceMatrix(index: u32) -> mat4x4<f32> {
|
|
|
2615
3095
|
return u_draw.instance0;
|
|
2616
3096
|
}
|
|
2617
3097
|
|
|
3098
|
+
fn a3dInstanceNormalMatrix(model: mat4x4<f32>) -> mat3x3<f32> {
|
|
3099
|
+
// GLSL parity: v_normal = u_normalMatrix * transpose(inverse(mat3(instance))) * a_normal.
|
|
3100
|
+
let m = mat3x3<f32>(model[0].xyz, model[1].xyz, model[2].xyz);
|
|
3101
|
+
let adj0 = cross(m[1], m[2]);
|
|
3102
|
+
let adj1 = cross(m[2], m[0]);
|
|
3103
|
+
let adj2 = cross(m[0], m[1]);
|
|
3104
|
+
let det = dot(m[0], adj0);
|
|
3105
|
+
let safeDet = select(det, 1.0, abs(det) < 0.00001);
|
|
3106
|
+
return mat3x3<f32>(
|
|
3107
|
+
vec3<f32>(adj0.x, adj1.x, adj2.x) / safeDet,
|
|
3108
|
+
vec3<f32>(adj0.y, adj1.y, adj2.y) / safeDet,
|
|
3109
|
+
vec3<f32>(adj0.z, adj1.z, adj2.z) / safeDet);
|
|
3110
|
+
}
|
|
3111
|
+
|
|
2618
3112
|
@vertex
|
|
2619
3113
|
fn ${vertexEntry}(@location(0) position: vec3<f32>, @location(1) normal: vec3<f32>, @builtin(instance_index) instanceIndex: u32) -> VertexOutput {
|
|
2620
3114
|
var output: VertexOutput;
|
|
2621
3115
|
let model = instanceMatrix(instanceIndex);
|
|
2622
|
-
let worldPosition = (model * vec4<f32>(position, 1.0)).xyz;
|
|
3116
|
+
let worldPosition = (u_draw.modelMatrix * model * vec4<f32>(position, 1.0)).xyz;
|
|
2623
3117
|
output.position = a3dWebGPUClipPosition(u_draw.modelViewProjection * model * vec4<f32>(position, 1.0));
|
|
2624
|
-
output.normal = normalize((
|
|
3118
|
+
output.normal = normalize((u_draw.normalMatrix * vec4<f32>(a3dInstanceNormalMatrix(model) * normal, 0.0)).xyz);
|
|
2625
3119
|
output.uv = vec2<f32>(0.5, 0.5);
|
|
2626
3120
|
output.worldPosition = worldPosition;
|
|
2627
3121
|
output.tangent = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|