Как я могу повторить цветовые ограничения NES с пиксельным шейдером HLSL?


13

Так как 256-цветный режим устарел и больше не поддерживается в режиме Direct3D, мне пришла в голову идея использовать вместо этого пиксельный шейдер для имитации палитры NES всех возможных цветов, чтобы объекты с затуханием и тому подобное не имели плавных затуханий с альфа-каналами. , (Я знаю, что объекты не могли действительно исчезать в NES, но у меня есть все объекты, которые появляются и исчезают на сплошном черном фоне, что было бы возможно при смене палитры. Кроме того, экран затухает и исчезает при паузе что, как я знаю, было возможно и при смене палитры, как это было сделано в нескольких играх Mega Man.) Проблема в том, что я почти ничего не знаю о шейдерах HLSL.

Как это сделать?


Комментарий ниже описывает подход с использованием пиксельного шейдера, но простого ограничения цвета, подобного этому, можно добиться с помощью соответствующего руководства по стилю для вашей команды художников.
— Эван

Вы просто хотите уменьшить палитру или также хотите выполнить дизеринг (возможно также с использованием шейдеров). в противном случае ответ zezba9000 кажется мне лучшим
— tigrou

Я хочу просто уменьшить возможные цвета до палитры NES. Он мне нужен был в основном для захвата эффектов альфа-канала и других эффектов чернил, потому что в режиме 256 цветов он их ловит, но Direct3D больше не поддерживает режим 256 цветов, поэтому в нем эффекты сглаживаются в реальных цветах.
— Майкл Аллен Крэйн

Ответы:


4

В пиксельном шейдере вы можете передать 256x256 Texture2D с цветами палитры, выстроенными в ряд горизонтально. Тогда ваши текстуры NES будут преобразованы в прямые 3DD текстуры2D с преобразованием каждого пикселя в значение индекса 0-255. Существует формат текстуры, который использует только красное значение в D3D9. Таким образом, текстура будет занимать только 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;
}

Третий способ состоит в том, чтобы просто подделать низкокачественное изображение 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
Я имею в виду 256x1, а не 256x256? Также обязательно отключите билинейную фильтрацию для обеих текстур, иначе вы получите смешение между «записями» палитры.
— Натан Рид

Кроме того, с помощью этой схемы вы не можете использовать освещение или математические вычисления для значений текстуры, поскольку все, что вы делаете, скорее всего просто отправит вас в совершенно другую часть палитры.
— Натан Рид

@ Натан Рид Вы можете сделать освещение. Вы просто рассчитываете свет на "palletColor", прежде чем вернуть его значение цвета. Также вы можете сделать текстуру 256x1, но аппаратное обеспечение может использовать 256x256 в любом случае, и 256x256 - самый быстрый размер для использования на большинстве аппаратных средств. Если только это не изменило idk.
— zezba9000

Если вы делаете освещение после палитризации, то это, вероятно, даже не будет одним из цветов NES. Если это то, что вы хотите, это нормально - но это не похоже на то, о чем спрашивал вопрос. Что касается 256, это возможно, если у вас есть графический процессор старше 10 лет ... но все, что новее, будет поддерживать прямоугольные текстуры степени 2, например 256x1.
— Натан Рид

Если ему просто не нужны плавные затухания, он может сделать следующее: "palletColor.a = (float) floor (palletColor.a * fadeQuality) / fadeQuality;" Он мог бы сделать то же самое, что и метод 3D-текстуры, но с 2D-текстурой, изменив строку 4 на: "float4 palletColor = tex2D (PalletTexture, float2 (colorIndex.x, colorIndex.a);" Альфа-канал просто индексирует другой поддон слои на одной 2D текстуре
— zezba9000

3

Если вы на самом деле не заботитесь об использовании текстурной памяти (и идея использовать безумный объем текстурной памяти для получения ретро-стиля имеет своего рода извращенную привлекательность), вы можете создать трехмерную текстуру 256x256x256, отображающую все комбинации RGB в выбранную палитру. , Тогда в вашем шейдере он просто становится одной строкой кода в конце:

return tex3d (paletteMap, color.rgb);

Может даже не потребоваться пройти весь путь до 256x256x256 - может быть достаточно что-то вроде 64x64x64 - и вы даже можете изменить отображения палитры на лету, используя этот метод (но при значительных затратах из-за большого динамического обновления текстуры).


Это может быть лучшим методом, если вы хотите сделать освещение, альфа-смешивание или любой другой тип математики на ваших текстурах, а затем привязать конечный результат к ближайшему цвету NES. Вы можете предварительно рассчитать объемную текстуру, используя эталонное изображение, подобное этому ; если он настроен на фильтрацию ближайших соседей, вы можете получить что-то такое маленькое, как 16x16x16, что совсем не будет много памяти.
— Натан Рид

1
Это крутая идея, но она будет намного медленнее и не будет совместима со старым оборудованием. 3D-текстуры будут сэмплировать намного медленнее, чем 2D-текстуры, а 3D-текстуры также потребуют гораздо большей пропускной способности, что еще больше замедлит их. Новые карты это не имеет значения, хотя, но все же.
— zezba9000

1
Зависит от того, сколько лет вы хотите пойти. Я считаю, что поддержка 3D-текстуры восходит к оригинальной GeForce 1 - достаточно ли ей 14 лет?
— Максимус Минимус

Lol Ну, да, может быть, они никогда не использовали эти карты, думаю, я думал больше о GPU Phone. Сегодня существует множество целей, которые не поддерживают 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);
}

примечание: я написал это из головы, но я действительно уверен, что он скомпилируется и будет работать для вас


Другая возможность - использовать формат текстуры D3DFMT_R3G3B2 , который на самом деле совпадает с 8-битной графикой. Когда вы помещаете данные в эту текстуру, вы можете использовать простую битовую операцию на байт.

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

Это хороший пример. Только я думаю, что ему нужно было поменять цветную палитру. В этом случае ему придется использовать что-то вроде моего примера.
— zezba9000

Это не цветовая палитра NES вообще. NES не использовал 8-битный RGB, он использовал фиксированную палитру из 50–60 цветов в пространстве YPbPr.
— Сэм Хоцевар
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.