Sorunsuz tilemap oluşturma (kenarlıksız bitişik görüntüler)


18

Bir fayans seti görüntüsünden fayans çizerek tilemaps çizen bir 2D oyun motorum var. Varsayılan olarak OpenGL GL_REPEATsadece bir kısmını değil tüm dokuyu ( ) sarayabileceğinden, her bir karo ayrı bir dokuya bölünür. Daha sonra aynı döşemenin bölgeleri birbirine bitişik hale getirilir. Amaçlandığı gibi çalışırken şöyle görünür:

Dikişsiz Tilemap

Bununla birlikte, kesirli ölçeklemeyi uygulamaya koyar koymaz dikişler görünür:

Dikişli Tilemap

Bu neden oluyor? Bunun, dörtlülerin sınırlarını karıştıran doğrusal filtrelemeden kaynaklandığını düşündüm, ancak yine de nokta filtrelemeyle oluyor. Şimdiye kadar bulduğum tek çözüm, tüm konumlandırma ve ölçeklendirmenin yalnızca tamsayı değerlerde gerçekleşmesini sağlamak ve nokta filtrelemeyi kullanmaktır. Bu, oyunun görsel kalitesini düşürebilir (özellikle alt piksel konumlandırması artık çalışmaz, bu nedenle hareket o kadar düzgün değildir).

Denediğim / düşündüğüm şeyler:

  • kenar yumuşatma dikişleri azaltır, ancak tamamen ortadan kaldırmaz
  • mipmaplemeyi kapatmanın hiçbir etkisi yoktur
  • her döşemeyi ayrı ayrı işleyin ve kenarları 1 piksel ekstrüde edin - ancak bu, bir seferde fayans bölgelerini oluşturamayacağından ve saydamlık alanlarının kenarları boyunca başka eserler oluşturduğundan bir de-optimizasyon.
  • kaynak görüntülerin etrafına 1 piksellik bir kenarlık ekleyin ve son pikselleri tekrarlayın - ancak artık ikisinin gücü değil, NPOT desteği olmayan sistemlerde uyumluluk sorunlarına neden oluyor
  • döşenmiş görüntüleri işlemek için özel bir gölgelendirici yazmak - ama sonra farklı ne yapardınız? GL_REPEATpikseli görüntünün karşı tarafından kenarlardan tutmalı ve saydamlık almamalıdır.
  • geometri tam olarak bitişiktir, kayan nokta yuvarlama hatası yoktur.
  • fragman gölgelendiricisi aynı rengi döndürmek için sabit kodlanmışsa , dikişler kaybolur .
  • dokular olarak belirlendiyse GL_CLAMPyerine GL_REPEAT, dikişleri kaybolur (render yanlış olsa da).
  • dokular olarak belirlendiyse GL_MIRRORED_REPEAT, dikişler kaybolur (render yine yanlış olmasına rağmen).
  • arka planı kırmızı yaparsam, dikişler hala beyazdır. Bu, şeffaflıktan ziyade opak beyazdan bir örneklem aldığını gösterir.

Böylece dikişler sadece GL_REPEATayarlandığında görünür . Herhangi bir nedenle sadece bu modda, geometrinin kenarlarında bir miktar kanama / sızıntı / şeffaflık vardır. Nasıl olabilir? Tüm doku opaktır.


Bununla ilgili olabileceğinden şüphelendiğim için doku örnekleyicinizi kelepçelemeyi veya kenarlık rengini ayarlamayı deneyebilirsiniz. Ayrıca, GL_Repeat sadece UV koordinatlarını sarar, bu yüzden sadece bir kısmını değil, tüm dokuyu tekrarlayabileceğini söylediğinizde kişisel olarak biraz kafam karıştı.
Evan

