Unity Background Image Blur



Salah satu tugas yang mungkin dihadapi oleh pengembang aplikasi Unity adalah menempatkan gambar saat ini di latar belakang untuk mengalihkan perhatian pengguna ke sesuatu yang baru, seperti menu atau pesan yang muncul. Artikel ini menceritakan tentang pengalaman menyelesaikan masalah ini oleh pengembang yang memiliki pengetahuan dasar di Unity, tanpa menggunakan sumber daya eksternal atau lisensi Unity tambahan. Saya berharap materi ini akan bermanfaat bagi mereka yang menghadapi masalah yang sama dan tidak menemukan solusi yang efektif di berbagai tahap itu.

Menyoroti elemen antarmuka penting (dan menghapus yang tidak perlu) adalah salah satu alat utama untuk meningkatkan pengalaman pengguna. Ini terutama diucapkan ketika bekerja dengan aplikasi seluler. Peralihan perhatian ini dapat dilakukan dengan berbagai cara - dengan lancar menjauhkan tombol di luar tepi layar, menggelapkan latar belakang, perilaku dinamis, dll. Ini termasuk mengaburkan gambar. Efek ini, di satu sisi, mengurangi kontras elemen latar belakang, dan menjadi lebih sulit bagi mata untuk menangkapnya. Di sisi lain, kabur memiliki efek bawah sadar ketika gambar menjadi tidak fokus, dan kami melihatnya secara berbeda. Pendekatan ini sering digunakan dalam gim, dan dari contoh gim terkenal di Unity, kita dapat menyebutkan momen saat memilih menu pencarian Hearthstone atau dilayar penyelesaian duel .

Sekarang bayangkan kita ingin melakukan hal yang sama atau serupa di aplikasi favorit kita. Kami juga tidak memiliki anggaran dan memiliki sedikit pengalaman dengan Unity.

Bagian Satu - Shader


Pikiran pertama yang muncul adalah bahwa segala sesuatu harus sudah terwujud. Tentunya menggunakan shader. Dan itu tentu harus di kotak Unity. Upaya pencarian dan pemasangan cepat menunjukkan bahwa ada shader di Unity, tetapi ternyata, itu adalah bagian dari Aset Standar, dan itu tidak tersedia untuk lisensi gratis.

Tapi ini kabur! Efek dasar yang sederhana secara matematis dan juga harus mudah diimplementasikan dan diintegrasikan ke dalam proyek. Bahkan jika kita tidak tahu apa-apa tentang shader. Selanjutnya, buka Internet, dan pencarian dengan cepat menunjukkan bahwa ada kode sumber shaders siap pakai untuk efeknya, dan bahkan yang berbeda.

Untuk implementasi pertama, shader ini diambil . Untuk mengujinya, cukup tambahkan shader sebagai file sumber ke proyek, kaitkan dengan material, ikat material ke Image.

Bagian Dua - Antarmuka Pengguna


Jika kita peduli dengan pengguna aplikasi, maka perlu memperhatikan antarmuka pengguna. Jika Anda hanya menambahkan efek blur ke latar belakang dan melupakannya, maka ini tidak semua konsisten dengan prinsip-prinsip internal menciptakan produk.

Akibatnya, untuk tindakan selanjutnya, Anda perlu menyentuh apa yang terjadi, dan kemudian berpikir dan menyesuaikan sedemikian rupa sehingga menimbulkan persepsi yang diperlukan pengguna. Poin ini sangat peka konteks. Bergantung pada kontras gambar, variasi warna dan faktor lainnya, berbagai nilai dan alat tambahan dapat dipilih. Dalam kasus kami, pada tahap ini, hasil yang baik diperoleh dengan jari-jari buram 25 (parameter shader) dan Transparan tambahan 70% (di luar shader melalui Gambar terpisah). Namun, ini hanya gambar latar belakang akhir. Transisi itu sendiri, menurut perasaan di telepon, terlalu tajam.

