1.手机的坐标空间

我们都知道要想在手机上随心所欲的绘制图形,就必须了解手机的坐标体系。下图就是将坐标映射到手机屏幕的坐标。

图1手机屏幕基本坐标系

2.OpenGL基本图形

在OpenGL里,只能绘制点,直线以及三角形。

三角形是最基本的图形,因为它的结构如此稳定,它随处可见,比如桥梁的结构化构件,它有三条边用来连接它的三个顶点,如果我们拿掉其中一个顶点,剩下的就是一条直线,如果我们再拿掉一个点,就只剩下一个点了。

点和直线可以用于某些效果,但是只有三角形才能用来构造拥有复杂的对象和纹理的场景。在OpenGL里,我们把单独的点放在一个组里构建出三角形,再告诉OpenGL如何连接这些点。我们想要构建的所有东西都要用点,直线和三角形定义,如果想构建更复杂的图形,例如拱形,那我们就需要用足够的点拟合这样的曲线。

3.使数据可以被OpenGL存取

当我们在模拟器或者设备上编译和运行Java代码的时候,它并不是直接运行在硬件上的,相反,它运行在一个特殊的环境上,即Dalvik虚拟机。运行在虚拟机上的代码不能直接访问本地环境,除非通过特定的API。

Dalvik虚拟机还使用了垃圾回收机制。这意味着,当虚拟机检测到一个变量,对象或者其他内存片段不在被使用时,就会这些内存释放掉以备重用,它也能腾挪内存以提高空间使用效率。

本地环境并不是这样工作的,它不期望内存块会被移来移去或者被自动释放。

Android之所以这样设计,是因为开发者在开发程序的时候不必关心特定的CPU或者机器架构,也不必关心底层的内存管理。这通常都能工作得很好,除非要与本地系统交互,必须OpenGL。OpenGL作为本地系统库直接运行在硬件上,没有虚拟机,也没有垃圾回收或内存压缩。

Dalvik方案是Android主要特点之一,但是,如果代码运行在虚拟机内部,那它怎么与OpenGL通信呢?有两种技术,第一种技术是使用Java本地接口JNI,这个技术已经由Android软件开发部提供,当调用android.opengl.GLES20包里方法时,软件开发包实际上就是在后台使用JNI调用本地系统库。

第二种技术就是改变内存分配的方式,Java有一个特殊的类集合,它们可以分配本地内存块,并且把Java数据复制到本地内存。本地内存可以被本地环境存取,而不受垃圾回收器的管理。

图2 从Dalvik到OpenGL传输数据

示例:

