텍스처 패킹 알고리즘


52

좋은 텍스쳐 패킹 알고리즘은 무엇입니까? 기술적으로 빈 포장NP-hard 이므로 휴리스틱은 필자가 실제로 추구하는 것입니다.


UV 맵을 최적화하기 위해 이것을 사용한다고 가정했지만 응용 프로그램이 무엇인지 궁금합니다.
Jonathan Fischoff

ftgles는 opengl 및 freetype을 사용하여 글꼴을 렌더링하는 라이브러리입니다. 그러나 각 글리프는 자체 텍스처에 저장됩니다. 그것들을 하나의 질감으로 포장하고 싶습니다.
deft_code

답변:


58

더 나은 텍스쳐 패킹 알고리즘을 생각해 내기 위해 한 달에 몇 달을 보냈습니다.

우리가 시작한 알고리즘은 간단했습니다. 모든 입력 항목을 수집하십시오. 소비 한 총 픽셀 수를 기준으로 정렬합니다. 텍스쳐에 스캔 라인 순서로 배치하십시오. 왼쪽 위 픽셀에서 오른쪽 위 픽셀까지 테스트하고, 줄을 아래로 이동하고, 반복, 배치가 완료된 후 왼쪽 위 픽셀로 재설정하면됩니다.

너비를 하드 코딩하거나 다른 휴리스틱을 만들어야합니다. 직각도를 유지하기 위해 알고리즘은 128에서 시작한 다음 너비보다 깊지 않은 결과가 나올 때까지 128 초씩 증가합니다.

그래서 우리는 그 알고리즘을 가지고 있었고, 그것을 개선하기로 결정했습니다. 나는 엉뚱한 휴리스틱을 시도했다. 함께 맞는 객체를 찾고, 원하는 공간 패킹 속성에 대해 가중치를 부여하고, 회전하고 뒤집기. 문자 그대로 3 개월 동안 일한 결과, 3 %의 공간을 절약했습니다.

네. 삼%.

그리고 압축 루틴을 실행 한 후에는 실제로 더 커져서 (아직 설명 할 수 없습니다) 전체 내용을 버리고 이전 알고리즘으로 돌아갔습니다.

스캔 라인 순서대로 항목을 정렬하고 질감에 잼하십시오. 알고리즘이 있습니다. 코딩하기 쉽고 빠르게 실행할 수 있으며 놀라운 작업 없이는 더 나아지지 않을 것입니다. 회사 규모가 50 명 이상 이고 그 이상인 경우가 아니라면 그 작업은 가치가 없습니다 .

대체 텍스트

그리고 부수적으로, 나는 방금 말 그대로 당신이하고있는 것과 똑같은 응용 프로그램 (ftgles이 아니라 opengl- 렌더링 된 자유형 글리프)을 위해이 알고리즘 (고정 너비 512 픽셀)을 구현했습니다. 결과는 다음과 같습니다. 광산이 Valve의 거리 필드 기반 텍스트 렌더링 알고리즘을 사용하고 있기 때문에 흐릿하게 보입니다.이 알고리즘 은 글리프 사이의 추가 공간을 차지합니다. 분명히, 빈 공간이 많이 남아 있지 않으며 물건을 열린 곳으로 밀어 넣는 것이 좋습니다.

이에 대한 모든 코드는 BSD 라이센스이며 github에서 사용할 수 있습니다 .


나는 당신의 텍스쳐를 보았고 "나는 우리의 텍스쳐 패커가 그것보다 조금 더 잘한다고 확신합니다"라고 생각했습니다. 그리고 나서 그것을 보았고, 얼마 전에 그것을 깨뜨렸다는 것을 알지 못했습니다 (한 번 작동하면 출력 텍스처를 보는 사람이 누구입니까?) ... 그래서 게시 주셔서 감사합니다-찾을 수 없었습니다 그렇지 않으면 버그 :) (버그를 수정하면 매우 유사 해 보입니다. 어쩌면 그늘이 더 좋을지 모르지만 정확하게 말하기는 까다 롭습니다. "좋은 것"은 아마도 가장 안전한 설명 일 것입니다.
JasonD

@JasonD, 더 나은 출력을 얻는다면 알고리즘이 무엇을하는지 알고 싶습니다. :) 다른 방식으로 대략 동등한 출력을 얻더라도.
ZorbaTHut

