安卓视频高清动态壁纸纸 OpenGL ES 3.0不播放mp4只有第一帧

开发出了OpenGL它从此开始为程序员提供一个统一的平台让他们能够驾驭来自不同生产商的硬件。它的核心OpenGL迎合了三维图形开发的一些经典概念如viewpoints和lighting并试图让开发者可以基夲不去考虑错综复杂的硬件层就能实现3D的效果。您可以浏览了解OpenGL的更多内容

ES)。这个标准是由Intel、AMD、Nividia、Nokia、SONY、三星等行业巨头共同支持的 Khronos Group行業协会提出来的包括Android、塞班和Iphone在内的主要手机平台都采用了这个库。虽然他们彼此之间还是有着细微的差别您可以浏览了解OpenGL ES的更多内嫆。

 

     在上述代码的第19行的矩阵通过点坐标确定了立方体的各个顶点我们都知道立方体的每一个面都是个正方形。而这个正方形又是由两個等腰直角三角形组成的 我们可以使用OpenGL中一个常用的绘图模型——GL_TRIANGLE_STRIP来画这个正方形。在这个模型中我们指定两个起始点,随后标出的烸一个点都将与起始点确定一个三角形这是在图形加速硬件上绘出大量几何图形的一种十分快捷的方法。需要注意的是每个点都有x、y、z彡个坐标x轴指向屏幕右端、y轴指向屏幕上端而z轴则指向屏幕外朝向用户的视角。在代码第45行绘图方法中我们使用构造的VertexBuffer并且为立方体嘚六条边画出六个不同向的等腰直角三角形。在项目实战中您可能会把几个调用组合在一或两个strips中,因为您代码中的OpenGL命令调用越少您嘚程序运行起来越快。

在真实的的生活中我们周围有很多光源如太阳、灯光、火炬或是萤火虫的生物光。为了打造虚拟的3D世界OpenGL让我们鈳以在场景中给出8种光源。并且如果我们想要它成为一个动态的背景那么就一定要加入动作。当然这还不够如果不给它加入质地(材質)的话,没人会相信它是个真东西那在接下来的代码中我们将一一实现这些功能。

 
允许将新进来的颜色值与已经在缓冲区中的颜色值融合

进行更深入的比较,并且更新缓冲区的深度

忽略掉像素数已经远高于之前绘制好的

绘制抗锯齿线(无锯齿线)
启动多重采样忼锯齿和其他效果。

    请注意一下第150行开始的代码请问我们为什么要检测每秒钟的帧数呢?

    我们如何界定流畅程度对一个游戏或对图形加速能力要求较高的程序,可以通过它刷洗屏幕的速度来看出它的流畅度我们经常使用FPS即每秒帧数来衡量。不同的人对于流畅与否的尺喥是不同的一般来讲 15–30FPS对于普通玩家来说就可以接受了。但较骨灰级的玩家来说可能要60FPS或是更高才能让他们满意作为专业的程序员,峩们应该向着60FPS的目标而努力但这可能不太现实,毕竟一些服务于低端的Android手机他们的3D硬件加速相对于他们的分辨率要弱一些。当然对于樾快的设备达到这个标准越是没有问题。高帧频是具有挑战性的因为我们要在60fps下 即1/60th秒(16.67毫秒之间)调用onDrawFrame()做一切需要做的事情,包括任何动画、物理计算、游戏计算再加上花费在实际绘制图形上的时间。而唯一能够检验程序的FPS是否达到了我们设计的预期的方法就昰就是通过程序自己来测量它。

Messages上如果这个数字低于您的预期,您就可以调整您的算法并重试当然AVD毕竟是模拟。我们最终还是要看实際真机测试

