http://www.tuicool.com/articles/uiayYrI
OpenGL學習腳印: 坐標變換過程(vertex transformation)
http://blog.csdn.net/wangdingqiaoit/article/details/51594408
寫在前面
前面幾節分別介紹了模型變換,視變換,以及給出了投影矩陣和視口變換矩陣的推導,本節從全局把握一遍OpenGL坐標轉換的過程,從整體上認識坐標變換過程。相關矩陣的數學推導過程請參考前面幾節對應的內容。
通過本節可以了解到
- 坐標變換的各個階段
- 利用GLM數學庫實現坐標變換
坐標變換的全局圖
OpenGL中的坐標處理過程包括模型變換、視變換、投影變換、視口變換等過程,如下圖所示:
在上面的圖中,注意,OpenGL只定義了裁剪坐標系、規范化設備坐標系和屏幕坐標系,而局部坐標系(模型坐標系)、世界坐標系和照相機坐標系都是為了方便用戶設計而自定義的坐標系,它們的關系如下圖所示(來自Chapter 7. World in Motion):
圖中左邊的過程包括模型變換、視變換,投影變換,這些變換可以由用戶根據需要自行指定,這些內容在頂點着色器中完成;而圖中右邊的兩個步驟,包括透視除法、視口變換,這兩個步驟是OpenGL自動執行的,在頂點着色器處理后的階段完成。
各個變換階段的理解
下面分別對每個階段的變換做一個總結,以幫助理解。
模型變換——從模型坐標系到世界坐標系
局部坐標系(模型坐標系)是為了方便構造模型而設立的坐標系,建立模型時我們無需關心最終對象顯示在屏幕哪個位置。模型的原點定位也可以有所不同,例如下面在模型坐標系定義的模型:
模型變換的主要目的是通過變換使得用頂點屬性定義或者3d建模軟件構造的模型,能夠按照需要,通過縮小、平移等操作放置到場景中合適的位置。通過模型變換后,物體放置在一個全局的世界坐標系中,世界坐標系是所有物體交互的一個公共坐標系。例如下面的圖中在模型坐標系定義的茶壺模型(來自World, View and Projection Transformation Matrices):
茶壺通過模型變換,轉換到世界坐標系中(來自World, View and Projection Transformation Matrices):
模型變換包括:旋轉、平移、縮放、錯切等內容。例如將物體從一個位置p=(x,y,z),移動到另一個位置p′=(x′,y′,z′)的過程,用矩陣表示為:
應用多個模型變換時,注意變換執行的順序影響變換的結果,一般按照縮放–》旋轉—》平移的順序執行;另外,注意旋轉和縮放變換的不動點問題。這些內容在模型變換一節已經介紹了,這里不再贅述。利用GLM數學庫實現模型變換,例如平移變換示例代碼為:
glm::mat4 model; // 構造單位矩陣 model = glm::translate(model, glm::vec3(0.0f, 0.0f,-0.5f));
- 1
- 2
- 1
- 2
視變換——從世界坐標系到相機坐標系
視變換是為了方便觀察場景中物體而設立的坐標系,在這個坐標系中相機是個假想的概念,是為了便於計算而引入的。相機坐標系中的坐標,就是從相機的角度來解釋世界坐標系中位置。相機和場景的示意圖如下所示(來自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));
- 1
- 2
- 3
- 1
- 2
- 3
投影變換——從世界坐標系到裁剪坐標系
投影方式決定以何種方式成像,投影方式有很多種,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表示的剪裁平面分別為:近裁剪平面z=−nearVal,以及遠裁剪平面z=−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);
- 1
- 2
- 1
- 2
經過投影變換后,物體坐標變換到了裁剪坐標系,經過OpenGL自動執行的透視除法后,變換到規范化設備坐標系中。透視除法就是將裁剪坐標系中坐標都除以wc成分的過程。
視口變換——從NDC到屏幕坐標
視變換是將規范化設備坐標(NDC)轉換為屏幕坐標的過程,如下圖所示:
視口變化通過函數:
glViewport(GLint sx , GLint sy , GLsizei ws , GLsizei hs);
glDepthRangef(GLclampf ns , GLclampf fs );
兩個函數來指定。其中(sx,sy)表示窗口的左下角,ns和 fs指定遠近剪裁平面到屏幕坐標的映射關系。視口變換矩陣的推導可以參考前面的投影矩陣和視口變換矩陣一節。用上面的glViewport和glDepthRangef函數指定參數后,視口變換由OpenGL自動執行,不需要額外代碼。
坐標變換的計算過程
上述過程從坐標計算角度來看,如下圖所示:
坐標變換的程序實現
在程序中,我們需要在兩個部分做處理。
第一,編寫頂點着色器程序如下:
#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; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
第二,在主程序中傳遞變換矩陣到頂點着色器,並繪制場景中物體,代碼如下:
// 投影矩陣 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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
例如利用圓形坐標系指定相機位置,繪制的立方體如下圖所示:
參考資料
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