Membuat sprite buram dengan kecepatan


9

Setelah menambahkan kecepatan ke permainan saya, saya merasa seperti tekstur saya berkedut. Saya pikir itu hanya mata saya, sampai akhirnya saya menangkapnya dalam tangkapan layar:

masukkan deskripsi gambar di sini

Yang di sebelah kiri adalah apa yang merender dalam game saya; yang di sebelah kanan adalah sprite asli, ditempelkan. (Ini adalah tangkapan layar dari Photoshop, diperbesar 6x.)

Perhatikan ujungnya alias - hampir seperti rendering sub-pixel. Bahkan, jika saya tidak memaksa sprite saya (yang memiliki posisi dan kecepatan sebagai ints) untuk menggambar menggunakan nilai integer, saya bersumpah bahwa MonoGame sedang menggambar dengan nilai floating point. Tapi ternyata tidak.

Apa yang bisa menjadi penyebab hal-hal ini tampak buram? Itu tidak terjadi tanpa kecepatan diterapkan.

Lebih tepatnya, SpriteComponentkelas saya memiliki Vector2 Positionbidang. Ketika saya menelepon Draw, pada dasarnya saya gunakan new Vector2((int)Math.Round(this.Position.X), (int)Math.Round(this.Position.Y))untuk posisi itu.

Saya punya bug sebelum di mana bahkan objek stasioner akan gugup - itu karena saya menggunakan Positionvektor lurus dan tidak membulatkan nilai ints. Jika saya menggunakan Floor/ Ceilingbukannya bulat, sprite tenggelam / melayang (perbedaan satu pixel bagaimanapun juga) tetapi masih menarik buram.


1
Mungkinkah ini terkait dengan pemfilteran tekstur yang diterapkan?
OriginalDaemon

Apakah Anda memiliki kode untuk shader Anda? Beberapa orang menambahkan setengah piksel pada kedua sumbu untuk membuat piksel terpusat. Namun tidak yakin, saya pikir itu hanya DirectX.
William Mariager

3
Menonaktifkan penyaringan adalah solusi bukan solusi.
Archy

1
Rendering tidak tahu apa-apa tentang kecepatan Anda, jadi baik posisi, ukuran atau apa pun yang Anda berikan kepada SpriteBatch. Gambar berbeda atau ada kesalahan sebelum Anda menambahkan kecepatan. Bagaimana tepatnya Anda memanggil SpriteBatch.Draw?
Archy

1
@ ashes999 apakah Anda men-debug panggilan ini dan memeriksa this.X, this.Y dan this.origin? Apa ini diatur untuk orisinal? Pada dasarnya tidak ada cara hasil render berbeda ketika Draw call ini sama.
Archy

Jawaban:


3

Yah, ini memalukan.

Ternyata saya tidak menggambar menggunakan posisi integer, tetapi posisi mengambang. Archy menunjuk saya ke jalan yang benar dengan komentarnya@ashes999 did you debug this call and checked this.X, this.Y and this.origin?

Segera setelah saya menambahkan pernyataan jejak, saya perhatikan bahwa tidak ada yang terlacak. Ternyata SpriteComponentkelas saya benar menggunakan nilai integer, tetapi SpriteSheetComponentnilai float saya masih digunakan dari mentah Vector2 Position.

Meskipun saya tidak mencoba penyaringan dan penjepitan tekstur, saya curiga ini bukan perubahan yang benar, karena saya menggambar gambar 2D dengan lebar dan tinggi yang sama dengan gambar sumber (tanpa penskalaan).


1
Selesai, senang Anda menemukannya!
Archy

2

XNA menggunakan DirectX9 yang menggunakan pusat piksel sebagai lokasinya. Ini terlihat ketika menggunakan overload berbasis Vector2 dari kelas draw. Saya percaya bahwa mengurangi (0,5, 0,5) harus memperbaiki masalah Anda.

Info lebih lanjut di sini.


Secara teknis saya tidak bisa melakukan ini, karena saya menyampaikan dalam Vector2 dengan int, intsebagai argumen. Selain itu, semuanya terlihat sangat baik ketika saya tidak memiliki benda bergerak.
ashes999

