데칼 아틀라스 Mip Bleeding 줄이기: 패딩 계산 수식과 UV 수축 전처리 파이프라인
데칼 아틀라스의 Mip Bleeding이 왜 생기는지 텍셀 필터 풋프린트와 밉맵 단계로 설명하고 최대 LOD 기반 패딩 수식, 알파 인지형 경계 확장, UV 수축, 압축·검증 절차까지 엔진과 렌더러가 달라도 적용할 수 있는 실무 파이프라인으로 정리합니다.

핵심 요약
데칼 아틀라스의 Mip Bleeding은 밉맵과 bilinear·anisotropic 필터의 샘플 풋프린트가 타일 경계를 넘어 이웃 데칼의 텍셀을 섞으면서 발생합니다. 최대 LOD와 필터 여유를 기준으로 기본 레벨 패딩을 계산하고 알파 인지형 경계 확장 후 밉맵을 생성해야 합니다.
기본 패딩은 다음처럼 계산할 수 있습니다.
여기서 p_0은 기본 레벨의 패딩, L_max는 실제로 사용될 최대 밉 레벨, g는 해당 밉 레벨에서 유지할 최소 동일 타일 여유 텍셀, r은 필터 커널 보호 여유입니다. 일반적인 bilinear 필터에서는 r = 1부터 시작하고 실제 플랫폼의 anisotropic filtering과 압축 결과를 포함해 검증합니다.
데칼 아틀라스 Mip Bleeding은 왜 발생하는가?
아틀라스의 각 데칼은 하나의 큰 텍스처 안에 배치됩니다. 기본 레벨에서 UV가 타일 내부를 가리키더라도 화면에서 데칼이 작아지면 GPU는 더 낮은 밉 레벨을 선택합니다. 이때 한 샘플이 참조하는 영역은 기본 레벨의 단일 텍셀보다 넓어집니다.
타일 사이에 충분한 패딩이 없으면 다음과 같은 순서로 색이 섞입니다.
- 밉맵 생성 단계에서 인접 타일의 색이 평균에 포함됩니다.
- bilinear 또는 trilinear filtering이 타일 경계 주변의 색을 보간합니다.
- BCn·ASTC 같은 블록 압축이 경계의 색과 알파를 다시 양자화합니다.
- 결과적으로 데칼 가장자리에 다른 데칼의 색, 검은색 또는 투명 배경이 나타납니다.
특히 알파가 0인 픽셀의 RGB가 검은색으로 남아 있으면 투명한 영역도 안전하지 않습니다. RGB와 알파를 별개로 처리하면 밉맵 보간이나 압축 과정에서 검은 테두리가 생길 수 있습니다.

