在以前的文章中,繪製了一些圖形,可是有個問題, 以三角形爲例,根據設置的座標,應該顯示正三角形,而手機上運行卻被拉長爲等邊三角形(底邊比左右兩邊短)。java
若是手機橫向防止,三角形就被壓扁了。android
瞭解兩個座標系以後,這個問題的緣由就清楚了。git
opengl 的座標系是歸一化設備座標系,原點在屏幕中心,橫向是橫座標,縱向是縱座標,範圍都是[-1,1] 。github
android設備的屏幕座標系遠點在屏幕左上角,橫向是橫座標,縱向是縱座標,可是縱座標的正方向向下,範圍由手機屏幕像素大小決定。數組
我們先無論座標系的方向,由於方向和拉伸/壓扁的問題沒有關係。bash
從兩座標系定義的範圍來看,歸一化設備座標系定義了一個正方形區域,而屏幕座標系定義的區域由具體像素決定,通常手機都是矩形。ide
假如,屏幕尺寸爲 1920 * 1080,opengl中的一個頂點的座標是(0.5,0.5),則換算成屏幕座標就是 (960,540)。也就是說,這個點在歸一化設備座標中,距離x、y軸的距離是同樣的,而在屏幕座標系中,到x、y軸的距離就不同了,也就是上文問題中拉伸、壓扁的意思。spa
修復這個問題很簡單,加上一個虛擬座標系。code
這個虛擬座標系的範圍由屏幕尺寸計算出來,將屏幕寬高中較小的一方映射到範圍 [-1,1] 上,較大的一方映射到範圍 [-(big/small),big/small] 。orm
使用時,頂點的座標在虛擬座標系下定義,在着色器中使用正交投影矩陣換算爲歸一化設備座標,而後opengl渲染到屏幕座標系上,繪製出來的圖形就不會變形了。
例如,屏幕尺寸爲 1920 * 1080,則虛擬座標系的範圍是 x:[-1920/1080,1920/1080],y:[-1,1]。
而後咱們在虛擬座標系下定義頂點座標 (0.5,0.5)。
接着着色其中經過正交投影矩陣,將頂點座標換算爲 (0.28……,0.5),這裏的0.28…… 是個近似小數。
opengl 渲染到屏幕座標系上的座標是(~540,540),x座標在精度範圍內無限接近y座標,也就是說頂點在屏幕座標系下,到x、y軸的距離也是同樣了,也就不會再變形了。
下面看看正交投影是怎麼進行換算的。
android 中 Matrix 類中 orthoM() 方法能夠用來生成一個正交投影矩陣,具體參數的含義看下面的代碼註釋。
/** * Computes an orthographic projection matrix. * * @param m 存放結果矩陣的數組 * @param mOffset 結果矩陣的起始偏移值 * @param left x軸最小範圍 * @param right x軸最大範圍 * @param bottom y軸最小範圍 * @param top y軸最大範圍 * @param near z軸最小範圍 * @param far z軸最大範圍 */
public static void orthoM(float[] m, int mOffset,float left, float right, float bottom, float top,float near, float far) {}
複製代碼
生成的結果矩陣的元素值以下
計算正交投影矩陣
public final float[] projectionMatrix = new float[16];
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//設置視口
glViewport(0, 0, width, height);
//計算正交投影矩陣,修正變形
float aspectRatio = width > height ?
(float)width / (float)height : (float)height / (float)width;
if (width > height){
orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);
}else {
orthoM(projectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
}
}
複製代碼
頂點着色器中使用正交投影矩陣進行換算
#version 300 es
in vec4 vPosition;
uniform mat4 matrix;
void main()
{
gl_Position = matrix * vPosition;
}
複製代碼
繪製時將計算出的矩陣傳入着色器中
//傳入正交矩陣修復變形
int matrixLoc = glGetUniformLocation(program, VERTEX_ATTRIB_PROJECTION_MATRIX);
glUniformMatrix4fv(matrixLoc, 1, false, projectionMatrix, 0);
複製代碼
其餘就和之前同樣了,能夠參考以前的文章。
本文梳理了歸一化設備座標系、屏幕座標系、虛擬座標系三個座標系之間的關係,經過正交投影,完成虛擬座標到歸一化設備座標的換算,進而修復圖形變形的問題。