OpenGL ES透視投影實(shí)現(xiàn)方法(四)
在之前的學(xué)習(xí)中,我們知道了一個(gè)頂點(diǎn)要想顯示到屏幕上,它的x、y、z分量都要在[-1,1]之間,我們回顧一下渲染管線的圖元裝配階段,它實(shí)際上做了以下幾件事:剪裁坐標(biāo)、透視分割、視口變換。圖元裝配的輸入是頂點(diǎn)著色器的輸出,抓喲是物體坐標(biāo)gl_Position,之后到光柵化階段。
圖元裝配
剪裁坐標(biāo)
當(dāng)頂點(diǎn)著色器寫入一個(gè)值到gl_Position時(shí),這個(gè)點(diǎn)要求必須在剪裁空間中,即它的x、y、z坐標(biāo)必須在[-w,w]之間,任何這個(gè)范圍之外的點(diǎn)都是不可見(jiàn)的。
這里需要注意以下,對(duì)于attribute類型的屬性量。OpenGL會(huì)用默認(rèn)的值替換屬性中未指定的分量,前三個(gè)分量會(huì)被設(shè)定為0,最后一個(gè)分量w會(huì)被設(shè)定為1.
站在gl_position的角度來(lái)說(shuō),[-w,w]之間的坐標(biāo)點(diǎn)才是可見(jiàn)的,否則都是不可見(jiàn)會(huì)被剪裁掉。往前看,在做投影變換的時(shí)候我們說(shuō),在視景體內(nèi)的物體有效,視景體外的會(huì)被剪裁,實(shí)際上是對(duì)應(yīng)的,剪裁就是發(fā)生在圖元裝配階段判斷所有的坐標(biāo)是否在[-w,w]之間。
剪裁實(shí)際上就是判斷每一個(gè)最小三角形、直線、點(diǎn)單元的坐標(biāo)是否規(guī)范。
透視除法
對(duì)上面的剪裁坐標(biāo)的點(diǎn)的x、y、z坐標(biāo)除以它的w分量,除以w的坐標(biāo)叫做歸一化設(shè)備坐標(biāo)。如果w分量大,除以w后的點(diǎn)就接近(0,0,0),在三維空間中,距離我們較遠(yuǎn)的坐標(biāo)如果它的w分量較大,進(jìn)行透視除法后,就距離原點(diǎn)越近,原點(diǎn)作為遠(yuǎn)處物體的消失點(diǎn),就有三維場(chǎng)景的效果。
視口變換
前面已經(jīng)使用過(guò)視口變換的函數(shù)glViewport了,視口是一個(gè)而為矩形窗口區(qū)域。是OpenGL渲染操作最終顯示的地方。
public static native void glViewport( int x, int y, int w, int h );
從歸一化設(shè)備坐標(biāo)(x,y,z)到窗口坐標(biāo)(X,Y,Z)的轉(zhuǎn)換公式

上面公式中的f和n是如下API設(shè)置的
public static native void glDepthRangef( float n, float f );
n,f指定所需的深度范圍,n,f的取值限于(0.0,1.0)之間,n,f的默認(rèn)值為0.0和1.0
glDepthRangef函數(shù)和glViewport函數(shù)指定的值用于將頂點(diǎn)位置從歸一化設(shè)備坐標(biāo)轉(zhuǎn)換為窗口坐標(biāo)。
利用w分量產(chǎn)生三維效果
在前面的代碼中,修改傳入的頂點(diǎn)坐標(biāo),增加w分量
float[] vertexArray = new float[] {
(float) -0.5, (float) -0.5, 0, 1,
(float) 0.5, (float) -0.5, 0, 1,
(float) -0.5, (float) 0.5, 0, 3,
(float) 0.5, (float) 0.5, 0, 3
};
同時(shí)修改頂點(diǎn)著色器:
private String vertexShaderCode = "uniform mat4 uMVPMatrix;"
+ "attribute vec4 aPosition;"
+ "void main(){"
+ "gl_Position = uMVPMatrix * aPosition;"
+ "}";
以及獲取uProjectionMatrix以及傳入頂點(diǎn)數(shù)據(jù)對(duì)應(yīng)的代碼,就可以看到如下所示效果

