要說明datenwolf的答案,3D空間和2D畫布之間的座標映射正是您想要的。你gl.viewport
控制它,你傳遞給着色器的矩陣。
gl.viewport
只是簡單地屏蔽了您正在繪製的畫布上的一個矩形像素。大多數情況下,這與您的畫布的尺寸完全匹配,但有些情況下您只想繪製其中的一部分。 (分屏遊戲,例如。)你的畫布,你畫從這裏走出的視口將被稱爲區域。如果你願意,你可以認爲它和「帆布」意思一樣。
在最簡單的情況下,視口始終在X軸和Y軸上都有一個從-1到1的隱式座標系。這是頂點着色器輸出的gl_Position
的空間。如果在(-1,-1)處輸出頂點,它將位於視口的左下角。 (1,1)處的頂點將位於右上角。 (是的,我現在忽略了深度)使用這個,你可以構造幾何圖形來映射到那個空間,並且在沒有任何矩陣變換的情況下繪製它,但這可能有點尷尬。
爲了使生活更輕鬆,我們使用投影矩陣。投影矩陣只是將幾何圖形從某個任意3D空間轉換爲視口所需的-1到1空間的投影矩陣。最常見的是透視矩陣。你如何創建它看起來有點不同,這取決於您使用的庫,但通常是這樣的:
var fov = 45;
var aspectRatio = canvas.width/canvas.height;
var near = 1.0;
var far = 1024.0;
var projectionMat = mat4.perspective(fov, aspect, near, far);
我不打算進入什麼所有這些值的含義,但你可以清楚地看到,我們使用畫布寬度和高度來幫助設置此投影。這可以使其不會看到拉伸或壓扁取決於畫布的大小。然而,要理解的是,在空間中取任意3D點並乘以該矩陣將產生一個映射到-1到1空間的點,並考慮與「相機」和其他所有物體的距離。 (它可能實際上超出了這個界限,但這僅僅意味着它不在相機中。)這就是我們的3D場景看起來像3D的原因。但是,也可以創建專門用於繪製2D幾何的投影矩陣。這就是所謂的正交矩陣,設置通常看起來是這樣的:
var left = 0;
var top = 0;
var right = canvas.width;
var bottom = canvas.height;
var near = 1.0;
var far = 1024.0;
var projectionMat = mat4.ortho(left, right, bottom, top, near, far);
這個矩陣是比它忽略你的位置的z分量完全透視矩陣不同。相反,此矩陣將平面座標(如像素)轉換爲-1到1的範圍。因此,您的場景看起來並不是3D,但更容易控制屏幕上顯示的事物的確切位置。因此,使用上面的矩陣,如果我們給它一個頂點(16,16,0),它將出現在我們畫布上的(16,16)(假設視口與畫布尺寸相同)。因此,當你想繪製像平面UI元素的東西時,這就是你想要的矩陣類型!
好的部分是,因爲這些只是您傳遞給着色器的值,您可以使用完全不同的矩陣從一個繪製調用到下一個。通常,您將使用透視矩陣繪製所有3D幾何圖形,然後使用正交矩陣繪製所有UI。
道歉,如果這有點散漫。我從來都不擅長解釋所有這些數學問題。
感謝您的長篇解釋更新。我知道GL視口及其映射到[-1,-1]。我可以看到使用正交投影矩陣可以有助於在「2d」中進行渲染。然而,這會弄亂我所有的其他對象。也許我應該改寫一下我的問題:如何將世界轉換爲屏幕座標? – Tom
它會如何混淆你的其他對象?您將繼續使用您的標準透視矩陣呈現它們。您只需要爲2D元素使用正交矩陣。 – Toji
那麼,這將需要我不斷改變視角矩陣,我對此並不滿意。 此外,表示軸的「箭頭」的模型仍然是3d而不是2d。 – Tom