转动时为 2D 精灵添加厚度
Adding thickness to a 2D sprite when turning
在我的游戏中,我的实体像一张纸一样转动,如下图以半速显示:https://imgur.com/a/u2suen6
我想在实体转动时给它们一些厚度,使它们更像硬纸板而不像纸一样薄。
我考虑过使用 Pixmap 来检测和扩展边缘像素并为图像提供一些三维度。我还考虑过沿 x 轴复制图像以获得相同的效果。在这两个想法中,Pixmap 在我看来最有希望。但是,我想知道是否有更好的解决方案。
我正在使用 GLSL 着色器在转动时为实体提供高光和阴影,正如您在 gif 中看到的那样。我认为只要掌握了正确的知识,我就可以使用相同的着色器程序实现我想要的效果。
我的着色器看起来像这样:
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform vec2 u_resolution;
uniform vec3 color;
void main()
{
vec4 col = vec4(color, 0.0);
gl_FragColor = texture2D(u_texture, v_texCoords) * v_color + col;
}
我认为可以根据我传递给它的 uniform vec3 color
进行计算(其值范围从 0, 0, 0
到 1, 1, 1
。1 是突出显示,0 是成为影子)。不幸的是,我不了解着色器。
如果你们中的任何人有诀窍,你能引导我朝着正确的方向前进吗?或者让我知道我是否应该坚持 Pixmap 的想法。
编辑:我正在努力避免使用 3D 模型,因为我使用 2d 正交相机需要 6.4k 行代码。
编辑 2: 我认为如果我尝试使 sprite 看起来像 3D,反射着色器看起来不会很好。我放弃了着色器,采用了像素图的想法,并计划在没有任何着色器的情况下对像素图实现阴影和反射。虽然到目前为止看起来不错,没有反射。
我最终采用了我的像素图想法。我想分享我的代码,以便其他人可以知道我如何使用 2D 厚度。
请注意在下面的代码中:
dir
is a floating point value in the range -1.0 to 1.0. It tells the program where the sprite is in its turn. -1
means facing fully left. 1
meaning right. 0
means that it's 'facing' the camera.
right
is a boolean that tells the program which direction the entity is turning. true
means that the entity is turning from left to right. false
means from right to left.
代码:
private Texture getTurningImage(TextureRegion input, int thickness)
{
if(Math.abs(dir) < 0.1)
dir = (right ? 1 : -1) * 0.1f;
Texture texture = input.getTexture();
if (!texture.getTextureData().isPrepared())
{
texture.getTextureData().prepare();
}
Pixmap pixmap = texture.getTextureData().consumePixmap();
Pixmap p = new Pixmap(64, 64, Pixmap.Format.RGBA8888);
p.setFilter(Pixmap.Filter.NearestNeighbour);
Pixmap texCopy = new Pixmap(input.getRegionWidth(), input.getRegionHeight(), Pixmap.Format.RGBA8888);
// getting a texture out of the input region. I can't use input.getTexture()
// because it's an animated sprite sheet
for (int x = 0; x < input.getRegionWidth(); x++)
{
for (int y = 0; y < input.getRegionHeight(); y++)
{
int colorInt = pixmap.getPixel(input.getRegionX() + x, input.getRegionY() + y);
Color c = new Color(colorInt);
colorInt = Color.rgba8888(c);
texCopy.drawPixel(x, y, colorInt);
}
}
pixmap.dispose();
float offsetVal = Math.round(thickness/2.0) * (float) -Math.cos((dir * Math.PI)/2);
if(offsetVal > -1.23/Math.pow(10, 16))
{
offsetVal = 0;
}
// generate the pixel colors we'll use for the side view
Pixmap sideProfile = new Pixmap(1, 64, Pixmap.Format.RGBA8888);
for (int y = 0; y < texCopy.getHeight(); y++)
{
for (int x = 0; x < texCopy.getWidth(); x++)
{
int colorInt = texCopy.getPixel(x, y);
if(new Color(colorInt).a != 0 && new Color(texCopy.getPixel(x + 1, y)).a == 0)
{
Color c = new Color(colorInt);
c.mul(.8f); // darken the color
c.a = 1;
colorInt = Color.rgba8888(c);
sideProfile.drawPixel(0, y, colorInt);
continue;
}
}
}
// drawing the bottom layer
p.drawPixmap(texCopy, 0, 0, 64, 64, (int) (Math.round(-offsetVal) + (64 - texCopy.getWidth()*Math.abs(dir))/2), 0, (int)(64*Math.abs(dir)), 64);
// drawing the middle (connecting) layer
// based on the edge pixels of the bottom layer, then translated to be in the middle
for (int y = 0; y < p.getHeight(); y++)
{
int colorInt = sideProfile.getPixel(0, y);
for (int x = 0; x < p.getWidth(); x++)
{
if(new Color(p.getPixel(x, y)).a != 0 && new Color(p.getPixel(x + 1, y)).a == 0)
{
for(int i = 0; i <= 2 * Math.round(Math.abs(offsetVal)); i++) // the for the length between the top and bottom
{
p.drawPixel(x + i - 2 * (int)Math.round(Math.abs(offsetVal)), y, colorInt);
}
}
}
}
// drawing the top layer
p.drawPixmap(texCopy, 0, 0, 64, 64, (int) (Math.round(offsetVal) + (64 - texCopy.getWidth()*Math.abs(dir))/2), 0, (int)(64*Math.abs(dir)), 64);
// flip if facing left
if(dir < 0)
{
p = flipPixmap(p);
}
return new Texture(p);
}
我的 flipPixmap
方法如下所示(从堆栈溢出中窃取):
private Pixmap flipPixmap(Pixmap src)
{
final int width = src.getWidth();
final int height = src.getHeight();
Pixmap flipped = new Pixmap(width, height, src.getFormat());
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
flipped.drawPixel(x, y, src.getPixel(width - x - 1, y));
}
}
return flipped;
}
这是结果:D https://imgur.com/a/wGeHg9D
在我的游戏中,我的实体像一张纸一样转动,如下图以半速显示:https://imgur.com/a/u2suen6
我想在实体转动时给它们一些厚度,使它们更像硬纸板而不像纸一样薄。
我考虑过使用 Pixmap 来检测和扩展边缘像素并为图像提供一些三维度。我还考虑过沿 x 轴复制图像以获得相同的效果。在这两个想法中,Pixmap 在我看来最有希望。但是,我想知道是否有更好的解决方案。
我正在使用 GLSL 着色器在转动时为实体提供高光和阴影,正如您在 gif 中看到的那样。我认为只要掌握了正确的知识,我就可以使用相同的着色器程序实现我想要的效果。
我的着色器看起来像这样:
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform vec2 u_resolution;
uniform vec3 color;
void main()
{
vec4 col = vec4(color, 0.0);
gl_FragColor = texture2D(u_texture, v_texCoords) * v_color + col;
}
我认为可以根据我传递给它的 uniform vec3 color
进行计算(其值范围从 0, 0, 0
到 1, 1, 1
。1 是突出显示,0 是成为影子)。不幸的是,我不了解着色器。
如果你们中的任何人有诀窍,你能引导我朝着正确的方向前进吗?或者让我知道我是否应该坚持 Pixmap 的想法。
编辑:我正在努力避免使用 3D 模型,因为我使用 2d 正交相机需要 6.4k 行代码。
编辑 2: 我认为如果我尝试使 sprite 看起来像 3D,反射着色器看起来不会很好。我放弃了着色器,采用了像素图的想法,并计划在没有任何着色器的情况下对像素图实现阴影和反射。虽然到目前为止看起来不错,没有反射。
我最终采用了我的像素图想法。我想分享我的代码,以便其他人可以知道我如何使用 2D 厚度。
请注意在下面的代码中:
dir
is a floating point value in the range -1.0 to 1.0. It tells the program where the sprite is in its turn.-1
means facing fully left.1
meaning right.0
means that it's 'facing' the camera.
right
is a boolean that tells the program which direction the entity is turning.true
means that the entity is turning from left to right.false
means from right to left.
代码:
private Texture getTurningImage(TextureRegion input, int thickness)
{
if(Math.abs(dir) < 0.1)
dir = (right ? 1 : -1) * 0.1f;
Texture texture = input.getTexture();
if (!texture.getTextureData().isPrepared())
{
texture.getTextureData().prepare();
}
Pixmap pixmap = texture.getTextureData().consumePixmap();
Pixmap p = new Pixmap(64, 64, Pixmap.Format.RGBA8888);
p.setFilter(Pixmap.Filter.NearestNeighbour);
Pixmap texCopy = new Pixmap(input.getRegionWidth(), input.getRegionHeight(), Pixmap.Format.RGBA8888);
// getting a texture out of the input region. I can't use input.getTexture()
// because it's an animated sprite sheet
for (int x = 0; x < input.getRegionWidth(); x++)
{
for (int y = 0; y < input.getRegionHeight(); y++)
{
int colorInt = pixmap.getPixel(input.getRegionX() + x, input.getRegionY() + y);
Color c = new Color(colorInt);
colorInt = Color.rgba8888(c);
texCopy.drawPixel(x, y, colorInt);
}
}
pixmap.dispose();
float offsetVal = Math.round(thickness/2.0) * (float) -Math.cos((dir * Math.PI)/2);
if(offsetVal > -1.23/Math.pow(10, 16))
{
offsetVal = 0;
}
// generate the pixel colors we'll use for the side view
Pixmap sideProfile = new Pixmap(1, 64, Pixmap.Format.RGBA8888);
for (int y = 0; y < texCopy.getHeight(); y++)
{
for (int x = 0; x < texCopy.getWidth(); x++)
{
int colorInt = texCopy.getPixel(x, y);
if(new Color(colorInt).a != 0 && new Color(texCopy.getPixel(x + 1, y)).a == 0)
{
Color c = new Color(colorInt);
c.mul(.8f); // darken the color
c.a = 1;
colorInt = Color.rgba8888(c);
sideProfile.drawPixel(0, y, colorInt);
continue;
}
}
}
// drawing the bottom layer
p.drawPixmap(texCopy, 0, 0, 64, 64, (int) (Math.round(-offsetVal) + (64 - texCopy.getWidth()*Math.abs(dir))/2), 0, (int)(64*Math.abs(dir)), 64);
// drawing the middle (connecting) layer
// based on the edge pixels of the bottom layer, then translated to be in the middle
for (int y = 0; y < p.getHeight(); y++)
{
int colorInt = sideProfile.getPixel(0, y);
for (int x = 0; x < p.getWidth(); x++)
{
if(new Color(p.getPixel(x, y)).a != 0 && new Color(p.getPixel(x + 1, y)).a == 0)
{
for(int i = 0; i <= 2 * Math.round(Math.abs(offsetVal)); i++) // the for the length between the top and bottom
{
p.drawPixel(x + i - 2 * (int)Math.round(Math.abs(offsetVal)), y, colorInt);
}
}
}
}
// drawing the top layer
p.drawPixmap(texCopy, 0, 0, 64, 64, (int) (Math.round(offsetVal) + (64 - texCopy.getWidth()*Math.abs(dir))/2), 0, (int)(64*Math.abs(dir)), 64);
// flip if facing left
if(dir < 0)
{
p = flipPixmap(p);
}
return new Texture(p);
}
我的 flipPixmap
方法如下所示(从堆栈溢出中窃取):
private Pixmap flipPixmap(Pixmap src)
{
final int width = src.getWidth();
final int height = src.getHeight();
Pixmap flipped = new Pixmap(width, height, src.getFormat());
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
flipped.drawPixel(x, y, src.getPixel(width - x - 1, y));
}
}
return flipped;
}
这是结果:D https://imgur.com/a/wGeHg9D