OpenGL에서 레트로 스타일 팔레트 교환 효과 만들기


37

내가 일하고 있어요 Megaman의 내가 런타임에 특정 픽셀의 색상을 변경해야 -like 게임. 참고 : Megaman 에서 선택한 무기를 변경하면 주 무기의 팔레트가 선택된 무기를 반영하도록 변경됩니다. 스프라이트의 색상이 모두 변경되는 것은 아니며 특정 색상 만 변경됩니다 .

프로그래머가 팔레트와 픽셀과 팔레트 인덱스 사이의 논리적 매핑에 액세스 할 수 있기 때문에 이러한 종류의 효과는 NES에서 일반적이고 매우 쉽게 수행 할 수 있습니다. 그러나 현대 하드웨어에서는 팔레트 개념이 동일하지 않기 때문에 조금 더 어려워집니다.

내 모든 텍스처는 32 비트이며 팔레트를 사용하지 않습니다.

내가 원하는 효과를 얻는 방법은 두 가지가 있지만이 효과를 쉽게 달성 할 수있는 더 좋은 방법이 있는지 궁금합니다. 내가 아는 두 가지 옵션은 다음과 같습니다.

  1. "팔레트 교환"동작을 수행하려면 셰이더를 사용하고 GLSL을 작성하십시오.
  2. 셰이더를 사용할 수없는 경우 (예 : 그래픽 카드에서 셰이더를 지원하지 않기 때문에) "원본"텍스처를 복제하고 미리 적용된 색상 변경으로 다른 버전을 생성 할 수 있습니다.

이상적으로는 셰이더를 사용하고 싶습니다. 직접 보이는 것처럼 보이고 복제 된 텍스처 방법에 반대되는 추가 작업이 거의 필요하지 않습니다. 색상을 변경하기 위해 텍스처를 복제하는 것이 VRAM을 낭비하고 있다고 걱정합니다. 걱정할 필요가 없습니까?

편집 : 나는 받아 들여진 대답의 기술을 사용하여 끝났고 여기에 참조 용 셰이더가 있습니다.

uniform sampler2D texture;
uniform sampler2D colorTable;
uniform float paletteIndex;

void main()
{
        vec2 pos = gl_TexCoord[0].xy;
        vec4 color = texture2D(texture, pos);
        vec2 index = vec2(color.r + paletteIndex, 0);
        vec4 indexedColor = texture2D(colorTable, index);
        gl_FragColor = indexedColor;      
}

두 텍스처 모두 32 비트이며 하나의 텍스처는 모두 같은 크기 (내 경우에는 6 색)의 여러 팔레트를 포함하는 조회 테이블로 사용됩니다. 소스 픽셀의 빨간색 채널을 색상 표의 색인으로 사용합니다. 이것은 Megaman과 같은 팔레트 스와핑을 달성하는 매력처럼 작동했습니다!

답변:


41

몇 가지 문자 텍스처에 VRAM을 낭비하는 것에 대해 걱정하지 않습니다.

내가 네 사용하는 옵션 2 길을 가야하는 것입니다 (즉, 맞는이 경우 다른 텍스처 또는 다른 UV 오프셋과) : 더 유연하고 데이터 중심의 코드에 미치는 영향, 적은 버그, 덜 걱정.


메모리에 수많은 스프라이트 애니메이션이 포함 된 수많은 캐릭터를 축적하기 시작하면 OpenGL에서 권장하는 do-it-yourself 팔레트를 사용하여 시작할 수 있습니다 .

팔레트 텍스처

주요 GL 공급 업체가 EXT_paletted_texture 확장에 대한 지원을 중단했습니다. 새 하드웨어에 팔레트 텍스처가 실제로 필요한 경우 쉐이더를 사용하여 그 효과를 얻을 수 있습니다.

쉐이더 예 :

//Fragment shader
#version 110
uniform sampler2D ColorTable;     //256 x 1 pixels
uniform sampler2D MyIndexTexture;
varying vec2 TexCoord0;

void main()
{
  //What color do we want to index?
  vec4 myindex = texture2D(MyIndexTexture, TexCoord0);
  //Do a dependency texture read
  vec4 texel = texture2D(ColorTable, myindex.xy);
  gl_FragColor = texel;   //Output the color
}

