1. 前言 2. 我对OpenGL
ES2.0的理解
3. 如何告诉OpenGL我们要画什么
4. OpenGL
ES2.0
的管线(pipeline)
Table
of Contents
OpenGL
ES 2.0学习笔记
2 我曾经在网上搜寻各种关于OpenGL
ES2.0的资料,发现关于android中OpenGL
ES2.0开发的中文资料少的可怜,而成体系
的资料更是凤毛麟角.所以我就动了一些念头,希望将自己学习OpenGL的过程记录下来,帮助到其他希望学习OpenGL的人.在学习的过程中,我也曾经获得其他开源者的帮助,说起来这也算是投桃报李吧,哈哈.
由于这里主要是记录我学习中的一些感想.可能有些地方讲的并不直观,有些地方只做了简要阐述.大家也可以在网上找其
他资料作为参考.
网上资源:
1.OpenGL
ES1.0资料
2.NEHE
OpenGL教程,同样是成体系的教学,缺点:不是针对android,而且
讲述的是OpenGL
ES1.0固定管线的,相比2.0
之后可编程管线有些过时了.
3.NEHE
android
OpenGL
ES教程,好不容易找到的教程,附带有github的开源项目代码作为讲解.唯一的不足就是
讲解内
容为英文.
4.OpenGL
ES着色语言,学习2.0以后的OpenGL,掌握shader着色语言那是必须的.
5.OpenGL官网,英语好的就直接看这个吧
6.意外发现的学习OpenGL
ES2.0的好网站.当然仍然需要英文~~
7.依然是着色语言的教程,不过这个内容更丰富
OpenGL
ES 2.0学习笔记
3 前言 OpenGL
ES2.0是一套针对移动设备的应用程序接口,对于程序员而言他其实就是一个API文档.只不过这套API是专门针对
图形硬件的.它在Android平台上的调用是依靠
客户端-服务端的形式体现的.Android系统本身内置的OpenGL实现是服务
端,我们自己编写的Android程序是客户端.客户端负责提交命令,由服务端转化为硬件能接受的命令,最终执行并产生图像
内容.
首先因为移动设备本身的图形绘制只支持2D,想要做出绚丽的3D效果必须得借助OpenGL.也许有人会说,在android中使
用camera不也能绘制出3D效果么,对此我只能说:Camera只是对OpenGL做了一个封装,它本身仍然是属于OpenGL范畴.
其次OpenGL直接操作native层的内存.在android上会有更高的运行效率.而且由于OpenGL的绘制是在在主线程之外
的另一条线程里操作,所以会更加流畅,不会阻塞主线程.
为了能适应各种硬件平台,所以OpenGL
ES2.0并不包含任何执行窗口任务或者处理用户输入的函数,我们需要通过应用程
序本身所运行的窗口系统来提供相应操作才能处理这些操作.比如说:android手机上,我们想要调用OpenGL的话就必须得
使用GLSurfaceView来渲染图形.因为GLSurfaceView可以为自己创建一个窗口,并在视图(View
Hierarchy)层次上穿个洞
让底层的OpenGL
surface显示出来.对于大多数情况下这就足够了.(但是由于GLSurfaceView本身就是window的一部分.
所以它没法像其他的View一样进行动画和变形操作.)另外
OpenGL
ES2.0也不包含任何表达三维模型,读取图像文件的操
作,这个时候,我们需要通过一系列的几何图元(点,线,三角形)来创建三维空间的物体.GLSurfaceView和图元(点线
面)将是我们后面讲述的重点.
Android为我们提供了一个专门的用在3D画图上的GLSurfaceView.这个类被放在一个单独的包android.opengl里面,其中实
现了其他View所不具备的操作:
(1)
具有OpenGL|ES调用过程中的错误跟踪,检查工具,这样就方便了Opengl编程过程的debug
; (2)
所有的画图是在一个专门的Surface上进行,这个Surface可以最后被组合到android的View体系中
; (3)
它可以根据EGL的配置来选择自己的buffer类型,比如RGB565,depth=16
(这里有点疑问,SurfaceHolder的类型是SURFACE_TYPE_GPU,内存就是从EGL分配过来的
(4)
所有画图的操作都通过render来提供,而且render对Opengl的调用是在一个单独的线程中
(5)
Opengl的运行周期与Activity的生命周期可以协调
下面我们再看看利用GLSurface画3D图形的一个典型的Sequence
(1)
选择你的EGL配置(就是你画图需要的buffer类型)
[optional]
: setEGLConfigChooser(boolean)
setEGLConfigChooser(EGLConfigChooser)
setEGLConfigChooser(int,
int,
int,
int,
int,
int)
(2)
选择是否需要Debug信息
[optional]
: setDebugFlags(int)
setGLWrapper(GLSurfaceView.GLWrapper).
(3)
为GLSurfaceView注册一个画图的renderer
: setRenderer(GLSurfaceView.Renderer)
(4)
设置reander
mode,可以为持续渲染或者根据命令
来渲染,默认是continuous
rendering
[optional]:
setRenderMode(int)
这里有一个要注意的地方就是必须将Opengl的运行和Activity的生命周期绑定在一起,也就是说Activity
pause的时候,opengl的渲染也必须pause.另外GLSurfaceView还
class
MyGLSurfaceView
extends
GLSurfaceView
{ private
MyRenderer
mMyRenderer;
public
void
start()
{ mMyRenderer
= ...;
setRenderer(mMyRenderer);
} public
boolean
onKeyDown(int
keyCode,
KeyEvent
event)
{ if (keyCode
== KeyEvent.KEYCODE_DPAD_CENTER)
{ queueEvent(new
Runnable()
{ This
method
will
be called
on the
rendering
thread:
public
void
run()
{ mMyRenderer.handleDpadCenter();
return
true;
1.我对OpenGL
ES2.0的理解
(1)OpenGL
ES2.0是什么(2)为什么要用OpenGL
ES2.0呢(3)怎么在android上使用OpenGL
ES2.0呢OpenGL
ES 2.0学习笔记
4 我对OpenGL
ES2.0的理解
} return
super.onKeyDown(keyCode,
event);
} } GLSurfaceView是Android提供的一个非常值得学习
的类,它实际上是一个如何在View中添加画图线程的例子,如何在Java
中使用线程的例子,如何添加事件队列的例子,一个使用SurfaceView画图的经典Sequence,一个如何定义Debug信息的例
子,觉得把它看懂了可以学到很多知识
,具体的源码在:/framworks/base/opengl/java/android/opengl/GLSurfaceView.java
. OpenGL
ES 2.0学习笔记
5 我对OpenGL
ES2.0的理解
在讲后面内容的时候我们需要先思考一个问题,假设让你来画一幅画的话,你需要知道哪些要素了才能开始去画这幅画呢Android中View的源码实现绘制的时候经历了三步,首先是测量控件的大小,因为只有知道大小了才能在布局中进行定位.
其次就是确定位置,不确定位置的话就不知道要画在哪.最后才是开始绘制.
如果按照这个步骤,那么OpenGL
ES2中是如何去测量,定位还有绘制的呢相比起Android中View,OpenGL的的定位测量要简单的多,他只需要我们在它的坐标系上
定义一些顶点,这些顶点可以有
很多属性,但最重要的属性就是位置.位置属性通过x,y,z坐标来体现.通过这些顶点与顶点的组合就形成了不同的图形,
而这些图形的位置也根据每个点的位置而确定了下来.
通过代码来体现就是
float[]
tableVertices
= { 0f,
0f,0f,
0.5f,-0.8f,0f,
0.5f,-0.8f,0f,
0.5f,0.8f,0f,
0.5f,0.8f,0f,
0.5f,-0.8f,0f
但这不就是一个普通的float数组么,float数组是java的类型,而OpenGL使用的是却是另外一种语言.那么我们怎么把这份数
据传递给运行在本地环境的OpenGL呢?下面就会解答这个问题.
要知道,Android运行代码的时候并不是直接运行在硬件上的,而是运行在Dalvik虚拟机上的.运行在虚拟机上的代码不能直
接访问本地环境.除非通过特定的API的接口.Android这样设计其实也是为了降低系统的耦合性,让开发者不用去关心CPU
等硬件,专心搞软件就行了.这样做一般也没问题,但在与本地系统交互的时候就比较麻烦了,比如说OpenGL.OpenGL作
为本地系统库直接运行在硬件上.
继续上面那个问题:如何将java数据传递给本地系统库中的OpenGL.
有两种技术可以实现:1.使用本地接口JNI技术,这个
技术已经集成在Android系统android.opengl.GLES20中,我们可以直接调用.
2.改变内存分配的方式,java有一个特殊的集
合,他们可以分配本地内存快,并且把java的数据复制到本地内存.而本地内存是可以被本地环境读取,这样就可以达到传
递数据的目的.
代码实现如下:
private
FloatBuffer
vertexData;
//缓存的顶点数据
vertexData
= allocateDirect分配本地内存float数组的长度乘以每一个float类型占用的字节数
ByteBuffer.allocateDirect(tableVertices.length
* BYTES_PER_FLOAT)
告诉字节缓冲区按照本地字节序组织它的内容
当一个值占用多个字节,比如32位整形数,字节按照从高位到低位
或者从
排什么顺序其实不重要,重要的是程序运行过程中所有的字节都得按同样的顺序排序,
//不能有的排正序,有的排倒序.
而 //order(ByteOrder.nativeOrder()就能达到这个目的.
order(ByteOrder.nativeOrder())
根据分配的内存获取一个能分配本地内存的FloatBuffer实例对象.
asFloatBuffer();
将顶点数据
内存从虚拟机中拷贝到本地
vertexData.put(tableVertices);
如何告诉OpenGL我们要画什么
1.OpenGL中的定位和测量
2.如何将java数据传递给本地系统库中的OpenGL.
OpenGL
ES 2.0学习笔记
6 如何告诉OpenGL我们要画什么
现在OpenGL已经可以读取这些数据,那么它会如何去使用这些数据呢这就涉及到OpenGL管线的概念了.
OpenGL
ES 2.0学习笔记
7 如何告诉OpenGL我们要画什么
OpenGL
ES 2.0学习笔记
8 OpenGL
ES2.0
的管线(pipeline)