Bagaimana saya bisa meniru batasan warna NES dengan shader piksel HLSL?

13

Jadi karena 256 mode warna didepresiasi dan tidak lagi didukung dalam mode Direct3D, saya mendapat ide untuk menggunakan pixel shader sebagai gantinya untuk mensimulasikan palet NES dari semua warna yang mungkin sehingga objek yang memudar dan yang lainnya tidak memiliki fade out yang halus dengan saluran alpha . (Saya tahu objek tidak bisa benar-benar pudar pada SEN, tetapi saya memiliki semua objek yang pudar dan keluar pada latar belakang hitam pekat, yang mungkin terjadi dengan bertukar palet. Juga, layar memudar saat Anda jeda yang saya tahu juga mungkin dengan swapping palet seperti yang dilakukan dalam beberapa game Mega Man.) Masalahnya adalah, saya hampir tidak tahu tentang HLSL shaders.

Bagaimana saya melakukannya?

Michael Allen Crain
sumber
Komentar di bawah ini menjelaskan pendekatan menggunakan pixel shader, tetapi hanya batasan warna seperti ini dapat dicapai dengan menggunakan panduan gaya yang sesuai untuk tim seni Anda.
Evan
apakah Anda hanya ingin mengurangi palet atau juga ingin melakukan dithering (mungkin menggunakan shader juga). jika tidak, jawaban zezba9000 sepertinya yang terbaik untuk saya
tigrou
Saya ingin mengurangi kemungkinan warna pada palet NES. Saya membutuhkannya terutama untuk menangkap efek saluran alfa dan efek tinta lainnya karena dalam mode 256 warna menangkapnya tetapi Direct3D tidak lagi mendukung mode 256 warna sehingga di dalamnya efeknya dihaluskan dalam warna yang sebenarnya.
Michael Allen Crain

Jawaban:

4

Dalam pixel shader, Anda dapat memasukkan 256x256 Texture2D dengan warna palet yang semuanya berbaris secara horizontal dalam satu baris. Maka tekstur NES Anda akan dikonversi menjadi direct3D Texture2Ds dengan piksel yang dikonversi ke nilai indeks 0-255. Ada format tekstur yang hanya menggunakan nilai merah di D3D9. Jadi teksturnya hanya membutuhkan 8bits per pixel, tetapi data yang masuk ke shader adalah dari 0-1.

// Pixel shader mungkin terlihat seperti ini:

