2014-10-01 3 views
10

복고 스타일의 작은 게임을 만들기 위해 LibGDX를 사용하려고합니다. 플레이어에게 여러 문자의 색상을 선택하게하고 싶습니다. PNG 인덱스 이미지로드 및 프로그래밍 방식으로 팔레트 업데이트 중 ... UOpenGL 쉐이더로 팔레트 바꾸기 시뮬레이션하기 (LibGDX에서)

컬러 팔레트가 과거와 비슷한 것처럼 보였습니다. 비슷한 결과를 얻는 가장 좋은 방법은 셰이더를 사용하는 것입니다 .

What I'm trying to do

내 의도는이 개 이미지를 사용하는 것입니다

여기에 내가 지금 노력하고있어 설명하는 이미지입니다. 그중 하나 인 pixel_guy.png은 6 색 (이 색은 원래 팔레트) 인 png 이미지입니다. 다른 이미지 colortable.png은 각각 6 색 팔레트 (각 행은 다른 팔레트)를 포함하는 6x6 픽셀 png입니다. colortable.png의 첫 번째 픽셀 행의 색은 pixel_guy.png에서 사용 된 색과 일치합니다 (첫 번째/원래 색 팔레트가 될 것이며 다른 행은 2-6 팔레트가 될 것입니다.) 달성하려는 것은 colortable의 첫 번째 팔레트를 사용하는 것입니다. index pixelguy colors를 선택하고 셰이더에 숫자 (2에서 6까지)를 보내서 팔레트를 변경하십시오.

몇 가지 조사를 마친 후 post in gamedev stackexchange을 찾았고 분명히 내가 찾고 있던 것이므로 테스트 해 보려고했습니다.

꼭지점 및 조각 쉐이더를 만들고 내 텍스처 (팔레트를 교환하고 싶었던 팔레트와 그 팔레트를 여러 팔레트로 포함하고있는 팔레트)를로드했지만 예상치 못한 흰색 이미지가 출력되었습니다.

내 버텍스 쉐이더 :

attribute vec4 a_position; 
attribute vec4 a_color; 
attribute vec2 a_texCoord0; 

uniform mat4 u_projTrans; 

varying vec4 v_color; 
varying vec2 v_texCoords; 

void main() { 
    v_color = a_color; 
    v_texCoords = a_texCoord0; 
    gl_Position = u_projTrans * a_position; 
} 

내 조각 쉐이더 :

// Fragment shader 
// Thanks to Zack The Human https://gamedev.stackexchange.com/questions/43294/creating-a-retro-style-palette-swapping-effect-in-opengl/ 

uniform sampler2D texture; // Texture to which we'll apply our shader? (should its name be u_texture?) 
uniform sampler2D colorTable; // Color table with 6x6 pixels (6 palettes of 6 colors each) 
uniform float paletteIndex; // Index that tells the shader which palette to use (passed here from Java) 

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;  
} 

내가 바인딩 질감을하고 쉐이더에 팔레트 번호 전달하는 데 사용되는 코드 :

package com.test.shaderstest; 

import com.badlogic.gdx.ApplicationAdapter; 
import com.badlogic.gdx.Gdx; 
import com.badlogic.gdx.graphics.GL20; 
import com.badlogic.gdx.graphics.Texture; 
import com.badlogic.gdx.graphics.g2d.SpriteBatch; 
import com.badlogic.gdx.graphics.glutils.ShaderProgram; 

public class ShadersTestMain extends ApplicationAdapter { 
    SpriteBatch batch; 

    Texture imgPixelGuy; 
    Texture colorTable; 

    private ShaderProgram shader; 
    private String shaderVertIndexPalette, shaderFragIndexPalette; 

    @Override 
    public void create() { 
     batch = new SpriteBatch(); 

     imgPixelGuy = new Texture("pixel_guy.png"); // Texture to which we'll apply our shader 
     colorTable = new Texture("colortable.png"); // Color table with 6x6 pixels (6 palettes of 6 colors each) 

     shaderVertIndexPalette = Gdx.files.internal("shaders/indexpalette.vert").readString(); 
     shaderFragIndexPalette = Gdx.files.internal("shaders/indexpalette.frag").readString(); 

     ShaderProgram.pedantic = false; // important since we aren't using some uniforms and attributes that SpriteBatch expects 

     shader = new ShaderProgram(shaderVertIndexPalette, shaderFragIndexPalette); 
     if(!shader.isCompiled()) { 
      System.out.println("Problem compiling shader :("); 
     } 
     else{ 
      batch.setShader(shader); 
      System.out.println("Shader applied :)"); 
     } 

     shader.begin(); 
     shader.setUniformi("colorTable", 1); // Set an uniform called "colorTable" with index 1 
     shader.setUniformf("paletteIndex", 2.0f); // Set a float uniform called "paletteIndex" with a value 2.0f, to select the 2nd palette 
     shader.end(); 

     colorTable.bind(1); // We bind the texture colorTable to the uniform with index 1 called "colorTable" 
    } 

