--- id: wiki-2026-0508-edge-bleeding title: Edge Bleeding category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Texture Bleeding, Atlas Bleeding] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [graphics, texture, rendering] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: GLSL/HLSL framework: WebGL/Three.js/Unity/Unreal --- # Edge Bleeding ## 매 한 줄 > **"매 texture sampling 의 인접 texel이 unintended 하게 leak — atlas seam의 colored line으로 manifest."**. 매 mipmap downsample / bilinear interpolation 매 texel boundary를 cross 하면서 발생. 매 sprite atlas, texture array, lightmap에서 매 visible artifact. ## 매 핵심 ### 매 원인 - **Bilinear filter**: 매 sample point 매 texel center 사이 → 인접 texel weighted. - **Mipmap downsample**: 매 box filter 매 atlas neighbor를 average. - **UV precision**: float precision 매 정확한 boundary 매 hit 못 함. - **Anisotropic filtering**: 매 large footprint → 매 더 많은 neighbor sampling. ### 매 manifestation - Sprite atlas: 매 sprite edge 매 인접 sprite color line. - Tilemap: 매 tile seam 매 dark/bright line. - Skybox cubemap: 매 face boundary 매 visible seam. - Lightmap: 매 chart boundary 매 dark crack. ### 매 응용 (해결책) 1. **Padding (gutter)**: 매 sprite 사이 1-2px transparent / replicated edge. 2. **Half-texel UV inset**: UV 매 `(0.5/W, 0.5/H)` ~ `(1 - 0.5/W, 1 - 0.5/H)` 의 clamp. 3. **CLAMP_TO_EDGE**: wrap mode 매 repeat → clamp. 4. **Conservative UV**: 매 mesh UV 매 atlas region 의 inside 의 inset. 5. **Mipmap-aware padding**: 매 mip level 별 padding (1px → 2px → 4px). ## 💻 패턴 ### Half-texel inset (GLSL) ```glsl uniform vec2 atlasSize; // e.g. (2048, 2048) uniform vec4 spriteRect; // x, y, w, h in pixels vec2 sampleUV(vec2 localUV) { vec2 halfTexel = 0.5 / atlasSize; vec2 minUV = spriteRect.xy / atlasSize + halfTexel; vec2 maxUV = (spriteRect.xy + spriteRect.zw) / atlasSize - halfTexel; return mix(minUV, maxUV, localUV); } ``` ### Atlas packer with padding (TS) ```typescript function packSprite(sprite: ImageData, padding = 2): PackedSprite { const w = sprite.width + padding * 2; const h = sprite.height + padding * 2; const padded = new ImageData(w, h); // edge replicate (not transparent) for bilinear safety for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { const sx = clamp(x - padding, 0, sprite.width - 1); const sy = clamp(y - padding, 0, sprite.height - 1); copyPixel(sprite, sx, sy, padded, x, y); } } return { data: padded, padding }; } ``` ### Three.js NearestFilter (no bleed, no smooth) ```javascript texture.minFilter = THREE.NearestFilter; texture.magFilter = THREE.NearestFilter; texture.generateMipmaps = false; texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping; ``` ### Mipmap-aware padding count ```typescript function paddingForMipLevels(levels: number): number { return Math.pow(2, levels - 1); // 4 levels → 8px padding } ``` ### Conservative cubemap seam fix (HLSL) ```hlsl float3 SampleCubeSeamless(TextureCube tex, SamplerState s, float3 dir, float roughness) { float mip = roughness * 8.0; float texSize = 512.0 / pow(2, mip); float scale = 1.0 - 1.0 / texSize; return tex.SampleLevel(s, dir * scale, mip).rgb; } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Pixel art sprite | NearestFilter + padding 1px | | 3D texture atlas (smooth) | Half-texel inset + padding (2 * mip levels) | | Tilemap | Padding + clamp + per-tile texture array (best) | | Cubemap | GL_TEXTURE_CUBE_MAP_SEAMLESS (GL 3.2+) | | Lightmap | Chart padding 4px + dilation | **기본값**: 매 padding 2px + half-texel inset, 매 mipmap 매 사용 시 2 * (mip levels) px. ## 🔗 Graph - 변형: [[Atlas Bleeding]] - 응용: [[Texture Array]] - Adjacent: [[Mipmap]] ## 🤖 LLM 활용 **언제**: sprite atlas / tilemap / lightmap 매 visible seam, 매 mipmap 매 사용한 atlas, 매 cubemap face 경계 artifact. **언제 X**: single texture (no atlas), 매 NEAREST filter 매 사용 + no mipmap, fully procedural texture. ## ❌ 안티패턴 - **Transparent padding alone**: 매 bilinear가 transparent와 mix → 매 dark fringe. - **Padding 너무 small**: mip level 3+ 매 도달 시 여전히 leak. - **Repeat wrap on atlas**: 매 opposite edge 매 sample → 완전한 wrong color. - **UV exactly at texel boundary**: 매 float precision 의해 random side 매 sample. - **Generate mipmap on packed atlas**: 매 mip downsample 매 인접 sprite를 average → mid-mip levels artifact. ## 🧪 검증 / 중복 - Verified (Real-Time Rendering 4ed, Unity / Unreal docs, OpenGL spec). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Edge bleeding 원인 / padding 전략 / mip-aware solution |