在 OpenGL 座標系統 文章中,根據點的座標變換得出了以下的公式:java
這個公式每左乘一個矩陣,都表明了一種座標系的變換。git
轉化爲着色器腳本語言以下:github
attribute vec4 a_Position;
uniform mat4 u_ModelMatrix;
uniform mat4 u_ProjectionMatrix;
uniform mat4 u_ViewMatrix;
void main()
{
gl_Position = u_ProjectionMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
}
複製代碼
本篇文章就主要是對投影矩陣來分析的。bash
OpenGL 在觀察空間轉換到裁剪空間時,須要用到投影矩陣。而在着色器腳本中,也須要提供一個投影矩陣給對應的 u_ProjectionMatrix
變量。微信
首先要在程序裏綁定到對應的變量,而後再給變量賦值。ide
// 綁定到着色器腳本中的對應變量
private static final String U_ProMatrix = "u_ProjectionMatrix";
private int uProMatrixLocation;
uProMatrixLocation = glGetUniformLocation(mProgram,U_ProMatrix);
// 給變量賦值,projectionMatrix 爲投影矩陣
glUniformMatrix4fv(uProMatrixLocation,1,false,projectionMatrix,0)
複製代碼
正如前文講到的,投影矩陣會建立一個視景體對物體座標進行裁剪,獲得的裁剪座標再通過透視除法以後,就會獲得歸一化設備座標。歸一化設備座標再通過視口轉換,最終將座標映射到了屏幕上。函數
OpenGL 提供了兩種投影方式:正交投影和透視投影。post
無論是正交投影仍是透視投影,最終都是將視景體內的物體投影在近平面上,這也是 3D 座標轉換到 2D 座標的關鍵一步。ui
而近平面上的座標接着也會轉換成歸一化設備座標,再映射到屏幕視口上。spa
爲了解決以前的圖像拉伸問題,就是要保證近平面的寬高比和視口的寬高比一致,並且是以較短的那一邊做爲 1 的標準,讓圖像保持居中。
OpenGL 提供了 Matrix.orthoM
函數來生成正交投影矩陣。
/** * Computes an orthographic projection matrix. * * @param m returns the result 正交投影矩陣 * @param mOffset 偏移量,默認爲 0 ,不偏移 * @param left 左平面距離 * @param right 右平面距離 * @param bottom 下平面距離 * @param top 上平面距離 * @param near 近平面距離 * @param far 遠平面距離 */
public static void orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far) 複製代碼
須要注意的是,咱們的左、上、右、下距離都是相對於近平面中心的。
近平面的座標原點位於中心,向右爲 軸正方向,向上爲
軸正方向,因此咱們的 left、bottom 要爲負數,而 right、top 要爲正數。同時,近平面和遠平面的距離都是指相對於視點的距離,因此 near、far 要爲正數,並且
。
能夠在 GLSurfaceView 的 surfaceChanged 裏面來設定正交投影矩陣。
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
float aspectRatio = width > height ? (float) width / (float) height : (float) height / (float) width;
if (width > height){
Matrix.orthoM(projectionMatrix,0,-aspectRatio,aspectRatio,-1f,1f,0f,10f);
}else {
Matrix.orthoM(projectionMatrix,0,-1f,1f,-aspectRatio,aspectRatio,0f,10f);
}
}
複製代碼
這樣的話,就把近平面的寬高比設定與視口的寬高比一致了。
OpenGL 提供了兩個函數來建立透視投影矩陣:frustumM
和 perspectiveM
。
frustumM 函數建立的視景體是一個錐形。
它的視景體有點相似於正交投影,在參數理解上基本都相同的。
/**
* Defines a projection matrix in terms of six clip planes.
*
* @param m the float array that holds the output perspective matrix
* @param offset the offset into float array m where the perspective
* matrix data is written
* @param left
* @param right
* @param bottom
* @param top
* @param near
* @param far
*/
public static void frustumM(float[] m, int offset,
float left, float right, float bottom, float top,
float near, float far)
複製代碼
須要注意的是 near 和 far 變量的值必需要大於 0 。由於它們都是相對於視點的距離,也就是照相機的距離。
當用視圖矩陣肯定了照相機的位置時,要確保物體距離視點的位置在 near 和 far 的區間範圍內,不然就會看不到物體。
因爲透視投影會產生近大遠小的效果,當照相機位置不變,改變 near 的值時也會改變物體大小,near 越小,則離視點越近,至關於物體越遠,那麼顯示的物體也就越小了。
固然也能夠 near 和 far 的距離不動,改變攝像機的位置來改變觀察到的物體大小。
OpenGL 還提供了 perspectiveM
函數來建立投影矩陣,它的視景體和 frustumM
函數相同,可是構造的參數有所不一樣。
/**
* Defines a projection matrix in terms of a field of view angle, an
* aspect ratio, and z clip planes.
*
* @param m the float array that holds the perspective matrix
* @param offset the offset into float array m where the perspective
* matrix data is written
* @param fovy field of view in y direction, in degrees
* @param aspect width to height aspect ratio of the viewport
* @param zNear
* @param zFar
*/
public static void perspectiveM(float[] m, int offset,
float fovy, float aspect, float zNear, float zFar)
複製代碼
視景體再也不須要肯定近平面左、上、右、下距離了。
經過視角來決定咱們能看到的視野大小。視角就是圖中所示的那個夾角。另外的參數是視口的寬高比,還有近平面和遠平面的距離,參數個數減小了。
上述圖片左邊是 90 視角,右邊是 45 度視角。顯然,視野角度越大,則看到的內容更多,可是物體顯得更小,而視野角度越小,則看的內容更少,但物體顯得更大。
和 frustumM
不一樣的是,一旦肯定了視角和寬高比,那麼整個攝像機視野也就肯定了,此時完整的錐形視野已經造成了,也就是說物體的近大遠小效果已經完成了。這時,近平面距離和遠平面距離只是想要截取錐形視野中的那一部分了。不像在frustumM
函數中,近、遠平面的距離還可以調整近大遠小的效果。
具體代碼詳情,能夠參考個人 Github 項目:
https://github.com/glumes/AndroidOpenGLTutorial
最後,若是以爲文章不錯,歡迎關注微信公衆號:【紙上淺談】