本文介绍了模拟调色板互换使用OpenGL着色器(在LibGDX)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我试图用LibGDX做一个复古风格的小游戏,我想要让玩家选择的几个字符的颜色,所以我想到了加载PNG索引图像,然后更新调色板编程。 。如何错了我^^ UI'm trying to use LibGDX to make a retro-style little game, and I'd like to let the players to choose the colors of several characters, so I thought about loading png indexed images and then updating palettes programatically... How wrong I was ^^U看起来颜色pallettes是过去的事情,也似乎达到了类似的结果最好的选择是通过使用着色器It seems that color pallettes are something of the past, and also seems that the best option to achieve a similar result is by using Shaders.下面是一个图像解释什么,我想现在:Here is an image explaining what I'm trying right now:我的目的是用2个图像。他们中的一个, pixel_guy.png 是一个只有6种颜色(这些颜色是原来的调色板),PNG图像。其它图像, colortable.png ,将包含6调色板的各6种颜色(每行是一个不同的调色板)6×6像素PNG。从 colortable.png 的像素的第一行的颜色将匹配 pixel_guy.png 中使用的颜色,那会成为第一个/原装面板,和其他行会的调色板2-6。我尽量做到是使用colortable的第一个调色板索引pixelguy颜色,然后通过发送一个数字(2〜6)的改变调色板着色器。My intention is to use 2 images. One of them, pixel_guy.png is a png image with only 6 colors (those colors are its original palette). The other image, colortable.png, would be a 6x6 pixel png that contains 6 palettes of 6 colors each (each row is a different palette). The colors from the first row of pixels of colortable.png would match the colors used in pixel_guy.png, that would be the first/original palette, and the other rows would be palettes 2 to 6. What I try to achieve is to use colortable's first palette to index pixelguy colors and then change the palette by sending a number (from 2 to 6) to the shader.之后做一些研究,我发现了一个post在gamedev stackexchange ,显然这是我一直在寻找的,所以我试图对它进行测试。After doing some research, I found a post in gamedev stackexchange, and apparently it was what I was looking for, so I tried to test it.我创建的顶点和片段着色器和装载我的纹理(一个其调色板我想交换,并包含若干调色板的一个),但输出的是一个意想不到的白色图像。I created the Vertex and Fragment Shaders and loaded my textures (the one whose palette I wanted to swap, and the one that contained several palettes for that), but the output is an unexpected white image.我的顶点着色器: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;}我的片段着色器:My Fragment Shader: // Fragment shader// Thanks to Zack The Human http://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;}在code我用来做纹理的结合,并通过调色板数量的着色器:The code I used to make the texture binding and pass the palette number to the shader: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(); }}我不知道这是一个浮点值传递给片段的统一的正确途径。不知道任何有关如何在code段我试图用作品。I Don't know if that's the correct way to pass a float value to an uniform of the fragment. Not sure either about how the code snippet I tried to use works. 编辑:我试图通过TenFour04建议的修改和他们的工作完美:) I tried the changes suggested by TenFour04 and they worked flawlessly :)下面是pre-处理精灵Here is the pre-processed sprite我已经更新了版本库的变化,(Java的code ,片段着色器这里),如果有人有兴趣,可以在这里下载: https://bitbucket.org/hcito/libgdxshadertestI have updated the repository with the changes, (Java code here, Fragment Shader here), in case somebody is interested it can be downloaded here: https://bitbucket.org/hcito/libgdxshadertest 编辑2:我刚刚加入到存储库TenFour04谏(到调色板索引传递给每个精灵中的R声道调用sprite.setColor()方法)最后的优化,再次,它的工作完美:)Edit 2: I've just added to the repository the last optimization that TenFour04 adviced (to pass the palette index to each sprite within the R channel calling sprite.setColor() method), and again it worked perfect :)推荐答案我注意到的一些问题。 1)在你的片段着色器,不应该 VEC2指数= VEC2(color.r + PALETTEINDEX,0)是 VEC2指数= VEC2(color.r,PALETTEINDEX); ,所以精灵质感告诉你哪一行和PALETTEINDEX告诉你的颜色表的哪一列看1) In your fragment shader, shouldn't vec2 index = vec2(color.r + paletteIndex, 0); be vec2 index = vec2(color.r, paletteIndex);, so the sprite texture tells you which row and the paletteIndex tells you which column of the color table to look at? 2)的调色板指数被用作纹理坐标,所以它必须是一个介于0和1,设置这样的:2) The palette index is being used as a texture coordinate, so it needs to be a number between 0 and 1. Set it like this: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 ,所以你需要在设置你的制服每一帧,而不是在创建。可能是一个好主意,还绑定你的次要纹理每帧为好,如果你做任何多纹理后。3) A call to shader.end happens inside batch.end so you need to set your uniforms on every frame, not in create. Probably a good idea to also bind your secondary texture each frame as well, in case you do any multitexturing later.@Overridepublic 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)你可能还需要透明的像素保持透明。所以,我会改变你的片段着色器的最后一行preserve阿尔法:5) You probably also want transparent pixels to stay transparent. So I would change the last line of your fragment shader to preserve alpha:gl_FragColor = vec4(indexedColor.rgb, color.a); 6)这一次也可能是你都拿到全白的原因。在您的片段着色器,你应该用 v_texCoords 而不是 gl_TexCoord [0] .xy ,这与桌面的东西OpenGL和在OpenGL ES不使用。所以,你的片段着色器应该是:6) This one is probably the reason you were getting all white. In your fragment shader, you should be using v_texCoords instead of gl_TexCoord[0].xy, which is something from desktop OpenGL and not used in OpenGL ES. So your fragment shader should be: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)你的源子画面需要$ P $对 - 处理以映射到调色板查找表的列。您的着色器是拉动从纹理的红色通道的坐标。因此,你需要做的,你的源图像中的颜色替换每个像素的颜色。您可以通过手工提前做到这一点。这里有一个例子:7) Your source sprite needs to be pre-processed to map to the columns of your palette lookup table. Your shader is pulling a coordinate from the red channel of the texture. Therefore you need to do a color replacement of each pixel color in your source image. You can do this by hand ahead of time. Here's an example:肤色指数2出了六列(0-5)的。所以,就像我们计算的PALETTEINDEX,我们将它规范化为像素的中心: skinToneValue =(2 + 0.5)/ 6 = 0.417 。该图6是为六列。然后在你的绘图程序,你需要的红适当的值。Skin tone is index 2 out of the six columns (0-5). So just like we calculated the paletteIndex, we normalize it to the center of pixel: skinToneValue = (2+0.5) / 6 = 0.417. The 6 is for the six columns. Then in your drawing program, you need the appropriate value of red. 0.417 * 255 = 106 ,这是6A十六进制,所以你想要的颜色#6A0000。更换所有皮肤像素,这颜色。等的其他色调。0.417 * 255 = 106, which is 6A in hex, so you want color #6A0000. Replace all the skin pixels with this color. And so on for the other hues. 编辑:还有一个优化是,你可能希望能够批量所有的精灵在一起。现在是这样的,你必须把所有你的精灵为每个单独的调色板,并调用 batch.end 为他们每个人。您可以通过将 PALETTEINDEX 到每个精灵的顶点颜色,因为我们没有使用顶点颜色反正避免这种情况。One more optimization is that you probably want to be able to batch all your sprites together. The way it is now, you will have to group all your sprites for each palette separately and call batch.end for each of them. You can avoid this by putting paletteIndex into the vertex color of each sprite, since we weren't using vertex color anyway.所以,你可以将其设置为任何精灵的四色通道,我们假设R声道。如果使用Sprite类,可以调用 sprite.setColor(PALETTEINDEX,0,0,0); 否则叫 batch.setColor(PALETTEINDEX,0 ,0,0); 之前调用 batch.draw 每个精灵So you could set it to any of the four color channels of the sprite, let's say the R channel. If using the Sprite class, you can call sprite.setColor(paletteIndex, 0,0,0); Otherwise call batch.setColor(paletteIndex,0,0,0); before calling batch.draw for each of the sprites.顶点着色器需要声明变化的浮动PALETTEINDEX; ,并将其设置是这样的:The vertex shader will need to declare varying float paletteIndex; and set it like this:paletteIndex = a_color.r;片段着色器需要声明变化的浮动PALETTEINDEX; ,而不是统一浮动PALETTEINDEX; 。当然,你不再应该叫 shader.setUniformf(PALETTEINDEX,PALETTEINDEX); 这篇关于模拟调色板互换使用OpenGL着色器(在LibGDX)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云! 08-05 15:56