一、前期基础是知识储备
笔者计划写三篇文章来详细分析OpenGL ES基础的同时也是入门关键的三个点:
①OpenGL ES是什么?与OpenGL的关系是什么?——概念部分
②使用OpenGL ES绘制2D/3D图形的第一步:定义图形;——运用部分
③使用OpenGL ES绘制出②步骤中定义好的图形:——运用部分,难点所在
通过这三篇文章的分析,就像给万丈高楼垫定了基石,万丈高楼平地起,后面利用OpenGLES做各种效果,各种变换都是建立在这三步的图形编程理解之上的。
话不多说正文开始
(1)什么是OpenGL?
原作者 硅谷图形公司(SGI)
开发者 KhronosGroup
稳定版本 4.6(2017年7月31日 )
编程语言 C
操作系统 跨平台
类型 API
OpenGL(英语:Open Graphics Library,译名:开放图形库或者“开放式图形库”)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。这个接口由近350个不同的函数调用组成,用来从简单的图形比特绘制复杂的三维景象。OpenGL常用于CAD、虚拟实境、科学可视化程序和电子游戏开发。
为了加强它的多语言和多平台特性,已经用很多语言开发了OpenGL的各种绑定和移植——Java、Python、Perl、VisualBasic等等。
(2)什么是OpenGL ES?
原作者 KhronosGroup
开发者 KhronosGroup
稳定版本 3.1(2014年3月17日 )
编程语言 C
操作系统 跨平台
系统平台 跨平台
类型 API
OpenGL ES(OpenGLfor Embedded Systems)是 OpenGL 三维图形API的子集,针对手机、掌上电脑和游戏主机等嵌入式设备而设计。该API由Khronos集团定义推广,科纳斯是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。
OpenGL ES是从OpenGL裁剪定制而来的,去除了glBegin/glEnd,四边形(GL_QUADS)、多边形(GL_POLYGONS)等复杂图元等许多非绝对必要的特性。
其实换句话说,由于手机等终端由于GPU、内存、性能等各方面的限制,不可能像电脑端那样完美执行OpenGL,所以,Khronos集团就为OpenGl提供了一个子集—OpenGl ES。
经过多年发展,现在主要有两个版本,OpenGL ES 1.x针对固定管线硬件的,OpenGL ES 2.x针对可编程管线硬件。现在Android开发使用的灵活度和可定制化程度更高的OpenGL ES 2.0版本,入门门槛相对于之前的OpenGL ES 1.x更高。
(3)OpenGL ES 1.x和OpenGL ES 2.x的区别——说明为什么OpenGL ES 2.0开发难度提高
释义—OpenGL ES2.0, you make me write everything?—OpenGL ES2.0,你要让我自己实现所有事?
OpenGL ES版本內容变化有显著的差异,1.x还是fixed function pipeline(固定管线硬件),到了2.x就变成programmable pipeline(可编程管线硬件),也就是说,使用2.x的时候,尽管写最简单的程式(如基本作图、三角形、矩形或动作translate、rotate、scale等),一定要写shader(着色器/渲染器)才能运作,因而提高了入门的门槛。
OpenGL ES 1.x是以OpenGL1.5规格來制定,强调API使用硬体加速来实现;
OpenGL ES 2.x则是以OpenGL2.0來制定,强调使用可程式化3D绘图管线,可以产生shader和program object,藉由OpenGL ES Shading Language來写vertex shader(顶点着色器)和fragment shader(片元着色器)。
这是OpenGL ES 1.x固定管线硬件的运作方式;(关注橙色部分)
这是OpenGL ES 2.x可编程管线硬件的运作方式;(关注橙色部分)
从上面两幅图的对比,我们知道2.x跟1.x版的差异在于vertex shader(顶点着色器)是原本的 transform 和 lighting 的部分,fragment shader(片元着色器)則是对应到 texture、colour sum、fog、alpha test 等功能;也就是说,这些(橙色的部分)本來在fixed function pipeline 時会由系統來做的計算,現在在programmable pipeline 都要自己写 shader程式來做计算了,所以掌握的难度有所提高。
二、上代码,简单使用OpenGL ES
PS:准备工作
我们用的是Android Studio;
支持OpenGL ES 2.0或更高版本的Android设备。
前面,一直在说使用OpenGL ES2.0难度有所提高……这里又说简单使用,是不是矛盾了?真的,相信笔者,这里说简单使用,是因为我们真的只是把使用框架建立起来,所以简单。分四步走:
第一步:在 Manifest 配置文件中声明使用 OpenGL ES2.0;
<!--0x00020000 代表使用 OpenGL ES 2.0 接口-->
<uses-featureandroid:glEsVersion="0x00020000" android:required="true"/>
第二步:为 OpenGL ES 图形创建一个 Activity;
使用 OpenGL ES 的Android 应用跟其它类型的应用主要的区别体现在 Activity 布局内容上的差异。通常我们会使用 TextView、Button等已经封装好的控件。而如果使用 OpenGL ES 的我们就得继承GLSurfaceView 来自己写子类实现了。
public class OpenGLSample01Activity extends AppCompatActivity {
GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle("OpenGLSample01");
// Create a GLSurfaceView instance and set it
// as the ContentView for this Activity.
mGLSurfaceView = new MyGLSurfaceView(this);
setContentView(mGLSurfaceView);
}
}
我们自定义一个MyGLSurfaceView,获得实例之后,作为参数传入setContentView中作为视图。到此为止,Activity创建完毕。
第三步:自定义一个 MyGLSurfaceView 对象,继承自GLSurfaceView;
GLSurfaceView,看到这里有没有想起Android中View的孪生兄弟SurfaceView,专门用来处理需要频繁刷新的页面的SurfaceView,其实GLSurfaceView在OpEnGL中只是作为View容器,用来存放图形的,并且和Activity实现绑定。
class MyGLSurfaceView extends GLSurfaceView {
private final MyGLRenderer mRenderer;
public MyGLSurfaceView(Context context){
super(context);
// Create an OpenGL ES 2.0 context
setEGLContextClientVersion(2);
mRenderer = new MyGLRenderer();
// Set the Renderer for drawing on the GLSurfaceView
setRenderer(mRenderer);
// Set the RenderMode
mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
}
从代码我们可以看到,GLSurfaceView自己并不做太多和绘制图形相关的任务。绘制对象的任务是由该 View 中配置的 GLSurfaceView.Renderer 所控制的。在代码中,我们设置了GL ES版本,并将GLSurfaceView和Render连接起来。RenderMode有两种RENDERMODE_WHEN_DIRTY 和RENDERMODE_CONTINOUSLY,前者是懒惰渲染,需要手动调用glSurfeaceView.requestRender() 才会进行更新,而后者则是不停渲染。
第四步:实现GLSurfaceView.Renderer接口,配置自己的渲染器;
在一个使用 OpenGL ES 的应用中,必定少不了 GLSurfaceView.Renderer 接口的实现。该接口会控制和其相关联的GLSurfaceView 的绘制行为和内容(即绘制的图形和图形的特效),Renderer这个类中有三个方法会被系统调用,所以需要实现下面三个方法:
onSurfaceCreated():在Surface被创建时回调,用来配置 View 的 OpenGL ES 环境,只会被回调一次;
onDrawFrame():在绘制每一帧的时候回调;
onSurfaceChanged():在每次Surface尺寸变化时回调,例如当设备的屏幕方向发生改变时。
下面是一个非常基本的 OpenGL ES 渲染器的实现,它仅仅在GLSurfaceView 中画一个黑色的背景:
public class MyGLRenderer implements GLSurfaceView.Renderer {
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
public void onDrawFrame(GL10 unused) {
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
//利用glViewport()设置Screen space的大小,在onSurfaceChanged中回调
GLES20.glViewport(0, 0, width, height);
}
}
Render接口重写的三个方法中调用的GLES20的API方法解析:
glClearColor()-设置清空屏幕用的颜色,接收四个参数分别是:红色、绿色、蓝色和透明度分量,0表示透明,1.0f相反;
glClear()-清空屏幕,清空屏幕后调用glClearColor()中设置的颜色填充屏幕;
glViewport()-设置视图的尺寸,这就告诉了OpenGL可以用来渲染surface的大小。
通过以上四步,我们就做完了一个简单但是颇为完整的OpenGL ES的绘制流程,是不是如笔者之前所说的还是比较简单的:
①创建一个GlSurfaceView,绑定至Activity;
②为这个GlSurfaceView设置渲染;
③在GlSurfaceView.renderer中绘制处理显示数据;
每一步的职责和任务清晰。那么为什么说颇为完整,因为不涉及到具体的图形的绘制和图形特效的处理,也即没有出现前文花大量笔墨阐述的shader着色器的相关运用,那么shader着色器到底如何使用,图形如何定义和绘制,我们在接下来的文章里接着分析。