Mengganti palet dalam game menggunakan shader

Di blog ini saya akan menunjukkan teknik favorit saya yang saya gunakan secara aktif dalam permainan Vagabond saya: mengganti palet.

Swap palet adalah perubahan pada palet tekstur. Dalam artikel tersebut, kami menerapkannya menggunakan shader. Di masa lalu, ini adalah teknik yang berguna, memungkinkan Anda untuk menambah variabilitas sumber daya tanpa membuang-buang memori. Hari ini digunakan dalam generasi prosedural untuk menciptakan sumber daya baru.



Persiapan Gambar


Langkah pertama adalah menyiapkan gambar untuk mengganti palet. Dalam bitmap, setiap piksel berisi warna, tetapi kita harus mengandung indeks warnanya di palet. Karena ini, kami akan memisahkan struktur gambar (area dengan warna yang sama) dari warna asli.

Bahkan, beberapa format gambar mendukung metode penyimpanan ini. Misalnya, format PNG memiliki kemampuan untuk menyimpan warna yang diindeks. Sayangnya, banyak pustaka pemuatan gambar membuat larik warna, bahkan jika gambar disimpan dalam mode yang diindeks. Ini juga berlaku untuk perpustakaan SFML yang saya gunakan. Di dalam, ia menggunakan stb_image , yang secara otomatis "menghapus palet" gambar, yaitu. mengganti indeks dengan warna palet yang sesuai.

Karena itu, untuk menghindari masalah ini, Anda perlu menyimpan gambar dan palet secara terpisah. Gambar direkam dalam nuansa abu-abu, dan tingkat abu-abu dari setiap piksel sesuai dengan indeks warnanya di palet.

Berikut adalah contoh dari apa yang kami harapkan akan terima:


Untuk mencapai ini, saya menggunakan fungsi Python kecil yang menggunakan perpustakaan Pillow :

import io import numpy as np from PIL import Image def convert_to_indexed_image(image, palette_size): # Convert to an indexed image indexed_image = image.convert('RGBA').convert(mode='P', dither='NONE', colors=palette_size) # Be careful it can remove colors # Save and load the image to update the info (transparency field in particular) f = io.BytesIO() indexed_image.save(f, 'png') indexed_image = Image.open(f) # Reinterpret the indexed image as a grayscale image grayscale_image = Image.fromarray(np.asarray(indexed_image), 'L') # Create the palette palette = indexed_image.getpalette() transparency = list(indexed_image.info['transparency']) palette_colors = np.asarray([[palette[3*i:3*i+3] + [transparency[i]] \ for i in range(palette_size)]]).astype('uint8') palette_image = Image.fromarray(palette_colors, mode='RGBA') return grayscale_image, palette_image 

Pertama, fungsi mengubah gambar ke mode palet. Dia kemudian menafsirkannya kembali sebagai gambar abu-abu. Kemudian ambil paletnya. Tidak ribet, pekerjaan utamanya dikerjakan oleh perpustakaan Pillow.

Shader


Setelah menyiapkan gambar, kami siap menulis shader untuk mengganti palet. Ada dua strategi untuk mentransfer palet ke shader: Anda bisa menggunakan tekstur atau array yang homogen. Saya menemukan bahwa lebih mudah untuk menggunakan array yang homogen, jadi saya menggunakannya.

Ini adalah shader saya, saya menulisnya di GLSL, tetapi saya pikir itu dapat dengan mudah ditransfer ke bahasa lain untuk membuat shader:

 #version 330 core in vec2 TexCoords; uniform sampler2D Texture; uniform vec4 Palette[32]; out vec4 Color; void main() { Color = Palette[int(texture(Texture, TexCoords).r * 255)]; } 

Kami hanya menggunakan tekstur untuk membaca saluran merah dari piksel saat ini. Saluran merah adalah nilai titik apung dalam rentang dari 0 hingga 1, jadi kami mengalikannya dengan 255 dan mengonversinya menjadi int untuk mendapatkan tingkat abu-abu asli dari 0 hingga 255, yang disimpan dalam gambar. Selanjutnya kita menggunakannya untuk mendapatkan warna dari palet.

Animasi di awal artikel diambil dari tangkapan layar dalam game di mana saya menggunakan palet berikut untuk mengubah warna tubuh karakter:

Source: https://habr.com/ru/post/id480836/


All Articles