原博客: http://blog.csdn.net/wangdingqiaoit/article/details/51594408
通過本節可以瞭解到
- 座標變換的各個階段
- 利用GLM數學庫實現座標變換
座標變換的全局圖
OpenGL中的座標處理過程包括模型變換、視變換、投影變換、視口變換等過程,如下圖所示:
在上面的圖中,注意,OpenGL只定義了裁剪座標系、規範化設備座標系和屏幕座標系,而局部座標系(模型座標系)、世界座標系和照相機座標系都是爲了方便用戶設計而自定義的座標系,它們的關係如下圖所示(來自Chapter 7. World in Motion):
圖中左邊的過程包括模型變換、視變換,投影變換,這些變換可以由用戶根據需要自行指定,這些內容在頂點着色器中完成;而圖中右邊的兩個步驟,包括透視除法、視口變換,這兩個步驟是OpenGL自動執行的,在頂點着色器處理後的階段完成。
各個變換階段的理解
下面分別對每個階段的變換做一個總結,以幫助理解。
模型變換——從模型座標系到世界座標系
局部座標系(模型座標系)是爲了方便構造模型而設立的座標系,建立模型時我們無需關心最終對象顯示在屏幕哪個位置。模型的原點定位也可以有所不同,例如下面在模型座標系定義的模型:
模型變換的主要目的是通過變換使得用頂點屬性定義或者3d建模軟件構造的模型,能夠按照需要,通過縮小、平移等操作放置到場景中合適的位置。通過模型變換後,物體放置在一個全局的世界座標系中,世界座標系是所有物體交互的一個公共座標系。例如下面的圖中在模型座標系定義的茶壺模型(來自World, View and Projection Transformation Matrices):
茶壺通過模型變換,轉換到世界座標系中(來自World, View and Projection Transformation Matrices):
模型變換包括:旋轉、平移、縮放、錯切等內容。例如將物體從一個位置
應用多個模型變換時,注意變換執行的順序影響變換的結果,一般按照縮放–》旋轉—》平移的順序執行;另外,注意旋轉和縮放變換的不動點問題。這些內容在模型變換一節已經介紹了,這裏不再贅述。利用GLM數學庫實現模型變換,例如平移變換示例代碼爲:
glm::mat4 model; // 構造單位矩陣
model = glm::translate(model, glm::vec3(0.0f, 0.0f,-0.5f));
視變換——從世界座標系到相機座標系
視變換是爲了方便觀察場景中物體而設立的座標系,在這個座標系中相機是個假想的概念,是爲了便於計算而引入的。相機座標系中的座標,就是從相機的角度來解釋世界座標系中位置。相機和場景的示意圖如下所示(來自World, View and Projection Transformation Matrices):
OpenGL中相機始終位於原點,指向-Z軸,而以相反的方式來調整場景中物體,從而達到相同的觀察效果。例如要觀察-z軸方向的一個立方體的右側面,可以有兩種方式:
- 立方體不動,讓相機繞着+y軸,旋轉+90度,此時相機鏡頭朝向立方體的右側面,實現目的。完成這一旋轉的矩陣記作
Ry(π2) - 相機不動,讓立方體繞着+y軸,旋轉-90度,此時也能實現同樣的目的。注意這時相機沒有轉動。完成這一旋轉的矩陣記作
Ry(−π2)
OpenGL中採用方式2的觀點來解釋視變換。再舉一個例子,比如,一個物體中心位於原點,照相機也位於初始位置原點,方向指向-Z軸。爲了對物體的+Z面成像,那麼必須將照相機從原點移走,如果照相機仍然指向-Z軸,需要將照相機沿着+Z軸方向後退。假若照相機不移動,我們可以通過將物體沿着-Z軸後退d個單位,則變換矩陣爲:
通過在世界座標系中指定相機的位置,指向的目標位置,以及viewUp向量來構造一個相機座標系,通過視變換矩陣將物體座標由世界座標系轉換到相機座標系,視變換矩陣的推導已經在視變換一節介紹,感興趣地可以去參考。利用GLM數學庫實現視變換的代碼爲:
glm::mat4 view = glm::lookAt(eyePos,
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f));
投影變換——從世界座標系到裁剪座標系
投影方式決定以何種方式成像,投影方式有很多種,OpenGL中主要使用兩種方式,即透視投影(perspective projection)和正交投影( orthographic projection)。
1.正交投影是平行投影的一種特殊情形,正交投影的投影線垂直於觀察平面。平行投影的投影線相互平行,投影的結果與原物體的大小相等,因此廣泛地應用於工程製圖等方面。
2.透視投影的投影線相交於一點,因此投影的結果與原物體的實際大小並不一致,而是會近大遠小。因此透視投影更接近於真實世界的投影方式。
兩者的示意圖如下:
在OpenGL中成像時的效果如下所示(圖片來自Modern OpenGL):
上面的圖中,紅色和黃色球在視見體內,因而呈現在投影平面上,而綠色球在視見體外,沒有在投影平面上成像。指定視見體通過(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal)6個參數來指定。注意在相機座標系下,相機指向-z軸,nearVal和farVal表示的剪裁平面分別爲:近裁剪平面
使用
glOrtho(xleft, xright, ybottom, ytop, znear, zfar);
或者類似API指定正交投影,參數意義形象表示爲下圖所示(來自World, View and Projection
Transformation Matrices):
使用
void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal);
void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
或者類似的API指定透視投影的視見體,其參數含義如下圖所示(來自World, View and Projection Transformation Matrices):
關於投影矩陣的推導,可以參考前面的投影矩陣和視口變換矩陣一節。利用GLM數學庫實現視透視投影變換的代碼示例爲:
glm::mat4 projection = glm::perspective(glm::radians(45.0f),
(GLfloat)(WINDOW_WIDTH)/ WINDOW_HEIGHT, 1.0f, 100.0f);
經過投影變換後,物體座標變換到了裁剪座標系,經過OpenGL自動執行的透視除法後,變換到規範化設備座標系中。透視除法就是將裁剪座標系中座標都除以
視口變換——從NDC到屏幕座標
視變換是將規範化設備座標(NDC)轉換爲屏幕座標的過程,如下圖所示:
視口變化通過函數:
glViewport(GLint
glDepthRangef(GLclampf
兩個函數來指定。其中(
座標變換的計算過程
上述過程從座標計算角度來看,如下圖所示:
座標變換的程序實現
在程序中,我們需要在兩個部分做處理。
第一,編寫頂點着色器程序如下:
#version 330
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
layout(location = 2) in vec2 textCoord;
out vec3 VertColor;
out vec2 TextCoord;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);
VertColor = color;
TextCoord = textCoord;
}
第二,在主程序中傳遞變換矩陣到頂點着色器,並繪製場景中物體,代碼如下:
// 投影矩陣
glm::mat4 projection = glm::perspective(glm::radians(45.0f),
(GLfloat)(WINDOW_WIDTH)/ WINDOW_HEIGHT, 1.0f, 100.0f);
// 視變換矩陣
glm::mat4 view = glm::lookAt(eyePos,
glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
// 模型變換矩陣
glm::mat4 model = glm::mat4();
model = glm::translate(model, glm::vec3(-0.25f, 0.0f, 0.0f));
// 使用uniform變量傳遞MVP矩陣
glUniformMatrix4fv(
glGetUniformLocation(shader.programId, "projection"),
1, GL_FALSE, glm::value_ptr(projection)); // 傳遞投影矩陣
glUniformMatrix4fv(
glGetUniformLocation(shader.programId, "view"),
1, GL_FALSE, glm::value_ptr(view));// 傳遞視變換矩陣
glUniformMatrix4fv(
glGetUniformLocation(shader.programId, "model"),
1, GL_FALSE, glm::value_ptr(model)); // 傳遞模型變換矩陣
// 繪製物體
glDrawArrays(GL_TRIANGLES, 0, 36);
例如利用圓形座標系指定相機位置,繪製的立方體如下圖所示:
參考資料
1.World, View and Projection Transformation Matrices
2.GLSL Programming/Vertex Transformations
相關資源
1.OpenGL 101: Matrices - projection, view,
model
2.songho OpenGL Transformation
3.The
Perspective and Orthographic Projection Matrix
4.songho OpenGL Projection Matrix
5. glOrtho
6. glFrustum
7. gluPerspective
8. gluLookAt