Rendering tilemap mulus (gambar berdekatan tanpa batas)

18

Saya memiliki mesin gim 2D yang menggambar tilemaps dengan menggambar ubin dari gambar tileset. Karena secara default OpenGL hanya dapat membungkus seluruh tekstur ( GL_REPEAT), dan bukan hanya sebagian saja, setiap ubin dibagi menjadi tekstur yang terpisah. Kemudian daerah dari ubin yang sama diberikan berdekatan satu sama lain. Begini tampilannya saat berfungsi sebagaimana dimaksud:

Tilemap tanpa jahitan

Namun begitu Anda memperkenalkan penskalaan pecahan, lapisan muncul:

Tilemap dengan jahitan

Mengapa ini terjadi? Saya pikir itu karena penyaringan linier memadukan batas paha depan, tetapi itu masih terjadi dengan penyaringan titik. Satu-satunya solusi yang saya temukan sejauh ini adalah untuk memastikan semua penentuan posisi dan penskalaan hanya terjadi pada nilai integer, dan menggunakan penyaringan titik. Ini dapat menurunkan kualitas visual game (khususnya bahwa penempatan sub-pixel tidak lagi berfungsi sehingga gerakannya tidak begitu mulus).

Hal-hal yang saya coba / pertimbangkan:

  • antialiasing mengurangi, tetapi tidak sepenuhnya menghilangkan, lapisannya
  • mematikan mipmapping, tidak berpengaruh
  • merender setiap ubin secara individual dan mengekstrusi tepinya dengan 1px - tetapi ini adalah de-optimisasi, karena tidak dapat lagi merender wilayah ubin dalam sekali jalan, dan membuat artefak lain di sepanjang tepi bidang transparansi
  • tambahkan perbatasan 1px di sekitar gambar sumber dan ulangi piksel terakhir - tetapi kemudian tidak lagi menjadi dua, yang menyebabkan masalah kompatibilitas dengan sistem tanpa dukungan NPOT
  • menulis shader khusus untuk menangani gambar ubin - tetapi lalu apa yang akan Anda lakukan secara berbeda? GL_REPEATharus mengambil piksel dari sisi berlawanan dari gambar di perbatasan, dan tidak memilih transparansi.
  • geometri persis berbatasan, tidak ada kesalahan pembulatan titik mengambang.
  • jika shader fragmen dikode keras untuk mengembalikan warna yang sama, jahitannya hilang .
  • jika teksturnya diatur GL_CLAMPsebagai ganti GL_REPEAT, pelipatannya hilang (meskipun renderingnya salah).
  • jika tekstur diatur ke GL_MIRRORED_REPEAT, jahitannya hilang (walaupun renderingnya salah lagi).
  • jika saya membuat latar belakang merah, jahitannya masih putih. Ini menunjukkan bahwa sampel itu berwarna putih buram dari suatu tempat daripada transparansi.

Jadi jahitan hanya muncul ketika GL_REPEATdiatur. Untuk beberapa alasan dalam mode ini saja, di tepi geometri ada beberapa perdarahan / kebocoran / transparansi. Bagaimana itu bisa terjadi? Seluruh teksturnya buram.