透視投影
然而這樣讓物體產(chǎn)生三維效果的做法太死板了,如果我們還要讓物體平移縮放旋轉(zhuǎn),這樣固定的指定w的值就不太好了。
透視投影這個(gè)時(shí)候就能派上用場(chǎng)了,利用透視投影矩陣自動(dòng)生成w的值。投影矩陣主要是為w產(chǎn)生正確的值,這樣在渲染管線的后續(xù)操作中做透視除法,遠(yuǎn)處的物體就看起來(lái)比進(jìn)出物體小,很容易想到,可以利用頂點(diǎn)位置的z分量,將這個(gè)距離映射到w分量上,z越大,w也越大。
有兩個(gè)函數(shù)可以生成透視投影矩陣frustumM和perspectiveM。參數(shù)具體含義可以參考下面的圖
public static void perspectiveM(float[] m, // 生成的投影矩陣
int offset,
float fovy, // 視角角度
float aspect, // 近平面的寬高比
float zNear, // 近平面
float zFar) // 遠(yuǎn)平面
frustumM函數(shù)原型
public static void frustumM(float[] m, int offset, float left, float right, float bottom, float top, // 近平面左右下上部與中心點(diǎn)的距離 float near, float far //近平面和元平面與攝像機(jī)觀察點(diǎn)的距離 )

透視投影背后的數(shù)學(xué)原理
創(chuàng)建下面的矩陣

a表示視角焦距,焦距等于1/tan(視野/2)
取aspect=1.8,視野45度即a = 1,f = 10,n = 5,得到的透視投影矩陣為

計(jì)算下面幾個(gè)點(diǎn)

上面這三個(gè)點(diǎn)越來(lái)越遠(yuǎn),通過(guò)透視投影后,z和w都變大了,可以想到,在后面的透視除法時(shí),x和y分量都會(huì)變小,于是就會(huì)出現(xiàn)距離越遠(yuǎn),匯聚到一個(gè)點(diǎn),也就是三維效果。
同時(shí)也可以看到,上面的幾個(gè)點(diǎn)他們的z坐標(biāo)都是負(fù)值,這也從側(cè)面表達(dá)了,事實(shí)上所有的有效的點(diǎn)z坐標(biāo)必須是負(fù)值,也就是從攝像機(jī)的坐標(biāo)來(lái)看是在z軸負(fù)方向,也就是必須在視景體里面,這一點(diǎn)通過(guò)攝像機(jī)矩陣來(lái)保證。
前面使用正交投影,它的矩陣不會(huì)使得w粉量增加,于是通過(guò)透視除法也不會(huì)使w分量增加,所以正交投影不會(huì)出現(xiàn)近大遠(yuǎn)小的效果,透視投影會(huì)出現(xiàn)近大遠(yuǎn)小的效果
透視投影例子
在上面矩形Demo的基礎(chǔ)上修改上面的正方形的頂點(diǎn)數(shù)據(jù)
float vertices[] = new float[] {
(float) -0.5, (float) -0.5 + + (float)(-0.1*i), (float) (1*i),
(float) 0.5, (float) -0.5 + + (float)(-0.1*i), (float) (1*i),
(float) -0.5, (float) 0.5 + + (float)(-0.1*i), (float) (1*i),
(float) 0.5, (float) 0.5 + + (float)(-0.1*i), (float) (1*i)
};
在繪圖時(shí),定義一個(gè)數(shù)組,傳遞不同的i值,比如繪制四個(gè)正方形,這四個(gè)正方形的距離越來(lái)越遠(yuǎn)。
mRectangles = new Rectangle[5];
for (int i = 0; i < mRectangles.length; i++) {
mRectangles[i] = new Rectangle(i);
}
在onSurfaceChanged函數(shù)里面設(shè)置攝像機(jī)位置和透視投影矩陣
Matrix.perspectiveM(mProjectionMatrix, 0, 45, (float)width/height, 2, 15); Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0);
然后在onDrawFram函數(shù)里面繪制這5個(gè)矩形
for (Rectangle rectangle : mRectangles) {
Matrix.setIdentityM(mModuleMatrix, 0);
Matrix.rotateM(mModuleMatrix, 0, xAngle, 1, 0, 0);
Matrix.rotateM(mModuleMatrix, 0, yAngle, 0, 1, 0);
Matrix.multiplyMM(mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mViewProjectionMatrix, 0, mModuleMatrix, 0);
rectangle.draw(mMVPMatrix);
}
為了呈現(xiàn)出3d效果,增加觸摸旋轉(zhuǎn)事件,這樣滑動(dòng)屏幕就可以看到三維物體的全貌
public boolean onTouchEvent(MotionEvent e) {
float y = e.getY();
float x = e.getX();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dy = y - mPreviousY;
float dx = x - mPreviousX;
mMyRender.yAngle += dx;
mMyRender.xAngle+= dy;
requestRender();
}
mPreviousY = y;
mPreviousX = x;
return true;
}
然后就可以看到三維效果。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- OpenGL中的glutInitDisplayMode()函數(shù)的理解
- OpenGL關(guān)于glStencilFuncSeparate()和glStencilFunc()函數(shù)的區(qū)別講解
- OpenGL Shader實(shí)例分析(7)雪花飄落效果
- OpenGL Shader實(shí)例分析(1)Wave效果
- SDL2和OpenGL使用踩坑筆記經(jīng)驗(yàn)分享
- Android利用OpenGLES繪制天空盒實(shí)例教程
- android使用OPENGL ES繪制圓柱體
- opengl實(shí)現(xiàn)任意兩點(diǎn)間畫圓柱體
- OpenGL ES著色器使用詳解(二)
- OpenGL實(shí)現(xiàn)Bezier曲線的方法示例
相關(guān)文章
Android通過(guò)HttpURLConnection和HttpClient接口實(shí)現(xiàn)網(wǎng)絡(luò)編程
這篇文章主要介紹了Android通過(guò)HttpURLConnection和HttpClient接口實(shí)現(xiàn)網(wǎng)絡(luò)編程的相關(guān)資料,需要的朋友可以參考下2015-02-02
在啟動(dòng)欄制作android studio啟動(dòng)圖標(biāo)
這篇文章主要介紹了在啟動(dòng)欄制作android studio啟動(dòng)圖標(biāo)的相關(guān)知識(shí),需要的朋友可以參考下2018-03-03
Android Compose 屬性動(dòng)畫使用探索詳解
這篇文章主要為大家介紹了Android Compose 屬性動(dòng)畫使用探索詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Android7.0行為變更之適配File Provider的方法
這篇文章主要介紹了Android7.0行為變更之適配File Provider的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
Android進(jìn)程運(yùn)行中權(quán)限被收回導(dǎo)致關(guān)閉的問(wèn)題解決
在Android開(kāi)發(fā)中我們可能會(huì)遇到這樣的問(wèn)題,進(jìn)程還在運(yùn)行著某些權(quán)限卻被收回了,這就導(dǎo)致進(jìn)程崩潰被迫關(guān)閉,本篇文章將帶你了解這個(gè)問(wèn)題的發(fā)生與解決方法2021-10-10
Android 表情面板和軟鍵盤切換時(shí)跳閃問(wèn)題的解決方法
這篇文章主要介紹了Android 表情面板和軟鍵盤切換時(shí)跳閃問(wèn)題的解決方法,需要的朋友可以參考下2017-08-08
淺談AnDroidDraw+DroidDraw實(shí)現(xiàn)Android程序UI設(shè)計(jì)的分析說(shuō)明
本篇文章是對(duì)AnDroidDraw+DroidDraw實(shí)現(xiàn)Android程序UI設(shè)計(jì)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05

