타일셋 가장자리 확장 · 텍스처 번짐을 막는 아틀라스 여백
필터링과 밉맵은 타일 바로 바깥을 샘플링해서 이웃 타일이 선처럼 번집니다. 가장자리 확장은 각 타일의 테두리 픽셀을 바깥으로 복사하는, Phaser·LÖVE·Defold·Unity 타일맵에서 쓰는 고전적인 해결책입니다.
타일 사이의 가는 선이나 틈은 GPU가 타일 사각형 바로 바깥의 텍셀을 샘플링할 때 생깁니다. 선형 필터링, 밉맵, 정수가 아닌 위치의 카메라가 이웃 타일이나 빈 틈을 읽는 것입니다. 가장자리 확장은 각 타일의 맨 바깥 픽셀을 바깥 테두리로 복사해, 타일 밖에 떨어진 샘플도 타일 자신의 색을 읽게 해서 이를 고칩니다. 이 페이지는 격자 시트의 모든 타일을 확장해(기본 2px, 0~16) padded-atlas.png를 만들며, 타일은 여백 = 확장 폭, 간격 = 확장 폭 × 2에 놓입니다. 먼저 엔진을 확인하세요. Godot 4 TileSet은 이미 내부적으로 1px 여백을 둡니다.

- Tiles측정 확인· Pillow
- Padded atlas측정 확인· Pillow
- Seam report측정 확인· Pillow
타일 사이에 선이 생기는 이유
타일맵은 모든 타일을 아틀라스의 사각형 하나에서 그립니다. 최근접 필터에 카메라가 정수 픽셀 위에 있으면 화면 픽셀마다 사각형 안 텍셀 하나를 읽어 번짐이 없습니다. 선형 필터는 가장 가까운 텍셀 네 개를 섞으므로 가장자리의 샘플은 색의 최대 절반을 옆 텍셀에서 가져옵니다. 밉맵은 단계마다 2 × 2 블록을 평균하므로 작게 그릴수록 이웃이 더 섞이고, 정수가 아닌 확대나 카메라 위치는 샘플을 그 가장자리로 옮깁니다.
여백(padding)과 확장(extrude)은 다른 해결책입니다. 여백(TexturePacker의 shape padding)은 사각형 사이에 투명 픽셀을 남겨 이웃은 읽지 않지만 빈 틈을 읽게 되고, 이것이 어둡거나 비쳐 보이는 선이 됩니다. 확장은 가장자리 픽셀을 바깥으로 반복하므로(TexturePacker: "repeats the sprite's pixels at the border; sprite's size is not changed") 새어 들어오는 것도 타일 자신의 색입니다. 모서리끼리 딱 붙어야 하는 타일이라면 선을 숨기는 것은 확장입니다.
이미 이것을 하는 엔진도 있습니다. Godot 4의 TileSetAtlasSource.use_texture_padding은 기본으로 켜져 있고 타일마다 1픽셀을 더 두른 내부 텍스처를 만들므로, Godot 4 TileSet에는 대개 이 작업이 필요 없습니다. Tiled는 확장된 타일셋을 쓰도록 margin과 spacing을 지원하고, Phaser addTilesetImage는 tileMargin과 tileSpacing을 받습니다. 확장하면 이 두 숫자가 바뀌며, 이를 잊는 것이 가장 흔한 새 문제입니다.
예시: 203 × 186 시트를 2px 확장
시트는 16 × 16 타일 12 × 11개, 간격 1px입니다. 결과의 픽셀 위치:
원본 203 × 186: 16 × 16 타일 12 × 11개, 여백 0, 간격 1
확장 2 → 칸마다 16 + 2 × 2 = 20px → padded-atlas.png 12 × 20 = 240, 11 × 20 = 220
13번 1열, 1행: 원본 17,17 → 아틀라스 x = 1 × 20 + 2 = 22, y = 22 (픽셀 22~37)
테두리 x 20~21은 타일의 0번 열 반복, x 38~39는 15번 열 반복; 2 × 2 모서리는 모서리 픽셀 반복
엔진 여백 2, 간격 4 → Tiled margin 2 / spacing 4 · Phaser addTilesetImage(name, key, 16, 16, 2, 4) · Godot margins (2, 2), separation (4, 4)
여백만 같은 4px를 투명으로 두면: 37번과 38번 텍셀 중간의 선형 샘플은 타일 50%, 빈 곳 50%원본 시트의 1px 간격은 사라집니다. 아틀라스를 격자에서 새로 배치하기 때문입니다. 같은 ZIP의 metadata.json은 원본 영역(13번은 17,17)을 유지하므로, 여백 아틀라스에는 여백·간격 값을 쓰세요.
이렇게 작동합니다
- 타일마다 9번 복사: 타일, 바깥으로 늘인 네 변, 네 모서리 픽셀.
- 여백과 간격이 있는 시트에서도 작동합니다(측정한 격자로 타일을 찾음).
- 출력: 잘라 낸 타일·metadata.json(원본 영역)과 함께 padded-atlas.png. 아틀라스에서 각 타일은 여백 = 확장 폭, 간격 = 확장 폭 × 2에 놓입니다.
- Chromium·Firefox에서 기록된 품질 점검: 기준 확장 결과와 바이트 동일(1×1 칸 포함).
사용 방법
- 타일셋을 끌어다 놓습니다.
- 격자를 확인합니다.
- 확장 폭을 정합니다(보통 1~2px).
- 여백 아틀라스와 메타데이터를 받습니다.
결과 확인
padded-atlas.png를 엽니다. 크기가 열 수 × (타일 + 2 × 확장) × 행 수 × (타일 + 2 × 확장)이고, 0번 타일이 (확장, 확장)에서 시작해야 합니다.- 타일 하나의 테두리를 확대합니다. 테두리 픽셀이 모두 그 타일의 가장 가까운 가장자리 픽셀과 같아야 합니다.
- 엔진에서 여백 = 확장, 간격 = 2 × 확장으로 두고, 카메라를 타일 경계 위로 소수 단위로 움직이고 확대해 봅니다. 선이 보이지 않아야 합니다.
문제 해결
| 증상 | 유력한 원인 | 확인 방법 | 해결 |
|---|---|---|---|
| 카메라가 움직이거나 확대할 때만 선이 깜빡임 | 소수 위치와 선형 필터가 타일 경계 너머를 샘플링 | 카메라를 정수 픽셀 위치에 멈추면 선이 사라짐 | 아틀라스를 확장하거나, 최근접 필터와 정수 픽셀 카메라 위치 사용 |
| 맵을 축소했을 때만 선이 보임 | 밉맵이 이웃 타일을 포함한 블록을 평균함 | 타일셋 텍스처의 밉맵을 끄면 선이 사라짐 | 더 많이 확장하거나(2~4px), 2D 타일맵의 밉맵 끄기 |
| 확장한 뒤 모든 타일에 이웃 타일이 보임 | 엔진이 아직 예전 여백·간격을 씀 | 여백 아틀라스에서 0번 타일은 (확장, 확장)에서 시작해야 함 | 여백 = 확장, 간격 = 2 × 확장으로(확장 2 → 2와 4) |
| 타일의 투명한 부분 둘레에 어두운 테두리 | 완전히 투명한 픽셀 아래 색이 검정이고 필터가 이를 섞음. 확장은 타일 바깥에만 픽셀을 더함 | 가장자리 근처 투명 픽셀의 RGB 확인 | 가장자리 번짐 채우기로 투명 픽셀 아래 색 채우기 |
| 최근접 필터에 밉맵이 없어도 틈이 남음 | 타일 자체가 떨어져 배치됨: 맵 위치나 배율이 정수 픽셀이 아님 | 타일 위치와 카메라 배율 확인 | 맵이나 카메라를 고치기. 아틀라스로 고칠 수 있는 범위가 아니며 Nerulio도 바꿀 수 없음 |
출력 파일과 검증 방법
"측정 확인"은 내려받은 파일을 페이지 밖에서(Pillow·numpy·별도 파서) 다시 열어 그 속성을 측정했다는 뜻입니다. 게임 엔진에서 불러오는 것은 시험하지 않았습니다.
| 대상 | 파일 | 검증 |
|---|---|---|
| Tiles | tiles/tile-NNN.png · metadata.json | 측정 확인 Pillow잘라 낸 모든 타일이 원본 영역과 픽셀 단위로 같습니다(여백·간격·빈 타일·중복 포함). |
| Padded atlas | padded-atlas.png | 측정 확인 Pillow가장자리 픽셀을 바깥으로 복제하며, 기록된 atlas-padding 품질 점검이 Chromium·Firefox에서 통과합니다. |
| Seam report | PNG (healed) · measurements on screen | 측정 확인 Pillow이어지는 타일은 이음새 없음, 그라데이션은 이음새 있음으로 보고하며, 고친 타일의 경계 차이는 평균 8단계 미만입니다. |
검증 근거
합성 시트 10개 중 10개에서 실제 격자를 1순위로 감지(여백 1·간격 2 포함). 잘라 낸 타일 13/13이 원본 영역과 바이트 동일. 1600타일 자르기가 2.2초에 끝나고 취소할 수 있음. 이음새 판정은 타일 내부 기준으로 측정.
한계
- 규칙적인 격자만 지원하며, 4메가픽셀을 넘으면 타일 크기를 측정하지 않고 직접 입력합니다.
- 비슷한 타일 찾기는 픽셀 차이 기준이며 지각적 유사도가 아닙니다.
- "이음새 없애기"는 혼합 띠 안의 그림을 바꾸며 새로 그려 넣지 않습니다. 전후를 보여 줍니다.
- 2 × 2보다 작은 타일은 이음새를 잴 수 없습니다.
버전과 출처
docs/TILE-LAB.md와 tests/recipes-browser.py에서 측정했습니다. 1px 타일 2 × 1개 시트를 1px 확장하면 가장자리 픽셀이 분리된 6 × 3 아틀라스가 되고, 8px 타일·여백 1·간격 2 시트는 36 × 24 아틀라스가 되며 타일 픽셀 6 × 64개와 왼쪽 가장자리 픽셀 6 × 8개가 원본과 같았습니다. 32 × 32 시트의 여백 3, 1 × 1·1 × 16·16 × 1 칸의 여백 2에서 기준 확장 결과와 바이트 동일했습니다(Chromium·Firefox). 예시 위치는 같은 배치 함수로 다시 계산했습니다. 엔진 동작은 Godot 4.7, Tiled, Phaser, TexturePacker 공식 문서를 따릅니다.
참고한 공식 문서
자주 묻는 질문
엔진의 타일 설정을 바꿔야 하나요?
네. 타일 사이가 벌어집니다. 여백 아틀라스에서 여백은 확장 폭, 간격은 그 두 배입니다(확장 2 → 여백 2, 간격 4). metadata.json에는 원본 시트의 영역이 남습니다.
가장자리 번짐과 같은 건가요?
아닙니다. 확장은 아틀라스 속 각 타일 주위에 픽셀을 더하고, 번짐은 이미지 안의 투명 픽셀을 채웁니다.
무료인가요? 계정이 필요한가요?
이 페이지의 기능은 모두 무료이며 계정 없이 쓸 수 있습니다. 워터마크나 막혀 있는 내보내기가 없습니다.
스프라이트가 업로드되나요?
아니요. Studio는 이 브라우저 탭 안에서 디코딩·패킹·내보내기를 합니다. 프로젝트는 브라우저(IndexedDB)에 자동 저장되며 .nerulio 파일로 저장할 수 있습니다.
Aseprite나 엔진 플러그인을 설치해야 하나요?
아니요. .aseprite 파일은 브라우저에서 읽고 씁니다. 엔진별 번들에는 그 엔진에 필요한 것이 들어 있습니다(Godot는 가져오기 설정과 씬, Unity와 Godot 타일셋은 작은 편집기 스크립트).