(색인 된 색상의 8 비트 정사각형 텍스처 ColorTable)를 사용하여 MyIndexTexture( RGBA8의 팔레트)에서 샘플링 하는 것입니다. 레트로 스타일 팔레트의 작동 방식을 그대로 재현합니다.


위에서 인용 한 예제는 sampler2D실제로 one sampler1D+ one을 사용할 수있는 two를 사용합니다 sampler2D. 호환성 문제 ( OpenGL ES의 1 차원 텍스처 없음) 때문이라고 생각 하지만 데스크톱 OpenGL의 경우 다음과 같이 단순화 할 수 있습니다.

uniform sampler1D Palette;             // A palette of 256 colors
uniform sampler2D IndexedColorTexture; // A texture using indexed color
varying vec2 TexCoord0;                // UVs

void main()
{
    // Pick up a color index
    vec4 index = texture2D(IndexedColorTexture, TexCoord0);
    // Retrieve the actual color from the palette
    vec4 texel = texture1D(Palette, index.x);
    gl_FragColor = texel;   //Output the color
}

Palette"진짜"색상 (예를 들어 1 차원 텍스처입니다 GL_RGBA8)과 IndexedColorTexture의 2 차원 텍스처입니다 인덱스 색상 (일반적으로 GL_R8당신 256 개 지수에게 준다). 이를 만들려면 팔레트 화 된 이미지를 지원 하는 여러 가지 제작 도구 와 이미지 파일 형식 이 있으며 필요에 맞는 이미지를 찾을 수 있어야합니다.


2
정확히 내가 준 대답은 더 자세합니다. 내가 할 수 있다면 +2 할 것입니다.
— Ilmari Karonen

색상의 색인이 어떻게 결정되는지 이해하지 못하기 때문에이 작업을 수행하는 데 문제가 있습니다. 조금 더 확장 할 수 있습니까?
— Zack The Human

당신은 아마 색인 색상을 읽어야합니다 . 텍스처는 팔레트의 색상 (일반적으로 1 차원 텍스처)에 해당하는 인덱스의 2D 인덱스 배열이됩니다. OpenGL 웹 사이트에서 제공하는 예제를 단순화하기 위해 답변을 편집하겠습니다.
— Laurent Couvidou

죄송합니다. 원래 의견에 명확하지 않았습니다. 인덱스 색상과 작동 방식에 익숙하지만 셰이더 또는 32 비트 텍스처의 컨텍스트 내에서 색상이 작동하지 않는 방식에 대해서는 명확하지 않습니다.
— Zack The Human

글쎄, 여기에는 256 색을 포함하는 32 비트 팔레트 하나와 8 비트 인덱스의 텍스처 (0에서 255까지)가 있습니다. 내 편집;) 참조
— Laurent Couvidou

10

내가 생각할 수있는 두 가지 방법이 있습니다.

방법 # 1 : GL_MODULATE

스프라이트를 회색 음영으로, GL_MODULATE단일 단색으로 그릴 때 텍스처를 만들 수 있습니다 .

예를 들어

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

이것은 당신에게 많은 융통성을 허용하지는 않지만, 기본적으로 같은 그늘에서 더 밝고 어둡게 갈 수 있습니다.

방법 # 2 : if진술 (성능에 좋지 않음)

당신이 할 수있는 또 다른 일은 R, G 및 B와 관련된 일부 주요 값으로 텍스처에 색상을 지정하는 것입니다. 예를 들어 첫 번째 색상은 (1,0,0)이고 두 번째 색상은 (0,1,0), 세 번째 색상입니다 색상은 (0,0,1)입니다.

당신은 같은 몇 가지 유니폼을 선언

uniform float4 color1 ;
uniform float4 color2 ;
uniform float4 color3 ;

그런 다음 셰이더에서 최종 픽셀 색상은

float4 pixelColor = texel.r*color1 + texel.g*color2 + texel.b*color3 ;

color1, color2, color3그들이 쉐이더 실행되기 전에 설정 될 수 있도록, 유니폼 있으며, 다음 쉐이더는 단순히 나머지를 (무엇을 "소송"Megaman의에 따라서이다).

