YUAN
一种高效实现像素伪3D的方案实践.txt
Date: 2026/8/30Tags: 游戏开发图形学Shader

常规的做法

最近在开发SuperReap时,我需要实现一个吹风机在游戏中根据玩家的操控方向进行旋转的功能。而我们的游戏是一个2D像素等距视角游戏,对于这个需求来说,最常规的做法是针对不同方向的视角进行单独的美术素材的绘制。但是显然这不是一个聪明的做法。如果有美术表现需要变更,那么每一帧都需要进行修改,十分麻烦。

吹风机旋转演示


Sprite Stacking

通过调研,发现了一种叫作Sprite Stacking的方法来十分高效的实现。Sprite Stacking的本质就是利用一系列相互叠加的精灵图像,通过适当的偏移和旋转来营造出深度和立体的感觉。

Sprite Stacking 示例

你看到的这个模型并不是3D模型,尽管他看起来确实是3D的效果。实际上他是通过堆叠多个小图像来营造出有高度感的视觉效果

分层结构合成后的效果
分层结构合成后的效果

像素化的处理

前面提到,这是一个2D等距像素的游戏,而在上图所涉及到的一层层图像的四边是平直的,在游戏中旋转后就会有违和的感觉。我们需要让图像旋转后,还要有像素风格的锯齿边,所以还需要进行一步处理。

像素画处理前像素化 Shader 处理后
像素画处理前像素化 Shader 处理后

方案

这里我的方案是使用一个像素化的Shader

在进行旋转操作时,并非旋转图片,旋转的逻辑全都放到片元着色器中,而是从原图直接采样,计算某一个像素点在旋转后,该从原图哪一格获取颜色。

参考代码

fixed4 frag(v2f input) : SV_Target
{
    float2 outputUV = (input.texcoord - _SpriteUVRect.xy) / _SpriteUVRect.zw;
    float2 outputPixel = _Pixelate > 0.5
        ? min(floor(outputUV * _OutputPixelSize), _OutputPixelSize - 1.0) + 0.5
        : outputUV * _OutputPixelSize;
    float2 centeredPixel = outputPixel - _OutputPixelSize * 0.5;

    float sine;
    float cosine;
    sincos(_RotationRadians, sine, cosine);
    float2 sourceOffset = float2(
        cosine * centeredPixel.x + sine * centeredPixel.y,
        -sine * centeredPixel.x + cosine * centeredPixel.y);
    float2 sourcePixel = sourceOffset + _SourcePixelSize * 0.5;

    if (sourcePixel.x < 0.0 || sourcePixel.y < 0.0 ||
        sourcePixel.x >= _SourcePixelSize.x || sourcePixel.y >= _SourcePixelSize.y)
    {
        return 0;
    }

    float2 sampledPixel;
    if (_Pixelate > 0.5)
    {
        float blockSize = max(1.0, floor(_PixelBlockSize + 0.5));
        sampledPixel = floor(sourcePixel / blockSize) * blockSize + blockSize * 0.5;
        sampledPixel = min(sampledPixel, _SourcePixelSize - 0.5);
    }
    else
    {
        sampledPixel = sourcePixel;
    }

    float2 sampledUV = _SpriteUVRect.xy + sampledPixel / _SourcePixelSize * _SpriteUVRect.zw;
    fixed4 color = SampleSpriteTexture(sampledUV) * input.color;
    color.rgb *= color.a;
    return color;
}

适用的范围

局限: 在等距视角的2D游戏中,对静态的图像进行堆叠,让开发者可以更自由的调节,随心所欲的展示某个物体的不同角度,并且能够节省大量的时间。但如果你的制作对象依赖精美的逐帧动画,那么这种方式就不好处理,比如说对于游戏中的各种角色,他们往往都有着复杂的动画动作,还会在不同的状态之间切换,使用这种方式反而不好处理。


关于 SuperReap

本文的吹风机旋转效果,来自正在开发的像素风等距视角游戏 SuperReap(超级收割)。Steam 已开放 Demo 试玩,预计 2026 冬季发售——欢迎 加入愿望单

Published: 2026/8/30