跳轉到內容

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 程式設計

瀏覽並下載 完整程式碼
華夏公益教科書