前言
之前我們的所有圖形效果症副,都是變形的,比如我們原本繪制的是長寬比是1:1的涣脚,結(jié)果在手機屏幕上的效果展示卻是長方形。那么寥茫,本節(jié)課我們通過正交投影來解決這個問題遣蚀。
本節(jié)課主要講解如何去編寫相關(guān)代碼來解決問題,而具體的原理纱耻、概念芭梯、GL坐標體系變換等暫不做深入說明,會在之后的課程在講解弄喘。
歸一化設(shè)備坐標
在OpenGL中玖喘,我們要渲染的所有物體都要映射到x軸、y軸蘑志、z軸上的[-1, 1]范圍內(nèi)累奈,這個范圍內(nèi)的坐標被稱為歸一化設(shè)備坐標贬派,其獨立于屏幕的實際尺寸或者形狀。歸一化設(shè)備坐標假定的坐標空間是一個正方形费尽。如下圖
但是我們手機設(shè)備一般都不是正方形的赠群,而是長方形的。所以導(dǎo)致x和y兩個方向上旱幼,同樣的比例值查描,但是視覺上所占的長度卻是不一樣的。如下圖柏卤,繪制一個半徑占0.5的圓時冬三,效果卻是一個橢圓。
解決這個問題缘缚,一般我們的解決方案步驟如下:
- 在設(shè)置物體的坐標勾笆、尺寸時,將短邊視為標準邊桥滨,取值范圍是[-1,1]窝爪,而較長邊的取值范圍則是[-N,N],其中N≥1齐媒,N是長邊/短邊的比例系數(shù)图筹。
- 頂點著色器設(shè)置頂點參數(shù)的時候害碾,將長邊上的值從[-N,N]換算為[-1,1]的范圍內(nèi)谱邪。
步驟如下圖:
代碼實現(xiàn)
針對上面的解決步驟领追,步驟1只需要我們在設(shè)置頂點的時候按照這個標準即可。而步驟2則是本課程的關(guān)鍵唬血。
要對坐標向量進行換算望蜡,可以使用矩陣來解決問題。
在三維圖形學中拷恨,一般使用的是4階矩陣脖律。OpenGL中使用的是列向量,如[xyzw]T腕侄,所以與矩陣相乘時状您,矩陣在前,向量在后兜挨。
知道了原理之后,我們代碼實現(xiàn)上需要解決以下幾個問題:
- 如何獲得一個矩陣眯分,可以把坐標范圍從[-N,N]換算為[-1,1]的范圍內(nèi)
- 如何將矩陣傳遞到GLSL中
- 對于問題1拌汇,Android提供了Matrix.orthoM這個方法來處理矩陣。
- 對于問題2弊决,與獲取頂點索引類似噪舀,可以再GLSL中聲明一個mat4類型的矩陣變量魁淳,獲取其索引,再傳遞值給她
具體代碼實現(xiàn)如下:
private static final String VERTEX_SHADER = "" +
// mat4:4×4的矩陣
"uniform mat4 u_Matrix;\n" +
"attribute vec4 a_Position;\n" +
"void main()\n" +
"{\n" +
// 矩陣與向量相乘得到最終的位置
" gl_Position = u_Matrix * a_Position;\n" +
"}";
private int uMatrixLocation;
/**
* 矩陣數(shù)組
*/
private final float[] mProjectionMatrix = new float[]{
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
};
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
// 省略部分代碼
uMatrixLocation = getUniform("u_Matrix");
}
@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
// 邊長比(>=1)与倡,非寬高比
float aspectRatio = width > height ?
(float) width / (float) height :
(float) height / (float) width;
// 1. 矩陣數(shù)組
// 2. 結(jié)果矩陣起始的偏移量
// 3. left:x的最小值
// 4. right:x的最大值
// 5. bottom:y的最小值
// 6. top:y的最大值
// 7. near:z的最小值
// 8. far:z的最大值
if (width > height) {
// 橫屏
Matrix.orthoM(mProjectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);
} else {
// 豎屏or正方形
Matrix.orthoM(mProjectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
}
// 更新u_Matrix的值界逛,即更新矩陣數(shù)組
GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, mProjectionMatrix, 0);
}
參考
見Android OpenGL ES學習資料所列舉的博客、資料纺座。
GitHub代碼工程
本系列課程所有相關(guān)代碼請參考我的GitHub項目GLStudio息拜。
課程目錄
本系列課程目錄詳見 簡書 - Android OpenGL ES教程規(guī)劃