OpenGL之基礎(chǔ)
OpenGL之繪制簡(jiǎn)單形狀
OpenGL之顏色
調(diào)整屏幕寬高比
不做寬高比調(diào)整導(dǎo)致的問(wèn)題
如果不做任何處理,圖像會(huì)在橫屏模式下被壓扁,因?yàn)橹爸苯影炎鴺?biāo)傳遞給OpenGL時(shí),沒(méi)有考慮屏幕的寬高比
| 橫屏狀態(tài) | 豎屏狀態(tài) |
|---|---|
![]() image-20211221121517035.png
|
![]() image-20211221121648424.png
|
解決方案
不要直接在歸一化設(shè)備坐標(biāo)上工作,而應(yīng)該先在虛擬坐標(biāo)空間里工作,然后再使用投影技術(shù)把虛擬空間坐標(biāo)轉(zhuǎn)換回歸一化設(shè)備坐標(biāo)
虛擬空間坐標(biāo)范圍
在虛擬坐標(biāo)空間里,把較小的范圍固定在 [-1,1] 內(nèi),然后按屏幕尺寸的比例調(diào)整較大的范圍
例如,手機(jī)寬度是720,高度是1280
| 豎屏 | 橫屏 | |
|---|---|---|
| 寬度范圍 | [-1,1] | [-1280/720,1280/720] 即 [-1.78,1.78] |
| 高度范圍 | [-1.78,1.78] | [-1,1] |
正交投影
正交投影是投影技術(shù)的一種,使用正交投影,實(shí)際上定義了三維世界內(nèi)部的一個(gè)區(qū)域,也就是虛擬空間。在這個(gè)區(qū)域內(nèi)的所有東西都會(huì)顯示在屏幕上,而區(qū)域外的所有東西都會(huì)被裁剪掉,如下圖所示

