OpenGL ES 在Android平台的应用实践(1)

 

前言:OpenGL ES在Android平台的引入,使得Android渲染性能大大提高。为了加深对OpenGL ES机制的理解,同时也给初学的小伙伴一些入门的引导启示,后续会推出一系列的相关文章。欢迎感兴趣的小伙伴一起学习探讨,如有错误之处,也请批评指正,共同学习,共同进步。

1.OpenGL ES是什么?

   OpenGL是一种跨平台的编程语言,是图形硬件设备访问的软件库,只提供接口,不提供实现,不同的平台需要自己实现。用于图像的渲染,提高渲染效率。而OpenGL ES是OpenGL的子集,主要针对手机等嵌入式设备而设计。OpenGL ES 是OpenGL的剪裁版本,受嵌入式设备硬件自身性能的影响,去掉了OpenGL中许多不是必须的特性,如:GL_QUADS(四边形)与GL_POLYGONS(多边形)绘制模式以及glBegin(开始)/glEnd(结束)操作等。

     OpenGL ES有1.x,2.x,3.x  版本。1.x采用的是固定渲染管线,可以由硬件GPU支持或者用软件模拟实现,渲染能力有限,在纯软件模拟情况下性能也较弱。2.x采用可编程渲染管线,渲染能力也大大提高。由于在应用开发时采用的是2.x版本,所以这里只讲解2.x版本的知识,以后涉及新版本时,再进行补充。

2.渲染管线和着色器

2.1 OpenGL ES 1.x的渲染管线

   渲染管线一般由GPU内部处理图形信号的并行处理单元组成。这些并行处理单元相互独立,并行单元越多的GPU,渲染能力越强。

   OpenGL ES 1.x渲染流程:基本处理-->变换和光照-->图元装配-->光栅化-->纹理和颜色-->雾-->alpha测试-->剪裁测试-->深度测试-->模版测试-->颜色混合-->抖动-->帧缓冲。

2.2 OpenGL ES 2.x的渲染管线

   OpenGL ES1.x版本渲染流程是固定的,开发人员无法进行干预,虽然1.x版本的渲染能力很强,但是无法完成自己想要的某种特效,而2.x版本采用可编程的渲染管线,渲染的流程可以按照自己的需求来开发,增加了很大的灵活性。

   OpenGL ES 2.x渲染流程:基本处理-->顶点着色器-->图元装配-->光栅化-->片元着色器-->剪裁测试-->深度测试-->模版测试-->颜色混合-->抖动-->帧缓冲。

   2.x版本和1.x版本不同的地方已经用不同的颜色标识了出来,2.x采用不同的着色器替换1.x的相应部分功能,而着色器是一种可编程的语言,这样就可以通过编程灵活的控制输出,实现开发者想要的效果。

2.3 顶点着色器

  顶点着色器是一个可编程的处理单元,主要的任务是执行顶点的变换,光照,材质的应用等与顶点相关的操作。每一个顶点执行一次。下面是一个顶点着色器的例子:

uniform mat4 u_MVPMatrix;         //总变换矩阵attribute vec4 a_Color;           //顶点颜色attribute vec2 a_TexCoordinate;   //顶点对应的纹理坐标  varying vec4 v_Color;             //向片元着色器传递顶点颜色的变量varying vec2 v_TexCoordinate;     //向片元着色器传递纹理坐标的变量void main()                                                   {  v_Color = a_Color;//向片元着色器传递顶点颜色  v_TexCoordinate = a_TexCoordinate;//向片元着色器传递纹理坐标  gl_Position = u_MVPMatrix * a_Position;//告诉GPU该顶点最终的坐标  }

   在顶点着色器中,需要对输入的顶点数据做处理,将顶点进行坐标变换(一个顶点想要最终正确的显示到手机的屏幕上需要进行模型变换,视图变换,投影变换等),设置顶点显示的颜色值,设置顶点对应的光照值,设置顶点对应的纹理坐标值等,所有与顶点相关的操作都可以在顶点着色器中完成。

   在顶点着色器编程语言中,针对不同属性的特点,用不同的修饰符来修饰,如对于所有的顶点都具有相同属性值的属性,如代码中的变换矩阵u_MVPMatrix,对每一个顶点的坐标变换是一样的,则用uniform修饰,表示是全局统一的属性。对于每一个顶点有自己不同属性值的属性,如代码中的a_Position,每一个顶点有自己的坐标值,则用attribute修饰。需要由顶点着色器传给片元着色器的属性,需要用varying修饰,表示该属性是变量值,需要传给片元着色器做进一步的处理。

   顶点着色器中有一些内置的变量,如代码中的gl_Position表示顶点最终的位置坐标,还有gl_PointSize顶点的像素大小值等,OpenGL ES的渲染管线通过获取这些内置变量值来设置顶点最终的属性值。

2.4 片元着色器

   片元着色器用于处理片元值及其相关数据的可编程单元,主要执行纹理采样、颜色的汇总、雾的计算等操作,每个片元执行一次。

  那么什么是片元呢?片元其实就是像素,为什么叫片元不叫像素呢,因为在上屏渲染时,渲染的数据是存储在帧缓冲中的,帧缓冲中的数据是与像素一一对应的,而像素的值是由片元的值来最终确定的,但是像素的值并不是完全等于片元的值,如果片元在执行的过程中被丢弃,那么片元便对应不到帧缓冲中的像素,也就不是总是一一对应的,所以用片元来表示进入帧缓冲前的像素。

   下面是片元着色器的一个例子:​​​​​​​

