@@ -88,6 +88,69 @@ export let Quad_frag_wgsl: string = /*wgsl*/ `
8888 return FragmentOutput(color);
8989 }
9090`
91+ /**
92+ * Present blit for opaque canvases (CanvasConfig.alpha unset/false).
93+ * The swapchain always runs with alphaMode 'premultiplied' (see
94+ * Context3D.init), so whatever alpha the scene left in the color
95+ * buffer would be composited against the page. A default-configured
96+ * canvas must be opaque: a scene with no sky and no opaque geometry
97+ * (e.g. pure additive-particle demos) leaves alpha 0 in the color
98+ * buffer, and without this clamp the page background bleeds through
99+ * the render — the result then changes with the page's CSS color and
100+ * with per-compositor handling of out-of-range premultiplied values.
101+ * @internal
102+ */
103+ export let Quad_opaque_frag_wgsl : string = /*wgsl*/ `
104+ struct FragmentOutput {
105+ @location(auto) o_Target: vec4<f32>
106+ };
107+
108+ @group(1) @binding(0)
109+ var baseMapSampler: sampler;
110+ @group(1) @binding(1)
111+ var baseMap: texture_2d<f32>;
112+
113+ @fragment
114+ fn main(@location(auto) fragUV: vec2<f32>) -> FragmentOutput {
115+ var uv = fragUV ;
116+ uv.y = 1.0 - uv.y ;
117+ var color: vec4<f32> = textureSample(baseMap, baseMapSampler, uv );
118+ color.a = 1.0;
119+ return FragmentOutput(color);
120+ }
121+ `
122+ /**
123+ * Present blit for transparent canvases (CanvasConfig.alpha).
124+ * The swapchain is configured with alphaMode 'premultiplied', which
125+ * requires every component of rgb <= a; values outside that range are
126+ * undefined per the WebGPU spec (some compositors clamp rgb to a,
127+ * silently erasing additive content whose pixels carry light in rgb
128+ * but 0 coverage in alpha). Lift alpha to the brightest color
129+ * component so the output is always spec-valid: additive light then
130+ * composites as glow that partially occludes the page instead of
131+ * being clamped away.
132+ * @internal
133+ */
134+ export let Quad_premultiply_frag_wgsl : string = /*wgsl*/ `
135+ struct FragmentOutput {
136+ @location(auto) o_Target: vec4<f32>
137+ };
138+
139+ @group(1) @binding(0)
140+ var baseMapSampler: sampler;
141+ @group(1) @binding(1)
142+ var baseMap: texture_2d<f32>;
143+
144+ @fragment
145+ fn main(@location(auto) fragUV: vec2<f32>) -> FragmentOutput {
146+ var uv = fragUV ;
147+ uv.y = 1.0 - uv.y ;
148+ var color: vec4<f32> = textureSample(baseMap, baseMapSampler, uv );
149+ let coverage = saturate(max(color.r, max(color.g, color.b)));
150+ color.a = max(color.a, coverage);
151+ return FragmentOutput(color);
152+ }
153+ `
91154/**
92155 * @internal
93156 */
0 commit comments