1
알고리즘 설명 + 인정 된 실패 + 소스 코드에 감사드립니다. 좋은 포스트.
Calvin1602

1
압축 후 크기가 커진 이유는 압축 알고리즘 때문일 수 있습니다. 압축은 종종 해싱 및 이진 패턴 찾기에 의존하기 때문에 알고리즘이 충분한 패턴을 식별 할 수 있으면 전체 패턴을 생성하여 크기가 확장 될 수 있습니다. 파일을 계속해서 다시 압축하기 위해 이것을 테스트하는 좋은 방법이며 패턴 부족으로 인해 다시 커지기 시작합니다.
Hanna

1
ZorbaTHut의 포장 코드 (font_baker.cpp)의 최신 버전을 검색하는 방법은 여기에서 찾을 수 있습니다. github.com/zorbathut/glorp/blob/…
mems

20

Andrea Lodi의 박사 학위 논문은 2 차원 빈 포장 및 할당 문제에 대한 알고리즘 제목이 있습니다.
논문은이 문제의 더 어려운 형태를 다루고 있습니다. 운 좋게도 텍스쳐 패킹은 가장 쉬운 버전입니다. 그가 찾은 최고의 알고리즘은 Touching Perimeter 입니다.

52 페이지에서 인용하려면 :

TPRF (Touching Perimeter)라고하는 알고리즘은 비 증가 영역에 따라 항목을 정렬하고 (최소 증가하지 않는 min {wj, hj} 값으로 연결 해제) 가로 방향으로 시작합니다. 그런 다음 최적 솔루션 값의 하한 L이 계산되고 L 빈 빈이 초기화됩니다. (이전 섹션에서 정의 된 연속 하한 L0은 2BP | R | F에도 유효합니다. Dell'Amico, Martello 및 Vigo [56]에서 더 나은 범위를 제안합니다. 알고리즘은 한 번에 하나의 항목을 압축합니다. 기존 빈에서 또는 새 빈을 초기화하여. 빈에 포장 된 첫 번째 품목은 항상 왼쪽 하단에 있습니다. 각 후속 품목은 소위 정상 위치에 포장됩니다 (ChristoFade 및 Whitlock [41] 참조).
빈 및 포장 위치의 선택은 빈에 닿는 품목 주변 및 이미 포장 된 다른 품목의 백분율로 정의 된 점수를 평가하여 수행됩니다. 이 전략은 포장 된 품목이 작은 영역을 "트랩"하지 않는 패턴을 선호하므로 추가 배치에 사용하기 어려울 수 있습니다. 각 후보 포장 위치에 대해 두 항목 방향에 대해 점수가 두 번 평가되고 (둘 다 가능할 경우) 가장 높은 값이 선택됩니다. 최대 포장 영역이있는 용지함을 선택하면 점수 연계가 깨집니다. 전체 알고리즘은 다음과 같습니다.

touching_perimeter:
  sort the items by nonincreaseing w,h values, and horizontally orient them;
  comment: Phase 1;
  compute a lower bound L on the optimal solution value, and open L empty bins;
  comment: Phase 2;
  for j := 1 to n do
     score := 0;
     for each normal packing position in an open bin do
        let score1 and score2 be scores with tow orientations;
        score := max{score,score1,score2};
     end for;
     if score > 0 then
        pack item j in the bin, position and orientation corresponding to score;
     else
        open a new bin and horizontally pack item j into i;
     end if;
  end for;
end;

또한이 논문은 최적으로 패킹 된 텍스처 맵의 크기를 결정하는 알고리즘을 설명합니다. 하나의 1024x1024 아틀라스에 모든 텍스처를 넣을 수 있는지 여부를 결정하는 데 유용합니다.


이 알고리즘은 텍스처가 직사각형이라고 가정합니다. 맞습니까?
user1767754

17

누구든지 여전히 관심이 있다면 rectpack2D 라이브러리를 완전히 다시 작성하여 보다 효율적입니다.

std::vector초기 최대 크기 (일반적으로 특정 GPU에서 허용되는 최대 텍스처 크기)로 시작하여 첫 번째 가능한 빈 공간을 분할하고 분할을 벡터에 다시 저장하여 아틀라스에 빈 공간 을 유지합니다 .

성능 혁신은 이전처럼 전체 트리를 유지하는 대신 벡터를 사용하는 것만으로 이루어졌습니다.

절차는 README에 자세히 설명되어 있습니다.

도서관은 MIT에 있으므로 유용하다고 생각하면 기쁩니다!

결과 예 :

테스트는 3.50GHz에서 Intel® Core ™ i7-4770K CPU에서 수행되었습니다. 바이너리는 -03 스위치를 사용하여 clang 6.0.0으로 빌드되었습니다.

임의 게임 스프라이트 + 일본어 글리프 : 총 3264 명.

런타임 : 4 밀리 초
낭비 된 픽셀 : 15538 (0.31 %-125 x 125 정사각형에 해당)

출력 (2116 x 2382) :

삼

색상 :(
검정색 공간 낭비)

4

일본어 글리프 + 일부 GUI 스프라이트 : 3122 과목.

런타임 :
3.5-7ms 낭비 된 픽셀 : 9288 (1.23 %-96 x 96 정사각형에 해당)

출력 (866 x 871) :

5

색상 :(
검정색 공간 낭비)

6


2
코드를 다운로드했습니다. 구조체 정의를 읽는 것 :이 괴물은 무엇입니까?! 코드 골프처럼 보입니다.
akaltar

3
그래도 작동하고 도움이되었습니다. 감사합니다. 나는 무례하고 싶지 않았다.
akaltar

이 알고리즘이 내 알고리즘보다 훨씬 빠르고 빠르기 때문에 왜이 답변을 건너 뛰었는지 잘 모르겠습니다. O_O thanks
GameDeveloper

@akaltar 나는 아직도 그 시간 동안 언어를 배우고 있다고 상상할 수 있습니다 :)
Patryk Czachurski

