1.宽高比问题

我们如今相当熟悉这样一个事实,在OpenGL里,我们要渲染的一切物体都要映射到X轴和Y轴上[-1,1]的范围内,对于Z轴也一样。这个范围内的坐标被称为归一化设备坐标,其独立于屏幕实际尺寸或形状。

不幸的是,由于它们独立于实际的屏幕尺寸,假设直接使用它们,我们就会遇到问题。比如在横屏模式下被压扁的桌子。

如果实际的设备分辨率以像素为单位是1280*720,这在新的Android设备上是一个经常使用的分辨率。为了使讨论更加easy。让我们也临时假定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平移矩阵



既然理解了单位矩阵。让我们看一个很easy的矩阵类型---平移矩阵。它在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);

Android OpenGL ES(四)----调整屏幕的宽高比的更多相关文章

  1. Android OpenGL ES 开发(四): OpenGL ES 绘制形状

    在上文中,我们使用OpenGL定义了能够被绘制出来的形状了,现在我们想绘制出来它们.使用OpenGLES 2.0来绘制形状会比你想象的需要更多的代码.因为OpenGL的API提供了大量的对渲染管线的控 ...

  2. Android OpenGL ES(十二):三维坐标系及坐标变换初步 .

    OpenGL ES图形库最终的结果是在二维平面上显示3D物体(常称作模型Model)这是因为目前的打部分显示器还只能显示二维图形.但我们在构造3D模型时必须要有空间现象能力,所有对模型的描述还是使用三 ...

  3. Android OpenGL ES 开发教程 从入门到精通

    感谢,摘自:http://blog.csdn.net/mapdigit/article/details/7526556 Android OpenGL ES 简明开发教程 Android OpenGL ...

  4. Android OpenGL ES(十三)通用的矩阵变换指令 .

    Android OpenGL ES 对于不同坐标系下坐标变换,大都使用矩阵运算的方法来定义和实现的.这里介绍对应指定的坐标系(比如viewmodel, projection或是viewport) An ...

  5. Android OpenGL ES(八)绘制点Point ..

    上一篇介绍了OpenGL ES能够绘制的几种基本几何图形:点,线,三角形.将分别介绍这几种基本几何图形的例子.为方便起见,暂时在同一平面上绘制这些几何图形,在后面介绍完OpenGL ES的坐标系统和坐 ...

  6. Android OpenGL ES .介绍

    引自:http://blog.csdn.net/hgl868/article/details/6971624 1.    OpenGL ES 简介 Android 3D引擎采用的是OpenGL ES. ...

  7. Android OpenGL ES(七)基本几何图形定义 .

    在前面Android OpenGL ES(六):创建实例应用OpenGLDemos程序框架 我们创建了示例程序的基本框架,并提供了一个“Hello World”示例,将屏幕显示为红色. 本例介绍Ope ...

  8. Android OpenGL ES(六)创建实例应用OpenGLDemos程序框架 .

    有了前面关于Android OpenGL ES的介绍,可以开始创建示例程序OpenGLDemos. 使用Eclipse 创建一个Android项目 Project Name: OpenGLDemos ...

  9. Android OpenGL ES(五)GLSurfaceView .

    Android OpenGL ES 相关的包主要定义在 javax.microedition.khronos.opengles    GL 绘图指令 javax.microedition.khrono ...

随机推荐

  1. 读书笔记_Effective_C++_条款三十三:避免遮掩继承而来的名称

    名称的遮掩可以分成变量的遮掩与函数的遮掩两类,本质都是名字的查找方式导致的,当编译器要去查找一个名字时,它一旦找到一个相符的名字,就不会再往下去找了,因此遮掩本质上是优先查找哪个名字的问题. 而查找是 ...

  2. vultr vs digitalocean vs linode

    vultr官方网站:www.vultr.comdigitalocean官方网站:www.digitalocean.comlinode官方网站:www.linode.com 一般来说我们买VPS的时候都 ...

  3. eclipse鼠标变成十字架

    不知道按到什么或者点到什么button了,在eclipse里面鼠标就变成了十字架形式.解决的方法是按:alt+shift+a 原来alt+shift+a是框选代码的.长见识了!

  4. [Asp.net web api]缓存

    摘要 为了提高接口的性能,我们常做的优化就包括缓存,对经常访问但变化不大的数据进行缓存.或者使用http的缓存,减少请求的次数. web api缓存 在提供的api,我们也可以实现缓存,来减少访问的次 ...

  5. WebView具体介绍

    PART_0    侃在前面的话 WebView是Android提供给我们用来载入网页的控件.功能非常强大.我们经常使用的手机淘宝.手机京东的Androidclient里面大量使用到了WebView. ...

  6. c#中何时使用Empty()和DefalutIfEmpty()

    在项目中,当我们想获取IEnumerable<T>集合的时候,这个集合有可能是null.但通常的做法是返回一个空的集合. 假设有这样一个场景:当商店不营业时,返回一个空的IEnumerab ...

  7. 报错:未能加载文件或程序集“WebGrease, Version=1.5.1.25624, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)

    □ 背景 通过NuGet安装某程序包后,运行程序出现如上错误.   □ 分析 可能是程序集版本不兼容引起的,可以通过NuGet先把程序包删除,然后再安装最新或某个版本的程序包.   □ 解决方法 通过 ...

  8. win7无线网络共享

    一.最简单的方法: 1.使用360安全卫士 2.安装一个驱动人生 二.手工设置,参考:http://www.jb51.net/os/windows/63472.html

  9. 跟我一起学extjs5(08--自己定义菜单1)

    跟我一起学extjs5(08--自己定义菜单1) 顶部和底部区域已经作好,在顶部区域有一个菜单的button.这一节我们设计一个菜单的数据结构,使其能够展示出不相同式的菜单.因为准备搭建的是一个系统模 ...

  10. [Oracle] Insert All的妙用

    无条件的插入 Oracle中的insert all是指把同一批数据插入到不同的表中,假如如今有个需求:把t表中的数据分别插入t1,t2,假设你不知道insert all,你可能会使用insert插入2 ...