Android OpenGL ES(四)----調整屏幕的寬高比

1.寬高比問題


我們現在相當熟悉這樣一個事實,在OpenGL里,我們要渲染的一切物體都要映射到X軸和Y軸上[-1,1]的范圍內,對于Z軸也一樣。這個范圍內的坐標被稱為歸一化設備坐標,其獨立于屏幕實際尺寸或形狀。


不幸的是,因為它們獨立于實際的屏幕尺寸,如果直接使用它們,我們就會遇到問題,例如在橫屏模式下被壓扁的桌子。


?

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


?

如果設備是在豎屏模式下,那么[-1,1]的范圍對應1280像素高,卻只有720像素寬。圖像會在X軸顯得扁平,如果在橫屏模式,同樣的問題也會發生在Y軸上。


?

歸一化設備坐標假定坐標空間是一個正方形,如下圖所示:



然而,因為實際的視口可能不是一個正方形,圖像就會在一個方向上被拉伸,在另一個方向上被壓扁。在一個豎屏設備上,歸一化設備坐標上定義的圖像看上去就是在水平方向上被壓扁了:


? ? ? ?


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




2.適應寬高比


?

我們需要調整坐標空間,以使它把屏幕的形狀考慮在內,可行的一個方法是把較小的范圍固定在[-1,1]內,而按屏幕尺寸的比例調整較大的范圍。

?

舉例來說,在豎屏情況下,其寬度是720,而高度是1280,因此我們可以把寬度范圍限定在[-1,1],并把高度范圍調整為[-1280/720,1280/720]或[-1.78,1.78]。同理在橫屏模式情況下,把高度范圍設為[-1.78,1.78],而把高度范圍設為[-1,1]。


通過調整已有的坐標空間,最終會改變我們可用的空間。


?

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



3.使用虛擬坐標空間


?

調整坐標空間,以便我們把屏幕方向考慮進來,我們需要停止直接在歸一化設備坐標上工作,遙開始在虛擬坐標空間里工作。接下來,我們需要找到某種可以把虛擬空間坐標轉化回歸依化設備坐標的方法,讓OpenGL可以正確的渲染它們。這種轉換應該把屏幕方向計算在內,以使圖像在豎屏模式和橫屏模式看上去都一樣。


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




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





當我們使用正交投影把虛擬坐標變換回歸化設備坐標時,實際上定義了三維世界內部的一個區域。在這個區域內的所有東西都會顯示在屏幕上,而區域外的所有東西都會被剪裁掉。


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



4.線性代數基礎



OpenGL大量使用了向量和矩陣,矩陣的最重要的用途之一就是建立正交和透視投影。其原因之一是,從本質上來說,使用矩陣做投影只涉及對一組數據按順序執行大量的加法和乘法,這些運算在現代GPU上執行的非常快。

?


4.1向量



一個向量是一個有多個元素的一維數組。在OpenGL里,一個位置通常是一個四元素向量,顏色也一樣。我們使用的大多數向量一般都有四個元素。在下面的例子中, 我們可看到一個位置向量,它有一個X,一個Y,一個Z,一個W分量。




4.2矩陣



一個矩陣是一個有多個元素的二維數組。在OpenGL里,我們一般使用矩陣作向量投影,如正交或者透視投影,并且也用它們旋轉物體,平移物體以及縮放物體。我們把矩陣與每個要變換的向量相乘可實現這些變換。下面就是一個矩陣:


?



4.3矩陣與向量的乘法



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


?



規則就是矩陣第一行乘以向量第一列,以第一行為例:矩陣第一行第一個元素乘以向量第一列第一個元素,加上矩陣第一行第二個元素乘以向量第一列第二個元素,加上矩陣第一行第三個元素乘以向量第一列第三個元素,加上矩陣第一行第四個元素乘以向量第一列第四個元素。矩陣二,三,四行同理。



4.4單位矩陣



單位矩陣如下:


?


