今天給你們講講OpenGL ES
中的座標系和矩陣變換,OpenGL ES
中的座標系實際上有不少,在我以前的文章中,由於對應的效果對座標系的要求不高,所用的座標其實是跳過的一系列的座標變換,這點後面會給你們說,而矩陣變換就是將座標從一個座標系轉換到另外一個座標系下。android
咱們先來了解一下OpenGL ES
中的虛擬攝像機,在OpenGL ES
中,有一個虛擬的攝像機,咱們渲染出來的景像實際上就是這個虛擬攝像機所拍攝到的景像,這個虛擬攝像機的效果和咱們真實生活中的攝像機效果更相似,咱們能夠經過調整虛擬攝像機的位置、朝向等參數來獲得不一樣的觀察結果,進而獲得不一樣的渲染畫面,舉個形象一點的例子,例如咱們平時玩的3D遊戲,有至關一部分是用OpenGL ES
渲染的,咱們控制角色移動靠近一個物體時,物體就會變大,就像咱們拿着一個攝像機朝一個物體走過去同樣,拍攝到的物體就會變大,角色轉身時,能看到不一樣的畫面,就想是咱們拿着一個攝像機朝不一樣的方向拍。git
不過這個攝像機最終在OpenGL ES
中是以矩陣的形式呈現出來的,咱們先來看一張總體流程圖:github
這張圖給咱們展現了OpenGL ES
中的座標系及矩陣變換過程,具體過程是這樣:spa
OpenGL ES
有個世界座標系,咱們渲染的物體就是在世界座標系中,咱們的模型須要放到世界座標系中,那麼當咱們還沒放的時候,模型就和世界座標系沒有聯繫,它就還處於本身的座標系中,咱們叫作模型座標系、局部空間、局部座標系,也就是圖中的LOCAL SPACE。Camera
的位置、朝向的點座標、以及Camera
的上方向向量就能將觀察狀態定下來,而這些設置最終會轉換成OpenGL ES
中的視圖矩陣,對應圖中的VIEW MATRIXView Matrix
的變換後,咱們觀察它的結果就肯定了,圖中是從距離它必定的距離、上往下觀察它,這時候的點座標就來到了視圖座標系下,對應圖中的VIEW SPACE
OpenGL ES
提供正交投影和透視投影,正交投影沒有近大遠小的效果,無論在什麼距離上看,都同樣大,透視投影則有近大遠小的效果,也是符合咱們實際生活的一種效果,透視投影應用得比較多,可看下面這張經典圖:這有一個初學者可能會誤解的點,就是認爲OpenGL ES
的座標範圍就是0~1,超出0或1就會超出屏幕就看不見,這種理解其實不許確,座標範圍是多少,取決於說的是什麼座標系,咱們在平時作的更可能是2D渲染,經常就是像我以前的教程裏寫的座標那樣,直接使用0~1的NDC座標系,不須要矩陣變換,但實際上OpenGL ES
的世界座標系是沒有範圍的,是負無窮到正無窮,至於某些座標下的東西是否最後能渲染出來看到,這就取決於前面說的矩陣變換過程,例如將虛擬攝像機對準一個距離999999的物體,而且物體在裁剪區域內,也是能看到的,並非說座標必定要是0~1。3d
矩陣變換主要仍是用在3D渲染和一些特殊的2D效果上,例如一個偏轉變形的2D平面,若是直接設置NDC座標,出來的效果會有畸變,須要本身進行透視矯正,關於透視矯正,這裏先不展開說了。code
接下來咱們來看一下如何在OpenGL ES
中使用矩陣變換,首先看模型矩陣,前面提到過,模型矩陣是把座標從模型的局部座標系轉換到世界座標系,這個變換不只是位置的變換,還能夠有旋轉和縮放,例如把一個物體縮小一點、旋轉一點後放到世界座標系中的某個位置上,所以模型矩陣實際上包含的平移、旋轉和縮放,它就等於平移矩陣、旋轉矩陣和縮放矩陣相乘:orm
val translateMatrix = getIdentity()
val rotateMatrix = getIdentity()
val scaleMatrix = getIdentity()
val modelMatrix = getIdentity()
// 模型矩陣計算
// Calculate the Model matrix
Matrix.translateM(translateMatrix, 0, translateX, translateY, translateZ)
Matrix.rotateM(rotateMatrix, 0, rotateX, 1f, 0f, 0f)
Matrix.rotateM(rotateMatrix, 0, rotateY, 0f, 1f, 0f)
Matrix.rotateM(rotateMatrix, 0, rotateZ, 0f, 0f, 1f)
Matrix.scaleM(scaleMatrix, 0, scaleX, scaleY, scaleZ)
Matrix.multiplyMM(modelMatrix, 0, rotateMatrix, 0, scaleMatrix, 0)
Matrix.multiplyMM(modelMatrix, 0, modelMatrix, 0, translateMatrix, 0)
複製代碼
視圖矩陣則是對應前面說的虛擬攝像機,它共由虛擬攝像機的位置、朝向的點座標、以及虛擬攝像機的上方向向量肯定,OpenGL ES
提供了方法來獲得視圖矩陣,咱們只須要給它傳遞這些參數就好了:cdn
val viewMatrix = getIdentity()
// 視圖矩陣計算
// Calculate the View matrix
Matrix.setLookAtM(
viewMatrix,
0,
cameraPositionX, cameraPositionY, cameraPositionZ,
lookAtX, lookAtY, lookAtZ,
cameraUpX, cameraUpY, cameraUpZ
)
複製代碼
接下來是投影矩陣,前面提到投影矩陣有正交投影和透視投影兩種,本文中使用透視投影,它也是由OpenGL ES
提供的方法來獲得,所須要的參數爲近平面矩陣的上、下、左、右座標,近平面距離和遠平面矩離(這張圖中的Left、Right、Bottom、Top標在了遠平面上,實際在OpenGL ES
中生成透視投影矩陣的方法參數中的left
、抄下t
、bottom
、up
指的是近平面):blog
生成透視投影矩陣代碼以下:教程
val projectMatrix = getIdentity()
// 透視投影矩陣計算
// Calculate the Project matrix
Matrix.frustumM(
projectMatrix,
0,
nearPlaneLeft, nearPlaneRight, nearPlaneBottom, nearPlaneTop,
nearPlane,
farPlane
)
複製代碼
如今,模型矩陣、視圖矩陣和投影矩陣都生成了,下面將這三個矩陣相乘獲得最終的變換矩陣(MVP):
val mvpMatrix = getIdentity()
// MVP矩陣計算
// Calculate the MVP matrix
Matrix.multiplyMM(mvpMatrix, 0, viewMatrix, 0, modelMatrix, 0)
Matrix.multiplyMM(mvpMatrix, 0, projectMatrix, 0, mvpMatrix, 0)
複製代碼
而後將MVP
矩陣傳遞到Vertex Shader
中與頂點相乘進行矩陣變換:
#version 300 es
precision mediump float;
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_textureCoordinate;
layout(location = 2) uniform mat4 u_mvp;
out vec2 v_textureCoordinate;
void main() {
v_textureCoordinate = a_textureCoordinate;
gl_Position = u_mvp * a_position;
}"
複製代碼
我作一了個demo,能夠調節各類參數實時查看效果:
這個demo是渲染一個立方體,立方體每一個面上貼上一個花的紋理,上面這張圖是一個初始狀態,咱們來看一下初始的參數:
var translateX = 0f
var translateY = 0f
var translateZ = 0f
var rotateX = 0f
var rotateY = 0f
var rotateZ = 0f
var scaleX = 1f
var scaleY = 1f
var scaleZ = 1f
var cameraPositionX = 0f
var cameraPositionY = 0f
var cameraPositionZ = 5f
var lookAtX = 0f
var lookAtY = 0f
var lookAtZ = 0f
var cameraUpX = 0f
var cameraUpY = 1f
var cameraUpZ = 0f
var nearPlaneLeft = -1f
var nearPlaneRight = 1f
var nearPlaneBottom = -glSurfaceViewHeight.toFloat() / glSurfaceViewWidth
var nearPlaneTop = glSurfaceViewHeight.toFloat() / glSurfaceViewWidth
var nearPlane = 2f
var farPlane = 100f
複製代碼
和模型矩陣Model Matrix相關的參數是translate
、rorate
和scale
,這裏初始時咱們不對模型進行變換。
和視圖矩陣VIEW MATRIX相關的參數是CameraPosition
、lookAt
和CameraUp
,咱們把虛擬攝像機放在(0, 0, 5)
這個位置,而且讓它對向(0, 0, 0)
,而且攝像機的上方向是(0, 1, 0)
,也就是把攝像正立着。
和投影矩陣PROJECT MATRIX相關的參數是近平面nearPlane
上下左右和距離、以及遠平面farPlane
距離,咱們將近平面左右設爲-1和1,而且上下根據GLSurfaceView
和尺寸設置,這樣是爲了避免變形,近平面設置爲2,遠平面設置爲100。
咱們渲染的這個立方體頂點座標都是-1和1,也就是在原點那裏,因此上面的圖中咱們看到的效果就是從Z軸上的(0, 0, 5)
這個位置正對看向這個立方體,所以只能看到正面。
下面我調節一些參數看看效果:
上面中咱們經過設置模型矩陣的參數將立方體變換到了(3, 4, -5)
這個位置,由於咱們的攝像機沒動,仍是對着原點看,那麼至關於這個立方體在攝像機的右上方,所以攝像機能看到這個立方體的左面和下面。
再繼續看:
這個是把立方體旋轉了一下,沒什麼好解釋的。
再來看:
這個是旋轉加上了縮放,把x座標變小、y座標變大了,因此橫向的邊就短了,豎向的就長了。
再來:
這個是立方體旋轉加上再把虛擬攝像機看的點從(0, 0, 0)
變成了(0, 2, 0)
,所以效果就是立方體就在視野下方了。
再看:
這個是把虛擬攝像機的上方向從(0, 1, 0)
變成了(1, 1, 0)
,也就是原本虛擬攝像機是正立着的,如今變換歪了45度,因此拍攝到的畫面也歪了45度。
好了,還有不少種狀況,你們能夠到demo裏玩玩,看看渲染出來的效果是否與本身的理解和預期一致。
這節的內容比較複雜,若是有疑問,歡迎給我留言討論哈。
代碼在我github
的OpenGLESPro
項目中,本文對應的是SampleMatrixTransform
,項目連接:github.com/kenneycode/…
感謝閱讀!