if더 많은 색상이 필요한 경우 명령문 을 사용할 수도 있지만 등식 검사가 올바르게 작동해야합니다 (텍스처 R8G8B8파일의 텍스처는 대개 0에서 255 사이이지만 셰이더에서는 rgba float가 있습니다)

방법 # 3 : 다른 색상의 이미지를 스프라이트 맵에 중복 저장

여기에 이미지 설명을 입력하십시오

미친 듯이 메모리를 절약하지 않으려는 경우 이것이 가장 쉬운 방법 일 수 있습니다.


2
# 1은 단정 할 수 있지만 # 2와 # 3은 끔찍한 조언입니다. GPU는지도에서 색인을 생성 할 수 있으며 (기본적으로 팔레트가 있음) 두 제안보다 우수합니다.
— Skrylar

6

쉐이더를 사용합니다. 당신이 그들을 사용하지 않으려면 (또는 할 수 없다면) 나는 색상 당 하나의 질감 (또는 질감의 일부)을 가지고 깨끗한 흰색 만 사용합니다. 이를 통해 glColor()색상에 대한 추가 텍스처 생성에 대해 걱정할 필요없이 텍스처를 틴트 할 수 있습니다 (예 :) . 추가 텍스처 작업없이 런에서 색상을 바꿀 수도 있습니다.


이것은 좋은 생각입니다. 나는 실제로 이것을 잠시 생각했지만이 질문을 게시했을 때 마음에 들지 않았습니다. 이런 종류의 합성 텍스처를 사용하는 스프라이트에는 더 복잡한 그리기 로직이 필요하기 때문에 약간의 복잡성이 있습니다. 이 멋진 제안에 감사드립니다!
— Zack The Human

그렇습니다. 스프라이트 당 x 패스가 필요하기 때문에 약간의 복잡성이 추가 될 수 있습니다. 일반적으로 최소한 기본 픽셀 쉐이더 (넷북 GPU조차도)를 지원하는 오늘날의 하드웨어를 고려할 때, 대신 이것들을 사용하겠습니다. 건강 막대 또는 아이콘과 같은 특정 항목을 색조로 지정하려는 경우 깔끔 할 수 있습니다.
— 마리오

3

"모든 스프라이트의 색상이 변경되는 것은 아니며 특정 색상 만 변경됩니다."

오래된 게임은 팔레트 트릭을 수행했습니다. 요즘에는 여러 텍스처를 사용하고 다양한 방법으로 결합 할 수 있습니다.

색상을 변경할 픽셀을 결정하는 데 사용되는 별도의 회색조 마스크 텍스처를 만들 수 있습니다. 텍스처의 흰색 영역은 전체 색상 수정을 수신하고 검은 영역은 변경되지 않습니다.

셰이더 언어에서 :

vec3 baseColor = 텍스처 (BaseSampler, att_TexCoord) .rgb;
부동 량 = 텍스처 (MaskSampler, att_TexCoord) .r;
vec3 색상 = 믹스 (baseColor, baseColor * ModulationColor, 양);

또는 다양한 텍스처 컴 바이 너 모드에서 고정 기능 멀티 텍스처링을 사용할 수 있습니다.

당신이 이것에 관해 갈 수있는 많은 다른 방법들이 있습니다; 각 RGB 채널에 서로 다른 색상의 마스크를 넣거나 HSV 색상 공간에서 색조를 이동하여 색상을 조정할 수 있습니다.

더 간단한 또 ​​다른 옵션은 각 구성 요소에 대해 별도의 텍스처를 사용하여 스프라이트를 간단히 그리는 것입니다. 머리카락, 갑옷, 부츠 등의 질감이 하나씩 있습니다.


2

첫째, 일반적으로 자전거 타기 (팔레트 자전거 타기)라고하므로 Google-fu에 도움이 될 수 있습니다.

이것은 당신이 생성하고자하는 효과와 정적 2D 컨텐츠 또는 동적 3D 물건을 다룰 때 (즉, 스프라이트 / 텍스처를 다루거나, 전체 3D 장면을 렌더링 한 다음 팔레트를 교환하려는 경우)에 따라 다릅니다. 또한 자신을 여러 색상으로 제한하거나 풀 컬러를 사용하는 경우.