Shader yang terinstal pada gambar dihitung pada setiap frame, dan oleh karena itu, untuk membuat perpindahan yang lancar, cukup untuk secara dinamis mengubah parameter radius blur dan transparansi. Anda dapat mengatur ini dengan cara yang berbeda, tetapi pada dasarnya, pemrosesan adalah pembaruan di Handler pembaruan tergantung pada waktu. Transisi itu sendiri dalam versi final aplikasi adalah 0,2 detik, tetapi ternyata, ini penting untuk persepsi pengguna.

Bagian Tiga - Produktivitas


Dalam beberapa bulan terakhir, saya telah mendengar keluhan dari beberapa pengguna yang berbeda (bahkan bukan programmer) bahwa program game sering menggunakan semua sumber daya komputer yang tersedia menganggur dan tanpa rem. Sebagai contoh, ini dapat dinyatakan dalam penggunaan maksimum kartu video dalam hal program diminimalkan ke baki, atau terlepas dari segala sesuatu dan pemuatan konstan satu utas prosesor. Alasannya jelas secara intuitif - penghasilan pengembang atau penerbit tidak bergantung pada pengoptimalan (orang-orang tidak memperhatikan hal-hal seperti itu ketika membeli), dan pengembang biasa kemungkinan besar lebih peduli tentang KPI lokal dan kebutuhan untuk menunda tenggat waktu. Namun, dalam praktiknya, saya tidak ingin masuk dalam kategori ini.

Setelah menyelesaikan tahap sebelumnya pada peluncuran berikutnya dengan efek blur latar belakang, setelah memegang telepon di tangan Anda selama beberapa waktu, perasaan hangat muncul. Jika Anda menghabiskan lebih banyak waktu di menu, maka semuanya menjadi jauh lebih buruk. Dan bahkan jika pengguna dalam menu mungkin dan kemungkinan besar tidak menghabiskan banyak waktu, maka saya tidak ingin menjatuhkan baterai sama sekali.

Analisis menunjukkan bahwa shader yang dipilih di atas memiliki kompleksitas asimptotik O (r 2 ) tergantung pada radius yang dipilih. Dengan kata lain, jumlah perhitungan per titik menjadi 625 kali lebih besar jika Anda meningkatkan radius blur dari 1 menjadi 25. Dalam hal ini, perhitungan terjadi pada setiap frame.

Langkah pertama dalam solusi adalah gagasan bahwa tidak semua shader sama-sama bermanfaat, dan efek blur dapat diimplementasikan dengan cara yang berbeda. Untuk melakukan ini, Anda dapat mengambil blur terpisah, intinya adalah untuk mengaburkan hanya garis-garis secara horizontal, dan kemudian hanya garis-garis secara vertikal. Sebagai hasilnya, kita mendapatkan O (r) , dan ceteris paribus kompleksitas turun dengan urutan besarnya. Cara tambahan akan mengambil mipmap yang lebih kecil, yang sudah mengambil beberapa pekerjaan blur. Shader

ini diambil sebagai dasarnya. Namun, efek buramnya ternyata tidak mencukupi, dan dengan kontras grafik yang tinggi, gambar menjadi “terkelupas”. Untuk mendapatkan efek yang lebih baik, distribusi telah diubah (elemen GRABPIXEL). Jika dalam shader, dari tautan dari 0,18 ke 0,05 dari jari-jari 4, maka dalam versi kami dari 0,14 hingga 0,03 dari jari-jari 6 (ess, tetapi jumlah semuanya harus 1).

Dengan demikian, kompleksitas pemrosesan dikurangi oleh 1-2 urutan besarnya. Tapi ini tidak perlu dihentikan.

Bagian Empat - Latar Belakang Statis


Namun demikian, jika kita meninggalkan shader pada gambar yang ada, maka itu terus bekerja, melakukan perhitungan yang sama pada setiap frame. Jika sesuatu terjadi di latar belakang, maka kita perlu melakukannya. Tapi ini sepenuhnya opsional jika latar belakangnya statis. Dengan demikian, setelah kekaburan dinamis, Anda dapat mengingat hasilnya, letakkan di latar belakang dan matikan shader.