precision mediump float;            //指定中等精度,精度越高,性能损耗越高uniform sampler2D u_Texture;        //2D纹理varying vec4 v_Color;               //接收来自顶点着色器的颜色varying vec2 v_TexCoordinate;       //接收来自顶点着色器的纹理坐标void main()                        {    //通过将片元的颜色和片元对应的纹理值进行综合,得到最终的片元颜色值    gl_FragColor = (v_Color *  texture2D(u_Texture, v_TexCoordinate));}

    在片元着色器中,主要是通过一系列的计算操作,确定最终片元要显示的颜色,最终的颜色值需要保存到内置变量gl_FragColor中。与顶点着色器中一样,对于所有片元属性值都一致的属性用uniform修饰,如代码中的u_Texture ,是一个2D的纹理,所有的片元都使用同一个纹理图。由顶点着色器传递过来的变量值则由varying修饰的变量来接收,如代码中的v_Color,v_TexCoordinate。根据传递过来的纹理坐标,从纹理图中相应坐标获取到纹理值,这个过程称为纹理采样,采样函数为texture2D()。这里只是给出了一个简单的计算,实际应用中会更复杂。

3.CPU和GPU的区别

    为什么OpenGL ES能够对渲染进行加速呢?答案是因为OpenGL ES借助于GPU来完成实际的渲染,以往我们开发的程序都是在CPU上进行计算,而图形处理相关的应用程序会移植到GPU上进行复杂的运算操作,那么CPU和GPU的区别是什么呢?GPU的优势在哪里呢?下面借用网上的一张图,看一下CPU和GPU的区别。

                                            

从上图可以看到,CPU是由控制单元,逻辑运算单元,存储单元组成,而GPU的组成和CPU是类似的,只不过其中包含的比例不一样。总结的不同点如下:

不同点CPU(中央处理器)GPU(图形处理单元)
组成ALU占很小部分ALU占绝大部分
处理能力小型串行计算并行处理能力强悍
核数不多于两位数超过三位数(NVIDIA Fermi有512核)

    正是因为GPU中的并行处理单元远远超过CPU,使得GPU的并行处理能力更强,更适合做简单重复的大量并行计算,在图形处理程序中,每个像素是独立的,可以并行进行处理,这就给GPU很大的发挥空间,渲染速度大大加快。

4.变换与投影

    物体有自己的坐标空间,但是如果想让物体正确的显示到手机的屏幕上,就需要按照一定的规则,将物体的坐标变换到一个正确的空间内,才能显示,这个规则就是MVP变换,即模型(Model)变换,视图(View)变换,投影(Projection)变换。

    模型变换是将物体从自己的坐标空间转换到世界空间,具有不同的控件位置,用以区别不同的物体,通过的操作就是平移,旋转,缩放。模型变换如下图:

                                                     

    视图变换是将物体从世界坐标系变换到视图坐标系,即以观察点为坐标原点的空间,类似于我们用眼睛看物体,不同的角度看到的结果是不同的。视图变换如下图:

                                          

    投影变换分为正交投影变换和透视投影变换。正交投影的特点是远处的物体和近处的物体投影的大小相同,透视投影具有近大远小的效果。如下图:

                       

    投影变换后,物体会投影到近平面上,然后通过视口变换最终显示到手机屏幕上。如下图:

                                                

5.绘制方式

    OpenGL ES中支持的图元分为3类,即点、线段、三角形。用不同的图元进行绘制可以组成以下不同的绘制模式:

                                                  

6.纹理映射

    纹理映射是将纹理空间中的纹理像素映射到屏幕空间中的像素的过程。简单理解就是将一幅图片映射到一个二维或三维物体的表面的过程,使物体呈现出了纹理的图案。纹理坐标取值范围不管是横向还是纵向都是0.0-1.0。纹理的宽高尺寸必须是2的n次方,否则程序运行会产生问题。如下图所示,左侧是一个纹理图,右侧为一个三角形三个顶点,将纹理图中的对应坐标映射到顶点上,就可以得到纹理坐标围成的图案。这里其实我们只是将纹理中的三个坐标映射到了三个顶点,实际上,需要将纹理中的每个坐标映射到右侧顶点围成的每个片元上,那么其他点是怎么映射的呢?答案是通过渲染管线自己去插值,不需要我们自己操作,只需指定对应映射顶点即可。

                             

7.EGL介绍与流程

    OpengGL ES是一套跨平台的API接口,那么它是如何跨平台的呢?答案是EGL,EGL是OpengGL ES与原生窗口系统之间的接口,在二者之间起到了桥梁的作用,所以OpenGL ES在不同平台才可以复用相同的代码,以实现跨平台。EGL的作用就像java虚拟机一样,正因为Java虚拟机的存在,才使得java可以跨平台。

    EGL封装了整个渲染的流程。如下:

(1)获取显示设备​​​​​​​

EGL10 mEgl = (EGL10) EGLContext.getEGL();EGLDisplay mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

(2)初始化显示器​​​​​​​

int[] version = new int[2];mEgl.eglInitialize(mEglDisplay, version);

(3)选择EGL配置

EGLConfig mEglConfig=mEgl.eglChooseConfig(mEglDisplay , attributes, null, 0,num_config);

(4)创建EGL上下文环境

mEgl.eglCreateContext(display, mEglConfig,EGL10.EGL_NO_CONTEXT,attrib_list);

(5)获取EGL绘图

EGLSurface mEGLSurface= mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, getSurfaceTexture(), null);

(6)设置当前线程为渲染的环境

mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)

(7)循环绘制,交换显存​​​​​​​

while(1){      mRenderer.onDrawFrame(gl);      mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);}

    以上是EGL封装的渲染流程,在Android中,框架层已经为我们提供了一个封装好了的类,即GLSurfaceView,通过这个类和OpenGL ES 的API就可以完成渲染的工作,而不用关注EGL的流程。当然如果这个类不满足需求,也可以自己实现,也就要自己仿照GLSurfaceView实现EGL的整个渲染流程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值