OpenGL 學(xué)習(xí)教程
Android OpenGL ES 學(xué)習(xí)(一) – 基本概念
Android OpenGL ES 學(xué)習(xí)(二) – 圖形渲染管線和GLSL
Android OpenGL ES 學(xué)習(xí)(三) – 繪制平面圖形
Android OpenGL ES 學(xué)習(xí)(四) – 正交投屏
Android OpenGL ES 學(xué)習(xí)(五) – 漸變色
Android OpenGL ES 學(xué)習(xí)(六) – 使用 VBO、VAO 和 EBO/IBO 優(yōu)化程序
Android OpenGL ES 學(xué)習(xí)(七) – 紋理
代碼工程地址: https://github.com/LillteZheng/OpenGLDemo.git
這里的內(nèi)容基本參考于 https://www.jianshu.com/p/51a405bc52ed ,這里再補(bǔ)充些矩陣相關(guān)的知識(shí)。
這里先簡(jiǎn)單解決變形的問(wèn)題,關(guān)于 OpenGL 更多圖形矩陣變換,等后面再詳細(xì)講。
一. 歸一化設(shè)備坐標(biāo)
在OpenGL中,我們要渲染的所有物體都要映射到x軸、y軸、z軸上的[-1, 1]范圍內(nèi),這個(gè)范圍內(nèi)的坐標(biāo)被稱為歸一化設(shè)備坐標(biāo),其獨(dú)立于屏幕的實(shí)際尺寸或者形狀。
OpenGL 的坐標(biāo) 它是個(gè)正方形的:
而手機(jī)屏幕是長(zhǎng)方形的,橫豎屏的效果都不一樣。同樣的比例,視覺(jué)上是不一樣的,比如繪制一個(gè)半徑為 0.5 的圓,效果卻是一個(gè)橢圓:
二. 解決方案
解決此問(wèn)題,可以把一個(gè)物品的坐標(biāo),通過(guò)平移,縮放的方式,塞到到這個(gè)歸一化坐標(biāo)里面就可以了。
可以使用 正交投屏 來(lái)處理變形的問(wèn)題,因?yàn)檎煌队?,沒(méi)有遠(yuǎn)近距離的關(guān)系。
比如一個(gè)手機(jī)是豎屏,分辨率 1920x1080 ,怎么樣把它放到 [-1,1] 里面?
有兩個(gè)步驟
- 以短邊為基準(zhǔn),比如 1080,取值為 [-1,1],那邊長(zhǎng)邊縮放后 n = 長(zhǎng)/寬 就是 [-n,n],比如 1920/1080 ≈ 1.78
- 頂點(diǎn)著色器,在設(shè)置坐標(biāo)位置的時(shí)候,從 [-n,n] 換算到 [-1,1] 范圍內(nèi)即可。
三. 代碼實(shí)現(xiàn)
上面的步驟中,第一步比較好實(shí)現(xiàn),在內(nèi)容變化后,以短邊為基準(zhǔn),拿到寬高的比例。
第二步如何實(shí)現(xiàn)?
在 OpenGL 中,我們使用 vec4 ,即4分量,圖形的變量,使用的是矩陣,而OpenGL中使用的是列向量,如[xyzw]T,所以與矩陣相乘時(shí),矩陣在前,向量在后。
知道了原理之后,我們代碼實(shí)現(xiàn)上需要解決以下幾個(gè)問(wèn)題:
- 如何獲得一個(gè)矩陣,可以把坐標(biāo)范圍從[-N,N]換算為[-1,1]的范圍內(nèi)
- 如何將矩陣傳遞到GLSL中
- 對(duì)于問(wèn)題1,Android提供了Matrix.orthoM這個(gè)方法來(lái)處理矩陣。
- 對(duì)于問(wèn)題2,與獲取頂點(diǎn)索引類似,可以再GLSL中聲明一個(gè)mat4類型的矩陣變量,獲取其索引,再傳遞值給它
在之前多邊形的代碼中,修改頂點(diǎn)代碼如下,增加一個(gè)矩陣變量:
/**
* 頂點(diǎn)著色器:之后定義的每個(gè)都會(huì)傳1次給頂點(diǎn)著色器
* 修改頂部著色器的坐標(biāo)值,即增加個(gè)舉證x向量
*/
private const val VERTEX_SHADER = """#version 300 es
layout(location = 0) in vec4 a_Position;
// mat4:4×4的矩陣
uniform mat4 u_Matrix;
void main()
{
// 矩陣與向量相乘得到最終的位置
gl_Position = u_Matrix * a_Position;
gl_PointSize = 30.0;
}
"""
private const val U_COLOR = "u_Color"
private const val U_MATRIX = "u_Matrix"
//單位矩陣,單位矩陣乘以任何數(shù)都等于乘數(shù)本身
private val UnitMatrix = floatArrayOf(
1f, 0f, 0f, 0f,
0f, 1f, 0f, 0f,
0f, 0f, 1f, 0f,
0f, 0f, 0f, 1f
)
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
GLES30.glClearColor(1f, 1f, 1f, 1f)
....
uMatrix = getUniform(U_MATRIX)
}
在 gl 變化時(shí),設(shè)置矩陣
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES30.glViewport(0, 0, width, height)
val aspectRatio = if (width > height) {
width.toFloat() / height
} else {
height.toFloat() / width
}
// 1. 矩陣數(shù)組
// 2. 結(jié)果矩陣起始的偏移量
// 3. left:x的最小值
// 4. right:x的最大值
// 5. bottom:y的最小值
// 6. top:y的最大值
// 7. near:z的最小值
// 8. far:z的最大值
// 由于是正交矩陣,所以偏移量為0,near 和 far 也不起作用
if (width > height){
Matrix.orthoM(UnitMatrix,0,-aspectRatio,aspectRatio,-1f,1f,-1f,1f)
}else{
Matrix.orthoM(UnitMatrix,0,-1f,1f,-aspectRatio,aspectRatio,-1f,1f)
}
//更新 matrix 的值,即把 UnitMatrix 值,更新到 uMatrix 這個(gè)索引
GLES30.glUniformMatrix4fv(uMatrix,1,false, UnitMatrix,0)
}
near 和 far 這里,可以直接參考正交投影的公式
任何出現(xiàn)在近平面之前或遠(yuǎn)平面之后的坐標(biāo)都會(huì)被裁剪掉,所以,這里的 near =-1,far =1,表示這個(gè)坐標(biāo)的范圍在 [-1,1] 這個(gè)區(qū)間內(nèi)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-540416.html
參考:
嗶哩嗶哩 視頻:https://www.bilibili.com/video/BV12t4y1c7zd/?spm_id_from=333.337.search-card.all.click
https://www.jianshu.com/p/51a405bc52ed文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-540416.html
到了這里,關(guān)于Android OpenGL ES 學(xué)習(xí)(四) -- 正交投影的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!