Selanjutnya, mari kita coba dengan cuplikan kode. Mempersiapkan tekstur tujuan untuk seluruh layar mungkin terlihat seperti ini:

_width = Screen.width / 2;
_height = Screen.height / 2;
_texture = new Texture2D(_width, _height);

var fillColor = Color.white;
var fillColorArray = _texture.GetPixels();
for (var i = 0; i < fillColorArray.Length; ++i)
{
	fillColorArray[i] = fillColor;
}
_texture.SetPixels(fillColorArray);
_texture.Apply();

_from.GetComponent<Image>().sprite = Sprite.Create(_texture, new Rect(0, 0, _texture.width, _texture.height), new Vector2(0.5f, 0.5f));

Artinya, tekstur disiapkan di sini yang 2 kali lebih kecil secara horizontal dan vertikal dari layar. Ini dapat dilakukan hampir selalu, karena kita tahu bahwa ukuran layar perangkat adalah kelipatan 2, dan jika dilakukan, ini akan mengurangi ukuran tekstur dan memfasilitasi pengaburan.

RenderTexture temp = RenderTexture.GetTemporary(_width, _height);
Graphics.Blit(_from_image.mainTexture, temp, _material);
RenderTexture.active = temp;

_texture.ReadPixels(new Rect(0, 0, temp.width, temp.height), 0, 0, false);
_texture.Apply();

_to_image.sprite = Sprite.Create(_texture, new Rect(0, 0, _texture.width, _texture.height), new Vector2(0.5f, 0.5f));
RenderTexture.ReleaseTemporary(temp);

Untuk mengambil gambar dengan mainTexture menggunakan shader (pada _material) Graphics.Blit digunakan. Selanjutnya, kita menghafal hasil dalam tekstur yang disiapkan dan menempatkannya di Gambar target.

Semuanya akan baik-baik saja, dalam praktiknya, ia dihadapkan dengan efek sedemikian rupa sehingga, tergantung pada perangkatnya, gambar latar belakangnya terbalik. Alasan setelah beberapa studi tentang masalah ini dan debugging menjadi jelas - perbedaan dalam sistem koordinatDirect3D dan OpenGL, yang tidak bisa disembunyikan Unity. Pada saat yang sama, shader kami mendeteksi UV dan memprosesnya dengan benar, dan inversi sudah terjadi di lingkungan Unity (ReadPixels). Ada banyak tips di internet, seperti "jika Anda telah terbalik, balikkan sendiri", atau "ubah tanda UV". Menggunakan makro UNITY_UV_STARTS_AT_TOP tidak membantu untuk mendapatkan generalisasi yang baik untuk semua perangkat yang diuji. Selain itu, misalnya, kami menemukan kasus seperti itu sehingga jika Anda mempersiapkan perakitan untuk emulasi dalam Xcode pada iPhone, dan Unity tidak menggunakan Logam, tetapi hanya OpenGLES, maka Anda perlu mencegat kasus seperti emulasi perangkat yang berjalan pada perangkat lunak lain.

Setelah mencoba sejumlah opsi, saya memilih dua tablet. Yang pertama adalah memaksa rendering kamera(pengaturan forceIntoRenderTexture pada saat pengambilan gambar). Yang kedua adalah penentuan jenis sistem grafis on the fly melalui SystemInfo.graphicsDeviceType, yang memungkinkan untuk mendefinisikan OpenGL-like atau Direct3D-like (grup pertama adalah OpenGLES2, OpenGLES3, OpenGLCore dan Vulkan). Selanjutnya, dalam implementasi kami untuk Direct3D-like, kita perlu menyerahkan, yang dilakukan secara prosedural (misalnya, seperti ini ).

Kesimpulan


Saya harap artikel ini akan bermanfaat bagi seseorang dalam mengatasi penyapu yang tidak dikenal, meningkatkan kehidupan pengguna dan memahami Unity. Jika Anda melihat efeknya dalam penggunaan pertempuran, maka di Unity terlihat seperti ini . Dalam aplikasinya, sensasinya agak berbeda, tetapi animasi ini dapat memberikan ide yang cukup.

All Articles