From f39ee9e787b1e7a731ee3efdbfb812a12781bbd0 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Fri, 12 Jun 2026 13:44:44 +0300 Subject: [PATCH] Renderer: inset region UVs by half a texel to kill tile seams Region UVs ran exactly to the region edges, so at fractional zoom a tile's edge fragments could sample past U1/V1 into the atlas's transparent padding (point filtering), drawing thin black seams between tiles that flickered while zooming. Inset the precomputed UVs by half a texel (texel-centre to texel-centre) so sampling stays inside the region. Co-Authored-By: Claude Opus 4.8 --- src/MrGameEng.Graphics/Texture2DRegion.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/MrGameEng.Graphics/Texture2DRegion.cs b/src/MrGameEng.Graphics/Texture2DRegion.cs index ea44d11..68278c8 100644 --- a/src/MrGameEng.Graphics/Texture2DRegion.cs +++ b/src/MrGameEng.Graphics/Texture2DRegion.cs @@ -43,10 +43,13 @@ public sealed class Texture2DRegion // texture может быть null только в headless-тестах. if (texture is not null) { - U0 = bounds.X / (float)texture.Width; - V0 = bounds.Y / (float)texture.Height; - U1 = (bounds.X + bounds.Width) / (float)texture.Width; - V1 = (bounds.Y + bounds.Height) / (float)texture.Height; + // Полутексельный inset: UV идут от центра крайнего текселя к центру крайнего, а не по + // самым кромкам региона. С point-фильтрацией это гарантирует, что края тайла никогда не + // сэмплят прозрачный «жёлоб» атласа (Padding) — иначе при дробном зуме видны чёрные швы. + U0 = (bounds.X + 0.5f) / texture.Width; + V0 = (bounds.Y + 0.5f) / texture.Height; + U1 = (bounds.X + bounds.Width - 0.5f) / texture.Width; + V1 = (bounds.Y + bounds.Height - 0.5f) / texture.Height; } }