AshleysBrain
sumber
Anda dapat mencoba mengatur sampler tekstur Anda untuk dijepit, atau menyesuaikan warna tepi, karena saya kira itu mungkin terkait dengan itu. Juga, GL_Repeat hanya membungkus koordinat UV, jadi saya pribadi agak bingung ketika Anda mengatakan itu hanya dapat mengulangi seluruh tekstur, daripada sebagian.
Evan
1
Mungkinkah Anda memperkenalkan retakan di jala Anda? Anda dapat mengujinya dengan mengatur shader untuk hanya mengembalikan warna solid daripada mengambil sampel tekstur. Jika Anda masih melihat celah pada titik itu, mereka berada dalam geometri. Fakta bahwa antialiasing mengurangi lapisan menunjukkan bahwa ini mungkin menjadi masalah, karena antialiasing seharusnya tidak mempengaruhi sampling tekstur.
Nathan Reed
Anda dapat menerapkan pengulangan ubin individu dalam atlas tekstur. Anda perlu melakukan beberapa koordinat matematika tekstur tambahan dan memasukkan batas perbatasan di sekitar setiap ubin sekalipun. Artikel ini menjelaskan banyak hal ini (meskipun sebagian besar dengan fokus pada konvensi koordinat tekstur D3D9 yang menjengkelkan). Atau, jika implementasi Anda cukup baru Anda dapat menggunakan tekstur array untuk peta ubin Anda; dengan asumsi setiap ubin memiliki dimensi yang sama ini akan bekerja dengan baik dan tidak akan memerlukan matematika koordinat tambahan.
Andon M. Coleman
Tekstur 3D dengan GL_NEARESTpengambilan sampel dalam Rarah koordinat juga berfungsi sama baiknya dengan tekstur array untuk sebagian besar hal dalam skenario ini. Pemetaan pemetaan tidak akan berfungsi, tetapi menilai dengan aplikasi Anda, Anda mungkin tidak perlu memetakan peta mini.
Andon M. Coleman
1
Dalam hal apa rendering "salah", bila disetel ke CLAMP?
Martijn Courteaux

Jawaban:

1

Jahitan adalah perilaku yang benar untuk pengambilan sampel dengan GL_REPEAT. Pertimbangkan ubin berikut:

ubin pembatas

Pengambilan sampel pada tepi ubin menggunakan nilai fraksional mencampur warna dari tepi dan tepi yang berlawanan, menghasilkan warna yang salah: Tepi kiri harus hijau, tetapi merupakan campuran dari hijau dan krem. Tepi kanan harus krem, tetapi juga merupakan warna campuran. Terutama garis-garis krem ​​pada latar belakang hijau sangat terlihat, tetapi jika Anda melihat lebih dekat Anda dapat melihat perdarahan hijau ke tepi:

ubin berskala

antialiasing mengurangi, tetapi tidak sepenuhnya menghilangkan, lapisannya

MSAA bekerja dengan mengambil lebih banyak sampel di sekitar tepi poligon. Sampel yang diambil kiri atau kanan tepi akan "hijau" (sekali lagi, mengingat tepi kiri gambar pertama), dan hanya satu sampel akan "di" tepi, pengambilan sampel sebagian dari area "beige". Ketika sampel dicampur efeknya akan berkurang.

Solusi yang memungkinkan:

Bagaimanapun Anda harus beralih ke GL_CLAMPuntuk mencegah pendarahan dari piksel di sisi berlawanan dari tekstur. Maka Anda memiliki tiga opsi:

  1. Aktifkan anti-aliasing untuk memperlancar transisi antar ubin sedikit.

  2. Atur penyaringan tekstur ke GL_NEAREST. Ini memberikan semua piksel tepi yang keras, sehingga tepi poligon / sprite menjadi tidak bisa dibedakan, tetapi itu jelas mengubah gaya permainan sedikit.

  3. Tambahkan batas 1px yang sudah dibahas, pastikan perbatasan memiliki warna ubin yang berdekatan (dan bukan warna tepi yang berlawanan).

    • Ini mungkin juga saat yang tepat untuk beralih ke atlas tekstur (hanya membuatnya lebih besar jika Anda khawatir tentang dukungan NPOT).
    • Ini adalah satu-satunya solusi "sempurna", memungkinkan GL_LINEARpenyaringan-seperti di tepi poligon / sprite.
Jens Nolte
sumber
Oh, terima kasih - membungkus tekstur tidak menjelaskannya. Saya akan melihat ke solusi itu!
AshleysBrain
6

Karena secara default OpenGL hanya dapat membungkus seluruh tekstur (GL_REPEAT), dan bukan hanya sebagian, setiap ubin dibagi menjadi tekstur yang terpisah. Kemudian daerah dari ubin yang sama diberikan berdekatan satu sama lain.