1
Bir şekilde ağınıza çatlaklar sokmanız mümkün mü? Gölgelendiriciyi bir doku örneklemek yerine yalnızca düz bir renk döndürecek şekilde ayarlayarak test edebilirsiniz. O noktada hala çatlaklar görüyorsanız, bunlar geometride. Kenar yumuşatmanın dikişleri azaltması, bunun sorun olabileceğini düşündürmektedir, çünkü kenar yumuşatma doku örneklemesini etkilememelidir.
Nathan Reed

Bir doku atlasında tek tek fayansların tekrarını uygulayabilirsiniz. Ekstra doku koordinat matematiği yapmanız ve her döşemenin etrafına kenarlık metinleri eklemeniz gerekir. Bu makale , bunun çoğunu açıklamaktadır (çoğunlukla D3D9'un sinir bozucu doku koordinat sözleşmesine odaklanarak). Alternatif olarak, uygulamanız yeterince yeniyse, döşeme haritanız için dizi dokularını kullanabilirsiniz; her döşemenin aynı boyutlara sahip olduğunu varsayarsak, bu harika çalışır ve ekstra koordinat matematiği gerektirmez.
Andon M. Coleman

Koordinat yönünde GL_NEARESTörnekleme ile 3B dokular Rda bu senaryoda çoğu şey için dizi dokularının yanı sıra çalışır . Mipmapleme işe yaramayacaktır, ancak uygulamanıza bakarak muhtemelen mipmap'lara ihtiyacınız yoktur.
Andon M. Coleman

1
CLAMP olarak ayarlandığında oluşturma ne şekilde "yanlış" olur?
Martijn Courteaux

Yanıtlar:


1

Dikişler GL_REPEAT ile örnekleme için doğru davranıştır. Aşağıdaki kutucuğu düşünün:

sınır kiremit

Döşemenin kenarlarına kesirli değerler kullanarak örnekleme, kenardan ve karşı kenardan renkleri karıştırır, bu da yanlış renklerle sonuçlanır: Sol kenar yeşil olmalı, ancak yeşil ve bejden oluşan bir karışımdır. Sağ kenar bej olmalı, ancak karışık bir renktir. Özellikle yeşil arka plandaki bej çizgiler çok görünür, ancak yakından bakarsanız kenardaki yeşil kanamayı görebilirsiniz:

ölçekli kiremit

kenar yumuşatma dikişleri azaltır, ancak tamamen ortadan kaldırmaz

MSAA, çokgen kenarları etrafında daha fazla örnek alarak çalışır. Kenarın soluna veya sağına alınan örnekler "yeşil" olacaktır (yine ilk resmin sol kenarı göz önünde bulundurularak) ve "bej" alandan kısmi olarak örneklenen kenardan yalnızca bir örnek "" "olacaktır. Numuneler karıştırıldığında etki azalacaktır.

Muhtemel çözümler:

Her durumdaGL_CLAMP , dokunun karşı tarafındaki pikselden kanamayı önlemek için geçmelisiniz . Sonra üç seçeneğiniz var:

  1. Döşemeler arasındaki geçişi biraz yumuşatmak için kenar yumuşatmayı etkinleştirin.

  2. Doku filtrelemeyi olarak ayarlayın GL_NEAREST. Bu, tüm piksellerin sert kenarlarını verir, bu nedenle çokgen / sprite kenarları ayırt edilemez hale gelir, ancak oyunun stilini biraz değiştirir.

  3. Daha önce tartışılan 1 piksel kenarlığını ekleyin, kenarlığın bitişik döşemenin rengine sahip olduğundan (karşı kenarın renginden değil) emin olun.

    • Bu aynı zamanda bir doku atlasına geçmek için iyi bir zaman olabilir (sadece NPOT desteği hakkında endişeleniyorsanız daha büyük hale getirin).
    • Bu, GL_LINEARçokgenlerin / spriteların kenarlarında benzer filtreleme sağlayan tek "mükemmel" çözümdür .

Ooh, teşekkürler - dokunun etrafına sarmak onu açıklıyor. Bu çözümlere bakacağım!
AshleysBrain

6

Varsayılan olarak OpenGL, sadece bir parçasını değil tüm dokusunu (GL_REPEAT) sarabileceğinden, her bir karo ayrı bir dokuya bölünür. Daha sonra aynı döşemenin bölgeleri birbirine bitişik hale getirilir.

OpenGL'de tek, sıradan dokulu bir dörtlü göstermeyi düşünün. Herhangi bir ölçekte dikiş var mı? Hayır asla. Amacınız, tüm sahne karolarınızı tek bir dokuya sıkıca paketlemek ve ardından bunu ekrana göndermek. DÜZENLEME Bunu daha da açıklığa kavuşturmak için: Her bir karo dörtlü üzerinde ayrı sınırlayıcı köşeler varsa , çoğu durumda haksız görünen dikişlere sahip olacaksınız . GPU donanımı bu şekilde çalışır ... kayan nokta hataları mevcut bakış açısına göre bu boşlukları oluşturur ... birleşik bir manifoldda kaçınılan hatalar, çünkü iki yüz aynı manifoldda bitişikse(submesh), donanım onları dikişsiz hale getirecektir, garanti. Bunu sayısız oyun ve uygulamada gördünüz. Bunu bir kez ve herkes için önlemek için iki kat köşeleri olmayan tek bir alt şasi üzerinde sıkıca paketlenmiş bir dokuya sahip olmalısınız. Bu sitede ve başka yerlerde defalarca ortaya çıkan bir sorun: fayanslarınızın köşe köşelerini birleştirmezseniz (her 4 fayans bir köşe tepe noktasını paylaşır), dikişleri bekleyin.

(Haritanın dört köşesi dışında köşelere bile ihtiyacınız olmayabileceğini düşünün ... gölgeleme yaklaşımınıza bağlıdır.)

Çözmek için: tüm karo dokularınızı boşluk bırakmadan bir FBO / RBO'ya (1: 1) yerleştirin , ardından bu FBO'yu varsayılan çerçeve tamponuna (ekran) gönderin. FBO'nun kendisi temelde tek bir doku olduğundan, ölçekleme konusunda boşluklarla sonuçlanamazsınız. Bir ekran piksel sınırına düşmeyen tüm texel sınırları, GL_LINEAR .... kullanıyorsanız tam olarak istediğiniz gibi harmanlanacaktır. Standart yaklaşım budur.

Bu aynı zamanda ölçeklendirme için birkaç farklı yol açar:

  • FBO'yu oluşturacağınız dörtlüün boyutunu ölçeklendirme
  • bu dörtlü UV'leri değiştirmek
  • kamera ayarlarıyla uğraşmak

Bunun bizim için iyi olacağını sanmıyorum. % 10 yakınlaştırmayla bir alanı 10 kat daha büyük hale getirmemizi önerdiğiniz anlaşılıyor. Bu, doluluk oranı sınırlı cihazlar için iyi sonuç vermez. Ayrıca, özellikle GL_REPEAT'in neden sadece dikişlere neden olduğunu ve başka bir modun neden yapmadığını hala gizem duyuyorum. Bu nasıl olabildi?
AshleysBrain

@AshleysBrain Non sequitur. Ben ediyorum şaşırtan Ekstrenizde tarafından. Lütfen yakınlaştırmayı% 10 artırmanın doldurma oranını 10 kat artırdığını açıklayın. Ya da yakınlaştırma oranını 10 kat arttırmak - görüntü kırpması tek bir geçişte% 100'den fazla doluluk oranını önleyeceğinden? Nihai amacınızı, daha fazla, daha az değil - muhtemelen daha verimli ve kesintisiz bir şekilde, nihai çıktılarınızı ortak bir teknik olan RTT kullanarak birleştirerek tam olarak göstermeyi öneriyorum . Donanım, yalnızca 2D bir motor olduğu için görünüm kırpma, ölçekleme, karıştırma ve enterpolasyonu neredeyse ücretsiz olarak halledecektir.
Mühendis

@AshleysBrain Mevcut yaklaşımınızla ilgili sorunları daha net hale getirmek için sorumu düzenledim.
Mühendis

@ArcaneEngineer Merhaba, dikişler sorununu mükemmel bir şekilde çözen yaklaşımınızı denedim. Ancak, şimdi, dikişleri yerine piksel hataları var. Burada bahsettiğim sorun hakkında bir fikriniz olabilir . Bunun sebebinin ne olabileceği hakkında bir fikrin var mı? WebGL'de her şeyi yaptığımı unutmayın.
Nemikolh

1
@ArcaneEngineer Yeni bir soru soracağım, burada açıklamak çok hantal. Rahatsızlık için özür dilerim.
Nemikolh

1

Görüntülerin bir yüzey olarak görünmesi gerekiyorsa, bunları bir 3B kafes / düzlemde bir yüzey dokusu (ölçek 1x) olarak oluşturun. Her birini ayrı bir 3B nesne olarak oluşturursanız, yuvarlama hataları nedeniyle her zaman dikişleri olacaktır.

@ Nick-Wiggill'in cevabı doğru, sanırım yanlış anladınız.


0

Denemeye değer 2 olasılık:

  1. Doku filtrelemek GL_NEARESTyerine kullanın GL_LINEAR. (Andon M. Coleman tarafından belirtildiği gibi)

    GL_LINEAR(aslında) görüntüyü o kadar hafif bulanıklaştıracaktır (en yakın pikseller arasında enterpolasyon), bu da bir pikselden diğerine renkli yumuşak bir geçiş sağlar. Bu, dokularınızın birçok durumda daha iyi görünmesine yardımcı olabilir, çünkü dokularınızın bu bloklu piksellerin her yerinde olmasını önler. Ayrıca, bir döşemenin bir pikselinden biraz kahverengi, bitişik döşemenin bitişik yeşil pikseline bulaştırılabilir.

    GL_NEARESTen yakın pikseli bulur ve bu rengi kullanır. İnterpolasyon yok. Sonuç olarak, aslında biraz daha hızlı.

  2. Her döşemenin doku koordinatlarını biraz küçültün.

    Görüntüdeki döşemeyi genişletmek yerine, döşemeyi yazılımda küçültmek dışında, her bir döşemenin etrafına bu ekstra pikseli bir tampon olarak eklemek gibi. 16x16px kutucuk yerine 14x14px kutucuk oluştururken.

    Çok fazla kahverengi karıştırılmadan 14.5x14.5 karolarla bile kurtulabilirsiniz.

--Düzenle--

Seçenek # 2'deki açıklamanın görselleştirilmesi

Yukarıdaki resimde gösterildiği gibi, yine de iki dokudan oluşan gücü kullanabilirsiniz. Böylece NPOT dokularını desteklemeyen donanımı destekleyebilirsiniz. Değişen yerine gelen gitme, senin doku koordinatları olduğu (0.0f, 0.0f)için (1.0f, 1.0f)sen den gider (0.03125f, 0.03125f)için (0.96875f, 0.96875f).

Bu, tex-coord'larınızı oyunun etkin çözünürlüğünü azaltan karonun içine hafifçe koyar (donanımda olmasa da, iki dokuya sahip olmanız gerekir), ancak dokuyu genişletmekle aynı oluşturma efektine sahip olmalıdır.


Zaten GL_NEAREST (nokta filtreleme olarak da bilinir) denedim ve düzeltmedi bahsetti. NPOT cihazlarını desteklemem gerektiğinden # 2 yapamıyorum.
AshleysBrain

Bazı şeyleri temizleyebilecek bir düzenleme yaptı. ("NPOT [(ikisinin gücü olmayan)] cihazları destekleme ihtiyacı" nın, dokuları 2'nin gücünde tutmanız gereken çizgiler boyunca bir şey anlamına geldiğini varsayıyorum)
Wolfgang Skyler

0

İşte bunun olabileceğini düşündüğüm şey: İki kiremit çarpıştığında, kenarlarının x bileşeni eşit olmalıdır. Bu doğru değilse, ters yöne yuvarlanabilirler. Bu yüzden tam olarak aynı x değerine sahip olduklarından emin olun. Bunun olmasını nasıl sağlıyorsunuz? Diyelim ki, 10 ile çarpın, yuvarlak ve sonra 10'a bölün (bunu köşeleriniz tepe gölgelendiriciye girmeden önce yalnızca CPU'da yapın). Bu doğru sonuçlar vermelidir. Ayrıca, dönüşüm matrisleri kullanarak kiremitle kiremit çizmeyin, ancak matriks çarpımlarıyla birlikte kayıplı IEEE kayan nokta sisteminden yanlış sonuçların gelmediğinden emin olmak için bunları toplu bir VBO'ya koyun.

Bunu neden öneriyorum? Çünkü tecrübelerime göre, köşe gölgelendiriciden çıkarken köşelerin tam olarak aynı koordinatları varsa, karşılık gelen üçgenleri doldurmak kesintisiz sonuçlar yaratacaktır. Matematiksel olarak doğru olan bir şeyin IEEE nedeniyle biraz kapalı olabileceğini unutmayın. Sayılarınız üzerinde ne kadar çok hesap yaparsanız, sonuç o kadar az doğru olur. Ve evet, matris çarpımları, VBO'nuzu oluştururken yalnızca bir çarpma ve ek olarak yapılabilecek oldukça bazı işlemler gerektirir ve bu da daha doğru sonuçlar verir.

Sorun da olabilecek bir spritesheet (atlas olarak da bilinir) kullanmanız ve dokuyu örneklerken, bitişik karo dokusunun piksellerinin seçilmesidir. Bunun olmamasını sağlamak için UV eşlemelerinde küçük bir sınır oluşturmaktır. Yani, 64x64 döşemeniz varsa, UV eşlemeniz biraz daha az kapsamalıdır. Ne kadar? Oyunlarda dikdörtgenin her iki yanında 1 çeyrek piksel kullandığımı düşünüyorum. Bu yüzden UV'lerinizi x bileşenleri için 1 / (4 * widthOfTheAtlas) ve y bileşenleri için 1 / (4 * heightOfTheAtlas) ile kaydırın.


Teşekkürler, ancak daha önce de belirttiğim gibi, geometri kesinlikle tam olarak bitişiktir - aslında tüm koordinatlar tam tamsayılarla sonuçlanır, bu yüzden söylemek kolaydır. Burada hiçbir atlas kullanılmaz.
AshleysBrain

0

Bunu 3B alanda çözmek için Wolfgang Skyler'ın önerisiyle doku dizilerini karıştırdım. Her bir döşemeye (toplam 120x120, 128x128) gerçek dokumun etrafına "sarılmış" görüntü ile doldurabileceğim veya genişletilen taraf için 8 piksellik bir kenarlık koydum. Örnekleyici görüntüyü enterpolasyon yaparken bu alanı okur.

Şimdi filtreleme ve mipmap oluşturma ile örnekleyici 8 piksellik kenarlığın tamamını kolayca okuyabilir. Bu küçük problemi yakalamak için (küçük diyorum çünkü geometri gerçekten çarpık veya uzaktayken sadece birkaç pikselde gerçekleşiyor), fayansları bir doku dizisine böldüm, böylece her karonun kendi doku alanı var ve kenarları.

Sizin durumunuzda (2D / düz), kesinlikle mükemmel bir piksel sahnesi oluşturmaya ve ardından sonucun istenen kısmını Nick Wiggil'in önerdiği gibi görüntü alanına ölçeklemeye devam ediyorum.

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.