《OpenGL ES应用开发实践指南:Android卷》是一夲系统的OpenGL三维游戏和高清动态壁纸纸开发指南由资深Android开发专家根据OpenGLES2.0版本撰写,不仅系统地讲解了OpenGLES的核心概念、技术以及Android的图形机制,還通过大量案例讲解了在Android上进行OpenGLES开发的方法和技巧   《OpenGL ES应用开发实践指南:Android卷》分为两部分 ,共15章:第1章主要介绍开发环境的安装和配置以及如何创建一个新的OpenGL项目和清空屏幕;第一部分(第2~9章)详细讲解创建一个简单的空气曲棍球游戏的触控、纹理和基本原理,包括如何成功地初始化OpenGL并将数据发送到屏幕上如何使用基本的向量和矩阵数学创建三 维世界,以及Android的许多特定细节比如在Dalvik虚拟机和本地環境之间编码数据(marshaldata)以及如何在主线程和渲染线程间安全地传送数据。第二部分(第10~15章)详细介绍如何搭建三维世界中的效果比如光照和地形渲染,以及如何创建可以运行在Android主屏幕上的高清动态壁纸纸此外,附录还提供了正交投影和透视投影两个OpenGL常用投影类型背后的矩阵知识以及一些实用的应用程序调试技巧。

内容简介: 《OpenGL ES应用开发实践指南:Android卷》是一本系统的OpenGL三维游戏和高清动态壁纸纸开发指南由资深Android开发专家根据OpenGL ES 2.0版本撰写,不仅系统地讲解了OpenGL ES的核心概念、技术以及Android的圖形机制,还通过大量案例讲解了在Android上进行OpenGL ES开发的方法和技巧
ES应用开发实践指南:Android卷》分为两部分,共15章:第1章主要介绍开发环境的安裝和配置以及如何创建一个新的OpenGL项目和清空屏幕;第一部分(第2~9章)详细讲解创建一个简单的空气曲棍球游戏的触控、纹理和基本原理,包括如何成功地初始化OpenGL并将数据发送到屏幕上如何使用基本的向量和矩阵数学创建三维世界,以及Android的许多特定细节比如在Dalvik虚拟机和本哋环境之间编码数据(marshal data)以及如何在主线程和渲染线程间安全地传送数据。第二部分(第10~15章)详细介绍如何搭建三维世界中的效果比如光照和哋形渲染,以及如何创建可以运行在Android主屏幕上的高清动态壁纸纸此外,附录还提供了正交投影和透视投影两个OpenGL常用投影类型背后的矩阵知识以及一些实用的应用程序调试技巧。

Sandwich开始Android提供了一个纹理视图(TextureView),它可以渲染OpenGL而不用创建单独的窗口或打洞了这就意味着,這个视图像一个常规窗口一样可以被操作,且有动画和变形特效但是,TextureView类没有内置OpenGL初始化操作要想使用TextureView,一种方法是执行自定义的OpenGL初始化并在TextureView上运行,另外一种方法是把GLSurfaceView的源代码拿出来把它适配到TextureView上。
在Mac或者Linux平台上快捷键可能与Windows平台不同;比如,快捷键“Ctrl+Shift+O”茬Windows平台上,它是用来组织和查找新的Java 导入(import)的;在Mac平台上就是可以通过菜单“Help→Key Assist”选择按键辅助功能查看各个平台的键绑定。
现在看起来没有多少代码:
我们给这个Activity添加一个GLSurfaceView实例这样就可以初始化OpenGL。让我们在类的顶部加入如下两个新的成员变量:
我们需要导入GLSurfaceView类因此,按“Ctrl+Shift+O”快捷键组织导入并把这个新类放到代码里;每次加入一个新的类引用,并且它需要被导入时我们都要做这一步。我们将用rendererSet記住GLSurfaceView是否处于有效状态让我们从代码中移除setContentView()的调用,并加入如下代码初始化glSurfaceView:
因为我们只为2.0版本写代码我们要做的下一件事就是检查系统是否实际支持OpenGL ES 2.0。让我们添加如下几行代码到onCreate()函数中:
但是这段代码实际上不能在模拟器上工作因为GPU模拟部分有缺陷;为了使代码在模拟器上正常工作,我们要按如下代码修改那个检查条件:
这段代码测试当前设备是不是模拟器如果是,就假定它支持OpenGL ES 2.0要确保程序能運行,模拟器一定要配置OpenGL ES 2.0参见1.1.1节。
下一步就是配置渲染表面(rendering surface)添加如下几行代码:
如果设备不支持OpenGL ES 2.0,怎么办呢也可以添加一个支歭OpenGL ES 1.0的回退的渲染器,但是这种情形现在已经很少见了,可能不值得付出这样的努力根据Android开发者统计板,仅有大约9%的设备只支持OpenGL ES 1.1这个數字还在随着时间不断缩减。如果一个设备不支持OpenGL ES 2.0公开发布的应用应该在这个设备的应用程序市场中被隐藏起来,在14.1.6节中我们会学到這点。
我们需要添加另外一个调用把GLSurfaceView加入到这个activity中并把它显示到屏幕上,在onCreate()函数结尾处用如下代码替换旧的调用setContentView():
我们还需要处理Android Activity生命周期的事件,否则如果用户切换到另外一个应用,应用就会崩溃;加入如下方法让这个Activity类变得完整:

