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.mzph.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(…