diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index f788b83..c879195 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -830,6 +830,7 @@ public sealed class GameWindow : IDisposable ContextFlags.ForwardCompatible, new APIVersion(4, 3)), VSync = false, // off during development so the perf overlay shows true framerate + Samples = 4, // A.5 T20: MSAA 4x for A2C foliage smoothing }; _window = Window.Create(options); diff --git a/src/AcDream.App/Rendering/Shaders/mesh_modern.frag b/src/AcDream.App/Rendering/Shaders/mesh_modern.frag index c5d9a02..1145dc7 100644 --- a/src/AcDream.App/Rendering/Shaders/mesh_modern.frag +++ b/src/AcDream.App/Rendering/Shaders/mesh_modern.frag @@ -80,8 +80,11 @@ void main() { vec4 color = texture(tex, vec3(vTexCoord, float(vTextureLayer))); // Two-pass alpha-test (N.5 Decision 2). + // A.5 T20: opaque pass writes alpha as-sampled so GL_SAMPLE_ALPHA_TO_COVERAGE + // derives the MSAA sample mask from it — ClipMap foliage edges become smooth. + // Discard only fully-transparent (α < 0.05); the GPU handles coverage masking. if (uRenderPass == 0) { - if (color.a < 0.95) discard; // opaque pass + if (color.a < 0.05) discard; // opaque pass — kill truly empty only (A2C) } else { if (color.a >= 0.95) discard; // transparent pass if (color.a < 0.05) discard; // skip totally-empty diff --git a/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs b/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs index fcb9e66..5d35f68 100644 --- a/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs +++ b/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs @@ -488,6 +488,10 @@ public sealed unsafe class WbDrawDispatcher : IDisposable { _gl.Disable(EnableCap.Blend); _gl.DepthMask(true); + // A.5 T20: enable A2C for ClipMap foliage — GPU derives sample mask + // from the alpha written by mesh_modern.frag so foliage edges are + // smooth under MSAA 4x. A no-op for fully-opaque (α=1) batches. + _gl.Enable(EnableCap.SampleAlphaToCoverage); _shader.SetInt("uRenderPass", 0); _gl.BindBuffer(BufferTargetARB.DrawIndirectBuffer, _indirectBuffer); if (diag && _gpuQueriesInitialized) _gl.BeginQuery(QueryTarget.TimeElapsed, _gpuQueryOpaque); @@ -498,6 +502,7 @@ public sealed unsafe class WbDrawDispatcher : IDisposable (uint)_opaqueDrawCount, (uint)DrawCommandStride); if (diag && _gpuQueriesInitialized) _gl.EndQuery(QueryTarget.TimeElapsed); + _gl.Disable(EnableCap.SampleAlphaToCoverage); } // ── Phase 8: transparent pass ────────────────────────────────────────