跳转到内容

OpenGL 编程/现代 OpenGL 教程 2D

来自维基教科书,开放世界中的开放书籍

即使您不打算制作 3D 游戏,并且坚持使用 2D,OpenGL 仍然会提供宝贵的工具。

硬件加速

[编辑 | 编辑源代码]

在过去,2D 图形卡通过允许程序员将位图和精灵直接存储在卡中,以及一些用于执行基本复制(位块传输)的原语来提供硬件加速,这些复制操作可以带或不带 alpha 混合。

如今,这些功能正在被 OpenGL 及其(更通用的)纹理所取代。在 OpenGL 中进行 2D 编程基本上是显示面向屏幕的纹理,其 z 坐标始终设置为 0。这还引入了 2D 加速的急需标准化(例如,在 GNU/Linux + X11 下几乎无法获得 2D 加速)。

这种技术被几个图形库使用,包括 SFML、ClanLib 或 Gnash。

设置 2D 空间

[编辑 | 编辑源代码]

我们将使用正投影矩阵,其中没有透视(远处的物体看起来和近处的物体一样大 - 您可能已经在技术制图中或通过在 Blender 中键入 Numpad 5 看到过这一点)。

GLM 提供 glm::ortho 来计算这种投影。由于我们将直接操作像素,让我们使用物理屏幕的像素大小,而不是之前使用的 [-1, 1]。

此外,我们已经看到 OpenGL 的垂直轴是从下到上的,而传统的 2D 屏幕是从上到下的,所以让我们反转 Y 轴

  // glm::ortho(left, right, bottom, top, [zNear, zFar])
  glm::mat4 projection = glm::ortho(0, screen_width,
                                    screen_height, 0);

但是,由于我们正在进行现代编程,我们不应该忘记旧式的 2D 坐标吗?事实是,大多数图形格式也是从上到下的。TGA 和 BMP 是从下到上的,但几乎所有其他格式,尤其是格式库(JPG、PNG 等)都会为您提供从上到下的图片。我们在纹理教程中看到 OpenGL 对纹理使用从下到上的方式,因此它会将它们上下颠倒地显示!

反转 OpenGL 屏幕意味着我们的图片可以按原样上传到 OpenGL 图形卡,并以正确的方向显示。另一种方法是反转纹理坐标。

唯一需要注意的是,围绕 Z 轴的旋转角度也需要反转。

但也许反转坐标的主要原因是大多数用户期望 Y 坐标从上到下:例如检查 Gimp 和 Dia;还要检查其他 2D 游戏框架和库。一个值得注意的例外是 Inkscape(矢量绘图),其坐标从左下角开始,就像 OpenGL 一样。

上传纹理

[编辑 | 编辑源代码]

图形卡过去有奇特的限制,例如只允许使用 2 的幂尺寸。

在 OpenGL ES 2 中,只有在以下情况下才允许使用非 2 的幂纹理

  • GL_TEXTURE_MIN_FILTER 不使用 mipmap
  • GL_TEXTURE_WRAP_SGL_TEXTURE_WRAP_T 都设置为 GL_CLAMP_TO_EDGE

否则纹理将始终返回黑色[1]

让我们对我们的纹理做同样的事情。

显示精灵

[编辑 | 编辑源代码]

为了以最简单的方式将纹理“位块传输”到 OpenGL 缓冲区,可以使用带有纹理的一对三角形进行绘制

/* code here */

但是,您可以通过不调用 glClear 并使用脏矩形等技术来执行增量显示更新 - 尽管现在 GPU 通常足够快,可以避免实现这种优化。

在纹理上进行位块传输

[编辑 | 编辑源代码]

帧缓冲区/渲染缓冲区/重复使用作为纹理/…?

待办事项

针对 2D 进行优化

[编辑 | 编辑源代码]

由于我们在 2D 中工作,我们可以移除

  • 背面剔除是不必要的
  • 深度测试是不必要的

移除 z 坐标

[编辑 | 编辑源代码]

我们可以移除顶点中的 z 坐标以节省空间。保留 w 坐标为 1,以便我们可以使用变换矩阵。

说到变换矩阵,GLM 只提供 4x4 变换矩阵。为了将变换矩阵从 4x4 缩减为 3x3,我们可以

  • 编程一组新的函数来使用 3x3 矩阵。
  • 删除第 3 行和第 3 列

这是一个 3D 仿射矩阵

xx  yx  zx  tx
xy  yy  zy  ty
xz  yz  zz  tz
0   0   0   1

这是一个 2D 仿射矩阵

xx  yx  tx
xy  yy  ty
0   0   1

我们可以将 3D->2D 转换编码为

#define GLM_SWIZZLE
#include <glm/glm.hpp>
...
glm::mat3 mvp2D(mvp[0].xyw(), mvp[1].xyw(), mvp[3].xyw());

gl_Position 仍然是一个 vec4,所以

  gl_Position = mvp * vec4(v_coord, 1.0);

v_coord.z 的值并不重要,因为无论我们使用哪个 z,正投影视图始终以相同的方式显示纹理。

缩放整个屏幕

[编辑 | 编辑源代码]

您可以通过调整投影来执行缩放效果。例如,这是一个逐步的缩小效果

  float scale = glutGet(GLUT_ELAPSED_TIME) / 1000.0 * .2;  // 20% per second
  glm::mat4 projection = glm::ortho(0.0f, 1.0f*screen_width*scale, 1.0f*screen_height*scale, 0.0f);

精确/完美像素化

[编辑 | 编辑源代码]

OpenGL 有一个特殊的规则来在像素屏幕的中心绘制片段,称为“菱形退出规则”[2] [3]

因此,建议在绘制 2D 线之前在 X、Y 中添加一个小小的平移,这样就不会错过最后一个像素

glm::translate(glm::mat4(1), glm::vec3(0.375, 0.375, 0.));

待办事项:提供一个例子

注意:这似乎仅限于绘制基本图形,我在操作纹理时无法重现任何问题。

参考文献

[编辑 | 编辑源代码]
  1. "glTexParameter". Khronos.org. Retrieved 2015-08-19.
  2. "OpenGL ES 通用配置文件规范版本 2.0.25" (PDF). Khronos.org. 2010-11-02. 检索于 2011-11-12. - 第 3.4.1 节 基本线段光栅化
  3. "OpenGL 常见问题解答和故障排除指南 v1.2001.11.01 - 9.030 如何在我的 3D 渲染之上绘制 2D 控件?". 检索于 2011-11-12. - 如果需要精确的像素化,您可能需要在模型视图矩阵中添加一个小的平移

< OpenGL 编程

浏览并下载 完整代码
华夏公益教科书