我们如今准备好在代码中加入透视投影了。

Android的Matrix类为它准备了两个方法------frustumM()和perspectiveM()。

不幸的是。frustumM()的个缺陷,它会影响某些类型的投影,而perspectiveM()仅仅是从Android的ICS版本号開始才被引入,在早期的Android版本号里并没有这种方法。我们能够简单地支持ICS及其以上的版本号。可是这样会丢掉非常大一部分市场。一些用户依旧执行早期的Android版本号。

作为替代,我们能够创建我们自己的方法来实现投影矩阵。

1.创建自己的perspectiveM


在工具包中创建新的类MatrixHelper,在開始处增加例如以下方法签名:

public static void perspectiveM(float[] m,float yFovInDegress,float aspect,float n,float f){

计算焦距

我们要做的第一件事就是计算焦距,这将基于在Y轴上的视野。就在方法签名之后增加例如以下代码:

final float angleInRadians=(float)(yFovInDegress*Math.PI/180.0);

final float a=(float)(1.0/Math.tan(angleInRadians/2.0));

我们使用Java的Math类计算那个正切函数。由于它须要弧度角,所以我们把视野从度转换为弧度。接着计算焦距。

输出矩阵

我们如今能够更具上一节的数学证明直接写出矩阵。添加例如以下代码:

m[0]=a/aspect;

m[1]=0f;

m[2]=0f;

m[3]=0f;





m[4]=0f;

m[5]=a;

m[6]=0f;

m[7]=0f;





m[8]=0f;

m[9]=0f;

m[10]=-((f+n)/(f-n));

m[11]=-1f;





m[12]=0f;

m[13]=0f;

m[14]=-((2f*f*n)/(f-n));

m[15]=0f;

这就是把矩阵数据存到了參数m定义的浮点数组中。这个数组须要至少16个元素。

OpenGL把矩阵数据依照以列为主的顺序存储。这就意味着我们一次写一列数据,而不是一次写一行。前四个值是第一列。下一组四个数是第二列,以此类推。

2.開始使用投影矩阵


我们如今将转而使用那个透视投影矩阵了。

打开你的渲染类,并从onSurfaceChanged()去掉全部的代码。仅仅保留glViewPort()调用,增加例如以下代码:

MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width / (float) height, 1f, 10f);

这会用45度的视野创建一个透视投影。 这个视椎体从Z值为-1的位置開始。在Z值为-10的位置结束。

增加MatrixHelper的导入后,继续前面的程序执行。你能够会发现桌面不见了,由于我们没有给桌子指定Z的位置,默认情况下Z在为0的位置。由于这个视椎体是从Z值为-1的位置開始的,除非把它移动到那个距离内,否则我们无法看到桌子。

不要硬编码Z的值,在使用投影矩阵进行投影之前,让我们使用一个平移矩阵把桌子移出来。

按照惯例,我们把这个矩阵称为模型矩阵。

利用模型矩阵移动物体

在类的顶部增加例如以下矩阵的定义:

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

我们要使用这个矩阵把桌面移动到那个距离内。在onSurfaceChanged()结尾处,增加例如以下代码:

Matrix.setIdentityM(modelMatrix, 0);

Matrix.translateM(modelMatrix,0,0f,0f,-2f);

这就是把模型矩阵设为单位矩阵,在沿着Z轴平移-2。当我们把桌面的坐标与这个矩阵相乘的时候,那些坐标终于会沿着Z轴负方向移动2个单位。

相乘一次还是相乘两次

我们如今要做一个选择:我们依旧须要把这个矩阵应用于每一个顶点,因此第一个选项是给顶点着色器新增一个额外的矩阵。我们把每一个顶点都与这个模型矩阵相乘,让它们沿着Z轴负方向移动2个单位,接下来把每一个顶点与投影矩阵相乘。这样。OpenGL就能够做透视除法,并把这些顶点变换到归一化设备坐标了。

假设不想这么麻烦,另一个更好的方式:我们能够把模型矩阵与投影矩阵相乘,得到一个矩阵,然后把这个矩阵传递给顶点着色器。通过这样的方式我们就能够在着色器中仅保留一个矩阵。

选择适当的顺序

为了弄清楚我们应该使用那种顺序。让我们看一下仅仅使用投影矩阵的数学运算:

VerteXeye代表场景中的顶点与投影矩阵相乘之前的位置。

我们一旦增加模型矩阵来移动那个桌子,这个数学运算看起来就像这样。

VerteXmodel代表顶点在模型矩阵放进场景中之前的位置。把这两个表达式合并在一起。最后得到的公式例如以下:

为了使用一个矩阵替换这两个矩阵,我们不得不把投影矩阵乘以模型矩阵,就是把投影矩阵放在左边,把模型矩阵放在右边。

更新代码使用一个矩阵

让我们把这个新的矩阵代码封装一下。把以下的代码加到onSurfaceChanged()中的translate()调用后面:

final float[] temp=new float[16];

Matrix.multiplyMM(temp,0,projectionMatrix,0,modelMatrix,0);

System.arraycopy(temp, 0, projectionMatrix, 0, temp.length);

任何时候把两个矩阵相乘,都须要一个暂时变量来存储其结果。假设尝试直接写入这个结果,这个结果将是没有定义的。

我们首先创建了一个暂时的浮点数组用来存储其暂时结果。然后调用multiplyMM()把投影矩阵和模型矩阵相乘,其结果存进这个暂时数组。下一步。我们调用System.arraycopy()把结果存回projectMatrix,它如今包括模型矩阵与投影矩阵的组合效应。

