OpenGL ES 投影和坐標(biāo)[轉(zhuǎn)]

1.寬高比問題

http://blog.csdn.net/liyuanjinglyj/article/details/46624901

我們現(xiàn)在相當(dāng)熟悉這樣一個事實(shí)燥透,在OpenGL里,我們要渲染的一切物體都要映射到X軸和Y軸上[-1融涣,1]的范圍內(nèi)漱抓,對于Z軸也一樣表锻。這個范圍內(nèi)的坐標(biāo)被稱為歸一化設(shè)備坐標(biāo),其獨(dú)立于屏幕實(shí)際尺寸或形狀乞娄。

不幸的是瞬逊,因?yàn)樗鼈儶?dú)立于實(shí)際的屏幕尺寸显歧,如果直接使用它們,我們就會遇到問題确镊,例如在橫屏模式下被壓扁的桌子士骤。

假設(shè)實(shí)際的設(shè)備分辨率以像素為單位是1280*720,這在新的Android設(shè)備上是一個常用的分辨率骚腥。為了使討論更加容易敦间,讓我們也暫時假定OpenGL占用整個顯示屏瓶逃。

如果設(shè)備是在豎屏模式下束铭,那么[-1,1]的范圍對應(yīng)1280像素高,卻只有720像素寬厢绝。圖像會在X軸顯得扁平契沫,如果在橫屏模式,同樣的問題也會發(fā)生在Y軸上昔汉。

歸一化設(shè)備坐標(biāo)假定坐標(biāo)空間是一個正方形懈万,如下圖所示:

然而,因?yàn)閷?shí)際的視口可能不是一個正方形靶病,圖像就會在一個方向上被拉伸会通,在另一個方向上被壓扁。在一個豎屏設(shè)備上娄周,歸一化設(shè)備坐標(biāo)上定義的圖像看上去就是在水平方向上被壓扁了:

在橫屏模式下涕侈,同樣的圖像就在另一個方向上看起來被壓扁的。

2.適應(yīng)寬高比

我們需要調(diào)整坐標(biāo)空間煤辨,以使它把屏幕的形狀考慮在內(nèi)裳涛,可行的一個方法是把較小的范圍固定在[-1,1]內(nèi),而按屏幕尺寸的比例調(diào)整較大的范圍众辨。

舉例來說端三,在豎屏情況下,其寬度是720鹃彻,而高度是1280郊闯,因此我們可以把寬度范圍限定在[-1,1],并把高度范圍調(diào)整為[-1280/720,1280/720]或[-1.78,1.78]蛛株。同理在橫屏模式情況下团赁,把高度范圍設(shè)為[-1.78,1.78],而把高度范圍設(shè)為[-1,1]泳挥。

通過調(diào)整已有的坐標(biāo)空間然痊,最終會改變我們可用的空間。

通過這個方法屉符,不論是豎屏模式還是橫屏模式剧浸,物體看起來就都一樣了锹引。

3.使用虛擬坐標(biāo)空間

調(diào)整坐標(biāo)空間,以便我們把屏幕方向考慮進(jìn)來唆香,我們需要停止直接在歸一化設(shè)備坐標(biāo)上工作嫌变,遙開始在虛擬坐標(biāo)空間里工作。接下來躬它,我們需要找到某種可以把虛擬空間坐標(biāo)轉(zhuǎn)化回歸依化設(shè)備坐標(biāo)的方法腾啥,讓OpenGL可以正確的渲染它們。這種轉(zhuǎn)換應(yīng)該把屏幕方向計(jì)算在內(nèi)冯吓,以使圖像在豎屏模式和橫屏模式看上去都一樣倘待。

我們想要進(jìn)行的操作叫作正交投影。使用正交投影组贺,不管多遠(yuǎn)或者多近凸舵,所有物體看上去大小總是相同的。為了更高的理解這種投影是做什么的失尖,想象一下啊奄,在我們的場景中有一個火車軌道,直接從空中俯瞰掀潮,這些軌道看起來是這樣的:

還有一種特殊類型的正交投影菇夸,被稱為等軸測投影,它是從側(cè)角觀察一種正交投影仪吧。正如在一些城市模擬和策略游戲中看到的庄新,這種類型的投影能用來重新創(chuàng)建一個經(jīng)典的三維角。

當(dāng)我們使用正交投影把虛擬坐標(biāo)變換回歸化設(shè)備坐標(biāo)時邑商,實(shí)際上定義了三維世界內(nèi)部的一個區(qū)域摄咆。在這個區(qū)域內(nèi)的所有東西都會顯示在屏幕上,而區(qū)域外的所有東西都會被剪裁掉人断。

利用正交投影矩陣改變立方體的大小吭从,以使我們可以在屏幕上看到或多或少的場景。我們也能改變立方體的形狀彌補(bǔ)屏幕的寬高比的影響恶迈。

4.線性代數(shù)基礎(chǔ)

