转动时为 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, 01, 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