所以被稱為單位矩陣,是因為這個矩陣乘以任何向量總是得到與原來相同的向量。就像把任何數字乘以1會得到原來的數字一樣。



4.5平移矩陣



?

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



?

讓我們盾一個位置(2,2)的例子,這個位置Z默認是0,W默認是1.我們把這個向量沿X軸平移3,沿Y軸也平移3,因此,把Xtranslation賦值為3,Ytranslation賦值為3。結果如下:


?


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



5.正交投影



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

?

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


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


float[] m:目標數組,這個數組長度至少有16個元素,這樣它才能存儲正交投影矩陣。

int mOffset:結果矩陣起始的偏移值。

float left:X軸的最小范圍。

float right:X軸的最大范圍。

float bottom:Y軸的最小范圍。

float top:Y軸的最大范圍。

float near:Z軸的最小范圍。

float far:Z軸的最大范圍。


當我們調用這個方法的時候,它應該產生下面的正交投影矩陣:

?



個正交投影矩陣會把所有在左右之間,上下之間和遠近之間的事物映射到歸一化設備坐標中從-1到1的范圍,在這個范圍內所有事物在屏幕上都是可見的。


主要的區別就是Z軸有一個負值符號,它的效果是反轉Z坐標。這就意味著,物體離得越遠,Z坐標的負值會越來越小。之所以這樣完全是歷史和傳統的原因。




6.左手與右手坐標系統




為了更好的理解Z軸問題,我們需要理解左手坐標系統與右手坐標系統之間的區別。想知道一個坐標系統是左手的還是右手的,你拿出一只手,把大拇指指向X軸正值方向,然后把食指指向Y軸正值方向。


?

現在,把你的中指指向Z軸。如果你需要用左手做這些,那你看到的就是一個左手坐標系統;如果你需要用右手,那你看到的就是一個右手坐標系統。把你的中指指向Z軸,記住要把大拇指指向X軸方向,食指指向Y軸正值方向。如下圖:

?



其實左手還是右手選擇都沒關系,只是一個簡單的轉換。歸一化設備坐標使用的是左手坐標系統,而在OpenGL的早期版本,默認使用的確實右手坐標系統,其使用Z的負值增加表示距離增加。這就是為什么Android的Matrix會默認生成反轉Z的矩陣。



7.更新程序


?

7.1更新著色器


?

修改前一章的頂點著色器中的代碼如下:


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的矩陣。更新位置負值那一行:



gl_Position =u_Matrix*a_Position;


?

我們沒有傳遞數組中定義的位置,而是傳遞那個位置與一個矩陣的乘積。它意味著頂點數組不用再被翻譯為歸一化設備的坐標了,其將被理解為存在于這個矩陣所定義的虛擬坐標空間中。這個矩陣會把坐標從虛擬坐標空間變化回歸一化設備坐標。



7.2添加矩陣數組和一個新的uniform



?

打開上一節的LYJRenderer添加成員變量:


?

private static final String U_MATRIX="u_Matrix";


我們還需要一個頂點數組存儲矩陣:


?

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


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


?

private int uMatrixLocation;


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


uMatrixLocation=GLES20.glGetUniformLocation(program,U_MATRIX);



7.3創建正交投影矩陣



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


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);

}


?

這段代碼會創建一個正交投影矩陣,這個矩陣會把屏幕的當前方向計算在內。注意在Android中不只有一個Matrix類,因此你要確保導入了android.opengl.Matrix。


?

我們首先計算了寬高比,它使用寬和高中的較大值除以寬和高的較小值。不管是豎屏還是橫屏,這個值都一樣。



7.4傳遞矩陣給著色器



?

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


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

?

版權聲明:本文為博主原創文章,未經博主允許不得轉載。

?

轉載于:https://www.cnblogs.com/liyuanjinglyj/p/4656547.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374912.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374912.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374912.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用Spring AOP進行面向方面的編程

