Я пытаюсь использовать LibGDX для создания маленькой игры в стиле ретро, и я бы хотел, чтобы игроки выбирали цвета нескольких символов, поэтому я подумал о загрузке индексированных изображений png и затем программном обновлении палитр... Как я ошибался ^^ U
Кажется, что цветные паллеты - это что-то в прошлом, и также кажется, что лучший вариант для достижения аналогичного результата - использование шейдеров.
Вот изображение, объясняющее, что я пытаюсь сейчас:
Я намерен использовать 2 изображения. Один из них pixel_guy.png
- это png-изображение с 6 цветами (эти цвета являются его оригинальной палитрой). Другое изображение colortable.png
было бы размером 6x6 пикселей, которое содержит 6 палитр по 6 цветов каждая (каждая строка представляет собой другую палитру). Цвета из первой строки пикселей colortable.png
будут соответствовать цветам, используемым в pixel_guy.png
, это будет первая/оригинальная палитра, а в других строках будут палитры с 2 по 6. То, что я пытаюсь достичь, - это использовать цветную первую палитру, чтобы индексировать пиксельные цвета, а затем изменить палитру, отправив число (от 2 до 6) в шейдер.
После некоторого исследования я нашел сообщение в gamedev stackexchange, и, видимо, это то, что я искал, поэтому я попытался его протестировать.
Я создал шейдеры Vertex и Fragment Shaders и загрузил мои текстуры (ту, чью палитру я хотел поменять, и та, которая содержала несколько палитр для этого), но выход представляет собой неожиданное белое изображение.
Мой вершинный шейдер:
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/info/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, и они работали безупречно:)
Вот предварительно обработанный спрайт
Я обновил репозиторий с изменениями (Java-код здесь, Fragment Shader здесь), в случае, если кому-то это интересно, его можно скачать здесь: https://bitbucket.org/hcito/libgdxshadertest
Изменить 2: Я только что добавил в репозиторий последнюю оптимизацию, которую консультировал TenFour04 (передать индекс палитры каждому спрайту в R-канале, вызывающем метод sprite.setColor()), и снова он работал отлично:)