HLSLピクセルシェーダーでNESの色の制限を再現するにはどうすればよいですか?


13

256色モードは減価償却され、Direct3Dモードではサポートされなくなったため、代わりにピクセルシェーダーを使用して、可能なすべての色のNESパレットをシミュレートして、フェードオブジェクトやアルファチャネルによるスムーズフェードアウトがないようにするというアイデアを得ました。(オブジェクトがNESで実際にフェードアウトできないことはわかっていますが、パレットの交換で可能になる、黒一色の背景でフェードインおよびフェードアウトするすべてのオブジェクトがあります。また、一時停止すると画面がフェードインおよびフェードアウトします。いくつかのMega Manゲームで行われていたように、パレット交換でも可能だったことはわかっています。)問題は、HLSLシェーダーについてほとんど何も知らないことです。

どうすればいいのですか?


以下のコメントでは、ピクセルシェーダーを使用したアプローチについて説明していますが、このような色の制限は、アートチームに適したスタイルガイドを使用して実現できます。
エヴァン

パレットを減らすだけですか、ディザリングも実行しますか(シェーダーを使用することもできます)。そうでなければ、zezba9000の答えが私にとって最高のようです
-tigrou

NESパレットに使用できる色を減らしたいだけです。主にアルファチャネルエフェクトやその他のインクエフェクトをキャッチするために必要でした。256色モードではそれらをキャッチしますが、Direct3Dは256カラーモードをサポートしなくなったため、エフェクトはトゥルーカラーでスムーズになりました。
マイケルアレンクレイン

回答:


4

ピクセルシェーダーでは、パレットの色がすべて横一列に並んだ256x256 Texture2Dを渡すことができます。次に、NESテクスチャは、常にピクセルが0〜255のインデックス値に変換されたdirect3D Texture2Dに変換されます。D3D9の赤の値のみを使用するテクスチャ形式があります。そのため、テクスチャは1ピクセルあたり8ビットしか使用しませんが、シェーダーに入力されるデータは0〜1です。

//ピクセルシェーダーは次のようになります。

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

編集:より正確な方法は、テクスチャ内で垂直に整列する必要があるすべてのブレンドパレットバージョンを追加し、colorIndexの「アルファ」値でそれらを参照することです。

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

3番目の方法は、アルファカラーをトゥーンシェーディングすることにより、NESの低フェード品質を偽造することです。

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
256x256ではなく256x1を意味するのでしょうか?また、両方のテクスチャで双一次フィルタリングを無効にしてください。そうしないと、パレットの「エントリ」間でブレンドが発生します。
ネイサンリード

また、このスキームではテクスチャ値に対していかなる種類のライティングや計算もできません。何をするにしても、パレットのまったく別の部分に送られる可能性が高いからです。
ネイサンリード

@ネイサンリードあなたは照明を行うことができます。色値を返す前に、「palletColor」の光を計算するだけです。また、256x1テクスチャを作成することもできますが、ハードウェアは内部で256x256を使用する可能性があり、ほとんどのハードウェアで256x256が最も速いサイズです。それがidkを変更しない限り。
zezba9000

パレット化後にライティングを行うと、おそらくNESカラーの1つでもなくなるでしょう。それがあなたが望むものであるならば、それは結構です-しかし、それは質問が求めていたもののようには聞こえません。256に関しては、10年以上前の GPUがあれば可能ですが、それより新しいものは256x1のような長方形の2のべき乗テクスチャをサポートします。
ネイサンリード

滑らかなフェードアウトが必要ない場合は、「palletColor.a =(float)floor(palletColor.a * fadeQuality)/ fadeQuality;」を実行できます。3Dテクスチャーメソッドと同じことをすることもできますが、2行目のテクスチャーで4行目を次のように変更します: "float4 palletColor = tex2D(PalletTexture、float2(colorIndex.x、colorIndex.a);"単一の2Dテクスチャ上のレイヤー
zezba9000

3

テクスチャメモリの使用量をあまり気にしない場合(そしてレトロな外観を実現するために非常に多くのテクスチャメモリを吹き込むというアイデアは一種のひねくれた魅力を持っています)、選択したパレットにすべてのRGBの組み合わせをマッピングする256x256x256 3dテクスチャを構築できます。シェーダーでは、最後に1行のコードになります。

return tex3d (paletteMap, color.rgb);

256x256x256に至る必要はないかもしれません-64x64x64のようなもので十分かもしれません-そして、このメソッドを使用してその場でパレットマッピングを変更することもできます(ただし、大きな動的テクスチャ更新のため、かなりのコストがかかります)。


これは、テクスチャでライティング、アルファブレンディング、またはその他のタイプの数学を行い、最終結果を最も近いNESカラーにスナップする場合に最適な方法です。このような参照画像を使用して、ボリュームテクスチャを事前計算できます。最近傍フィルタリングに設定すると、16x16x16のような小さなもので大丈夫かもしれませんが、これはまったくメモリになりません。
ネイサンリード

1
これはクールなアイデアですが、はるかに遅くなり、古いハードウェアと互換性がなくなります。3Dテクスチャは2Dのものよりもはるかに遅いサンプリングを行い、3Dテクスチャはそれをさらに遅くするより多くの帯域幅を必要とします。新しいカードはこれは重要ではありませんが、それでもです。
zezba9000

1
何歳になりたいかによります。3Dテクスチャのサポートは元のGeForce 1に戻ると思います-14歳で十分ですか?
マキシマスミニマス

Lolまあ、そうかもしれませんが、それらのカードを使用したことはありません。PhoneGPUのインライン化を考えていたと思います。現在、3Dテクスチャをサポートしていないターゲットがたくさんあります。しかし、彼はD3Dを使用しており、OpenGLはWP8でさえこれをサポートしていると推測しています。それでも、3Dテクスチャは2Dテクスチャよりも多くの帯域幅を占有します。
zezba9000

1

(私のソリューションは両方とも、シェーダーを使用してその場でパレットを変更することを気にしない場合にのみ機能します)

任意のタイプのテクスチャを使用し、シェーダーで単純な計算を行うことができます。秘は、必要以上に多くの色情報を持っているということです。そのため、必要のない情報を取り除くだけです。

8ビットカラーは形式RRRGGGBBにあります。これにより、赤と緑の8階調と青の4階調が得られます。

このソリューションは、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);
}

注:私は頭の上からそれを書きましたが、それがあなたのためにコンパイルして動作することを本当に確信しています


別の可能性は、実際に8ビットグラフィックスと同じD3DFMT_R3G3B2テクスチャフォーマットを使用することです。このテクスチャにデータを配置すると、バイトごとに単純なビット操作を使用できます。

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

それは良い例です。彼だけがカラーパレットを交換できる必要があると思う。その場合、彼は私の例のようなものを使用する必要があります。
zezba9000

これはまったくNESカラーパレットではありません。NESは8ビットRGBを使用せず、YPbPrスペースで約50〜60色の固定パレットを使用しました。
サムホセバー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.