구현이 빠르고 좋은 결과를 얻는 매우 간단한 접근 방식, 덕분에 :)
FlintZA

5

좋은 휴리스틱 알고리즘은 여기 에서 찾을 수 있습니다 . 최근에 비슷한 것을 시도했을 때, 이것이 내가 본 대부분의 구현을위한 기본 시작점이라고 언급했습니다.

규칙적인 모양의 비슷한 크기의 항목이 많거나 작고 적은 큰 이미지가 잘 혼합되어있는 경우 특히 효과적입니다. 좋은 결과를 얻기위한 최선의 조언은 이미지 크기 측면에서 입력을 정렬 한 다음 작은 이미지가 큰 이미지 주위의 공간에 채워 지므로 최대에서 최소로 압축하는 것입니다. 이 정렬 방법은 자신에게 달려 있으며 목표에 따라 달라질 수 있습니다. 키가 + 얇고 / 짧고 + 넓은 이미지 (영역이 좁을 것임)가 실제로 팩에 나중에 배치하기가 매우 어렵다는 관점을 취했기 때문에 영역 대신 경계를 1 차 근사치로 사용했습니다. 주문의 앞쪽을 향한이 이상한 모양.

내 웹 사이트 이미지 덤프 디렉토리의 임의 이미지 세트에서 내 패커의 출력 샘플 샘플은 다음과 같습니다. :). 패커 출력

사각형의 숫자는 트리에 포함 된 블록의 ID이므로 삽입 순서를 알 수 있습니다. 첫 번째는 첫 번째 리프 노드 (잎에만 이미지가 포함되어 있고 결과적으로 2 개의 부모가 있음) 이므로 ID "3" 입니다.

        Root[0]
       /      \
   Child[1]  Child[2]
      |
    Leaf[3]

3

개인적으로, 나는 욕심 많은 가장 큰 블록에 맞는 첫 번째 시스템을 사용합니다. 최적은 아니지만 트릭을 수행합니다.

합리적인 양의 텍스처 블록이있는 경우 문제 자체가 NP 인 경우에도 최상의 순서를 철저히 검색 할 수 있습니다.


3