private float[] rectangle={

-0.5f,-0.5f,

0.5f,0.5f,

-0.5f,0.5f,

-0.5f,-0.5f,

0.5f,-0.5f,

0.5f,0.5f

private static final int BYTES_PER_FLOAT=4;

private final FloatBuffer vertexData;

vertexData=ByteBuffer

.allocateDirect(rectangle.length*BYTES_PER_FLOAT)

.order(ByteOrder.nativeOrder())

.asFloatBuffer();

vertexData.put(rectangle);

这里加入一个整型常量和一个FloatBuffer类型变量,一个java的浮点数有32位精度,而一个字节只有8位精度,这点可能看起来很明显,每个浮点数都占4个字节,而FloatBuffer用来在本地内存存储数据。

首先,我们使用ByteBuffer.allocateDirect()分配了一块本地内存,这块内存不会被垃圾回收器管理。这个方法需要知道要分配多少个字节的内存块,因为顶点都存储在一个浮点数组里,并且每个浮点数有4个字节,所以这块内存的大小应该是rectangle.length*BYTES_PER_FLOAT。

下一行告诉字节缓冲区按照本地字节序组织它的内容。本地字节序是指,当一个值占用多个字节时,比如32位整数,字节按照从最重要位到最不重要位或者相反顺序排列。可以认为这与从左到右或者从右到左写一个数类似。知道这个排序并不重要,重要的是作为一个平台要使用相同的排序,调用order(ByteOrder.nativeOrder())可以保证这一点。

最后,我们不愿意直接操作单独的字节,而是希望使用浮点数,因此,调用asFloatBuffer()得到一个可以反映底层字节的FloatBuffer类实例。然后就可以调用vertexData.put(rectangle)把数据从Dalvik的内存复制到本地内存了。当进程结束时,这块内存会被释放掉,所以,我们一般情况下不用关心它。但是,如果你在编写代码的时候,创建了很多ByteBuffer,或者随着程序运行产生了很多
ByteBuffer,你也许想学习一些碎片化以及内存管理的技术。

4.引入OpenGL管道

把图形画到屏幕上之前,它需要在OpenGL管道中传递,这就需要使用称为着色器,这些着色器会告诉图形处理单元如何绘制数据。有两种类型的着色器,在绘制任何内容到屏幕之前,需要定义它们。

顶点着色器:生成每个顶点的最终位置,针对每个顶点,它都会执行一次,一旦最终位置确定了,OpenGL就可以这些可见顶点的集合组装成点,直线以及三角形。

片段着色器:为组成点,直线或者三角形的每个片段生成最终的颜色,针对每个片段,它都会执行一次,一个片段是一个小小的,单一的颜色的长方形区域,类似于计算机屏幕上的一个像素。

一旦最后的颜色生成了,OpenGL就会把它们写到一块称为帧缓冲区的内存块中,然后,Android会把这个帧缓冲区显示到屏幕上。

图3 OpenGL管道概述

5.创建顶点着色器

在Android项目中创建raw文件,把着色器放入该文件夹下,便于引用。

示例simple_vertex_shader.glsl:

attribute vec4 a_Position;

void main()

{

gl_Position=a_Position;

}

这些着色器使用GLSL定义,GLSL是OpenGL的着色语言;这个着色语言的语法结构与C语言相似。

对于我们定义过的每个单一的定点,顶点着色器都会被调用一次;当它被调用的时候,它会在a_Position属性里接收当前顶点的位置,这个属性被定义为vec4类型。

一个vec4是包含了4个分量的向量;在位置的上下文中,可以认为这4个分量是X,Y,Z和W坐标,X,Y和Z对应一个三维位置,而W是一个特殊的坐标,后面会专门讲解W坐标,现在暂时略过。如果没有指定,默认情况下,OpenGL都是把向量的前三个坐标设为0,并把最后一个坐标设为1。

一个顶点会有几个属性,比如颜色和位置。关键词"attribute"就是把这些属性放进着色器的手段。

之后,可以定义main(),这是着色器的主要入口点;它所做的就是把前面定义过的位置复制到指定的输出变量gl_Position;这个着色器一定要给gl_Position赋值;OpenGL会把gl_Position中存储的位置当作顶点的最终位置,并把这些顶点组装成点,直线和三角形。

6.创建片段着色器

光栅化技术

移动设备的显示屏是由成千上万个小的,独立的部件组成,它们被称为像素;这些像素中的每一个都有能力显示几百万种不同颜色范围中的一种。然而,这实际上是一种视觉技巧:大多数显示器并不能真正创造几百万种颜色,所以每个像素通常是由三个单独的子构建构成的,它们发出红色,绿色,和蓝色的光,因为每个像素都非常小,人的眼睛会把红色,绿色和蓝色的光混合在一起,从而创造出巨量的颜色范围;把足够多的单独的像素放在一起,就能显示出多种颜色。

OpenGL通过“光栅化”的过程把每个点,直线及三角形分解成大量的小片段,它们可以映射到移动设备显示屏上,从而生成一幅图像。这些片段类似于显示屏上的像素,每个都包含单一的纯色。为了表示颜色,每个片段都有4个分量:其中红色,绿色,蓝色用来表示颜色,阿尔法分量用来表示透明度,

图4光栅化:生成片段

编写代码示例simple_fragment_shader.glsl:

precision mediump float;

uniform vec4 u_Color;

void main()

{

gl_FragColor=u_Color;

}

这个片段着色器中,文件顶部的第一行代码定义了所有浮点数据类型的默认精度。这就像Java代码中选择浮点数还是双精度浮点数一样。

可以选择lowp,mediump,highp,它们分别对应低精度,中精度及高精度;然而,只有某些硬件实现支持在片段着色器中使用highp。

细心阅读的可以发现为什么顶点着色器没有定义精度,其实顶点着色器同样也可以定义精度,但是对于一个顶点而言,精确度是最重要的,OpenGL设计者决定把顶点着色器的精度默认设置成最高级-highp。

你可能已经猜到了,高精度数据类型更加准确,但是这是以降低性能为代价的;对于片段着色器,出于最大兼容性的考虑,选择了mediump,这也是基于速度和质量的权衡。

这个片段着色器的剩余部分与早前定义的顶点着色器一样。不过这次我们要传递一个uniform,它叫做u_Color。它不像属性,每个顶点都要设置一个;一个uniform会让每个顶点都使用同一个值,除非我们在次改变它。如定点着色器中的位置所使用的属性一样,u_Color也是一个四分量向量,但是在颜色的上下文中,这四分量分别对应红色,绿色,蓝色和阿尔法。

接着我们定义了main(),它是这个着色器的住入口点,它把我们在uniform里定义的颜色复制到那个特殊的输出变量---gl_FragColor。着色器一定要给gl_FragColor赋值,OpenGL会使用这个颜色作为当前片段的最终颜色。

记住一个句话就完全了解片段着色器:片段着色器的主要目的就是告诉GPU每个片段的最终颜色应该是什么。

记住一个句话就完全了解顶点着色器:顶点着色器的主要目的就是确定每个顶点的最终位置。

版权声明:本文为博主原创文章,未经博主允许不得转载。

Android OpenGL ES(一)----必备知识的更多相关文章

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

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

  2. Android OpenGL ES(一)OpenGL ES介绍

    在学习Android OpenGL ES开发之前,你必须具备Java 语言开发经验和一些Android开发的基本知识,但并不需要有图形开发的经验,本教程也会涉及到一些基本的线性几何知识,如矢量,矩阵运 ...

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

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

  4. [工作记录] Android OpenGL ES: non-square texture - continue

    previous: [工作记录] Android OpenGL ES 2.0: square texture not supported on some device recently I found ...

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

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

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

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

  7. Android OpenGL ES .介绍

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

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

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

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

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

随机推荐

  1. Java程序内存分析:使用mat工具分析内存占用

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  2. pic/at89c2051 programmer

    http://dangerousprototypes.com/forum/viewtopic.php?t=170 It looks like the PICKIT2 uses a small boos ...

  3. 容器的end()方法

    容器的end()方法,返回一个迭代器,需要注意:这个迭代器不指向实际的元素,而是表示末端元素的下一个元素,这个迭代器起一个哨兵的作用,表示已经处理完所有的元素. 因此,在查找的时候,返回的迭代器,不等 ...

  4. 组件化CSS--管理你整站的CSS文件

    为什么要拆分样式文件? 更易于查找样式规则. 简化维护,方便管理. 还可以针对某一页面提供特定的样式. 为什么要添加桥接样式? 你可以随时添加或移除样式而不需要修改HTML 文档. 为什么要定义两种媒 ...

  5. JS可以做什么,它的能力范围 View----------Request/Submit------------------Server

    View----------Request/Submit------------------Server javascript--------><script>标签方式(页面,动态插 ...

  6. Tcsh脚本编程

    Tcsh主要用于Free BSD等UNIX系统中. 一.输出字符串Hello的示例脚本 Tcsh脚本的基本格式.编写方法及脚本中使用的命令等,与Bash脚本完全相同,只需要直接套用即可. [root@ ...

  7. c#高效的线程安全队列ConcurrentQueue<T>(上)

      ConcurrentQueue<T>队列是一个高效的线程安全的队列,是.Net Framework 4.0,System.Collections.Concurrent命名空间下的一个数 ...

  8. 深入理解 iOS 开发中的锁

    来源:伯乐在线 - 夏天然后 链接:http://ios.jobbole.com/89474/ 点击 → 申请加入伯乐在线专栏作者 摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大 ...

  9. TCP/IP协议原理与应用笔记23:路由选择模块 和 路由表的设计

    1. 路由选择模块 和 路由表的设计 2. 路由选择算法(路由模块在路由表中查找算法) (1)用IP分组中的目的IP地址查找路由表,使用匹配表项的下一跳地址完成分组交付 (2)匹配条件: dIP &a ...

  10. Android学习笔记⑤——UI组件的学习TextView相关

    TextView是一个强大的视图组件,直接继承了View,同时也派生出了很多子类,TextView其作用说白了就是在布局中显示文本,有点像Swing编程中的JLabel标签,但是他比JLabel强大的 ...