如何在 C 中旋转精灵而不产生像素间隙

问题描述 投票:0回答:2

我正在用 C 语言制作一个游戏引擎,我有一个旋转函数,可以将图像旋转一定程度。但它有一些问题。我之前有坐标 x 和 y,使用旋转变换将它们转换为 nx, ny,然后分配 dest[x, y] = source[nx, ny] ,效果很好并且没有点效果。但这种方法因为它从转换后的坐标中读取,所以从源图像的宽度和高度之外的东西读取,从而进入垃圾内存。我可以通过检查 nx 或 ny 是否超过宽度或高度或小于零来轻松解决此问题。但是,这会在旋转过程中切断精灵的部分内容,因为有时图像的高度大于宽度,反之亦然,并且对角线更长等。我知道我可以调整图像大小以适应这一点,但这似乎效率低下。

Example image 这就是没有边界检查的图像。

我的新方法是从正常的源坐标中读取并在渲染到目标之前对其进行转换,因此大小不再是限制,因此:dest[nx, ny] = source[x, y]。它可以渲染任何尺寸的图像并旋转它而无需裁剪,但它会产生一种点状抖动效果,这种效果在 45 度角中更为常见,而在基本角处则消失。 Here's 那是什么样子的。

这是我的代码,其中第一个方法被注释掉了

void drawRotation(Sprite src, int xloc, int yloc, int degrees)
{//()
    float radians = Deg2Rad(degrees);
    float cosr = cos(radians);
    float sinr = sin(radians);
    uint32_t *dest = (uint32_t *)Screen.memory+ (xloc-src.ox) + (yloc-src.oy) * Screen.width;
    for(int y = 0; y < src.height; y++)
    {
        for(int x = 0; x < src.width; x++)
        {
            int nx = (cosr * (x-src.ox) + sinr * (y-src.oy));
            int ny = (-sinr *(x-src.ox) + cosr * (y-src.oy));
            dest[(nx+src.ox) + (ny+src.oy) * Screen.width] = ((uint32_t*)src.memory)[(y) * src.width + (x)];
            // dest[(x) + (y) * Screen.width] = ((uint32_t*)src.memory)[(ny+src.oy) * src.width + (nx+src.ox)];

        }
    }
}

我尝试了这两种方法,并且更喜欢点式方法,只要它没有点即可。但只要有效,我对其他方式持开放态度。我宁愿不对看似如此小的东西使用插值,但如果没有其他方法......

我正在使用wingdi32图形库。图像使用 malloc 存储,并且是 void 指针。当转换为整数时,颜色的格式为 ARGB。

c bitmap rotation game-engine image-rotation
2个回答
0
投票

问题是,您循环遍历源图像中的每个像素,但像素映射不是 1:1,这意味着它们在显示图像中的像素可能比源图像中的像素多,反之亦然。这意味着您必须预料到多次绘制的间隙和像素。

一种解决方案是循环遍历所有显示像素,将其旋转回源图像(即计算源图像上的位置),检查该像素是否在源图像上以及是否检查颜色。这应该可行,但不是最有效的方法,特别是当您有多个源图像生成一个显示图像时。

当您拥有多个图像时,更有效的方法是:将源图像分割成三角形(如果是矩形图像,您将制作 2 个三角形)。旋转每个顶点(边)并存储新位置。然后循环显示图像并检查像素是否在三角形内,如果是,则确定(如果超过 1 个)应使用哪个源图像/三角形,并且您应该能够计算出从当前显示像素到顶点的距离计算源图像。

使用低级图形 API(例如 OpenGL)(我假设 Vulcan 和 DirectX 是相同的)来显示 2D 和 3D 图形时,会执行类似的操作。如果您对其中任何一个都不熟悉,我建议您学习如何使用这样的 API 或类似的库。通过这种方式,您可以提高对计算机图形学工作原理、如何使用矩阵(因为您没有在代码中使用它们)的理解,...


0
投票

最简单的解决方案是在原始图像中的每对像素之间创建一个“幻影”像素,并旋转它。内部循环的半伪代码:

for(int x = 0; x < src.width; x++)
{
    int nx = (cosr * (x-src.ox) + sinr * (y-src.oy));
    int ny = (-sinr *(x-src.ox) + cosr * (y-src.oy));
    dest[(nx+src.ox) + (ny+src.oy) * Screen.width] = ((uint32_t*)src.memory)[(y) * src.width + (x)];
    if ((x+1) < src.width) {
        nx = (cosr * ((x-src.ox) + 0.5) + sinr * (y-src.oy));
        ny = (-sinr * ((x-src.ox) + 0.5) + cosr * (y-src.oy));
        dest[(nx+src.ox) + (ny+src.oy) * Screen.width] = AVERAGE(((uint32_t*)src.memory)[(y) * src.width + (x)],((uint32_t*)src.memory)[(y) * src.width + (x+1)]);
    }
    if ((y+1) < src.height) {
        nx = (cosr * (x-src.ox) + sinr * ((y-src.oy) + 0.5));
        ny = (-sinr *(x-src.ox) + cosr * ((y-src.oy) + 0.5));
        dest[(nx+src.ox) + (ny+src.oy) * Screen.width] = AVERAGE(((uint32_t*)src.memory)[(y) * src.width + (x)],((uint32_t*)src.memory)[(y) * src.width + (x+1)]);
    }
}

如果位图是单色的,则不必为幻象像素找到平均值,只需在相邻像素也已设置的情况下创建一个平均值即可。

© www.soinside.com 2019 - 2024. All rights reserved.