創(chuàng)建正交投影矩陣
Matrix.orthoM() 方法可以創(chuàng)建一個(gè)正交投影矩陣,其方法原型如下
public static void orthoM(float[] m, int mOffset,
float left, float right, float bottom, float top,
float near, float far) {
參數(shù)說(shuō)明
| 參數(shù) | 說(shuō)明 |
|---|---|
| m | 最終生成的正交投影矩陣數(shù)組,長(zhǎng)度至少有16個(gè)元素,才能存儲(chǔ)正交投影矩陣 |
| mOffset | 正交投影矩陣起始的偏移值 |
| left | x軸的最小范圍 |
| right | x軸的最大范圍 |
| bottom | y軸的最小范圍 |
| top | y軸的最大范圍 |
| near | z軸的最小范圍 |
| far | z軸的最大范圍 |
上面的參數(shù)中l(wèi)eft、right、bottom、top、near、far對(duì)應(yīng)于上一幅圖中標(biāo)識(shí)的地方,也是形成虛擬坐標(biāo)空間的值,
調(diào)用這個(gè)方法的時(shí)候,會(huì)產(chǎn)生下面的正交投影矩陣:

這個(gè)正交投影矩陣會(huì)把所有在左右之間、上下之間和遠(yuǎn)近之間的事物映射到歸一化設(shè)備坐標(biāo)中從-1到1的范圍,在這個(gè)范圍內(nèi)的所有事物在屏幕上都是可見(jiàn)的
歸一化設(shè)備坐標(biāo)使用的是左手坐標(biāo)系統(tǒng)(使用左手,大拇指指向x軸正值方向、食指指向y軸正值方向),而在 OpenGL的早期版本,默認(rèn)使用的卻是右手坐標(biāo)系統(tǒng),其使用z的負(fù)值增加表示距離增加。這就是為什么Android的 Matrix會(huì)默認(rèn)生成反轉(zhuǎn)z的矩陣
坐標(biāo)系統(tǒng)
| 左手坐標(biāo)系統(tǒng) | 右手坐標(biāo)系統(tǒng) |
|---|---|
![]() image-20211221133109558.png
|
![]() image-20211221133131305.png
|
加入正交投影
修改頂點(diǎn)著色器
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_Matrix;
varying vec4 v_Color;
void main(){
v_Color=a_Color;
gl_Position=u_Matrix*a_Position;
gl_PointSize=10.0;
}
添加 uniform 的 u_Matrix,代表正交投影矩陣,并把它定義為一個(gè) mat4 類型,mat4 表示這個(gè) uniform 是一個(gè) 4×4 的矩陣
將當(dāng)前頂點(diǎn)的最終位置賦值為:位置與一個(gè)正交投影矩陣的乘積。這意味著頂點(diǎn)數(shù)組不再代表歸一化設(shè)備坐標(biāo)了,其將被理解為存在于正交投影矩陣所定義的虛擬坐標(biāo)空間中,這個(gè)矩陣會(huì)把坐標(biāo)從虛擬坐標(biāo)空間變換回歸一化設(shè)備坐標(biāo)
修改渲染器
public class MyRenderer implements GLSurfaceView.Renderer {
private final FloatBuffer mVertexBuffer;
private Context mContext;
private int a_color;
private int a_position;
private int u_matrix;
// 保存正交投影矩陣
private float[] projectionMatrix = new float[16];
public MyRenderer(Context context) {
mContext = context;
// 頂點(diǎn)位置有改動(dòng),因?yàn)楝F(xiàn)在代表虛擬空間中的坐標(biāo)了,范圍也不再是 [-1,1],
// 不過(guò)要注意的是,豎屏?xí)r,如果較大范圍軸(比如豎屏?xí)r的y軸)超過(guò)了1,在橫屏?xí)r會(huì)被截?cái)嗫床坏? float[] vertex = new float[]{
0f, 0f, 1f, 1f, 1f,
-0.5f, -0.8f, 0f, 0f, 0f,
0.5f, -0.8f, 0f, 0f, 0f,
0.5f, 0.8f, 0f, 0f, 0f,
-0.5f, 0.8f, 0f, 0f, 0f,
-0.5f, -0.8f, 0f, 0f, 0f,
-0.5f, 0f, 1f, 0f, 0f,
0.5f, 0f, 1f, 0f, 0f,
0f, -0.5f, 0f, 1f, 0f,
0f, 0.5f, 0f, 0f, 1f
};
mVertexBuffer = ByteBuffer.allocateDirect(vertex.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertex);
mVertexBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
glClearColor(1f, 1f, 1f, 1f);
int vertexShader = ShaderHelper.compileVertexShader(mContext, R.raw.ratio3_vertex);
int fragmentShader = ShaderHelper.compileFragmentShader(mContext, R.raw.ratio3_fragment);
int program = ProgramHelper.getProgram(vertexShader, fragmentShader);
glUseProgram(program);
a_color = glGetAttribLocation(program, "a_Color");
a_position = glGetAttribLocation(program, "a_Position");
// 獲取正交投影矩陣位置
u_matrix = glGetUniformLocation(program, "u_Matrix");
mVertexBuffer.position(0);
glVertexAttribPointer(a_position, POSITION_COMPONENT_COUNT, GL_FLOAT, false, STRIDE, mVertexBuffer);
glEnableVertexAttribArray(a_position);
mVertexBuffer.position(POSITION_COMPONENT_COUNT);
glVertexAttribPointer(a_color, COLOR_COMPONENT_COUNT, GL_FLOAT, false, STRIDE, mVertexBuffer);
glEnableVertexAttribArray(a_color);
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
glViewport(0, 0, width, height);
// 生成正交投影矩陣
float ratio = width > height ? (float) width / height : (float) height / width;
if (width > height) {
// 橫屏
Matrix.orthoM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, -1f, 1f);
} else {
// 豎屏
Matrix.orthoM(projectionMatrix, 0, -1f, 1f, -ratio, ratio, -1f, 1f);
}
}
@Override
public void onDrawFrame(GL10 gl10) {
glClear(GL_COLOR_BUFFER_BIT);
// 向著色器傳遞 正交投影矩陣
glUniformMatrix4fv(u_matrix, 1, false, projectionMatrix, 0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 6);
glDrawArrays(GL_LINES, 6, 2);
glDrawArrays(GL_POINTS, 8, 1);
glDrawArrays(GL_POINTS, 9, 1);
}
}
效果
| 豎屏 | 橫屏 |
|---|---|
![]() image-20211221140409098.png
|
![]() image-20211221140436697.png
|