假设我们如今执行这个程序,会发现这依旧是个平面的桌面。

3.添加旋转


既然我们已经有一个配置好的投影矩阵和一个能够移动的桌子的模型矩阵,那么我们须要做的就是旋转这张桌子。以便能够从某个角度观察它。假设使用旋转矩阵,我们仅仅须要一行代码就能够做到。我们还从来没实用过旋转,如今花一些时间了解一下这些旋转是怎么工作的。

须要弄清楚一件时是我们须要环绕哪个轴旋转以及旋转多少度。

让我们再看下图:

要搞清楚一个物体是怎么环绕一个给定的轴旋转,我们将使用右手坐标规则:伸出你的右手,握拳,让大拇指指向正轴方向。倘若是一个正角度的旋转,卷曲的手指会告诉你一个物体是怎么环绕那个轴旋转的。观察上图。当你把大拇指指向X轴正方向时,看看旋转的方向是怎么尾随手指的绕轴线卷曲的。

分别用X轴。Y轴和Z轴试一下这个旋转。假设绕Y轴旋转,桌子会绕着它的顶端和低端水平旋转。假设绕着Z轴旋转。桌子会在一个圆圈内旋转。我们想要做的是让桌子绕着X轴向后旋转,由于这会让桌子看起来更有层次。

旋转矩阵

我们将使用一个旋转矩阵去做实际的旋转。

旋转矩阵使用正弦和余弦三角函数把旋转转换成缩放因子。以下就是绕X轴旋转所用矩阵定义:

然后是绕Y轴旋转所用的矩阵:

最后,另一个绕Z轴旋转所用的矩阵:

把全部矩阵合并为一个通用的旋转矩阵。使其能够基于随意一个角度和向量旋转,这也是可能的。

作为一个測试,让我们试试绕着X轴旋转。

我们从一个点開始,它在原点上面一个单位,也就是Y值是1。

把它绕X轴旋转90度。首先,让我们准备这个旋转矩阵:

让把这个矩阵与这个点的位置向量相乘,看看得到了什么:

这个点从(0。1。0)被移动到了(0,0。1)。

假设我们回过头来看一下上面那个旋转坐标轴,并对X轴使用右手规则。我们能够看到正向旋转是怎样把一个点沿着一个绕X轴的圈移动的。

4.在代码中增加旋转


我们如今准备好把这个旋转增加代码了。回到onSurfaceChanged(),调整那个平移矩阵,并增加一个旋转矩阵,例如以下:

Matrix.translateM(modelMatrix,0,0f,0f,-2.5f);

Matrix.rotateM(modelMatrix,0,-60f,1f,0f,0f);

我们把这张桌子放得更远了一点,由于我们一旦把它旋转了,它的底部会距离我们更近。我们接着把它绕X轴旋转-60度。这会让桌子处于一个非常好的角度,就像我们站在它前面一样。

这张桌子如今看起来应该例如以下图所看到的:

Android OpenGL ES(六)----进入三维在代码中创建投影矩阵和旋转矩阵的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

  7. Android OpenGL ES .介绍

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

  8. Android OpenGL ES(五)GLSurfaceView .

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

  9. Android OpenGL ES(三)OpenGL ES API 命名习惯 .

    OpenGL ES是个跨平台的3D图形开发包规范,最常见的实现是采用C语言实现的,Android OpenGL ES 实现上是使用Java 语言对底层的C接口进行了封装,因此在android.open ...

随机推荐

  1. POJ 2955 区间DP Brackets

    求一个括号的最大匹配数,这个题可以和UVa 1626比较着看. 注意题目背景一样,但是所求不一样. 回到这道题上来,设d(i, j)表示子序列Si ~ Sj的字符串中最大匹配数,如果Si 与 Sj能配 ...

  2. 【12】link与@import的区别

    [12]link与@import的区别 link是HTML方式, @import是CSS方式 link最大限度支持并行下载,@import过多嵌套导致串行下载,出现FOUC link可以通过rel=& ...

  3. jquery 页面加载效果

    30个jquery 页面加载效果 30个jquery 页面加载效果   30 CSS Page Preload Animations   加载效果列表 Square Animations Demo 1 ...

  4. Python基础数据类型之字符串

    Python基础数据类型之字符串 一.Python如何创建字符串 在python中用引号将一些文本包起来就构成了字符串(引号可以是单引号.双引号.单三引号,双三引号,它们是完全相同的) >> ...

  5. 送信 okhttp

    package jp.co.gunmabank.minefocus.accountApp import android.content.Intentimport android.graphics.Co ...

  6. 刷题总结——支线剧情(bzoj3876费用流)

    题目: [故事背景] 宅男JYY非常喜欢玩RPG游戏,比如仙剑,轩辕剑等等.不过JYY喜欢的并不是战斗场景,而是类似电视剧一般的充满恩怨情仇的剧情.这些游戏往往 都有很多的支线剧情,现在JYY想花费最 ...

  7. spring之Annotation

    spring除了提供了@Autowired,还提供了以下几类annotation. 1.@Component, @Repository, @Service, @Controller @Reposito ...

  8. essential c++ 随笔

    编写一个C++程序: vector初始化两种方法: vector<int>elem_seq(seq_size); elem_seq[0]=1 elem_seq[1]=2; 另一种方法则是利 ...

  9. hdu 5288 ZCC loves straight flush

    传送门 ZCC loves straight flush Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K ...

  10. php生成压缩包

    $filename = "./" . date ( 'YmdH' ) . ".zip"; // 最终生成的文件名(含路径) // 生成文件 $zip = new ...