面向方面的編程(AOP)是指將輔助功能或支持功能與主程序的業務邏輯隔離開來的編程范例。 AOP是用于分離橫切關注點的有前途的技術,這在面向對象的編程中通常很難做到。 以此方式增加了應用程序的模塊化,并且維護變得非常容易。 橫切…

面試題24 二叉搜索樹的后序遍歷序列

題目描述 輸入一個整數數組&#xff0c;判斷該數組是不是某二叉搜索樹的后序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。1 class Solution {2 public:3 bool VerifySquenceOfBST(vector<int> sequence) {4 if (seque…

習題6-5 使用函數驗證哥德巴赫猜想 (20 分)

本題要求實現一個判斷素數的簡單函數&#xff0c;并利用該函數驗證哥德巴赫猜想&#xff1a;任何一個不小于6的偶數均可表示為兩個奇素數之和。素數就是只能被1和自身整除的正整數。注意&#xff1a;1不是素數&#xff0c;2是素數。 函數接口定義&#xff1a; int prime( int…

Linux學習筆記 (六)用戶管理命令

一、用戶帳號 1、超級用戶&#xff1a;具有操作系統中的最高權限&#xff0c;用來管理和維護操作系統。root用戶。 2、普通用戶&#xff1a;由root用戶來創建&#xff0c;在宿主目錄中具有完全權限。 3、程序用戶&#xff1a;由應用程序添加&#xff0c;維護某個應用程序運行。…

使用Spring Security保護GWT應用程序

在本教程中&#xff0c;我們將看到如何將GWT與Spring的安全模塊&#xff08;即Spring Security&#xff09;集成在一起。 我們將看到如何保護GWT入口點&#xff0c;如何檢索用戶的憑據以及如何記錄各種身份驗證事件。 此外&#xff0c;我們將實現自定義身份驗證提供程序&#x…

用Fragment制作的Tab頁面產生的UI重疊問題

本文出處&#xff1a;http://blog.csdn.net/twilight041132/article/details/43812745 在用Fragment做Tab頁面&#xff0c;發現有時候進入應用會同時顯示多個Tab內容&#xff0c;UI發生重疊。 當應用被強行關閉后&#xff08;通過手機管家軟件手動強關&#xff0c;或系統為節省…

習題6-6 使用函數輸出一個整數的逆序數 (20 分)

本題要求實現一個求整數的逆序數的簡單函數。 函數接口定義&#xff1a; int reverse( int number );其中函數reverse須返回用戶傳入的整型number的逆序數。 我的代碼&#xff1a; int reverse( int number ) {int n number,res 0,t 0;n (n>0)?n:-n;while(n ! 0){t…

Tomcat 7上具有RESTeasy JAX-RS的RESTful Web服務-Eclipse和Maven項目

開發Web服務的RESTful方法不斷受到越來越多的關注&#xff0c;并且似乎正在將SOAP淘汰。 我不會討論哪種方法更好&#xff0c;但是我相信我們都同意REST更輕量級。 在本教程中&#xff0c;我將向您展示如何使用RESTeasy開發RESTful服務以及如何將它們部署在Tomcat服務器上。 在…

appcmd命令導入導出站點與應用程序池

在IIS7上導出所有應用程序池的方法: %windir%\system32\inetsrv\appcmd list apppool /config /xml > c:\apppools.xml 這個命令會將服務器上全部的應用程序池都導出來,但有些我們是我們不需要的,要將他們刪掉.比如: DefaultAppPoolClassic .Net AppPool如果在導入時發現同名…

卸載apache

1、運行services.msc&#xff0c;在服務中停止 apache 服務。2、運行命令行程序&#xff0c;輸入 sc delete apache&#xff0c;刪除該服務3、刪除apache文件夾。轉載于:https://www.cnblogs.com/jiangjieqim/p/5357950.html

使用wowza和xuggler將RTMP轉換為RTSP