Pertimbangkan tampilan quad, textured quad biasa di OpenGL. Apakah ada jahitan, pada skala berapa pun? Tidak, tidak pernah. Tujuan Anda adalah untuk mendapatkan semua ubin adegan Anda dikemas dengan erat ke dalam satu tekstur, lalu mengirimkannya ke layar. EDIT Untuk memperjelas ini lebih lanjut: Jika Anda memiliki simpul pembatas diskrit pada setiap quad ubin, Anda akan memiliki jahitan yang tampaknya tidak dapat dibenarkan dalam sebagian besar keadaan. Begitulah cara kerja perangkat keras GPU ... kesalahan floating point membuat kesenjangan ini berdasarkan perspektif saat ini ... kesalahan yang dihindari pada manifold terpadu, karena jika dua wajah berdekatan dalam manifold yang sama(submesh), perangkat keras akan membuatnya tanpa jahitan, dijamin. Anda telah melihat ini di banyak game dan aplikasi. Anda harus memiliki satu tekstur yang sangat padat pada satu submesh tanpa simpul ganda untuk menghindari ini sekali dan untuk semua. Ini adalah masalah yang muncul berulang kali di situs ini dan di tempat lain: jika Anda tidak menggabungkan simpul sudut ubin Anda (setiap 4 ubin berbagi sudut sudut), harapkan jahitan.

(Pertimbangkan bahwa Anda bahkan mungkin tidak perlu simpul kecuali pada keempat sudut seluruh peta ... tergantung pada pendekatan Anda terhadap naungan.)

Untuk mengatasi: render (pada 1: 1) semua tekstur ubin Anda menjadi FBO / RBO tanpa celah, lalu kirim FBO itu ke framebuffer default (layar). Karena FBO itu sendiri pada dasarnya adalah satu tekstur, Anda tidak bisa berakhir dengan celah pada penskalaan. Semua batas texel yang tidak jatuh pada batas piksel layar akan tercampur jika Anda menggunakan GL_LINEAR .... yang persis seperti yang Anda inginkan. Ini adalah pendekatan standar.

Ini juga membuka sejumlah rute berbeda untuk penskalaan:

  • skala ukuran quad yang akan Anda render FBO
  • mengubah UV pada quad itu
  • mengutak-atik pengaturan kamera
Insinyur
sumber
Saya tidak berpikir ini akan bekerja dengan baik untuk kita. Sepertinya Anda mengusulkan bahwa pada zoom 10% kami membuat area 10 kali lebih besar. Ini bukan pertanda baik untuk perangkat dengan tingkat pengisian terbatas. Saya juga masih bingung mengapa secara khusus GL_REPEAT hanya menyebabkan jahitan, dan tidak ada mode lain yang melakukannya. Bagaimana bisa?
AshleysBrain
@AshleysBrain Non sequitur. Saya bingung dengan pernyataan Anda. Tolong jelaskan bagaimana meningkatkan zoom sebesar 10% meningkatkan tingkat pengisian sebesar 10x? Atau seberapa besar peningkatan pembesaran 10x - karena klip viewport akan mencegah laju pengisian lebih dari 100% pada satu pass? Saya menyarankan Anda menampilkan dengan tepat apa yang sudah Anda rencanakan - tidak lebih, tidak kurang - dengan cara yang tampaknya lebih efisien DAN mulus, dengan menyatukan hasil akhir Anda menggunakan RTT, teknik umum. Hardware akan menangani kliping viewport, scaling, blending, dan interpolasi tanpa biaya, mengingat ini hanyalah mesin 2D.
Insinyur
@AshleysBrain Saya telah mengedit pertanyaan saya untuk membuat masalah dengan pendekatan Anda saat ini jernih.
Insinyur
@ ArcaneEngineer Hai, Saya telah mencoba pendekatan Anda yang menyelesaikan masalah jahitan dengan sempurna. Namun, sekarang, alih-alih memiliki jahitan saya memiliki kesalahan piksel. Anda dapat memiliki gagasan tentang masalah yang saya maksudkan di sini . Apakah Anda tahu apa penyebabnya? Perhatikan bahwa saya melakukan segalanya di WebGL.
Nemikolh
1
@ArcaneEngineer Saya akan mengajukan pertanyaan baru, menjelaskan di sini terlalu rumit. Saya minta maaf atas gangguan yang terjadi.
Nemikolh
1