    @Override 
    public void render() { 
     Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1); 
     Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); 
     batch.begin(); 
     batch.draw(imgPixelGuy, 0, 0); // Draw the image with the shader applied 
     batch.end(); 
    } 
} 

플로트 값을 조각의 유니폼에 전달하는 올바른 방법인지는 알 수 없습니다. 내가 사용하려고 시도한 코드 스 니펫이 어떻게 작동하는지에 대해 확실하지 않습니다. 편집

: 나는 TenFour04가 제시 한 변화를 시도하고 완벽하게 일 :

다음

는 사전 처리 된 스프라이트

Pre-processed sprite

내가 변화와 저장소를 업데이트 한 것입니다 (자바 코드 here, 조각 쉐이더 here), 누군가가 관심이 있으시면 여기에서 다운로드하십시오 : https://bitbucket.org/hcito/libgdxshadertest

편집 2 : 방금 ​​TenFour04가 조언 한 마지막 최적화를 저장소에 추가했습니다. (Sprite.setColor() 메서드를 호출하는 R 채널 내의 각 스프라이트에 팔레트 색인을 전달하기 위해) 그리고 다시 완벽하게 작동했습니다.

+0

왜 투표가 중단됩니까? – Tenfour04

+0

모르겠다 :/나는 내 영어가별로 좋지 않다는 것을 알고 있고, 조금 어색하고 전부다.하지만 나는 정말로 내 최선을 다하려고 노력하고있다. ^^ U 저를 도와 주셔서 대단히 감사합니다, Tenfour :) 그건 그렇고, 당신의 대답을 upvote하는 데 5 포인트가 더 필요합니다 ^^ U) – hcito

답변

8

몇 가지 문제점을 발견했습니다.

1) 조각 쉐이더에서 vec2 index = vec2(color.r + paletteIndex, 0);vec2 index = vec2(color.r, paletteIndex);이 아니어야합니다. 그래서 스프라이트 텍스처가 어떤 행을 알리고 paletteIndex가 컬러 테이블의 어느 열을 볼 것인지 알려줍니다.

2) 팔레트 인덱스 텍스처 좌표로 사용되므로 이런 0과 1의 집합을 수 있어야되고 :

int currentPalette = 2; //A number from 0 to (colorTable.getHeight() - 1) 
float paletteIndex = (currentPalette + 0.5f)/colorTable.getHeight(); 
//The +0.5 is so you are sampling from the center of each texel in the texture 

3) shader.end 호출이 batch.end 안에 일어나는 따라서 모든 프레임에 유니폼을 설정해야합니다 (create은 아님). 나중에 멀티 텍스쳐 작업을 할 경우에 대비하여 각 프레임에도 보조 텍스처를 바인딩하는 것이 좋습니다.

@Override 
public void render() { 
    Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1); 
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); 

    colorTable.bind(1); 

    //Must return active texture unit to default of 0 before batch.end 
    //because SpriteBatch does not automatically do this. It will bind the 
    //sprite's texture to whatever the current active unit is, and assumes 
    //the active unit is 0 
    Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0); 

    batch.begin(); //shader.begin is called internally by this line 
    shader.setUniformi("colorTable", 1); 
    shader.setUniformf("paletteIndex", paletteIndex); 
    batch.draw(imgPixelGuy, 0, 0); 
    batch.end(); //shader.end is called internally by this line 

} 

4) GPU 어쨌든 당신을 위해이 작업을 수행 할 수 있지만, 버텍스 컬러를 사용하지 않기 때문에, 당신은 당신의 버텍스 쉐이더에서 v_color을 포함하는 두 줄을 제거 할 수 있습니다.

5) 투명한 픽셀이 투명하게 유지되기를 원할 수도 있습니다. 그래서 나는 알파를 유지하기 위해 당신의 조각 쉐이더의 마지막 줄을 변경합니다 : 이것은 아마 당신이 모든 화이트지고 있었다 이유

