Doku atlasında doku kanamasını nasıl önleyebilirim?


46

Benim oyunumda küplerden yapılmış Minecraft benzeri bir arazi var. Voksel verilerinden bir köşe tamponu üretiyorum ve farklı blokların görünümleri için bir doku atlası kullanıyorum:

voksel esaslı arazi için doku atlası

Sorun, uzak küplerin dokusunun, doku atlasındaki bitişik çinilerle karışmasıdır. Bu, küpler arasında yanlış renkte çizgiler oluşmasına neden olur (grafik kusurlarını görmek için aşağıdaki ekran görüntüsünü tam boyutunda görmeniz gerekebilir):

Arazi yanlış renkte çizgili ekran görüntüsü

Şimdilik bu enterpolasyon ayarlarını kullanıyorum ancak her kombinasyonu denedim ve GL_NEARESTmipmaplama olmadan bile daha iyi sonuçlar vermiyor.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

Ayrıca çininin biraz daha küçük bir alanını seçmek için doku koordinatlarında bir dengeleme eklemeye çalıştım, ancak istenmeyen etki kamera ile olan mesafeye bağlı olduğundan sorunu tamamen çözemez. Uzak mesafelerde şeritler yine de ortaya çıkar.

Bu doku kanamasını nasıl çözebilirim? Doku atlası kullanmak popüler bir teknik olduğundan, ortak bir yaklaşım olabilir. Ne yazık ki, yorumlarda açıklanan bazı nedenlerden dolayı, farklı dokulara veya doku dizilerine değiştiremiyorum.


4
Sorun mipmap oluşturma işleminde yatmaktadır - doku koordinatlarınız en büyük mip seviyesi için işe yarayabilir, ancak işler uzadıkça, kenarlık karoları birbirine karışır. Mipmap kullanmayarak (muhtemelen iyi bir fikir değil), koruma alanlarınızı (fayanslar arasındaki alanı) artırarak, koruma alanlarını dinamik olarak mip seviyesine dayalı bir gölgelendiriciyle büyüterek (zor) mipmaplar üretirken garip bir şeyler yapabilirsiniz. bir şey düşünemiyorum) ..
..bu

Ne yazık ki ne yazık ki mipmapping'ı kapatmak sorunu çözmüyor. Mipmaplar olmadan GL_LINEAR, benzer sonuç veren doğrusal enterpolasyon yapacaktır veya GL_NEARESTbloklar arasında şeritler halinde sonuçlanan en yakın pikseli seçecektir. Son sözü edilen seçenek azalır ancak piksel kusurunu ortadan kaldırmaz.
danijar

7
Çözümlerden biri, mipmap'ler oluşturmuş bir format kullanmaktır, o zaman sizin kendi mipmaplerinizi kendiniz oluşturabilir ve hatayı düzeltebilirsiniz. Başka bir çözüm, dokuyu örneklerken parça parçalayıcıda belirli bir mipmap seviyesini zorlamaktır. Başka bir çözüm, mipmapleri ilk mipmap ile doldurmaktır. Başka bir deyişle, dokunuzu oluştururken, aynı verileri içeren söz konusu görüntüye yalnızca alt görüntüler ekleyebilirsiniz.
Tordin,

1
Daha önce bu problemi yaşadım ve her farklı dokuya atlasınıza 1-2 piksel dolgu ekleyerek çözüldüm.
Chuck D

1
@Kylotan. Sorun mipmaplar, doğrusal enterpolasyon veya en yakın piksel toplama ile yapılmasından bağımsız olarak doku yeniden boyutlandırma ile ilgilidir.
danijar

Yanıtlar:


34

Tamam, burada iki sorunun olduğunu düşünüyorum.

İlk sorun mipmaplama ile ilgili. Genel olarak, atiplemeyi mipmap ile saf olarak karıştırmak istemezsiniz, çünkü tüm alt dokularınız tam olarak 1x1 piksel boyutunda değilse, doku kanaması yaşayacaksınız. Dolgu eklemek, sorunu sadece daha düşük bir mip seviyesine taşıyacaktır.

Genel bir kural olarak, genellikle atlasing yaptığınız 2B için mipmap yapmazsınız; mipmap yaptığınız yer olan 3D için, atlas yapmazsınız (aslında, kanamanın sorun olmayacağı bir şekilde cildiniz)

Ancak, şu anda programınıza devam ettiğini düşünüyorum, muhtemelen doğru doku koordinatlarını kullanmamanız ve bu nedenle dokularınızın kanaması.

8x8'lik bir doku varsayalım. Muhtemelen (0,0) ila (0,5, 0,5) uv koordinatlarını kullanarak sol üst çeyreği alıyorsunuz:

Yanlış haritalama

Ve sorun şu ki, 0.5 koordinatında, örnekleyici, sol tekelin yarısını ve sağ tek tek yarısını kullanarak hedef parçayı enterpolasyona sokacak. Aynı şey 0 koordinatında ve v koordinatlarında da aynı olacaktır. Bunun nedeni, merkezler yerine dokular arasındaki sınırları ele almanızdır.

