Come posso replicare i limiti di colore del NES con uno shader pixel HLSL?


13

Quindi, poiché la modalità 256 colori è deprezzata e non è più supportata nella modalità Direct3D, ho avuto l'idea di utilizzare un pixel shader invece di simulare la tavolozza NES di tutti i colori possibili in modo che gli oggetti in dissolvenza e quant'altro non abbiano dissolvenze sfumate uniformi con i canali alfa . (So ​​che gli oggetti non possono davvero svanire sul NES, ma ho tutti gli oggetti che si dissolvono dentro e fuori su uno sfondo nero solido, il che sarebbe possibile con lo scambio di palette. Inoltre, lo schermo si dissolve dentro e fuori quando si mette in pausa che so anche possibile con lo scambio di palette, come è stato fatto in alcuni giochi di Mega Man.) Il problema è che non so quasi nulla degli shader HLSL.

Come lo faccio?


Il commento qui sotto descrive l'approccio usando il pixel shader, ma semplicemente una limitazione del colore come questa potrebbe essere raggiunta usando una guida di stile appropriata per il tuo team artistico.
Evan,

vuoi solo ridurre la tavolozza o anche eseguire il dithering (possibile anche usando gli shader). altrimenti la risposta di zezba9000 mi sembra la migliore
tigrou,

Voglio solo ridurre i possibili colori alla palette NES. Ne avevo bisogno principalmente per catturare gli effetti del canale alfa e altri effetti di inchiostro perché in modalità 256 colori li cattura, ma Direct3D non supporta più la modalità 256 colori, quindi gli effetti sono sfumati con colori reali.
Michael Allen Crain,

Risposte:


4

Nel pixel shader è possibile passare un Texture2D 256x256 con i colori del pallet allineati orizzontalmente in fila. Quindi le trame NES verrebbero convertite in Texture2Ds direct3D con pixel sempre convertiti in un valore di indice 0-255. Esiste un formato texture che utilizza solo il valore rosso in D3D9. Quindi la trama occuperebbe solo 8 bit per pixel, ma i dati che entrano nello shader sarebbero compresi tra 0 e 1.

// Pixel shader potrebbe apparire così:

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

MODIFICA: Un modo più corretto sarebbe quello di aggiungere in tutta la versione del pallet di fusione che è necessario allineare verticalmente nella trama e fare riferimento a loro con il valore 'alpha' di colorIndex:

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

Un terzo modo sarebbe quello di falsificare la bassa qualità di dissolvenza del NES toon ombreggiando il colore 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;
}

1
Intendi 256x1, non 256x256, immagino? Inoltre, assicurati di disabilitare il filtro bilineare su entrambe le trame, altrimenti otterrai la fusione tra le "voci" della tavolozza.
Nathan Reed,

Inoltre, non puoi fare alcun tipo di illuminazione o matematica sui valori delle trame con questo schema, poiché qualsiasi cosa tu faccia probabilmente ti invierà semplicemente in una parte completamente diversa della tavolozza.
Nathan Reed,

@Nathan Reed Puoi fare l'illuminazione. Calcola la luce sul "palletColor" prima di restituire il suo valore di colore. Inoltre, potresti creare una trama 256x1, ma l'hardware potrebbe essere appena sotto il cofano utilizzare comunque 256x256 e 256x256 è la dimensione più veloce da usare sulla maggior parte dell'hardware. A meno che non sia cambiato idk.
zezba9000,

Se fai l'illuminazione dopo la palettizzazione, probabilmente non sarà più nemmeno uno dei colori NES. Se è quello che vuoi, va bene, ma non sembra quello che la domanda stava ponendo. Per quanto riguarda la cosa 256, questo è possibile se hai una GPU di età superiore a 10 anni ... ma qualcosa di più recente di quello supporterà sicuramente trame rettangolari power-of-2, come 256x1.
Nathan Reed,

