使用 OpenGL 的 2D Sprite 动画技术

Posted

技术标签:

【中文标题】使用 OpenGL 的 2D Sprite 动画技术【英文标题】:2D Sprite animation techniques with OpenGL 【发布时间】:2015-03-01 02:02:42 【问题描述】:

我目前正在尝试使用 OpenGL 4 设置 2D 精灵动画。 例如,我设计了一个使用 Gimp 平滑旋转的球。大约有 32 帧(4 行 8 帧)。

我的目标是在 2D 纹理中创建一个精灵图集,并将我的精灵数据存储在缓冲区 (VBO) 中。我的精灵矩形将始终相同(即 rect(0,0,32,32) ),但每次增加帧索引时我的纹理坐标都会改变。

不知如何修改坐标。

    由于精灵图块存储在多行中,如果在着色器中看起来难以管理它。 使用 glBufferSubData() 修改缓冲区内的精灵纹理坐标?

我在 OpenGL 1.x 上花了很多时间......几个月前我又回到了 OpenGL,但我意识到很多事情都发生了变化。我会尝试几种选择,但欢迎您的建议和经验。

【问题讨论】:

【参考方案1】:

因为精灵图块存储在几行中,如果看起来是 很难在着色器中管理它。

不是真的,你所有的精灵都是一样的大小,所以你得到了一个完美的统一网格,从一维索引到二维只是一个除法和模数的问题。并不难。

但是,您为什么还要将单个帧存储在 mxn 网格中?现在您可以将它们存储在一行中。但是,在现代 GL 中,我们有 array textures。这些基本上是一组独立的 2D 层,大小都相同。您只需通过 3D 坐标访问它们,第三个坐标是从 o 到 n-1 的层。这非常适合您的用例,并且可以消除边界处的纹理过滤/出血的任何问题,并且它也适用于 mipmapping(如果您需要的话)。当引入阵列纹理时,实现需要支持的最小层数是 64(现在要高得多),因此即使对于旧 GPU,32 帧也是小菜一碟。

【讨论】:

【参考方案2】:

你可以用一百万种方法做到这一点,但我要提出一个幼稚的解决方案:

创建一个具有 32(框架正方形)*2(三角形每框架正方形)*3(三角形顶点)*5(x、y、z、u、v 每个顶点)= 960 个空间的 VBO。以每帧 2 个三角形的方式填充所有精灵的顶点。

现在根据glDrawArrays 的文档,您可以指定从哪里开始以及渲染多长时间。使用它,您可以指定以下内容:

int indicesPerFrame = 960/32;
int indexToStart = indicesPerFrame*currentBallFrame;
glDrawArrays( GL_TRIANGLES, indexToStart, indicesPerFrame);

无需修改 VBO。现在从我的角度来看,一次只渲染 32 帧 1 帧是多余的。这个问题有更好的解决方案,但这是学习OpenGL4最简单的。

【讨论】:

感谢 sgtHale。这是我计划实施的解决方案的一部分:)【参考方案3】:

在 OpenGL 2.1 中,我使用的是您的第二个选项:

void setActiveRegion(int regionIndex)

    UVs.clear();

    int numberOfRegions = (int) textureSize / spriteWidth;

    float uv_x = (regionIndex % numberOfRegions)/numberOfRegions;
    float uv_y = (regionIndex / numberOfRegions)/numberOfRegions;

    glm::vec2 uv_up_left    = glm::vec2( uv_x                     , uv_y );
    glm::vec2 uv_up_right   = glm::vec2( uv_x+1.0f/numberOfRegions, uv_y );
    glm::vec2 uv_down_right = glm::vec2( uv_x+1.0f/numberOfRegions, (uv_y + 1.0f/numberOfRegions) );
    glm::vec2 uv_down_left  = glm::vec2( uv_x                     , (uv_y + 1.0f/numberOfRegions) );


    UVs.push_back(uv_up_left   );
    UVs.push_back(uv_down_left );
    UVs.push_back(uv_up_right  );

    UVs.push_back(uv_down_right);
    UVs.push_back(uv_up_right);
    UVs.push_back(uv_down_left);

    glBindBuffer(GL_ARRAY_BUFFER, uvBuffer);
    glBufferSubData(GL_ARRAY_BUFFER, 0, UVs.size() * sizeof(glm::vec2), &UVs[0]);
    glBindBuffer(GL_ARRAY_BUFFER, 0);


来源:http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-11-2d-text/

他实现了它来渲染 2D 文本,但它是相同的概念!

希望对你有所帮助!

【讨论】:

以上是关于使用 OpenGL 的 2D Sprite 动画技术的主要内容,如果未能解决你的问题,请参考以下文章

在 cocos2d 中使用 opengl-es 进行圆形裁剪

OpenGL glBindTexture() 崩溃

unity2021的sprite在哪

Simple2D-24 Sprite 渲染树

OpenGL sprite 以低质量绘制

Cocos2d-x入门之旅[3]动作