gl_FragColor = vec4(indexedColor.rgb, color.a); 

6). 프래그먼트 쉐이더에서 gl_TexCoord[0].xy 대신 v_texCoords을 사용해야합니다. 이는 데스크톱 OpenGL에서 가져온 것이므로 OpenGL ES에서는 사용되지 않습니다. 그래서 당신의 조각 쉐이더은 다음과 같아야합니다

uniform sampler2D texture; // Texture to which we'll apply our shader? (should its name be u_texture?) 
uniform sampler2D colorTable; // Color table with 6x6 pixels (6 palettes of 6 colors each) 
uniform float paletteIndex; // Index that tells the shader which palette to use (passed here from Java) 
varying vec2 v_texCoords; 

void main() 
{ 
    vec4 color = texture2D(texture, v_texCoords); 
    vec2 index = vec2(color.r, paletteIndex); 
    vec4 indexedColor = texture2D(colorTable, index); 
    gl_FragColor = vec4(indexedColor.rgb, color.a); // This way we'll preserve alpha  
} 

7) 소스 스프라이트는 팔레트 조회 테이블의 컬럼에 매핑하는 사전 처리 될 필요가있다. 셰이더가 텍스처의 빨간 채널에서 좌표를 당깁니다. 따라서 소스 이미지에서 각 픽셀 색상을 색상으로 대체해야합니다. 당신은 미리 손으로 이것을 할 수 있습니다. 다음은 그 예입니다.

스킨 톤은 6 개 열 (0-5) 중 인덱스 2입니다. 팔레트 인덱스를 계산 한 것처럼 픽셀 중심으로 정규화합니다 : skinToneValue = (2+0.5)/6 = 0.417. 6은 6 개의 열에 대한 것입니다. 그리기 프로그램에서 적색의 적절한 값이 필요합니다.

0.417 * 255 = 106 이것은 6 각형 (6A)이므로 # 6A0000의 색상이 필요합니다. 모든 스킨 픽셀을이 색으로 바꿉니다. 그리고 다른 색상에 대해서도 마찬가지입니다.


편집 :

하나 더 최적화 당신은 아마 모든 스프라이트 함께 배치 할 수 있도록하려는 것입니다. 지금과 같은 방법으로 각 팔레트마다 모든 스프라이트를 개별적으로 그룹화하고 각각에 대해 batch.end으로 전화해야합니다. 어쨌든 우리는 버텍스 컬러를 사용하지 않았기 때문에 을 각 스프라이트의 버텍스 컬러에 넣음으로써이를 피할 수 있습니다.

그래서 스프라이트의 네 가지 색상 채널 중 하나에 설정할 수 있습니다. R 채널을 예로 들어 보겠습니다. Sprite 클래스를 사용하는 경우 sprite.setColor(paletteIndex, 0,0,0);을 호출하거나 batch.setColor(paletteIndex,0,0,0);을 호출하여 각각의 스프라이트에 batch.draw을 호출 할 수 있습니다.

버텍스 쉐이더는이처럼 varying float paletteIndex;를 선언하고 설정해야합니다 :

paletteIndex = a_color.r; 

조각 쉐이더가 varying float paletteIndex; 대신 uniform float paletteIndex; 선언해야합니다.

물론 더 이상 shader.setUniformf("paletteIndex", paletteIndex);으로 전화하지 않아도됩니다.

+0

대단히 감사합니다 :) 나는 당신이 말한 변화를 시도했지만 여전히 흰색 사각형을 보여줍니다. 지금은 시간이 없지만 내일 더 철저히 테스트 할 수 있습니다. 어쨌든, 당신이 원한다면 저장소를 볼 수있다. (변경을 할 때 뭔가를 망쳤을 수도있다.) – hcito

+0

내가 한 실수 중 하나는 glActiveTexture 호출이'batch.end' 전에 ​​있어야한다는 것이다. 나는 당신이 uvs를 얻고있는 펑키 한 길을보고 싶었다. 또한, 귀하의 bitbucket에서 소스 스프라이트가 인코딩되지 않았 음을 알 수 있습니다. 나는 대답을 갱신 할 것이다. – Tenfour04

+0

또한 팔레트 번호를 계산하기 위해 getHeight 대신 getWidth를 사용했는데, 이는 팔레트가 행이 아니라 행이므로 잘못되었습니다. – Tenfour04

관련 문제