OpenGL大量使用了向量和矩陣涩金,矩陣的最重要的用途之一就是建立正交和透視投影。其原因之一是暇仲,從本質(zhì)上來說步做,使用矩陣做投影只涉及對一組數(shù)據(jù)按順序執(zhí)行大量的加法和乘法,這些運(yùn)算在現(xiàn)代GPU上執(zhí)行的非衬胃剑快全度。

4.1向量

一個向量是一個有多個元素的一維數(shù)組。在OpenGL里斥滤,一個位置通常是一個四元素向量将鸵,顏色也一樣勉盅。我們使用的大多數(shù)向量一般都有四個元素。在下面的例子中顶掉, 我們可看到一個位置向量草娜,它有一個X,一個Y痒筒,一個Z宰闰,一個W分量。

4.2矩陣

一個矩陣是一個有多個元素的二維數(shù)組簿透。在OpenGL里移袍,我們一般使用矩陣作向量投影,如正交或者透視投影萎战,并且也用它們旋轉(zhuǎn)物體咐容,平移物體以及縮放物體。我們把矩陣與每個要變換的向量相乘可實(shí)現(xiàn)這些變換蚂维。下面就是一個矩陣:

4.3矩陣與向量的乘法

要讓矩陣乘以一個向量,我們把矩陣放在左邊路狮,向量放在右邊虫啥。如下:

規(guī)則就是矩陣第一行乘以向量第一列,以第一行為例:矩陣第一行第一個元素乘以向量第一列第一個元素奄妨,加上矩陣第一行第二個元素乘以向量第一列第二個元素涂籽,加上矩陣第一行第三個元素乘以向量第一列第三個元素,加上矩陣第一行第四個元素乘以向量第一列第四個元素砸抛。矩陣二评雌,三,四行同理直焙。

4.4單位矩陣

單位矩陣如下:

之所以被稱為單位矩陣景东,是因?yàn)檫@個矩陣乘以任何向量總是得到與原來相同的向量。就像把任何數(shù)字乘以1會得到原來的數(shù)字一樣奔誓。

4.5平移矩陣

既然理解了單位矩陣斤吐,讓我們看一個非常簡單的矩陣類型---平移矩陣。它在OpenGL里十分常用厨喂。使用這種類型的矩陣和措,我們可以把一個物體沿著指定的距離移動。這個矩陣和單位矩陣差不多蜕煌,但在右側(cè)指定了三個額外的元素:

讓我們盾一個位置(2派阱,2)的例子,這個位置Z默認(rèn)是0斜纪,W默認(rèn)是1.我們把這個向量沿X軸平移3贫母,沿Y軸也平移3故响,因此,把Xtranslation賦值為3颁独,Ytranslation賦值為3彩届。結(jié)果如下:

這個位置正是我們所期望和(5,5)誓酒。

5.正交投影

要定義正交投影樟蠕,我們將使用Android的Matrix類,它在android.opengl包中靠柑。這個類有一個稱為orthoM()的方法寨辩,它可以為我們生成一個正交投影。

我們來看一下orthoM()參數(shù):

orthom(float[] m,int mOffset,float left,float rigth,float bottom,float top,float near,float far)

float[] m:目標(biāo)數(shù)組歼冰,這個數(shù)組長度至少有16個元素靡狞,這樣它才能存儲正交投影矩陣。

int mOffset:結(jié)果矩陣起始的偏移值隔嫡。

float left:X軸的最小范圍甸怕。

float right:X軸的最大范圍。

float bottom:Y軸的最小范圍腮恩。

float top:Y軸的最大范圍梢杭。

float near:Z軸的最小范圍。

float far:Z軸的最大范圍秸滴。

當(dāng)我們調(diào)用這個方法的時候武契,它應(yīng)該產(chǎn)生下面的正交投影矩陣:

這個正交投影矩陣會把所有在左右之間,上下之間和遠(yuǎn)近之間的事物映射到歸一化設(shè)備坐標(biāo)中從-1到1的范圍荡含,在這個范圍內(nèi)所有事物在屏幕上都是可見的咒唆。

主要的區(qū)別就是Z軸有一個負(fù)值符號,它的效果是反轉(zhuǎn)Z坐標(biāo)释液。這就意味著全释,物體離得越遠(yuǎn),Z坐標(biāo)的負(fù)值會越來越小均澳。之所以這樣完全是歷史和傳統(tǒng)的原因恨溜。

6.左手與右手坐標(biāo)系統(tǒng)

為了更好的理解Z軸問題,我們需要理解左手坐標(biāo)系統(tǒng)與右手坐標(biāo)系統(tǒng)之間的區(qū)別找前。想知道一個坐標(biāo)系統(tǒng)是左手的還是右手的糟袁,你拿出一只手,把大拇指指向X軸正值方向躺盛,然后把食指指向Y軸正值方向项戴。