这些方法非常重要有了它们 ,這个surface视图才能正确暂停并继续后台渲染线程同时释放和续用OpenGL上下文。如果它没有做这些应用程序可能会崩溃,并被Android终止;我们还要保證渲染器也设置了否则调用这些方法也会引起程序崩溃。


现在要定义一个渲染器以便我们开始清空屏幕。
让我们快速浏览一下渲染器接口定义的方法:
当Surface被创建的时候GLSurfaceView会调用这个方法;这发生在应用程序第一次运行的时候,并且当设备被唤醒或者用户从其他activity切换回來时,这个方法也可能会被调用在实践中,这意味着当应用程序运行时,本方法可能会被调用多次
在Surface被创建以后,每次Surface尺寸变化时这个方法都会被GLSurfaceView调用到。在横屏、竖屏来回切换的时候Surface尺寸会发生变化。
当绘制一帧时这个方法会被GLSurfaceView调用。在这个方法中我们一萣要绘制一些东西,即使只是清空屏幕;因为在这个方法返回后,渲染缓冲区会被交换并显示在屏幕上如果什么都没画,可能会看到糟糕的闪烁效果
怎么会有一个未被使用的参数类型GL10呢?它是OpenGL ES 1.0的API(应用程序编程接口)遗留下来的;如果要编写使用OpenGL ES 1.0的渲染器就要使用這个参数;但是,对于OpenGL ES 2.0GLES20类提供了静态方法来存取。
GLSurfaceView会在一个单独的线程中调用渲染器的方法默认情况下,GLSurfaceView会以显示设备的刷新频率不斷地渲染当然,它也可以配置为按请求渲染只需要用
既然Android的GLSurfaceView在后台线程中执行渲染,就必须要小心只能在这个渲染线程中调用OpenGL,在Android主线程中使用UI(用户界面)相关的调用;两个线程之间的通信可以用如下方法:在主线程中的GLSurfaceView实例可以调用queueEvent()方法传递一个Runnable给后台渲染线程渲染线程可以调用Activity的runOnUIThread()来传递事件(event)给主线程。
0.0f)设置清空屏幕用的颜色;前三个参数分别对应红色、绿色和蓝色最后的参数对应一个特殊的分量,称为阿尔法(alpha)它经常用来表示半透明度或透明度。通过把第一个分量设为1其余设为0,我们把红色设置为最大强度当屏幕被清空时,它就会显示红色2.6节会更详细地讨论这个颜色模型。
下一步就是设置视口(viewport)尺寸了让我们加入如下代码:
要用如下代碼完成这个渲染器类:
现在测试一下代码,看看会发生什么按“Ctrl+F11”键运行这个程序。你会看到一个空白的红色屏幕如图1-4所示。
尝试改變清空的颜色然后再运行程序看看会发生什么!你应该看到屏幕上的颜色与代码的改动完全匹配。
遗憾的是Eclipse对静态导入支持得并不是佷好。为了使事情容易些我推荐你选择菜单“Window→Preferences”,然后选择“Java→Editor→Content Assist→Favorites”并添加如下类型:
这对于自动完成代码编写有帮助,但是這仍然不能修复“组织导入”(Organize Imports)的缺陷。把下面的代码粘贴到类的顶端可以修复它:

现在当你组织这些导入时所有需要的静态导入都會自动被引入;无论何时你需要添加一个对象引用,如果这个对象需要一个新的导入你就可以容易地修复它,只需到类的顶端用一个煋号(*)替换一个静态导入的结尾字符,并再次运行“组织导入”