Jika gambar harus muncul sebagai satu permukaan, render ini sebagai satu tekstur permukaan (skala 1x) pada satu bidang / bidang 3D. Jika Anda membuat masing-masing sebagai objek 3D yang terpisah, ia akan selalu memiliki jahitan karena kesalahan pembulatan.

Jawaban oleh @ Nick-Wiggill benar, saya pikir Anda salah paham.

Barry Staes
sumber
0

2 kemungkinan yang mungkin patut dicoba:

  1. Gunakan GL_NEARESTsebagai ganti GL_LINEARuntuk pemfilteran tekstur Anda. (Seperti yang ditunjukkan oleh Andon M. Coleman)

    GL_LINEARakan (pada dasarnya) mengaburkan gambar sedikit (interpolasi antara piksel terdekat), yang memungkinkan transisi warna yang mulus dari satu piksel ke piksel berikutnya. Ini dapat membantu membuat tekstur terlihat lebih baik dalam banyak kasus, karena mencegah tekstur Anda memiliki piksel yang tebal di atasnya. Itu juga membuatnya sehingga sedikit cokelat dari satu piksel lebih dari satu ubin lebih dapat kabur ke piksel hijau yang berdekatan dari ubin yang berdekatan.

    GL_NEARESThanya menemukan piksel terdekat dan menggunakan warna itu. Tidak ada interpolasi. Akibatnya, sebenarnya sedikit lebih cepat.

  2. Kecilkan koordinat tekstur untuk setiap ubin sedikit.

    Agak seperti menambahkan piksel tambahan di sekitar setiap ubin sebagai buffer, kecuali alih-alih memperluas ubin pada gambar, Anda mengecilkan ubin di perangkat lunak. Ubin 14x14px saat merender bukan ubin 16x16px.

    Anda bahkan mungkin bisa lolos dengan 14.5x14.5 ubin tanpa terlalu banyak cokelat tercampur.

--edit--

Visualisasi penjelasan dalam opsi # 2

Seperti yang ditunjukkan pada gambar di atas, Anda masih dapat dengan mudah menggunakan kekuatan dua tekstur. Jadi Anda dapat mendukung perangkat keras yang tidak mendukung tekstur NPOT. Apa yang berubah adalah koordinat tekstur Anda, alih-alih dari (0.0f, 0.0f)menjadi (1.0f, 1.0f)Anda beralih (0.03125f, 0.03125f)ke (0.96875f, 0.96875f).

Itu menempatkan tex-coords Anda sedikit di dalam ubin yang mengurangi resolusi efektif dalam gim (meskipun tidak pada perangkat keras sehingga Anda masih memiliki tekstur dua kekuatan), namun harus memiliki efek rendering yang sama dengan memperluas tekstur.

Wolfgang Skyler
sumber
Saya sudah menyebutkan saya mencoba GL_NEAREST (alias penyaringan titik), dan itu tidak memperbaikinya. Saya tidak dapat melakukan # 2 karena saya perlu mendukung perangkat NPOT.
AshleysBrain
Membuat pengeditan yang dapat menghapus beberapa hal. (Saya berasumsi bahwa "perlu mendukung perangkat NPOT [(non-power-of-two)]" dimaksudkan untuk berarti sesuatu di sepanjang garis yang Anda butuhkan untuk menjaga tekstur pada beberapa kekuatan 2?)
Wolfgang Skyler
0

