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.