셰이더를 사용하는보다 완벽한 방법은 실제로 팔레트 텍스처로 색상 인덱스 이미지를 생성하는 것입니다. 텍스처를 만들지 만 색상 대신 색상을 찾고 색인을 나타내는 색상을 가진 다음 판인 색상의 다른 질감을 갖습니다. 예를 들어 빨간색은 텍스처 t 좌표이고 녹색은 s 좌표 일 수 있습니다. 셰이더를 사용하여 조회하십시오. 그리고 팔레트 텍스처의 색상을 애니메이션 / 수정하십시오. 이는 레트로 효과와 매우 유사하지만 스프라이트 또는 텍스처와 같은 정적 2D 컨텐츠에만 적용됩니다. 진정한 레트로 그래픽을 사용하는 경우 실제 인덱스 색상 스프라이트를 생성하고 팔레트와 팔레트를 가져올 무언가를 작성할 수 있습니다.

당신은 또한 '글로벌'팔레트를 사용하려는 경우 운동을 원할 것입니다. 오래된 하드웨어의 작동 방식과 마찬가지로 팔레트 하나에 필요한 메모리는 적지 ​​만 팔레트를 모든 이미지에서 동기화해야하기 때문에 아트웍을 만들기가 더 어렵지만 각 이미지에 고유 한 팔레트를 제공해야합니다. 그것은 당신에게 더 많은 유연성을 제공하지만 더 많은 메모리를 사용합니다. 물론 두 가지를 모두 할 수 있으며 색상 순환 작업에만 팔레트를 사용할 수 있습니다. 또한 색상을 제한하지 않으려는 정도의 운동을하십시오. 예를 들어 오래된 게임은 256 색을 사용합니다. 텍스처를 사용하는 경우 픽셀 수를 얻으므로 256 * 256이 제공합니다

흐르는 물과 같은 저렴한 가짜 효과를 원한다면 수정하려는 영역에 그레이 스케일 마스크가 포함 된 두 번째 텍스처를 만든 다음 마스트 된 텍스처를 사용하여 색조 / 채도 / 밝기를 회색조 마스크 텍스처. 더 넓을 수도 있고 색상 범위 내에서 색조 / 밝기 / 채도를 변경할 수도 있습니다.

풀 3D로 필요한 경우 인덱싱 된 이미지를 즉석에서 생성 해 볼 수 있지만 팔레트를 찾아야 할 때 속도가 느려질 수 있으며 색상 범위가 제한 될 수 있습니다.

그렇지 않으면 color == swaping color이면 쉐이더에서 가짜로 만들 수 있습니다. 속도가 느리고 제한된 수의 스와핑 색상으로 만 작동하지만 특히 2D 그래픽을 처리하고 어떤 스프리트에 색상 스와핑이 있는지 표시하고 다른 셰이더를 사용하는 경우 오늘날 하드웨어를 강조해서는 안됩니다.

그렇지 않으면 실제로 가짜로 만들려면 각 주마다 애니메이션 텍스처를 잔뜩 만드십시오.

다음은 HTML5에서 수행 된 팔레트 사이클링에 대한 훌륭한 데모입니다 .


0

색상 공간 [0 ... 1]을 두 개로 나눌 수있을만큼 빠르다고 생각합니다.

if (color.r > 0.5) {
   color.r = (color.r-0.5) * uniform_r + uniform_base_r;
} else {
  color.r *=2.0;
} // this could be vectorised for all colors

이런 식으로 0에서 0.5 사이의 색상 값은 일반 색상 값으로 예약됩니다. 0.5-1.0은 "변조 된"값을 위해 예약되어 있습니다. 여기서 0.5..1.0은 사용자가 선택한 모든 범위에 매핑됩니다.

색상 해상도 만 픽셀 당 256 값에서 128 값으로 잘립니다.

아마도 max (color-0.5f, 0.0f)와 같은 내장 함수를 사용하여 if를 완전히 제거 할 수 있습니다 (0 또는 1로 곱하기).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.