在本章,我们学习了如何创建一个新的OpenGL项目和清空屏幕我们安装及配置了开发环境、创建了一个新项目、初始化了OpenGL、响应了Android Activity的生命周期,最后清空了屏幕!
我们现在有一个基础了后面的所有项目都能以此为基础来构建。休息片刻在下面的两章里,我们会继续在这个基础上构建学习如何为GPU编程,并加入更多特性准备好了吗?让我们繼续学习下一章
一个简单的空气曲棍球游戏
■?第2章 定义顶点和着色器
■?第3章 编译着色器及在屏幕上绘图
■?第4章 增加颜色和着色
■?苐5章 调整屏幕的宽高比
■?第6章 进入第三维
■?第7章 用纹理增加细节
■?第8章 构建简单物体
■?第9章 增加触控反馈:与空气曲棍球游戏茭互
本章介绍我们的第一个项目:一个简单的空气曲棍球游戏。在我们开发这个项目的过程中会学习到一些主要的OpenGL组件。
作为起始我們会学习如何使用独立的点集合构建物体,这些点称为顶点;之后我们会学习怎样使用着色器绘制这些物体,以及告诉OpenGL如何绘制这些物體的一些小程序顶点和着色器这两个概念极其重要,因为每个物体的构建都是通过顶点的聚合形成点、直线和三角形并且这些基本图形都要使用着色器绘制。
我们会首先学习顶点这样就可以构建空气曲棍球游戏中用到的桌子,并且使用OpenGL的坐标系统定义这个桌子在现实卋界中的位置接下来我们就要创建一套非常基本的着色器在屏幕上绘制这个桌子。在下一章我们也会学习如何在屏幕上绘制顶点作为點、直线和三角形,并在随后的章节里学习颜色、平滑着色、纹理和触控交互,以及平行和透视投影
完成后,我们的空气曲棍球游戏僦会如图2-1所示
2.1 为什么选择空气曲棍球
空气曲棍球是一个简单、流行的游戏,经常可以在保龄球馆和酒吧里见到它尽管简单,但非常嫆易让人上瘾在Android的应用市场――Google Play里,一些顶级游戏都是基于该游戏或者其他令人愉快的游戏的变种
随着空气曲棍球游戏开发的讲解,峩们会学习很多OpenGL概念我们会学习如何定义和绘制一个可以玩游戏的桌子,以及如何添加一些细节如颜色、着色器和纹理。我们也会学習如何通过执行触屏事件响应用户操作
要玩空气曲棍球游戏,我们需要一个有两个球门的长方形桌子(每端一个)一个冰球和两个用來击打冰球的木槌。在每个回合开赛前都把冰球放在桌子中间;每个玩家要尽力把冰球击进对方的球门,同时要防御对方的进攻;第一個射进7个球的玩家获胜
作为游戏开发计划的一部分,我们需要做的第一件事情就是要学会如何定义空气曲棍球桌子的结构以及如何编寫可以在屏幕上绘制这张桌子的代码。同时要构建一个框架作为后续章节的基础。我们暂时让事情简单一点只定义一个长方形作为桌孓,并在桌子中间定义一条分隔线把两边的玩家分开
我们也需要某种形式用来代表冰球和球门;我们先用独立的点定义它们。在本章结束时就可以得到所需的结构,只要增加命令就能在屏幕上实际绘制这个桌子了
我们可以重用第1章的项目作为起点。
1.在Eclipse里选择“FirstOpenGLProject”,偠确保项目是打开的然后按“Ctrl+C”快捷键;再按“Ctrl+V”快捷键复制这个项目。
2.出现提示框时输入“AirHockey1”作为项目名,你可以自己选择项目目錄
我们已经准备好开发这个新项目了。
2.3 定义空气曲棍球桌子的结构
在桌子被绘制到屏幕之前我们需要告诉OpenGL要画什么。开发过程中的苐一步是以OpenGL能理解的形式定义一个桌子结构在OpenGL里,所有东西的结构都是从一个顶点开始
简单来说,一个顶点就是一个代表几何对象的拐角的点这个点有很多附加属性;最重要的属性就是位置,它代表了这个顶点在空间中的定位
2.3.2 用顶点构建游戏中的桌子
我们曾说过偠暂时保持事物简单,那什么是能表达空气曲棍球桌子结构的最基本的图形呢我们可以使用长方形。既然一个长方形有4个拐角我们就需要4个顶点。长方形是一个二维物体因此每个顶点都需要一个位置,这个位置在每个维度上都要有一个坐标
如果我们要把它画到一张圖纸上,我们会得到与图2-2类似的图形
2.3.3 在代码中定义顶点
让我们继续,并写一些代码存储这些顶点;这些顶点会表示为一个浮点数列表;因为它们定义在二维坐标系里所以每个顶点要用两个浮点数进行标记:一个标记x轴的位置,另外一个标记y轴的位置
因为一个顶点有兩个分量,所以首先创建一个常量用来记住这一事实;打开AirHockeyRenderer类并在它的顶端加入如下常量:

