OpenGL 编程/现代 OpenGL 教程 2D
即使您不打算制作 3D 游戏,并且坚持使用 2D,OpenGL 仍然会提供宝贵的工具。
在过去,2D 图形卡通过允许程序员将位图和精灵直接存储在卡中,以及一些用于执行基本复制(位块传输)的原语来提供硬件加速,这些复制操作可以带或不带 alpha 混合。
如今,这些功能正在被 OpenGL 及其(更通用的)纹理所取代。在 OpenGL 中进行 2D 编程基本上是显示面向屏幕的纹理,其 z 坐标始终设置为 0。这还引入了 2D 加速的急需标准化(例如,在 GNU/Linux + X11 下几乎无法获得 2D 加速)。
这种技术被几个图形库使用,包括 SFML、ClanLib 或 Gnash。
我们将使用正投影矩阵,其中没有透视(远处的物体看起来和近处的物体一样大 - 您可能已经在技术制图中或通过在 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
不使用 mipmapGL_TEXTURE_WRAP_S
和GL_TEXTURE_WRAP_T
都设置为GL_CLAMP_TO_EDGE
否则纹理将始终返回黑色[1]。
让我们对我们的纹理做同样的事情。
为了以最简单的方式将纹理“位块传输”到 OpenGL 缓冲区,可以使用带有纹理的一对三角形进行绘制
/* code here */
但是,您可以通过不调用 glClear
并使用脏矩形等技术来执行增量显示更新 - 尽管现在 GPU 通常足够快,可以避免实现这种优化。
帧缓冲区/渲染缓冲区/重复使用作为纹理/…?
待办事项
由于我们在 2D 中工作,我们可以移除
- 背面剔除是不必要的
- 深度测试是不必要的
我们可以移除顶点中的 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.));
待办事项:提供一个例子
注意:这似乎仅限于绘制基本图形,我在操作纹理时无法重现任何问题。
- 来自 OpenGL 维基教科书示例的“2d”:https://gitlab.com/wikibooks-opengl/modern-tutorials/tree/master/2d
- GLtron 项目中 2D 位块传输的简单实现
- GNU FreeDink 项目中带有可选调色板模拟的 2D 位块传输的 AGPL 许可实现
- SDL2 和 SFML 都提供了一个 OpenGL 加速的 2D API。
- ↑ "glTexParameter". Khronos.org. Retrieved 2015-08-19.
- ↑ "OpenGL ES 通用配置文件规范版本 2.0.25" (PDF). Khronos.org. 2010-11-02. 检索于 2011-11-12. - 第 3.4.1 节 基本线段光栅化
- ↑ "OpenGL 常见问题解答和故障排除指南 v1.2001.11.01 - 9.030 如何在我的 3D 渲染之上绘制 2D 控件?". 检索于 2011-11-12. - 如果需要精确的像素化,您可能需要在模型视图矩阵中添加一个小的平移