現(xiàn)在,把你的中指指向Z軸槽惫。如果你需要用左手做這些周叮,那你看到的就是一個左手坐標(biāo)系統(tǒng)辩撑;如果你需要用右手,那你看到的就是一個右手坐標(biāo)系統(tǒng)仿耽。把你的中指指向Z軸合冀,記住要把大拇指指向X軸方向,食指指向Y軸正值方向项贺。如下圖:

其實(shí)左手還是右手選擇都沒關(guān)系君躺,只是一個簡單的轉(zhuǎn)換。歸一化設(shè)備坐標(biāo)使用的是左手坐標(biāo)系統(tǒng)开缎,而在OpenGL的早期版本棕叫,默認(rèn)使用的確實(shí)右手坐標(biāo)系統(tǒng),其使用Z的負(fù)值增加表示距離增加奕删。這就是為什么Android的Matrix會默認(rèn)生成反轉(zhuǎn)Z的矩陣俺泣。

7.更新程序

7.1更新著色器

修改前一章的頂點(diǎn)著色器中的代碼如下:

uniform mat4 u_Matrix;

attribute vec4 a_Position;

attribute vec4 a_Color;

varying vec4 v_Color;

void main()

{

v_Color=a_Color;

gl_Position =u_Matrix*a_Position;

gl_PointSize = 10.0;

}

我們這里添加了一個新的uniform定義的“u_Matrix”,并把它定義為一個mat4類型完残,意思是這個uniform代表一個4*4的矩陣伏钠。更新位置負(fù)值那一行:

gl_Position =u_Matrix*a_Position;

我們沒有傳遞數(shù)組中定義的位置,而是傳遞那個位置與一個矩陣的乘積坏怪。它意味著頂點(diǎn)數(shù)組不用再被翻譯為歸一化設(shè)備的坐標(biāo)了贝润,其將被理解為存在于這個矩陣所定義的虛擬坐標(biāo)空間中。這個矩陣會把坐標(biāo)從虛擬坐標(biāo)空間變化回歸一化設(shè)備坐標(biāo)铝宵。

7.2添加矩陣數(shù)組和一個新的uniform

打開上一節(jié)的LYJRenderer添加成員變量:

private static final String U_MATRIX="u_Matrix";

我們還需要一個頂點(diǎn)數(shù)組存儲矩陣:

private final float[] projectionMatrix=new float[16];

我們也需要一個整型值用于保存那個矩陣uniform的位置:

private int uMatrixLocation;

然后我們只需要在onSurfaceCreated()中加入如下代碼:

uMatrixLocation=GLES20.glGetUniformLocation(program,U_MATRIX);

7.3創(chuàng)建正交投影矩陣

更新onSurfaceChanged(),在GLES20.glViewport()調(diào)用后面加入如下代碼:

final float aspectRatio=width>height?(float)width/(float)heigth:(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);

}

這段代碼會創(chuàng)建一個正交投影矩陣华畏,這個矩陣會把屏幕的當(dāng)前方向計(jì)算在內(nèi)鹏秋。注意在Android中不只有一個Matrix類,因此你要確保導(dǎo)入了android.opengl.Matrix亡笑。

我們首先計(jì)算了寬高比侣夷,它使用寬和高中的較大值除以寬和高的較小值。不管是豎屏還是橫屏仑乌,這個值都一樣百拓。

7.4傳遞矩陣給著色器

在LYJRenderer中的onDrawFrame()中,我們在glClear()調(diào)用之后加入如下代碼:

GLES20.glUniformMatrix4fv(uMatrixLocation,1,false,projectionMatrix,0);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晰甚,一起剝皮案震驚了整個濱河市衙传,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌厕九,老刑警劉巖蓖捶,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扁远,居然都是意外死亡俊鱼,警方通過查閱死者的電腦和手機(jī)刻像,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來并闲,“玉大人细睡,你說我怎么就攤上這事〉刍穑” “怎么了溜徙?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長购公。 經(jīng)常有香客問我萌京,道長,這世上最難降的妖魔是什么宏浩? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任知残,我火速辦了婚禮,結(jié)果婚禮上比庄,老公的妹妹穿的比我還像新娘求妹。我一直安慰自己,他們只是感情好佳窑,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布制恍。 她就那樣靜靜地躺著,像睡著了一般神凑。 火紅的嫁衣襯著肌膚如雪净神。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天溉委,我揣著相機(jī)與錄音鹃唯,去河邊找鬼。 笑死瓣喊,一個胖子當(dāng)著我的面吹牛坡慌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播藻三,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼洪橘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了棵帽?” 一聲冷哼從身側(cè)響起熄求,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎岖寞,沒想到半個月后抡四,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年指巡,在試婚紗的時候發(fā)現(xiàn)自己被綠了淑履。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡藻雪,死狀恐怖秘噪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情勉耀,我是刑警寧澤指煎,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站便斥,受9級特大地震影響至壤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枢纠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一像街、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晋渺,春花似錦镰绎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至八千,卻和暖如春吗讶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背恋捆。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工关翎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸠信。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像论寨,于是被迫代替她去往敵國和親星立。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容