HLSL 픽셀 셰이더로 NES의 색상 제한을 어떻게 복제 할 수 있습니까?


13

따라서 256 색상 모드가 감가 상각되고 Direct3D 모드에서 더 이상 지원되지 않기 때문에 가능한 모든 색상의 NES 팔레트를 시뮬레이션하기 위해 픽셀 셰이더를 사용하여 페이딩 객체와 알파 채널로 부드러운 페이드 아웃을하지 않는 아이디어를 얻었습니다. . (저는 NES에서 객체를 실제로 페이드 아웃 할 수 없다는 것을 알고 있지만 단색의 검정색 배경에서 페이드 인 및 페이드 아웃되는 모든 객체를 가지고 있습니다. 팔레트 스와핑으로 인해 가능합니다. 또한 일시 중지하면 화면이 페이드 인 및 페이드 아웃됩니다. 일부 Mega Man 게임에서와 같이 팔레트 스와핑으로도 가능하다는 것을 알고 있습니다.) 문제는 HLSL 셰이더에 대해서는 아무것도 모른다는 것입니다.

어떻게합니까?


아래 설명은 픽셀 셰이더를 사용한 접근 방식을 설명하지만 아트 팀에 적합한 스타일 가이드를 사용하여 이와 같은 색상 제한을 간단히 달성 할 수 있습니다.
Evan

팔레트를 줄이거 나 디더링을 수행하고 싶습니까 (쉐이더를 사용할 수도 있습니다). 그렇지 않으면 zezba9000 답변이 나에게 가장 좋은 것 같습니다
tigrou

가능한 색상을 NES 팔레트로 줄이려고합니다. 256 색 모드에서는 효과가 있지만 Direct3D는 더 이상 256 색 모드를 지원하지 않기 때문에 알파 채널 효과 및 기타 잉크 효과를 포착하기 위해 주로 필요했습니다.
Michael Allen Crain

답변:


4

픽셀 셰이더에서는 팔레트 색상이 모두 가로로 일렬로 정렬 된 256x256 Texture2D를 전달할 수 있습니다. 그런 다음 NES 텍스처는 픽셀을 0-255 인덱스 값으로 변환하여 direct3D Texture2D로 변환됩니다. 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
256x256이 아니라 256x1을 의미합니다. 또한 두 텍스처 모두에서 이중 선형 필터링을 비활성화해야합니다. 그렇지 않으면 팔레트 "항목"간에 블렌딩됩니다.
Nathan Reed

또한이 구성표로 텍스처 값에 대해 어떤 종류의 조명이나 수학도 수행 할 수 없습니다. 왜냐하면 당신이하는 것은 팔레트의 완전히 다른 부분으로 당신을 보낼 가능성이 있기 때문입니다.
Nathan Reed

@Nathan Reed 조명을 할 수 있습니다. 색상 값을 반환하기 전에 "palletColor"에서 빛을 계산하면됩니다. 또한 256x1 텍스처를 만들 수 있지만 하드웨어는 256x256을 사용하고 256x256은 대부분의 하드웨어에서 사용하는 가장 빠른 크기입니다. 그것이 idk를 변경하지 않는 한.
zezba9000

팔레 타이 징 후 조명을 사용하면 더 이상 NES 색상 중 하나가 아닐 수도 있습니다. 그것이 당신이 원하는 것이라면 괜찮습니다. 그러나 그것은 질문이 요구하는 것과 같지 않습니다. 256에 관해서는 10 년이 지난 GPU가 있다면 가능합니다 . 그러나 그보다 더 최신의 것은 확실히 256x1과 같은 직사각형 2의 파워를 지원합니다.
Nathan Reed

만약 그가 부드러운 페이드 아웃을 원하지 않는다면 그는 할 수 있습니다 : "palletColor.a = (float) floor (palletColor.a * fadeQuality) / fadeQuality;" 그는 라인 3을 "float4 palletColor = tex2D (PalletTexture, float2 (colorIndex.x, colorIndex.a);"로 변경하여 3D 텍스처 방법과 동일하지만 2D 텍스처를 사용할 수 있습니다. 알파 채널은 다른 팔레트를 색인화합니다. 2D 텍스처의 레이어
zezba9000

3

텍스처 메모리 사용에 신경 쓰지 않는다면 (그리고 레트로 룩을 달성하기 위해 미친듯한 양의 텍스처 메모리를 불어 넣는 아이디어는 일종의 왜곡 된 호소력이 있습니다) 모든 RGB 조합을 선택한 팔레트에 매핑하는 256x256x256 3d 텍스처를 만들 수 있습니다 . 그런 다음 셰이더에서는 마지막에 한 줄의 코드가됩니다.

return tex3d (paletteMap, color.rgb);

64x64x64와 같은 256x256x256까지 충분하지 않을 수도 있으며이 방법을 사용하여 팔레트 매핑을 즉시 변경할 수도 있습니다 (그러나 큰 동적 텍스처 업데이트로 인해 상당한 비용이 듭니다).


텍스처에 조명, 알파 블렌딩 또는 다른 유형의 수학을 수행하고 최종 결과를 가장 가까운 NES 색상으로 스냅하려는 경우이 방법이 가장 좋습니다. 당신은 같은 참조 이미지를 사용하여 볼륨 텍스처를 미리 계산 수있는 이 일을 ; 가장 가까운 이웃 필터링으로 설정하면 16x16x16 정도의 작은 크기로 벗어날 수 있으며 메모리가 많지 않습니다.
Nathan Reed

1
이것은 좋은 생각이지만 훨씬 느리고 구형 하드웨어와 호환되지 않습니다. 3D 텍스처는 2D 텍스처보다 훨씬 느리게 샘플링되며 3D 텍스처는 훨씬 더 많은 대역폭을 필요로하므로 훨씬 느려집니다. 최신 카드는 중요하지 않지만 여전히 중요합니다.
zezba9000

1
당신이 가고 싶은 나이에 따라 다릅니다. 3D 텍스처 지원이 원래 GeForce 1로 돌아가는 것 같습니다.
Maximus Minimus

Lol 글쎄요, 아마도 그 카드를 사용하지 않았을 것입니다. 전화 GPU와 더 인라인으로 생각하고 있다고 생각합니다. 현재 3D 텍스처 지원이없는 많은 대상이 있습니다. 그러나 그는 OpenGL이 아닌 D3D를 사용하기 때문에 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 색의 고정 팔레트를 사용했습니다.
sam hocevar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.