You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

软件渲染器旋转精灵时出现棋盘格效果的问题咨询

2D软件渲染器旋转精灵出现棋盘格错位问题

我正在编写一款2D软件渲染器,当将精灵旋转到特定角度时,像素会出现类似棋盘格的错位重叠效果(见动图:旋转错位问题)。我尝试过对旋转后的像素坐标进行取整、向下取整、向上取整操作,但都无法解决问题。看起来像素被放置到了错误位置并相互重叠,我推测可能是精度问题,但不确定具体原因。

相关代码片段

uint width = image.width;
uint height = image.height;

uint originX = width / 2;
uint originY = height;

float radians = angleDegrees * PI / 180.0f;

float sine = sin(radians);
float cosine = cos(radians);
for (uint r = 0; r < height; r++)
{
    for (uint p = 0; p < width; p++)
    {
        int translatedR = r - originY;
        int translatedP = p - originX;

        int rotatedR = round(translatedP * sine + translatedR * cosine) + originY;
        int rotatedP = round(translatedP * cosine - translatedR * sine) + originX;

        size_t index = (x + rotatedP + (frameBufferWidth * y)) + (frameBufferWidth * rotatedR);
        size_t index2 = p + (image.width * r);

        if (!image.pixels[index2].a)
            continue;
        
        frameBuffer[index].r = image.pixels[index2].r;
        frameBuffer[index].g = image.pixels[index2].g;
        frameBuffer[index].b = image.pixels[index2].b;
    }
}

问题根源与解决办法

  • 正向映射导致重叠/空隙:你当前是遍历原图像素,把每个像素旋转后写入帧缓冲。这种方式会造成多个原像素映射到同一目标像素(重叠覆盖),同时部分目标像素没有被任何原像素覆盖(留下空隙),这就是棋盘格效果的核心原因。正确做法是反向映射:遍历帧缓冲中精灵旋转后的目标区域,计算每个目标像素对应的原图像位置,再采样颜色填充。

  • 旋转原点设置异常:originY = height 将旋转原点设在了图像底部边缘,这大概率不是预期位置。改成 originY = height / 2,把原点设为图像中心,能避免旋转后出现不必要的偏移。

  • 帧缓冲索引计算错误:当前的 index 计算公式逻辑混乱,正确的帧缓冲索引应该是 (y + rotatedR) * frameBufferWidth + (x + rotatedP)(假设x、y是精灵在帧缓冲的起始坐标)。你当前的写法会导致坐标叠加错误,进一步加剧像素错位。

  • 采样方式优化:反向映射时,用最近邻采样(取整)可以保证每个目标像素都能找到对应原像素;如果想要更平滑的效果,可以用双线性采样,对原图像相邻四个像素插值计算颜色。

反向映射思路示例代码

// 计算旋转后的精灵尺寸(实际需精确计算包围盒,此处简化)
int rotatedWidth = static_cast<int>(width * fabs(cosine) + height * fabs(sine));
int rotatedHeight = static_cast<int>(height * fabs(cosine) + width * fabs(sine));
int rotatedOriginX = rotatedWidth / 2;
int rotatedOriginY = rotatedHeight / 2;

// 遍历目标帧缓冲区域
for (int destY = 0; destY < rotatedHeight; destY++) {
    for (int destX = 0; destX < rotatedWidth; destX++) {
        // 将目标坐标转换到旋转原点坐标系
        int tx = destX - rotatedOriginX;
        int ty = destY - rotatedOriginY;

        // 反向旋转(角度取负等价于用逆矩阵)
        float invSin = sin(-radians);
        float invCos = cos(-radians);
        float srcX = tx * invCos - ty * invSin + originX;
        float srcY = tx * invSin + ty * invCos + originY;

        // 检查原图像坐标是否有效
        if (srcX >= 0 && srcX < width && srcY >= 0 && srcY < height) {
            // 最近邻采样
            uint srcXInt = static_cast<uint>(round(srcX));
            uint srcYInt = static_cast<uint>(round(srcY));
            size_t srcIndex = srcXInt + srcYInt * width;
            size_t destIndex = (y + destY) * frameBufferWidth + (x + destX);
            
            if (image.pixels[srcIndex].a > 0) {
                frameBuffer[destIndex] = image.pixels[srcIndex];
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Nasir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 05:26:17