Se semplicemente non vuole dissolvenze fluide, potrebbe fare: "palletColor.a = (float) floor (palletColor.a * fadeQuality) / fadeQuality;" Poteva persino fare la stessa cosa del metodo di trama 3D ma con una trama 2D cambiando la linea 4 in: "float4 palletColor = tex2D (PalletTexture, float2 (colorIndex.x, colorIndex.a);" Il canale alfa indicizza solo diversi pallet strati su una singola trama 2D
zezba9000

3

Se non ti interessa davvero l'utilizzo della memoria delle trame (e l'idea di soffiare una quantità folle di memoria delle trame per ottenere un aspetto retrò ha una sorta di fascino perverso) potresti costruire una trama 3d 256x256x256 che mappa tutte le combinazioni RGB sulla tavolozza selezionata . Quindi nel tuo shader diventa solo una riga di codice alla fine:

return tex3d (paletteMap, color.rgb);

Potrebbe non essere nemmeno necessario andare fino a 256x256x256 - qualcosa come 64x64x64 potrebbe essere sufficiente - e potresti persino cambiare al volo le mappature della tavolozza usando questo metodo (ma a costi significativi a causa di un grande aggiornamento dinamico della trama).


Questo può essere il metodo migliore se desideri eseguire l'illuminazione, la fusione alfa o qualsiasi altro tipo di matematica sulle trame e quindi agganciare il risultato finale al colore NES più vicino. È possibile pre-calcolare la trama del volume usando un'immagine di riferimento come questa ; con l'impostazione sul filtro del vicino più vicino, potresti benissimo scappare con qualcosa di piccolo come 16x16x16, che non sarebbe affatto memoria.
Nathan Reed,

1
Questa è una bella idea, ma sarà molto più lenta e non compatibile con l'hardware precedente. Le trame 3D eseguiranno il campionamento molto più lentamente di quelle 2D e le trame 3D richiederanno anche molta più larghezza di banda che lo rallenterà ancora di più. Tuttavia, le nuove carte non contano, ma comunque.
zezba9000,

1
Dipende da quanti anni vuoi andare. Credo che il supporto per le trame 3d risalga all'originale GeForce 1: ha 14 anni?
Maximus Minimus,

Lol Beh, sì, forse lo fanno, non ho mai usato quelle carte, suppongo che stavo pensando più in linea con le GPU del telefono. Oggi ci sono molti target che non hanno il supporto per le trame 3D. Ma poiché sta usando D3D e non suppone OpenGL, anche WP8 lo supporta. Tuttavia, una trama 3D occuperebbe più larghezza di banda di una 2D.
zezba9000,

1

(entrambe le mie soluzioni funzionano solo se non ti interessa cambiare al volo i pallet con gli shader)

Puoi usare qualsiasi tipo di trama e fare un semplice calcolo su uno shader. Il trucco è che hai più informazioni sul colore di quelle che ti servono, quindi quello che ti sbarazzerai delle informazioni che non vuoi.

Il colore a 8 bit è in formato RRRGGGBB . Che ti dà 8 tonalità di rosso e verde e 4 tonalità di blu.

Questa soluzione funzionerà con qualsiasi texture in formato colore 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);
}

nota: l'ho scritto dalla cima della mia testa, ma sono davvero sicuro che si compili e funzionerà per te


Un'altra possibilità sarebbe quella di utilizzare il formato texture D3DFMT_R3G3B2 che è effettivamente lo stesso della grafica a 8 bit. Quando si inseriscono dati in questa trama, è possibile utilizzare una semplice operazione di bit per byte.

tex[index] = (R & 8) << 5 + ((G & 8) << 2) + (B & 4);

Questo è un buon esempio. Solo io penso che avesse bisogno di essere in grado di scambiare il pallet di colore. In quel caso avrebbe dovuto usare qualcosa come il mio esempio.
zezba9000,

Questa non è affatto la tavolozza dei colori NES. Il NES non utilizzava RGB a 8 bit, ma utilizzava una tavolozza fissa di circa 50-60 colori nello spazio YPbPr.
Sam Hocevar,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.