Problem z użyciem atlasów tekstur i przeciekania sąsiadujących tekstur ma związek ze sposobem liniowego filtrowania tekstur.
W przypadku dowolnego punktu tekstury, który nie jest próbkowany dokładnie w środku teksla, próbkowanie liniowe spróbuje pobrać 4 sąsiadujące tekseli i obliczyć wartość w zadanym miejscu jako średnią ważoną (na podstawie odległości od punktu próbkowania) średnią wszystkich 4 próbki.
Oto ładna wizualizacja problemu:
Ponieważ nie możesz użyć czegoś takiego GL_CLAMP_TO_EDGE
w atlasie tekstur, należy utworzyć tekstury graniczne wokół krawędzi każdej tekstury. Te tekstury graniczne zapobiegną zmianie sąsiednich próbek z zupełnie różnych tekstur w atlasie poprzez ważoną interpolację wyjaśnioną powyżej.
Pamiętaj, że podczas korzystania z filtrowania anizotropowego może być konieczne zwiększenie szerokości ramki. Wynika to z faktu, że filtrowanie anizotropowe zwiększy wielkość sąsiedztwa próbki pod ekstremalnymi kątami.
Aby zilustrować, co mam na myśli, używając obramowania wokół krawędzi każdej tekstury, rozważ różne tryby zawijania dostępne w OpenGL. Zwróć szczególną uwagę na CLAMP TO EDGE
.
Mimo że istnieje tryb o nazwie „Clamp to Border”, tak naprawdę nie jesteśmy tym zainteresowani. Ten tryb pozwala zdefiniować pojedynczy kolor, który będzie używany jako ramka wokół tekstury dla dowolnych współrzędnych tekstury, które nie mieszczą się w znormalizowanym [0.0 -1.0] zakres.
Chcemy odtworzyć zachowanie CLAMP_TO_EDGE
, w którym dowolna współrzędna tekstury poza odpowiednim zakresem dla (pod-) tekstury otrzymuje wartość ostatniego środka texel w kierunku, w którym była poza granicami. Ponieważ masz prawie całkowitą kontrolę nad współrzędne tekstury w systemie atlasu, jedynym scenariuszem, w którym (skuteczne) współrzędne tekstury mogą odnosić się do lokalizacji poza teksturą, jest średni ważony etap filtrowania tekstury.
Wiemy to GL_LINEAR
będą próbkować 4 najbliższych sąsiadów, jak pokazano na powyższym schemacie, więc potrzebujemy tylko granicy 1-texel. Jeśli korzystasz z filtrowania anizotropowego, możesz potrzebować szerszej ramki tekstowej, ponieważ zwiększa ona wielkość sąsiedztwa próbki w określonych warunkach.
Oto przykład tekstury, która lepiej ilustruje ramkę, chociaż dla twoich celów możesz zwiększyć szerokość 1 teksela lub 2 tekseli.
(UWAGA: obramowanie, o którym mówię, nie jest czarne wokół wszystkich czterech krawędzi obrazu, ale obszar, w którym wzór szachownicy przestaje się regularnie powtarzać)
Jeśli się zastanawiasz, oto dlaczego ciągle wspominam o filtrowaniu anizotropowym. Zmienia kształt sąsiedztwa próbki na podstawie kąta i może powodować użycie więcej niż 4 tekstur do filtrowania:
http://www.arcsynthesis.org/gltut/Texturing/ParallelogramDiag.svg
Im większy stopień anizotropii użyjesz, tym większe prawdopodobieństwo, że będziesz mieć do czynienia z okolicami próbnymi zawierającymi więcej niż 4 teksle. Obramowanie 2 tekstów powinno być odpowiednie w większości sytuacji filtrowania anizotropowego.
Last but not least, oto jak zbudowany jest spakowany atlas tekstur, który replikuje GL_CLAMP_TO_EDGE
zachowanie w obecności GL_LINEAR
filtra tekstury:
( Odejmij 1 od X i Y we czarnych współrzędnych, nie poprawiłem odczytu obrazu przed wysłaniem. )
Ze względu na przechowywanie granic, przechowywanie 4 256 x 256 tekstur w tym atlasie wymaga tekstury o wymiarach 516 x 516. Ramki są oznaczone kolorami w oparciu o sposób wypełnienia ich danymi tekstowymi podczas tworzenia atlasu:
- Czerwony = Zamień na tekst bezpośrednio poniżej
- Żółty = Zamień na texel bezpośrednio powyżej
- Zielony = Zastąp tekstem bezpośrednio po lewej stronie
- Niebieski = Zastąp tekstem bezpośrednio po prawej stronie
Skutecznie w tym upakowanym przykładzie każda tekstura w atlasie wykorzystuje region atlasu o wymiarach 258 x 258, ale wygenerowane zostaną współrzędne tekstury odwzorowane na widoczny region 256 x 256. Tekstury graniczne są zawsze używane tylko wtedy, gdy filtrowanie tekstur odbywa się na krawędziach tekstur w atlasie, a sposób, w jaki zostały zaprojektowane, naśladuje GL_CLAMP_TO_EDGE
zachowanie.
Jeśli zastanawiasz się, możesz zaimplementować inne typy trybów zawijania, stosując podobne podejście - GL_REPEAT
można je zrealizować, wymieniając tekstury lewej / prawej i górnej / dolnej granicy w atlasie tekstur oraz odrobinę sprytnej matematyki współrzędnych tekstury w moduł cieniujący. To jest trochę bardziej skomplikowane, więc na razie się nie martw. Ponieważ masz do czynienia tylko z arkuszami sprite, ogranicz się do GL_CLAMP_TO_EDGE
:)
GL_NEAREST
lubGL_LINEAR
do renderowania tekstury?