float4 mainPS() : COLOR0
{
    float4 colorIndex = tex2D(MainTexture, uv);
    float4 palletColor = tex2D(PalletTexture, float2(colorIndex.x, 0);
    return palletColor;
}

EDIT: Cara yang lebih benar adalah dengan menambahkan semua versi palet pencampur yang Anda butuhkan untuk menyelaraskan secara vertikal dalam tekstur dan mereferensikannya dengan nilai 'alpha' colorIndex Anda:

float4 mainPS() : COLOR0
{
    float4 colorIndex = tex2D(MainTexture, uv);
    float4 palletColor = tex2D(PalletTexture, float2(colorIndex.x, colorIndex.a);
    return palletColor;
}

Cara ketiga adalah dengan hanya memalsukan kualitas fade rendah NES dengan toon menaungi warna alfa:

float4 mainPS() : COLOR0
{
    float4 colorIndex = tex2D(MainTexture, uv);
    float4 palletColor = tex2D(PalletTexture, float2(colorIndex.x, 0);
    palletColor.a = floor(palletColor.a * fadeQuality) / fadeQuality;
    //NOTE: If fadeQuality where to equal say '3' there would be only 3 levels of fade.
    return palletColor;
}
zezba9000
sumber
1
Maksud Anda 256x1, bukan 256x256, saya kira? Juga, pastikan untuk menonaktifkan pemfilteran bilinear pada kedua tekstur, jika tidak Anda akan mendapatkan blending antara "entri" palet.
Nathan Reed
Juga, Anda tidak dapat melakukan segala jenis pencahayaan atau matematika pada nilai tekstur dengan skema ini, karena apa pun yang Anda lakukan kemungkinan hanya mengirim Anda ke bagian palet yang sama sekali berbeda.
Nathan Reed
@Nathan Reed Anda dapat melakukan pencahayaan. Anda cukup menghitung cahaya pada "palletColor" sebelum Anda mengembalikan nilai warnanya. Anda juga bisa membuat tekstur 256x1, tetapi perangkat keras mungkin hanya di bawah tenda menggunakan 256x256 dan 256x256 adalah ukuran tercepat untuk digunakan pada sebagian besar perangkat keras. Kecuali kalau itu berubah idk.
zezba9000
Jika Anda melakukan pencahayaan setelah paletisasi maka itu mungkin bahkan tidak akan menjadi salah satu warna NES lagi. Jika itu yang Anda inginkan, itu bagus - tetapi itu tidak terdengar seperti apa yang ditanyakan. Adapun 256 hal, itu mungkin jika Anda memiliki GPU lebih dari 10 tahun ... tetapi sesuatu yang lebih baru dari itu pasti akan mendukung tekstur power-of-2 persegi panjang, seperti 256x1.
Nathan Reed
Jika dia tidak ingin fadeouts mulus, dia bisa melakukan: "palletColor.a = lantai (float) (palletColor.a * fadeQuality) / fadeQuality;" Dia bahkan dapat melakukan hal yang sama dengan metode tekstur 3D tetapi dengan tekstur 2D dengan mengubah baris 4 menjadi: "float4 palletColor = tex2D (PalletTexture, float2 (colorIndex.x, colorIndex.a);" Saluran alpha hanya mengindeks palet yang berbeda lapisan pada tekstur 2D tunggal
zezba9000
3

Jika Anda tidak benar-benar peduli tentang penggunaan memori tekstur (dan gagasan untuk meniup memori tekstur dalam jumlah besar untuk mendapatkan tampilan retro memiliki semacam daya tarik yang keliru), Anda dapat membangun tekstur 3d 256x256x256 memetakan semua kombinasi RGB ke palet yang Anda pilih . Kemudian di shader Anda itu hanya menjadi satu baris kode di akhir:

return tex3d (paletteMap, color.rgb);

Bahkan mungkin tidak diperlukan untuk mencapai 256x256x256 - sesuatu seperti 64x64x64 mungkin cukup - dan Anda bahkan dapat mengubah pemetaan palet dengan cepat menggunakan metode ini (tetapi dengan biaya yang signifikan karena pembaruan tekstur dinamis yang besar).

Maximus Minimus
sumber
Ini mungkin metode terbaik jika Anda ingin melakukan pencahayaan, pencampuran alfa, atau jenis matematika lainnya pada tekstur Anda, dan kemudian snap hasil akhirnya ke warna NES terdekat. Anda dapat melakukan precompute tekstur volume Anda menggunakan gambar referensi seperti ini ; dengan diatur ke pemfilteran tetangga terdekat, Anda mungkin lolos dengan sesuatu yang sekecil 16x16x16, yang tidak akan banyak memori sama sekali.
Nathan Reed
1
Ini adalah ide yang keren, tetapi akan jauh lebih lambat dan tidak kompatibel dengan perangkat keras yang lebih lama. Tekstur 3D akan mengambil sampel jauh lebih lambat daripada yang 2D dan Tekstur 3D juga akan membutuhkan lebih banyak bandwidth yang akan memperlambatnya lebih banyak. Kartu yang lebih baru tidak masalah, tapi tetap saja.
zezba9000
1
Tergantung pada usia yang Anda inginkan. Saya percaya dukungan tekstur 3d kembali ke GeForce 1 asli - apakah sudah 14 tahun?
Maximus Minimus
Lol Yah ya mungkin mereka melakukannya, tidak pernah menggunakan kartu-kartu itu, kira saya berpikir lebih sejalan dengan GPU Telepon. Ada banyak target hari ini yang tidak memiliki dukungan tekstur 3D. Tetapi karena dia menggunakan D3D dan bukan OpenGL, tebak bahkan WP8 mendukung ini. Masih tekstur 3D akan memakan lebih banyak bandwidth daripada 2D.
zezba9000
1

(kedua solusi saya hanya berfungsi jika Anda tidak peduli tentang mengganti palet dengan menggunakan shader)

Anda dapat menggunakan semua jenis tekstur dan melakukan perhitungan sederhana pada shader. Triknya adalah Anda memiliki lebih banyak informasi warna daripada yang Anda butuhkan, jadi apa yang akan Anda singkirkan adalah informasi yang tidak Anda inginkan.

Warna 8bit dalam format RRRGGGBB . Yang memberi Anda 8 warna merah dan hijau dan 4 warna biru.

Solusi ini akan berfungsi untuk semua tekstur format warna RGB (A).

float4 mainPS() : COLOR0
{
    const float oneOver7 = 1.0 / 8.0;
    const float oneOver3 = 1.0 / 3.0;

    float4 color = tex2D(YourTexture, uvCoord);
    float R = floor(color.r * 7.99) * oneOver7;
    float G = floor(color.g * 7.99) * oneOver7;
    float B = floor(color.b * 3.99) * oneOver3;

    return float4(R, G, B, 1);
}

catatan: saya menulis itu dari atas kepala saya, tetapi saya sangat yakin itu akan dikompilasi dan bekerja untuk Anda


Kemungkinan lain , akan menggunakan format tekstur D3DFMT_R3G3B2 yang sebenarnya sama dengan grafis 8bit. Ketika Anda memasukkan data ke tekstur ini, Anda dapat menggunakan operasi bit sederhana per byte.

tex[index] = (R & 8) << 5 + ((G & 8) << 2) + (B & 4);
Notabene
sumber
Itu contoh yang bagus. Hanya saya pikir dia perlu bisa menukar palet warna. Dalam hal ini dia harus menggunakan sesuatu seperti contoh saya.
zezba9000
Ini bukan palet warna NES sama sekali. NES tidak menggunakan RGB 8-bit, menggunakan palet tetap sekitar 50 hingga 60 warna di ruang YPbPr.
sam hocevar