注意&#xff1a;這是我們的“ Xuggler開發教程 ”系列的一部分。 大家好&#xff01; 在過去的三個月中&#xff0c;我們一直在進行電話會議項目。 我們認為&#xff0c;使用諸如Flex之類的技術的基于Web的應用程序將是此類要求苛刻的項目的最佳方法。 隨著軟件的復雜性和電信…

如何設置網頁標題圖標

1、先制作一個名為favicon.ico的小圖標&#xff0c;并將其放到網站根目錄下 2、在html頁面<head></head>標簽內加入&#xff1a; <link rel"shortcut icon" href"favicon.ico" />OK轉載于:https://www.cnblogs.com/moshengr/p/4600281.h…

C語言pow函數的精度問題

&#xff08;1&#xff09;pow函數返回值是double類型 &#xff08;2&#xff09;pow原型 double pow(double x,double y); (3)sqrt原型 double sqrt(double x); 當輸出時函數值賦給整型就會出錯&#xff1a; int main()d {int N 1;scanf("%d",&N);for(int i …

習題2-1 求整數均值 (10 分)

吐槽一下&#xff0c;因為少打了一個空格&#xff0c;PTA上老是不給我過&#xff0c;還一直報結果錯誤&#xff0c;誤導我好久&#xff0c;即使是吹毛求疵&#xff0c;也應該提示我格式錯誤吧&#xff01;&#xff01; 原題&#xff1a; 本題要求編寫程序&#xff0c;計算4個整…

iframe高度自適應,終于解決了

一直被iframe的高度自適應的問題困擾著&#xff0c;在項目中也是多次遇到。網上也有不少相關的代碼&#xff0c;但是總不能滿足自己的要求。在頭痛了幾次之后終于下定決心解決這個問題。 本代碼主要解決的問題是&#xff1a;最外層滾動條隨著iframe高度動態變化的問題。如果ifr…

在領域驅動的設計,貧乏的領域模型,代碼生成,依賴項注入等方面……

埃里克埃文斯&#xff08;Eric Evans&#xff09;已制定了什么是域驅動設計&#xff08;DDD&#xff09;。 Martin Fowler是DDD的大力支持者和擁護者。 這些都是非凡的名字&#xff0c;幾乎可以肯定的是&#xff0c;他們正在支持一些有價值的東西。 我不是在這里對此爭論。 也許…

Javascript模塊化工具require.js教程

轉自&#xff1a;http://www.w3cschool.cc/w3cnote/requirejs-tutorial-1.html, http://www.w3cschool.cc/w3cnote/requirejs-tutorial-2.html 隨著網站功能逐漸豐富&#xff0c;網頁中的js也變得越來越復雜和臃腫&#xff0c;原有通過script標簽來導入一個個的js文件這種方式已…

數據值、列類型和數據字段屬性

數據值&#xff1a;數值型、字符型、日期型和空值等。數據列類型 2.1 數值類的數據列類型2.2 字符串類數據列類型 2.3 日期和時間型數據數據列類型 另外&#xff0c;也可以使用整形列類型存儲UNIX時間戳&#xff0c;代替日期和時間列類型&#xff0c;這是基于PHP的web項目中常…

全文搜索Apache Lucene簡介

在本教程中&#xff0c;我想談談Apache Lucene 。 Lucene是一個開源項目&#xff0c;提供基于Java的索引和搜索技術。 使用其API&#xff0c;很容易實現全文搜索 。 我將處理Lucene Java版本 &#xff0c;但請記住&#xff0c;還有一個名為Lucene.NET的.NET端口&#xff0c;以及…

函數scanf

本節介紹輸入函數 scanf 的用法。scanf 和 printf 一樣&#xff0c;非常重要&#xff0c;而且用得非常多&#xff0c;所以一定要掌握。 概述 scanf 的功能用一句話來概括就是“通過鍵盤給程序中的變量賦值”。該函數的原型為&#xff1a; # include <stdio.h> int scanf(…