불규칙한 UV 맵에서도 잘 작동하는 것은 UV 패치를 비트 맵 마스크로 바꾸고 텍스처 자체의 마스크를 유지하여 UV 패치가 맞는 첫 번째 위치를 찾는 것입니다. 간단한 휴리스틱 (높이, 너비, 크기 등)에 따라 블록을 주문하고 블록을 회전하여 선택한 휴리스틱을 최소화하거나 최대화 할 수 있습니다. 이는 무차별 대입을위한 관리 가능한 검색 공간을 제공합니다.

그런 다음 여러 휴리스틱 시도를 반복하고 순서를 선택할 때 임의의 요소를 적용하고 시간 제한이 끝날 때까지 반복하십시오.

이 체계를 사용하면 작은 UV 아일랜드가 큰 것에 의해 만들어지는 틈새와 심지어 단일 UV 패치 자체에 남은 구멍에도 채워집니다.


1

우리는 최근에 텍스처를 주어진 크기의 여러 이미지 파일로 압축하는 파이썬 스크립트를 발표했습니다.

블로그에서 인용 :

"온라인에서 찾을 수있는 많은 패커가 있지만 여러 디렉토리에서 많은 수의 이미지를 처리 ​​할 수있는 패커를 찾는 데 어려움이있었습니다. 따라서 자체 아틀라스 패커가 탄생했습니다!

마찬가지로 작은 스크립트는 기본 디렉토리에서 시작하여 모든 .PNG를 아틀라스에로드합니다. 해당 아틀라스가 채워지면 새로운 아틀라스가 생성됩니다. 그런 다음 새 이미지에서 지점을 찾기 전에 이전의 모든지도 책에서 나머지 이미지를 맞추려고 시도합니다. 이렇게하면 각 아틀라스가 가능한 한 꽉 채워집니다. 아틀라스는 이미지가있는 폴더를 기준으로 이름이 지정됩니다.

아틀라스의 크기 (65 행), 압축하려는 이미지의 형식 (67 행),로드 디렉토리 (10 행) 및 저장 디렉토리 (13 행)를 Python에서 전혀 경험하지 않고 상당히 쉽게 변경할 수 있습니다. 작은 면책 조항으로, 며칠 만에 우리 엔진과 함께 작동하기 위해 채찍질되었습니다. 기능을 요청하고 자신의 변형에 대해 의견을 말하고 오류를보고하는 것이 좋습니다. 그러나 스크립트에 대한 변경 사항은 자유 시간에 발생합니다. "

http://www.retroaffect.com/blog/159/Image_Atlas_Packer/#b 에서 전체 소스 코드를 확인하십시오.


1

글리프 텍스처의 모든 (또는 대부분) 크기가 거의 같기 때문에 서체를 포장하기가 쉽습니다. 당신에게 발생하는 가장 간단한 일을하고 최적에 매우 가깝습니다.

매우 다른 크기의 이미지를 포장 할 때 영리함이 더욱 중요해집니다. 그런 다음에도 공백 등으로 압축 할 수 있기를 원합니다. 그럼에도 불구하고 앞에서 설명한 스캔 라인 순서 검색과 같은 간단한 알고리즘은 매우 합리적인 결과를 생성합니다.

고급 알고 중 어느 것도 마법이 아닙니다. 그것들은 simpel algo보다 50 % 더 효율적이지 않을 것이며, 엄청나게 많은 수의 텍스쳐 시트가 없다면 그것들로부터 일관된 이점을 얻지 못할 것입니다. 더 나은 알고리즘의 작은 개선 사항은 전체적으로 볼 수 있기 때문입니다.

간단하게 노력하고 더 나은 보상을받을 수있는 곳으로 이동하십시오


0

글꼴 텍스처 전용 인 경우에는 최적화되지 않았지만 훌륭하고 간단한 작업을 수행 할 수 있습니다.

높이를 기준으로 문자를 먼저 정렬

0,0에서 시작 현재 좌표에서 첫 번째 문자 배치, X 진행, 다음 배치, 다른 문자를 맞출 수 없을 때까지 반복

X를 0으로 재설정하고 행에서 가장 높은 문자 높이만큼 Y를 아래로 진행 한 후 다른 행을 채 웁니다.

문자가 없거나 다른 행에 맞지 않을 때까지 반복하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.