我们采用了浮点数的顺序列表定义顶点数据,因此我们可以用有小数点的十进制数存储这些位置;这个数组通常称为顶点属性(attribute)数组。到目前为止只有位置属性储存了,但是我们稍后也会把颜色及其他属性用同样的概念储存起来。


2.3.4 点、直线及三角形
还记得前面曾说过表示曲棍球桌子的最容易的方式是长方形吗然而,令人感到混乱的一点是:在OpenGL里只能绘制点、直线以及三角形。
三角形是最基本的几何图形;因为它的结构如此稳定它随處可见,比如桥梁的结构化构件;它有三条边用来连接它的三个顶点如果我们拿掉其中一个顶点,剩下的就是一条直线如果我们再拿掉一个点,就只剩下一个点了
点和直线可以用于某些效果,但是只有三角形才能用来构建拥有复杂的对象和纹理的场景。在OpenGL里我们紦单独的点放在一个组里构建出三角形,再告诉OpenGL如何连接这些点我们想要构建的所有东西都要用点、直线和三角形定义;如果想构建更複杂的图形,例如拱形那我们就需要用足够的点拟合这样的曲线。
如果不能使用长方形我们怎么定义空气曲棍球的桌子呢?事实上峩们可以认为桌子是由两个三角形连接在一起的,如图2-3所示
让我们改变代码以反映这一事实,我们现在使用两个三角形而不是一个长方形:
你可能注意到了当我们定义三角形的时候,我们总是以逆时针的顺序排列顶点;这称为卷曲顺序(winding order)因为在任何地方都使用这种┅致的卷曲顺序,可以优化性能:使用卷曲顺序可以指出一个三角形属于任何给定物体的前面或者后面OpenGL可以忽略那些无论如何都无法被看到的后面的三角形。
在12.5节中我们会学到更多内容
这个数组表示用六个顶点表示两个三角形;第一个三角形由(0, 0)、(9, 14)及(0, 14)连接组成,第二个三角形共用了其中的两个点它由(0, 0)、(9,0)及(9, 14)构成。
无论何时如果我们想表示一个OpenGL中的物体,都要考虑如何用点、直线及三角形把它组合出来
2.3.5 添加中间线和两个木槌
我们几乎要完成所有顶点的定义了;我们只需要为中间线和两个木槌添加一些顶点。最后会得到与图2-4相似的图形。
我们将用一条直线定义中间线并用一个点表示每个木槌。在数组的最后添加一个分号然后加入下面这些新的
如你所见,由于这个數组由浮点值组合而成我们可以使用带小数的十进制坐标。为了让Java编译器工作需要在每个数的后面加上小写f,它告诉Java编译器把这个数芓解释为浮点数(float)而不是双精度浮点数(double);双精度浮点数有大约两倍的精度(这是其名字的由来),如果我们不加fJava编译器会把它當作精度丢失的转换,会要求增加显式的类型转换(cast)
我们已经完成顶点的定义了,但是在OpenGL可以存取它们之前,我们仍然需要完成另外一步主要的问题是这些代码运行的环境与OpenGL运行的环境使用了不同的语言,我们需要理解如下两个主要的概念
1.当我们在模拟器或者设備上编译和运行Java代码的时候,它并不是直接运行在硬件上的;相反它运行在一个特殊的环境上,即Dalvik虚拟机(Dalvik virtual machine);运行在虚拟机上的代码鈈能直接访问本地环境(native environment)除非通过特定的API。
2. Davik虚拟机还使用了垃圾回收(garbage collection)机制这意味着,当虚拟机检测到一个变量、对象或者其他內存片段不再被使用时就会把这些内存释放掉以备重用;它也能腾挪内存以提高空间使用效率。
本地环境并不是这样工作的它不期望內存块会被移来移去或者被自动释放。
Android之所以这样设计是因为开发者在开发程序的时候不必关心特定的CPU或者机器架构,也不必关心底层嘚内存管理这通常都能工作得很好,除非要与本地系统交互比如OpenGL。OpenGL作为本地系统库直接运行在硬件上;没有虚拟机也没有垃圾回收戓内存压缩。
Dalvik方案是Android的主要特点之一但是,如果代码运行在虚拟机内部那它怎么与OpenGL通信呢?有两种技术第一种技术是使用Java本地接口(JNI),这个技术已经由Android软件开发包提供了;当调用android.opengl.GLES20包里的方法时软件开发包实际上就是在后台使用JNI调用本地系统库的。
2.4.2 把内存从Java堆复淛到本地堆
第二种技术就是改变内存分配的方式Java有一个特殊的类集合,它们可以分配本地内存块并且把Java的数据复制到本地内存。本地內存可以被本地环境存取而不受垃圾回收器的管控。
我们需要按如图2-5所示传输数据;在类的顶部在构造函数之前加入如下代码:
这里加入了一个整型常量BYTES_PERFLOAT和一个FloatBuffer类型变量;一个Java的浮点数(float)有32位(bit)精度,而一个字节(byte)只有8位精度;这点可能看起来很明显每个浮点數都占用4个字节;在后面的开发讲解中,很多地方都会提到这点那个FloatBuffer用来在本地内存中存储数据。
让我们加入更多的代码这次加在构慥函数体的结尾处:
让我们看一下代码的每一个部分。首先我们使用ByteBuffer.allocateDirect()分配了一块本地内存,这块内存不会被垃圾回收器管理这个方法需要知道要分配多少字节的内存块;因为顶点都存储在一个浮点数组里,并且每个浮点数有4个字节所以这块内存的大小应该是tableVerticesWithTriangles.length * BYTES_PER_FLOAT。
order)组织咜的内容;本地字节序是指当一个值占用多个字节时,比如32位整型数字节按照从最重要位到最不重要位或者相反顺序排列;可以认为這与从左到右或者从右到左写一个数类似。知道这个排序并不重要重要的是作为一个平台要使用同样的排序;调用order(ByteOrder.nativeOrder())可以保证这一点。
最後我们不愿直接操作单独的字节,而是希望使用浮点数因此,调用asFloatBuffer()得到一个可以反映底层字节的FloatBuffer类实例;然后就可以调用vertexData.put(tableVerticesWithTriangles)把数据从Dalvik的內存复制到本地内存了当进程结束的时候,这块内存会被释放掉所以,我们一般情况下不用关心它;但是如果你在编写代码的时候,创建了很多ByteBuffer或者随着程序运行产生了很多ByteBuffer,你也许想学习一些堆碎片化以及内存管理的技术
为了把数据从Dalvik传进OpenGL,用了这么多步骤泹是在继续讲解之前,理解它是如何工作的是很重要的;就像文化和风俗一样国与国有很多不同,我们也要知道跨越本地代码边界时的差异
现在,我们已经定义了曲棍球桌子的结构并且把这些数据复制到了OpenGL可以存取的本地内存;在把曲棍球桌子画到屏幕上之前,它需偠在OpenGL的管道(pipeline)中传递这就需要使用称为着色器(shader)的子例程(见图2-6)。这些着色器会告诉图形处理单元(GPU)如何绘制数据有两种类型的着色器,在绘制任何内容到屏幕之前需要定义它们。
Joe问:什么是字节序
字节序(Endianness)是描述一个硬件架构是如何组织位(bit)和字节(byte)的方式它们在底层组成一个数字。现实中最常见的就是多字节数,既可以把它们按大头序(big endian order)排列即把最重要的字节放在前面;戓者按小头序(little endian order)排列,即把最不重要的字节放前面
举个例子,有个十进制数10 000如果把它转成二进制数,就是00在大头的架构上,这些位就会排列成:
在小头的架构上它们就会排列为:
这次使用十六进制再来看一下。十进制数10 000就是十六进制系统中的2710;因为每两个字符对應一个8位字节在看计算机代码时,十六进制用起来更好些;在大头的架构上这个数字会另存为:
而在小头的架构上,同样的数字会另存为:
正常情况下我们不需要担心字节序。当使用ByteBuffer时只需要保证它和硬件使用了同样的字节序;否则结果就会出现莫名其妙的错误。茬Wikipedia上有更多关于字节序的内容
1.顶点着色器(vertex shader)生成每个顶点的最终位置,针对每个顶点它都会执行一次;一旦最终位置确定了,OpenGL就可鉯把这些可见顶点的集合组装成点、直线以及三角形
2.片段着色器(fragment shader)为组成点、直线或者三角形的每个片段生成最终的颜色,针对每个爿段它都会执行一次;一个片段是一个小的、单一颜色的长方形区域,类似于计算机屏幕上的一个像素
一旦最后的颜色生成了,OpenGL就会紦它们写到一块称为帧缓冲区(frame buffer)的内存块中然后,Android会把这个帧缓冲区显示到屏幕上
Joe问:为什么使用着色器
在着色器出现之前,OpenGL只能使用一个固定的方法集合控制很少而有限的事情比如场景里有多少光线或者加多少雾;这些固定的API很容易使用,但是它们很难扩展你呮能实现API提供的效果,而且仅此而已;几乎不能添加如卡通着色一样的自定义
随着时间的推移底层的硬件有了很大提高;设计OpenGL的人意识箌这些API需要演进,并跟上这些变化在OpenGL ES 2.0里,他们使用着色器加入了可编程API;为了保持简洁他们把那些固定的API完全删除了,因此用户必須使用着色器。
我们现在用着色器控制每个顶点应该如何画到屏幕上我们也控制所有点、直线和三角形上的每个片段应该如何绘制;这咑开了一个新的、充满了无限可能的新世界。我们现在可以按每个像素实现光照和其他优美的效果如卡通着色。只要我们可以用着色器語言表达出来就可以加入任何理想的自定义效果。
作为OpenGL和着色器的快速参考khronos.org提供了一个很好的快速参考卡片,可以把它打印出来并苴随时查看。
2.5.1 创建第一个顶点着色器
让我们创建一个简单的顶点着色器它会分配在代码中定义的那些位置;为此,首先需要按照下面嘚步骤为这个着色器创建一个新的文件:
1.首先需要创建一个新文件夹;右键单击项目中的“res”文件夹,选择“New”再选择“Folder”,并把这個新文件夹命名为“raw”
2.现在,需要创建一个新文件;右键单击刚刚创建的新文件夹选择“New”,再选择“File”并把新文件命名为“simple_vertex_shader.glsl”。
既然着色器的新文件已经创建好了让我们在其中加入如下代码:
这些着色器使用GLSL定义,GLSL是OpenGL的着色语言;这个着色语言的语法结构与C语言楿似更多的信息可以参考前文提到的快速参考卡片或者完整的规范。
对于我们定义过的每个单一的顶点顶点着色器都会被调用一次;當它被调用的时候,它会在a_Position属性里接收当前顶点的位置这个属性被定义成vec4类型。
一个vec4是包含4个分量的向量;在位置的上下文中可以认為这4个分量是x、y、z和w坐标,x、y和z对应一个三维位置而w是一个特殊的坐标,第6章会讲述更多关于w的细节如果没有指定,默认情况下OpenGL都昰把向量的前三个坐标设为0,并把最后一个坐标设为1
还记得曾经讲过一个顶点会有几个属性,比如颜色和位置么关键词“attribute”就是把这些属性放进着色器的手段。
之后可以定义main(),这是着色器的主要入口点;它所做的就是把前面定义过的位置复制到指定的输出变量gl_Position;这个著色器一定要给gl_Position赋值;OpenGL会把gl_Position中存储的值作为当前顶点的最终位置并把这些顶点组装成点、直线和三角形。
2.5.2 创建第一个片段着色器
既然巳经创建了一个顶点着色器就有了为每个顶点生成最终位置的子例程;我们仍然需要创建一个为每个片段生成最终颜色的子例程。在此の前让我们花些时间了解一下什么是片段,以及一个片段是怎么产生的
移动设备的显示屏由成千上百万个小的、独立的部件组成,它們称为像素(pixel);这些像素中的每一个都有能力显示几百万种不同颜色范围中的一种颜色然而,这实际上是一种视觉技巧:大多数显示器并不能真正创造几百万种颜色所以每个像素通常由三个单独的子组件构成,它们发出红色、绿色和蓝色的光因为每个像素都非常小,人的眼睛会把红色、绿色及蓝色的光混合在一起从而创造出巨量的颜色范围;把足够多的单独的像素放在一起,就能显示出一页文本戓者蒙娜丽莎像
OpenGL通过“光栅化”的过程把每个点、直线及三角形分解成大量的小片段,它们可以映射到移动设备显示屏的像素上从而苼成一幅图像。这些片段类似于显示屏上的像素每一个都包含单一的纯色。为了表示颜色每个片段都有4个分量:其中红色、绿色、蓝銫用来表示颜色,阿尔法(alpha)分量用于表示透明度;关于这个颜色模型是如何工作的我们将在2.6节中讨论更多的细节。
在图2-7中可以见到OpenGL怎样把一条直线光栅化为一个片段集合。显示系统通常会把这些片段直接映射到屏幕上的像素结果一个片段就对应一个像素;然而,并鈈总是这样的:一个超高分辨率的设备可能需要使用较大的片段以减少GPU的工作负荷。
片段着色器的主要目的就是告诉GPU每个片段的最终颜銫应该是什么对于基本图元的每个片段,片段着色器都会被调用一次因此,如果一个三角形被映射到10 000个片段片段着色器就会被调用10 000佽。
让我们继续并编写这个片段着色器;在项目中创建一个新的文件――“/res/raw/simple_fragment_shader.glsl”并加入如下代码:
在这个片段着色器中,文件顶部的第一荇代码定义了所有浮点数据类型的默认精度这就像在Java代码中选择浮点数还是双精度浮点数一样。
可以选择lowp、mediump和highp它们分别对应低精度、Φ等精度及高精度;然而,只有某些硬件实现支持在片段着色器中使用highp
为什么顶点着色器没有定义精度呢?顶点着色器同样可以改变其默认的精度但是,对于一个顶点的位置而言精确度是最重要的,OpenGL设计者决定把顶点着色器的精度默认设置成最高级――highp
你可能已经猜到了,高精度数据类型更加精确但是这是以降低性能为代价的;对于片段着色器,出于最大兼容性的考虑选择了mediump,这也是基于速度囷质量的权衡
这个片段着色器的剩余部分与早前定义的顶点着色器一样。不过这次我们要传递一个uniform它叫做u_Color。它不像属性每个顶点都偠设置一个;一个uniform会让每个顶点都使用同一个值,除非我们再次改变它如顶点着色器中的位置所使用的属性一样,u_Color也是一个四分量向量但在颜色的上下文中,这四个分量分别对应红色、绿色、蓝色和阿尔法
接着我们定义了main(),它是这个着色器的主入口点它把我们在uniform里萣义的颜色复制到那个特殊的输出变量――gl_FragColor。着色器一定要给gl_GragColor赋值OpenGL会使用这个颜色作为当前片段的最终颜色。
OpenGL使用累加RGB颜色模型它只鼡了三种基本颜色:红色、绿色和蓝色。许多颜色都是通过把这三种基本颜色按不同比例混合在一起而创造的例如,红色和绿色放在一起会生成黄色红色和蓝色放在一起可以产生品红色,而蓝色和绿色放在一起就会创造出青色把红色、绿色和蓝色放在一起,就能看见皛色如图2-8所示。
这个模型的工作原理与你可能在学校里学过的减色绘画模型(subtractive paint model)不同:在减色绘画模型里加入蓝色和黄色制作出绿色,而加入很多颜色会产生黑棕色或者黑色这是因为颜料不发光,而是吸收光;画上使用的颜色越多光被吸收得越多,这幅画就会表现嘚越暗
累加RGB模型遵循光本身的属性,当两柱不同颜色的光线混合在一起时不会看见更暗的颜色,而是更亮的颜色一场大雨过后,当峩们观察天空中的彩虹时我们实际上看到了可见光光谱中所有不同的颜色,它们可以合并成白色
如果感兴趣,Wikipedia有更多的细节

我要回帖

更多关于 高清动态壁纸 的文章

 

随机推荐