Bunun yerine, yapmanız gereken her tekerleğin ortasını ele almak. Bu örnekte, kullanmak istediğiniz koordinatlar şunlardır:

Doğru haritalama

Bu durumda, her zaman her texel merkezini örnekleme olacak ve siz ... herhangi bir kanama almayalım sen mipmap kullanmak sürece bu durumda her zaman ölçekli subtexture düzgünce ulaşamadığı yerlere mip düzeyde başlayan kanama alacak piksel ızgarasının içine sığdırın .

Genellemek için, belirli bir tekere erişmek istiyorsanız, koordinatlar şu şekilde hesaplanır:

function get_texel_coords(x, y, tex_width, tex_height)
    u = (x + 0.5) / tex_width
    v = (y + 0.5) / tex_height
    return u, v
end

Buna "yarım piksel düzeltme" denir. İlgileniyorsanız , burada daha ayrıntılı bir açıklama var .


Açıklamanız çok umut verici görünüyor. Ne yazık ki şu anda ekran kartı kullanmak zorunda olduğum için henüz uygulayamıyorum. Onarılan kart gelirse bunu yapacağım. Ve o zamanlar atlasın mipmap'lerini, fayansların sınırlarını geçmeden kendi başıma hesaplayacağım.
danijar

@sharethis Kesinlikle atlas yapmak zorundaysanız, sübstitekslerinizin 2 güçte boyutta olduğundan emin olun, böylece kanamayı en düşük mip seviyelerine sınırlayabilirsiniz. Bunu yaparsanız, gerçekten kendi mipmaplerinizi oluşturmanıza gerek kalmaz.
Panda Pajama

PoT boyutlu dokular bile bile kanama artefaktlarına neden olabilir, çünkü bilinear filtreleme bu sınırlar boyunca örnekleme yapabilir. (En azından gördüğüm uygulamalarda.) Yarım piksel düzeltmesine gelince, bu durumda geçerli mi? Örneğin onun kaya dokusunu eşlemek istersem, bu kesinlikle U ve V koordinatları boyunca her zaman 0.0 ile 0.25 arasında olacak mı?
Kylotan

1
@Kylotan Doku boyutunuz eşitse ve tüm alt dokümanlar eşit boyutlara sahipse ve hatta koordinatlara yerleştirilmişse, doku boyutunu yarı yarıya keserken bilinear filtreleme alt dokular arasında karışmaz. İki güç için genelleştirin (eğer eserler görüyorsanız, muhtemelen bicubic kullanıyorsunuz, bilinear filtreleme değil). Yarım piksel düzeltmesiyle ilgili olarak, bu gereklidir, çünkü 0.25 en sağdaki tekstür değil, her iki alt-dokular arasındaki orta noktadır. Perspektife koymak için, rasterleştirici olduğunuzu hayal edin: Dokunun rengi (0,00, 0,25) nedir?
Panda Pajama

1
@sharethis, size yardımcı olabilecek, yazdığım bu cevabı kontrol edin, gamedev.stackexchange.com/questions/50023/…
Panda Pajama

19

Mipmap'lerle uğraşmaktan ve doku koordinatını eklemek için çok daha kolay bir seçenek fuzz faktörleri bir doku dizisi kullanmaktır. Doku dizileri 3d dokulara benzer, ancak 3. boyutta bozulma olmadan, bu nedenle "alt dokular" ın aynı boyutta olduğu doku atlasları için idealdir.

http://www.opengl.org/wiki/Array_Texture


Mevcutlarsa, çok daha iyi bir çözümdür - dokuları sarma gibi, atlaslarla özlediğiniz diğer özellikleri de alırsınız.
Jari Komppa

@JariKomppa. Doku dizileri kullanmak istemiyorum çünkü bu şekilde arazi için ekstra gölgelendiricilerim olacaktı. Yazdığım motor olabildiğince genel olduğundan, bu istisnayı yapmak istemiyorum. Hem doku dizilerini hem de tek dokuları kaldırabilecek bir gölgelendirici yaratmanın bir yolu var mı?
danijar

8
@sharethis “Genel” olmak istediğiniz için kesin olarak üstün teknik çözümleri reddetmek başarısızlık için bir reçetedir. Bir oyun yazarsanız, bir motor yazmayın.
sam hocevar

@SamHocevar. Bir motor yazıyorum ve projem belirli bir mimariye bağlı. Dolayısıyla, form bileşeni, yalnızca standart tamponlar ve standart bir doku sağlayacak şekilde arazi bileşenine dikkat etmemelidir.
danijar

1
@sharethis Tamam. Bir şekilde sorunun "Oyunda" kısmı tarafından yanlış yönlendirildim.
sam hocevar

6

Bu konuda çok uğraştıktan sonra nihayet bir çözüm buldum.

Her ikisini de kullanmak için, bir doku atlası ve mipmaplama yapmak için aşağı örneklemeyi kendim yapmam gerekiyor, çünkü OpenGL aksi takdirde atlastaki çini sınırları üzerinde enterpolasyon yapar. Dahası, doğru doku parametrelerini ayarlamam gerekiyordu çünkü mipmapler arasında enterpolasyon yapmak doku kanamalarına neden olacaktı.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