최대 밉 레벨을 어떻게 정해야 하는가?
텍스처 전체의 마지막 밉 레벨까지 무조건 보호할 필요는 없습니다. 데칼이 화면에서 의미 있는 크기로 보이는 최대 LOD를 정하고 그 범위에 맞춰 패딩을 계산하는 편이 메모리와 아틀라스 수용률에 유리합니다.
화면 풋프린트를 기본 레벨 텍셀 단위로 나타낸 값을 rho_max라고 하면 사용할 최대 밉 레벨은 다음과 같이 정의할 수 있습니다.
rho_max: 가장 큰 텍스처 샘플 풋프린트L_available: 생성된 밉 체인의 마지막 인덱스L_max: 패딩 계산에 사용할 최대 밉 레벨
예를 들어 rho_max = 16, g = 1, r = 1이면 L_max = 4입니다.
따라서 해당 타일은 좌우상하에 최소 32 기본 레벨 텍셀의 패딩을 확보해야 합니다. 이 값은 타일 외곽에 각각 적용되는 값이며 전체 폭과 높이는 각각 2p_0만큼 증가합니다.
밉 레벨 L에서 남는 패딩은 이상적으로 다음과 같이 줄어듭니다.
패딩을 모든 밉 레벨에서 최소 g만큼 유지하려면 필터 여유 r을 포함해 p_L >= g + r이 되어야 합니다.
패딩과 UV 수축은 어떤 차이가 있는가?
두 기법은 비슷해 보이지만 보호하는 대상이 다릅니다.
| 기법 | 보호 대상 | 적용 시점 | 부족할 때 나타나는 문제 |
|---|---|---|---|
| 패딩 또는 경계 확장 | 밉맵 생성과 필터 샘플 풋프린트 | 텍스처 전처리 | 데칼 색이 이웃 타일과 섞임 |
| UV 수축 | 샘플 중심이 아틀라스 외곽 경계에 접근하는 것 | UV 전처리 또는 버텍스 셰이더 | 타일 끝에서 이웃 색이 직접 샘플됨 |
| 알파 인지형 처리 | 투명 배경의 RGB·알파 보간 | 텍스처 전처리 | 검은 후광, 불필요한 불투명 테두리 |
패딩만 늘리면 UV가 외곽 경계에 너무 가까이 접근하는 상황을 완전히 해결할 수 없습니다. 반대로 UV만 수축하면 밉맵 자체에 이미 섞인 색을 되돌릴 수 없습니다. 일반적인 파이프라인에서는 두 처리를 함께 사용합니다.
UV 수축을 어떻게 계산할까?
패커가 반환하는 사각형을 패딩을 포함한 외곽 영역 R = (x, y, w, h)로 정의합니다. 아틀라스 크기는 W x H, 원본 데칼 UV는 u, v이며 0에서 1 사이입니다.
최대 LOD의 bilinear 보호 여유를 기본 레벨로 환산한 값은 다음과 같습니다.
수축된 UV는 다음처럼 계산합니다.
이때 w와 h는 반드시 2m_0보다 커야 합니다. 패딩을 포함한 외곽 영역을 수축하므로 원본 UV의 가장자리는 외곽 패딩 안쪽을 샘플하게 됩니다. 경계 확장된 색이 동일하다면 시각적으로는 원본 데칼의 가장자리처럼 보입니다.
회전 패킹이나 플립 패킹을 사용한다면 수축 전에 임의로 UV를 다시 회전시키지 말고 패커가 제공한 회전·반전 메타데이터를 같은 변환에 포함해야 합니다.
실무 전처리 파이프라인은 어떻게 구성할까?
flowchart LR
A[원본 RGBA와 알파 마스크] --> B[알파 인지형 경계 확장]
B --> C[패딩 포함 타일 패킹]
C --> D[전체 아틀라스 밉맵 생성]
D --> E[타일별 UV 수축 변환]
E --> F[강제 LOD와 압축 QA]
1. 데칼 경계를 분석한다
원본 이미지의 알파 마스크에서 실제 콘텐츠 영역을 계산합니다. 단순히 투명 픽셀까지 포함한 이미지 크기를 타일 크기로 사용하면 필요 이상으로 큰 패딩이 생길 수 있습니다.
여러 개의 분리된 알파 섬이 있는 데칼은 처리 정책을 먼저 정해야 합니다. 구멍을 투명하게 유지해야 한다면 타일 외곽으로만 경계 색을 확장하고 내부 투명 구멍까지 채우지 않습니다.
2. 알파 인지형으로 경계를 확장한다
가장 가까운 불투명 픽셀의 RGB와 알파를 바깥쪽으로 복사하는 방식이 기본입니다. 단순히 투명 영역을 검은색으로 채우면 필터링 시 검은 후광이 생깁니다.
- straight alpha를 사용하면 투명 픽셀의 RGB도 주변 색으로 채웁니다.
- premultiplied alpha를 사용하면 premultiplied RGB와 알파를 함께 확장합니다.
- 텍스처 임포터의 sRGB, 알파 보존, 압축 옵션이 전처리 규약과 일치해야 합니다.
- 경계 확장은 아틀라스에 패킹하기 전에 실행합니다.
3. 최대 LOD 기반 패딩을 계산한다
Python 3.11 기준의 간단한 계산 예시는 다음과 같습니다. available_max_lod는 생성할 밉 체인에서 사용할 마지막 LOD 인덱스입니다.
from math import floor, log2
def choose_max_lod(rho_max: float, available_max_lod: int) -> int:
footprint = max(rho_max, 1.0)
return max(0, min(floor(log2(footprint)), available_max_lod))
def calculate_padding(
max_lod: int,
min_gutter: int = 1,
filter_guard: int = 1,
compression_block: int = 1,
) -> int:
raw_padding = (1 << max_lod) * (min_gutter + filter_guard)
return (
(raw_padding + compression_block - 1) // compression_block
) * compression_block
BC1·BC3·BC5·BC7 계열은 4x4 텍셀 블록을 사용하므로 패딩과 타일 좌표를 4 텍셀 단위로 정렬하면 관리가 쉬워집니다. ASTC는 선택한 블록 크기에 맞춰 정렬합니다. 정렬만으로 경계 문제가 해결되는 것은 아니므로 압축된 결과를 별도로 확인해야 합니다.
4. 패딩을 포함해 패킹하고 밉맵을 생성한다
각 타일의 실제 콘텐츠 주변에 계산된 패딩을 배치한 뒤 전체 아틀라스를 생성합니다. 밉맵 생성은 패딩과 경계 확장이 끝난 후 실행해야 합니다.
패딩을 아틀라스 밉맵 생성 후에 추가하면 이미 낮은 밉 레벨에 섞인 이웃 색을 복구할 수 없습니다. 플랫폼이 밉별 텍스처 데이터를 직접 받는다면 각 밉 레벨에서 다시 경계를 확장하는 방식도 사용할 수 있습니다.
타일이 매우 작아지는 마지막 밉 레벨에서는 여러 타일이 하나의 텍셀에 합쳐질 수 있습니다. 이 레벨까지 타일별 색을 완벽히 보존해야 한다면 아틀라스 대신 Texture2DArray 또는 타일별 텍스처를 검토해야 합니다.
5. UV 수축값을 전처리하거나 셰이더에 전달한다
UV를 메시 버텍스에 직접 베이크할 수 있고 인스턴싱을 사용하는 경우에는 타일별 rect, rotation, flip 메타데이터를 셰이더에 전달할 수 있습니다.
def shrink_uv(uv, rect, atlas_size, max_lod, filter_guard=1):
x, y, w, h = rect
atlas_w, atlas_h = atlas_size
inset = (1 << max_lod) * filter_guard
if 2 * inset >= min(w, h):
raise ValueError
u = (x + inset + uv[0] * (w - 2 * inset)) / atlas_w
v = (y + inset + uv[1] * (h - 2 * inset)) / atlas_h
return u, v
런타임에서 타일마다 다른 LOD 제한이 필요하다면 HLSL의 ddx, ddy, SampleGrad 또는 엔진의 equivalent texture sampling API를 활용할 수 있습니다. 다만 UV 변환 이후의 미분값이 올바르게 전달되는지 확인해야 합니다. 과도한 SampleLevel 고정은 텍스처 선명도와 성능을 해칠 수 있으므로 디버그 용도와 실제 샘플링 용도를 구분합니다.
Mip Bleeding을 어떻게 검증할까?
다음 순서로 검증하면 원인을 빠르게 분리할 수 있습니다.
- 이웃 타일에 빨강, 초록, 파랑처럼 극단적으로 다른 색을 넣습니다.
- 셰이더에서 강제 LOD를 0부터 마지막 사용 LOD까지 순회합니다.
- 카메라 거리, 사선 각도, trilinear filtering, anisotropic filtering을 조합합니다.
- RGB와 알파 채널을 각각 시각화합니다.
- BCn 또는 ASTC 압축 전후를 비교합니다.
- 아틀라스 경계에서 색이 변하는 픽셀 수를 자동 측정합니다.
다음 표처럼 증상을 기준으로 원인을 좁힐 수 있습니다.
| 증상 | 우선 확인할 항목 | 해결 방향 |
|---|---|---|
| 특정 LOD에서만 이웃 색이 보임 | L_max와 p_0 계산 | 최대 사용 LOD까지 패딩 확대 |
| 모든 LOD에서 검은 테두리가 생김 | 알파 0 RGB, premultiplied alpha | 알파 인지형 RGB 확장과 블렌딩 규약 통일 |
| 압축 후에만 가장자리가 깨짐 | BCn·ASTC 블록 경계 | 블록 정렬, 압축 설정, 패딩 증가 |
| 회전된 타일만 잘못 샘플됨 | packer의 rotation·flip 메타데이터 | UV 변환 순서 수정 |
| 원거리에서 데칼이 뭉개짐 | 지나치게 큰 UV 수축 또는 LOD 고정 | 수축 여유와 샘플 LOD를 분리 조정 |
자주 묻는 질문 FAQ
패딩을 늘렸는데도 검은 테두리가 생기는 이유는 무엇인가?
대부분 알파가 0인 픽셀의 RGB가 검은색으로 남았거나 straight alpha와 premultiplied alpha 처리가 섞인 경우입니다. RGB와 알파를 함께 경계 확장하고 텍스처 임포터의 알파 규약을 확인해야 합니다.
패딩과 UV 수축 중 하나만 사용해도 되는가?
아틀라스의 밉맵과 필터 보간을 보호하려면 패딩이 필요하고 샘플 중심을 외곽 경계에서 떨어뜨리려면 UV 수축이 필요합니다. 고품질 데칼 아틀라스에서는 두 방법을 함께 적용하는 것이 안전합니다.
마지막 1x1 밉까지 패딩을 유지해야 하는가?
데칼이 해당 LOD에서 더 이상 시각적으로 구분되지 않는다면 마지막 밉까지 타일 색을 보존할 필요는 없습니다. 실제 화면 풋프린트에 영향을 주는 L_max를 정하고 더 낮은 단계에서 타일 간 분리가 필요하면 Texture2DArray를 검토합니다.
정리
안정적인 데칼 아틀라스는 패커 설정 하나로 끝나지 않습니다. 최대 샘플 LOD를 먼저 정하고 p_0 >= 2^L_max(g + r)로 패딩을 계산한 뒤, 알파 인지형 경계 확장, 패딩 포함 패킹, 밉맵 생성, UV 수축, 압축 QA를 하나의 전처리 파이프라인으로 묶어야 합니다.
특히 밉맵을 만든 뒤 패딩을 추가하거나 투명 픽셀의 RGB를 방치하는 실수가 가장 흔합니다. 타일별 메타데이터에 패딩, 외곽 rect, 회전·플립 정보, 최대 LOD를 함께 저장하면 아티스트 툴과 런타임 셰이더 사이의 규칙도 일관되게 유지할 수 있습니다.