Apa yang Anda maksud dengan objek yang bergerak? XNA tidak memiliki gagasan tentang gerak, ia menarik hal-hal yang Anda katakan juga. Ini adalah serangkaian snapshot stasioner. Coba juga menggunakan Rectangle overloads karena Anda membulatkan ke persegi panjang.
ClassicThunder

Saya memiliki implementasi kecepatan sendiri. SpriteComponentmemiliki Vector2posisi, yang bertambah sebagai mengapung tergantung pada kecepatan; ketika saya menggambar, saya membuat versi baru Vector2dengan integer (bulat) dari positionX dan Y saya. Ini bukan masalah, karena objek stasioner tanpa kecepatan tampak baik-baik saja.
ashes999

Jadi pepatah Anda bahwa Anda memiliki vektor posisi p dan vektor kecepatan v, tambahkan mereka seperti p + = v, lalu buat salinan p menggunakan nilai bulat. Dan bahwa Anda membuat nilai yang berbeda kemudian memiliki posisi yang sama dengan p + = v sebelumnya meskipun draw draw identik? Karena itu tidak masuk akal.
ClassicThunder

Ya, itulah yang dikatakan @Archy juga. Biarkan saya debug dan periksa lagi. Saya menambahkan p += vselama Update, dan membuat dengan new Vector2((int)Math.Round(p.X), (int)Math.Round(p.Y))).
ashes999

2

Rendering tidak tahu apa-apa tentang kecepatan Anda, jadi baik posisi, ukuran, atau apa pun yang Anda berikan kepada SpriteBatch. Gambar berbeda atau ada kesalahan sebelum Anda menambah kecepatan. Bagaimana tepatnya Anda memanggil SpriteBatch.Draw?

Apakah Anda men-debug panggilan ini dan memeriksa this.X, this.Y dan this.origin? Apa ini diatur untuk orisinal? Pada dasarnya tidak mungkin hasil render dengan kecepatan berbeda ketika Draw call ini sama.


1

Masalahnya adalah kemungkinan penyaringan tekstur dihidupkan. Jika Anda tidak yakin apa artinya itu, pertimbangkan situasi di mana Anda memiliki gambar dengan lebar 2 piksel: Pixel pertama berwarna hitam, piksel kedua berwarna putih. Jika Anda memperbesar tekstur ini, jika pemfilteran tekstur aktif, Anda akan melihatnya kabur dan area antara piksel hitam dan putih menggunakan warna abu-abu gradien.

Contoh klasik game dengan dan tanpa pemfilteran adalah Super Mario 64 yang memiliki pemfilteran sedangkan Doom tidak.

Ketika Anda tidak menggunakan kecepatan, Sprite Anda kemungkinan diposisikan sedemikian rupa sehingga pusat tekstur berada pada titik sampel dimana XNA (atau api apa pun yang digunakan sedang digunakan) meraih warna. Ketika Anda bergerak dengan kecepatan mengambang, maka posisi Sprite Anda berubah, sehingga titik sampel mungkin tidak berbaris langsung dengan pusat piksel, sehingga hasil akhirnya adalah bahwa warna yang merupakan rata-rata dari piksel terdekat sedang digunakan untuk membuat.

Anda dapat memverifikasi bahwa penyaringan dihidupkan dengan hanya membuat Sprite Anda sangat besar di layar. Jika buram, maka itu masalahnya.

Jika Anda harus memiliki akurasi piksel-sempurna dalam rendering, Anda akan ingin memiliki nilai float untuk posisi disimpan di suatu tempat, tetapi gunakan nilai integer untuk posisi objek yang Anda render ... atau tentu saja Anda bisa mematikan penyaringan jika gim Anda tidak membutuhkannya.

Anda mungkin juga ingin membaca ini .


Seperti yang saya sebutkan dalam pertanyaan saya, saya sudah menggunakan bilangan bulat untuk lokasi saya, dan ukuran gambar asli, ketika saya menelepon Draw. Jadi saya tidak yakin bagaimana itu bisa terjadi - tapi saya akan mencoba rendering skala besar dan melihat apakah mematikan penyaringan tekstur akan membantu.
ashes999

1

Cobalah mengatur SamplerState Anda ke SamplerState.PointClamp di panggilan SpriteBatch.Begin.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.