使用OpenGL从spritesheet渲染精灵?

时间:2009-10-14 19:49:06

标签: opengl sprite

想象一下以下场景:你有一组PNG格式的RPG角色spritesheets,你想在OpenGL应用程序中使用它们。

单独的字符(通常)大小为16 x 24像素(即24像素高),并且可以处于任何宽度和高度而不会留下填充。有点像这样:

Terra, from Final Fantasy VI
(来源:kafuka.org

我已经有了代码来确定给定帧索引和大小的基于整数的剪切矩形:

int framesPerRow = sheet.Width / cellWidth;
int framesPerColumn = sheet.Height / cellHeight;
framesTotal = framesPerRow * framesPerColumn;
int left = frameIndex % framesPerRow;
int top = frameIndex / framesPerRow;
//Clipping rect's width and height are obviously cellWidth and cellHeight.

使用frameIndex = 11, cellWidth = 16, cellHeight = 24运行此代码会返回剪裁(32, 24)-(48, 48),假设它的右/底与宽度/高度相对。

实际问题

现在,给定一个剪切矩形和一个X / Y坐标来放置精灵,我该如何在OpenGL中绘制它?首选 top 中的零坐标是首选。

4 个答案:

答案 0 :(得分:8)

你必须开始思考坐标在[0,1]范围内的“纹理空间”。

所以如果你有精灵表:

class SpriteSheet {
    int spriteWidth, spriteHeight;
    int texWidth, texHeight;

    int tex;

public:
    SpriteSheet(int t, int tW, int tH, int sW, int sH)
    : tex(t), texWidth(tW), texHeight(tH), spriteWidth(sW), spriteHeight(sH)
    {}

    void drawSprite(float posX, float posY, int frameIndex);
};

您所要做的就是将顶点纹理顶点提交给OpenGL:

    void SpriteSheet::drawSprite(float posX, float posY, int frameIndex) {
        const float verts[] = {
            posX, posY,
            posX + spriteWidth, posY,
            posX + spriteWidth, posY + spriteHeight,
            posX, posY + spriteHeight
        };
        const float tw = float(spriteWidth) / texWidth;
        const float th = float(spriteHeight) / texHeight;
        const int numPerRow = texWidth / spriteWidth;
        const float tx = (frameIndex % numPerRow) * tw;
        const float ty = (frameIndex / numPerRow + 1) * th;
        const float texVerts[] = {
            tx, ty,
            tx + tw, ty,
            tx + tw, ty + th,
            tx, ty + th
        };

        // ... Bind the texture, enable the proper arrays

        glVertexPointer(2, GL_FLOAT, verts);
        glTexCoordPointer(2, GL_FLOAT, texVerts);
        glDrawArrays(GL_TRI_STRIP, 0, 4);
    }

};

答案 1 :(得分:6)

Franks解决方案已经非常好了。

只是一个(非常重要的)旁注,因为有些评论建议不然。

请不要使用glBegin / glEnd。 不要告诉别人使用它。

唯一可以使用glBegin / glEnd的是你的第一个OpenGL程序。

阵列并不难处理,但是......

  • ......他们更快。
  • ......他们仍然可以使用更新的OpenGL版本。
  • ......他们将与GLES合作。
  • ...从文件加载它们要容易得多。

答案 2 :(得分:0)

我假设你正在学习OpenGL,只需要以某种方式让它工作。如果你需要原始速度,那就是着色器和顶点缓冲区以及各种简洁和复杂的东西。

最简单的方法是将PNG加载到纹理中(假设您能够将图像加载到内存中,确实需要htat),然后使用四边形设置适当的纹理坐标(它们从0到1)浮点坐标,因此您需要相应地划分纹理宽度或高度)。

使用glBegin(GL_QUADS),glTexcoord2f(),glVertex2f(),glEnd()以最简单(但不是最快)的方式绘制它。

为了使左上角为零,使用gluOrtho()设置视图矩阵与正常GL不同(查找该函数的文档,将top设置为0,将bottom设置为1或如果需要整数coords则设置screen_height)或只需更改你的绘图循环,然后执行glVertex2f(x / screen_width,1-y / screen_height)。

有更好更快的方法可以做到这一点,但如果您从头开始学习原始OpenGL,这可能是最简单的方法之一。

答案 3 :(得分:0)

如果可以的话,建议。我使用SDL加载我的纹理,所以我做的是: 我装了纹理 我决定如何将精灵表分成单独的精灵。 我将它们分成不同的表面 4.我为每个人制作一个纹理(我有一个精灵类来管理它们)。 5.释放表面。 这显然需要更多时间(显然)加载,但需要付费。 这样,它更容易(也更快),因为您只需要计算要显示的纹理的索引,然后显示它。然后,您可以根据需要缩放/翻译它,并调用显示列表将其呈现为您想要的任何内容。或者,您可以在立即模式下执行此操作:)