Inilah yang saya pikir mungkin terjadi: Di ​​mana dua ubin bertabrakan, komponen x pada tepinya harus sama. Jika itu tidak benar, mereka mungkin dibulatkan ke arah yang berlawanan. Jadi pastikan mereka memiliki nilai x yang persis sama. Bagaimana Anda memastikan ini terjadi? Anda dapat mencoba, katakanlah, kalikan dengan 10, bulat dan kemudian bagi dengan 10 (hanya lakukan ini pada CPU, sebelum simpul Anda masuk ke vertex shader). Itu harus memberikan hasil yang benar. Juga, jangan menggambar ubin dengan ubin menggunakan matriks transformasi, tetapi menempatkannya dalam batch VBO untuk memastikan bahwa hasil yang salah tidak datang dari sistem floating point IEEE yang hilang dalam kombinasi dengan perkalian matriks.

Mengapa saya menyarankan ini? Karena dari pengalaman saya, jika simpul memiliki koordinat yang sama persis ketika mereka keluar dari shader puncak, mengisi segitiga yang sesuai akan menghasilkan hasil yang mulus. Perlu diingat bahwa sesuatu yang benar secara matematis, mungkin agak tidak aktif karena IEEE. Semakin banyak perhitungan yang Anda lakukan pada angka-angka Anda, hasilnya akan kurang akurat. Dan ya, perkalian matriks membutuhkan cukup banyak operasi, yang mungkin dilakukan hanya dalam perkalian dan penambahan saat membuat VBO Anda, yang akan memberikan hasil yang lebih akurat.

Yang juga mungkin menjadi masalah adalah bahwa Anda menggunakan spritesheet (juga disebut atlas) dan ketika mengambil sampel tekstur, piksel dari tekstur ubin yang berdekatan akan diambil. Untuk memastikan bahwa ini tidak terjadi adalah membuat perbatasan kecil dalam pemetaan UV. Jadi, jika Anda memiliki ubin 64x64, pemetaan UV Anda harus mencakup sedikit lebih sedikit. Berapa banyak? Saya pikir saya menggunakan dalam game saya seperempat piksel di setiap sisi persegi panjang. Jadi, ganti rugi UV Anda dengan 1 / (4 * widthOfTheAtlas) untuk x komponen dan 1 / (4 * heightOfTheAtlas) untuk komponen y.

Martijn Courteaux
sumber
Terima kasih, tapi seperti yang sudah saya perhatikan, geometri pastinya benar-benar berbatasan - pada kenyataannya semua koordinat menghasilkan bilangan bulat yang tepat, sehingga mudah diketahui. Tidak ada atlas yang digunakan di sini.
AshleysBrain
0

Untuk mengatasi ini dalam ruang 3D, saya mencampur array tekstur dengan saran Wolfgang Skyler. Saya menempatkan batas 8-pixel di sekitar tekstur asli saya untuk setiap ubin (120x120, total 128x128) yang, untuk setiap sisi, yang dapat saya isi dengan gambar "terbungkus" atau sisi yang hanya diperpanjang. Sampler membaca area ini ketika sedang menginterpolasi gambar.

Sekarang dengan penyaringan dan pemetaan, sampler masih dapat dengan mudah membaca melewati seluruh batas 8 piksel. Untuk menangkap masalah kecil itu (saya katakan kecil karena itu hanya terjadi pada beberapa piksel ketika geometri benar-benar miring atau jauh), saya membagi ubin menjadi array tekstur, sehingga setiap ubin memiliki ruang tekstur sendiri dan dapat menggunakan penjepitan pada ujung-ujungnya.

Dalam kasus Anda (2D / datar), saya pasti akan pergi dengan rendering adegan pixel sempurna, dan kemudian menskalakan bagian yang diinginkan dari hasil ke viewport, seperti yang disarankan oleh Nick Wiggil.

mukunda
sumber