Bu parametrelerde kanama olmaz.

Özel mipmap'ler sağlarken dikkat etmeniz gereken bir şey var. Her kiremit zaten olsa bile, atlası küçültmek mantıklı olmadığından 1x1, maksimal mipmap seviyesinin sağladığınıza göre ayarlanması gerekir.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 7); // pick mipmap level 7 or lower

Diğer tüm cevaplar ve çok faydalı yorumlarınız için teşekkürler! Bu arada, hala doğrusal ölçeklendirmeyi nasıl kullanacağımı bilmiyorum ama sanırım imkân yok.


Çözülmüş olduğunu bilmek güzel. Bu cevabı benimki yerine doğru cevabı işaretlemelisin.
Panda Pajama

mipmap oluşturma yalnızca aşağı örnekleme için bir tekniktir ve bu da örnekleme için bir anlam ifade etmez. Buradaki fikir, eğer küçük bir üçgen çizecekseniz, sadece birkaç piksel dışına çıkarmak için büyük bir dokuyu örneklemenin çok zaman alacağı, bu nedenle önceden örnekleme yapıp uygun mip seviyesini kullanacağınız zaman küçük üçgenler çiziyorum. Daha büyük üçgenler için, daha geniş mip seviyesinden farklı bir şey kullanmak mantıklı değil, bu yüzden böyle bir şey yapmak bir hatadırglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_FILTER, GL_NEAREST_MIPMAP_LINEAR);
Panda Pajama

@PandaPajama. Haklısın, cevabımı düzelttim. Ayarlamak için GL_TEXTURE_MAX_FILTERiçin GL_NEAREST_MIPMAP_LINEARmipmap nedeniyle değil kanama nedenleri, ama enterpolasyon nedenle. Bu durumda, GL_LINEARbu en düşük mipmap seviyesinin zaten kullanılmasından dolayı eşdeğerdir .
danijar

@ danijar - Aşağı örneklemenin kendiniz nasıl yapıldığını ve aşağı örneklenen atlasın nerede (ve ne zaman) kullanılacağını OpenGL'e nasıl anlattığınızı daha ayrıntılı olarak açıklayabilir misiniz?
Tim Kane

1
@TimKane Atlası fayanslara bölün. Her mipmap seviyesi için, karoları bilinmeyen şekilde alt örnekleyin, birleştirin ve mipmap seviyesini parametre olarak belirterek GPU'ya yükleyin glTexImage2D(). GPU, ekrandaki nesnelerin boyutuna göre doğru seviyeyi otomatik olarak seçer.
danijar

4

Sorun MSAA'nın neden olabileceğine benziyor. Bkz bu cevabı ve bağlantılı makale :

"MSAA'yı açtığınızda, gölgelendiricinin piksel alanı içinde olan ancak üçgen alanı dışındaki örnekler için yürütülmesi mümkün olur"

Çözüm, centroid örneklemesini kullanmaktır. Doku koordinatlarının tepe noktası gölgelendiricisine sarılmasını hesaplıyorsanız, bu mantığı parça gölgelendiriciye taşımak da sorunu çözebilir.


Daha önce başka bir yorumda yazdığım gibi şu anda çalışan bir video kartım yok, bu yüzden asıl sorunun olup olmadığını kontrol edemiyorum. Bunu olabildiğince çabuk yapacağım.
danijar

Donanımdaki zehirlenmeyi etkisizleştirdim, ancak kanama hala devam ediyor. Parça gölgelendiriciye bakarak dokuyu zaten yapıyorum.
danijar

1

Bu eski bir konudur ve konuyla benzer bir sorunum vardı.

Doku koordinatlarım gayet iyi, ancak kamera pozisyonunu değiştirerek (elemanımın pozisyonlarını değiştirdi) şöyle:

public static void tween(Camera cam, Direction dir, Vec2 buffer, Vec2 start, Vec2 end) {
    if(step > steps) {
        tweening = false;
        return;
    }

    final float alpha = step/steps;

    switch(dir) {
    case UP:
    case DOWN:
        buffer = new Vec2(start.getX(), Util.lerp(start.getY(), (float)end.getY(), alpha));
        cam.getPosition().set(buffer);
        break;
    case LEFT:
    case RIGHT:
        buffer = new Vec2(Util.lerp(start.getX(), end.getX(), alpha), start.getY());
        cam.getPosition().set(buffer);
        break;
    }

    step += 1;
}

Tüm sorun Util.lerp () bir şamandıra ya da iki katı döndürürdü. Bu, fotoğraf makinesinin şebekeden çevirdiği ve doku parçacıklarıyla ilgili bazı garip sorunlara neden olduğu için, X'de> 0 ve Y'de> 0 olan yerlerde garip sorunlara neden oldu.

TLDR: değişkenleri kullanarak ara doldurma veya herhangi bir şey yapmayın

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.