opengles2.0应用,OpenGLes20
[OpengGL]渲染流程和程序流程
这节要说的是Opengles的渲染流程和程序流程,都是一些非常基础的东西,觉得已经熟悉的同学可以自行忽略。
以下是一幅经典的Opengl渲染管线流程图
Opengles中包含三种图元方式,点,线,三角形
包含对顶点数据的处理和转换
把所有输入的顶点数据作为输入,将所有点配装成指定图元的形状
(Opengl特有) 把基本图元形式的顶点的几何作为输入,可以通过产生新顶点构造出新的基本图元来生成其它形状
(Opengl特有)可以把基本图元细分为更多的基本图形,创建出更加平滑的视觉效果
像素化,图形映射到屏幕上网络的像素点,生成提供片段给片元着色器处理,光栅化包含一个剪裁处理,会计算舍弃超出定义视窗外的像素
为每一个像素点提供最终的颜色,这里会可以提供纹理题图,如果是3D场景其可以包含3D场景的一些额外数据,例如光线,阴影
对每个像素点进行深度测试,Alpha测试并进行颜色混合操作,进一步合成整个视窗每一个像素点最终要显示的颜色和透明度
如果从API的角度来分析,你会发现有更多的操作。
在输入顶点数据的时候需要线做顶点缓冲,这里可以使用顶点缓冲去对象(VBO),顶点数组对象(VAO)。VBO可以减少IO开销,并使用数组记录每一个快数据对应绑定的类型(顶点位置、法向量),这些数据会存放在GPU上。VAO是使用一个数组来存每一个VBO储存的数据、类型,每次回执时就不需要一个一个传递了。
经过片元着色之后,测试和混合也是分很多种
每一个片元在帧缓冲中对应的位置,若干对应的位置裁剪窗口中则将此片元送入下一个阶段,否则会丢弃此片元,可以在屏幕上指定区域绘制,不在这片区域不进行绘制
深度测试是用片元的深度值和帧缓冲中储存的对应位置的片元的深度值进行比较,深度值小的片元会覆盖或混合深度值大得片元。
模板测试 讲回执区域限定在一定的范围内,一般用于湖面倒影,镜像等场合
如果程序开启了Alpha混合,则可以根据上一阶段对应的片元和帧缓冲的位置片元进行alpha混合
抖动可以模拟更大的色深,需要自己编写算法实现,通过GL_DITHER来控制
opengles并不是直接在屏幕上进行绘制,是预先在帧缓冲区进行绘制,当绘制完之后再下将绘制的结果交换到屏幕上,因此每绘制新的一帧是都需要清除缓冲区的相关数据,否则会产生不正确的绘制效果。
这些都是基本的渲染流程,接下来说一下程序流程,以Android程序为例
这个之前需要了解一下Android中屏幕显示对Opengles的承载,
SurfaceTexture,TextureView, SurfaceView和GLSurfaceView
值得注意的是Android直接内置了Opengles,并内置了GL10,GL20,GL30的类,封装了Opengles的Android API,当然其中也屏蔽了一些细节,对于真正去理解opengles实现有一定的差距。
初学者很多会选用GLSurfaceView来做实现,例如简单绘制图形是没问题的。但是我们如果深入一点学习,例如滤镜,例如录制播放,还是需要使用SurfaceView来做的,因为SurfaceView可以控制绘制的线程,需要自己定义EGL环境,还有SurfaceTexture绑定,同样这也是初学者使用时的难点。
使用一个GLSurfaceView来显示一个三角形为例,这里就屏蔽了EGL、GLThread和SurfaceTexture使用的细节,重点关注在Opengles中的实现。
GLSurfaceView.Render提供三个回调接口
onSurfaceCreated 纹理窗口创建
onSurfaceChanged 视口大小更改
onDrawFrame 绘制
初始化的时候,准备好顶点着色器和片元着色器内容,这里面顶点做色器和片元着色器可以使用字符串读取,也可以使用glsl的shader文件来读取。
加载纹理
创建纹理空间-加载纹理资源-编译纹理
加载纹理绑定到程序
加载纹理-绑定程序
在GLsurfaceView绘制的时候,调用绘制渲染
清屏-指定使用程序-传入参数到着色器-允许GPU读取-绘制图形
日志打印
Opengles2.0对应的是Android 2.2(API 8)
Opengles3.0对应的是Android 4.3(API 18)
Opengles3.1对应的是Android 5.0(API 21)
先将顶点着色器和片段着色器文件贴出来(这是用来渲染普通视频用的),这是使用的OpenGLES3.0版本。(存在兼容性问题),下面只是一部分问题,且这里就不将bug的log写出来了,这是提示大家正确的写法。
顶点着色器
片段着色器
1.没有在着色器文件中标明使用版本的时候默认使用2.0版本。
在上面的着色器文件中添加#version 300 es即表明使用3.0版本,如果不添加则使用默认2.0版本(注意此行必须放在第一行)。同时注意使用3.0的api的时候必须添加此行。
2.3.0中attribute变成了in和out
OpenGL ES 3.0中将2.0的attribute改成了in,顶点着色器的varying改成out,片段着色器的varying改成了in,也就是说顶点着色器的输出就是片段着色器的输入,另外uniform跟2.0用法一样。
3.3.0中使用内置参数gl_FragColor会出问题
这里我们只能自定义颜色向量out vec4 vFragColor;
4.3.0中将GL_OES_EGL_image_external变为了GL_OES_EGL_image_external_essl3
在使用纹理扩展的时候,也就是uniform samplerExternalOES sTexture的时候。在3.0中我们使用GL_OES_EGL_image_external_essl3而不是GL_OES_EGL_image_external。使用相机采集纹理的时候就知道了
5.3.0中将纹理的处理方法统一为texture
在2.0中2D纹理和3D纹理处理分别使用texture2D和texture3D方法,而在3.0后使用texture统一处理。
6.in或者out变量等不能在函数内(如main函数内)声明
OpenglES3.0新特性
Android OpenGLES2.0(十六)——3D模型贴图及光照处理(obj+mtl)
在Android OpenGLES2.0(十四)——Obj格式3D模型加载中实现了Obj格式的3D模型的加载,加载的是一个没有贴图,没有光照处理的帽子,为了呈现出立体效果,“手动”加了光照,拥有贴图的纹理及光照又该怎么加载呢?
本篇博客例子中加载的是一个卡通形象皮卡丘,资源是在网上随便找的一个。加载出来如图所示:
obj内容格式如下:
mtl文件内容格式如下:
关于Obj的内容格式,在上篇博客中已经做了总结,本篇博客中使用的obj,可以看到f后面的不再跟的是4个数字,而是 f 2/58/58 3/59/59 17/60/60 这种样子的三组数,每一组都表示为顶点坐标索引/贴图坐标点索引/顶点法线索引,三个顶点组成一个三角形。而头部的 mtllib pikachu.mtl 则指明使用的材质库。
而mtl格式文件中,主要数据类型为:
模型加载和之前的模型加载大同小异,不同的是,这次我们需要将模型的贴图坐标、顶点法线也一起加载,并传入到shader中。其他参数,有的自然也要取到。
模型加载以obj文件为入口,解析obj文件,从中获取到mtl文件相对路径,然后解析mtl文件。将材质库拆分为诸多的单一材质。obj对象的 加载,根据具使用材质不同来分解为多个3D模型。具体加载过程如下:
顶点着色器
片元着色器
完成了以上准备工作,就可以调用 readMultiObj 方法,将obj文件读成一个或多个带有各项参数的3D模型类,然后将每一个3D模型的参数传入shader中,进而进行渲染:
王者荣耀是OpenGL ES2.0应用吗?
不是。
OpenGLES是KhronosGroup在2007年3月份制定的一种业界标准应用程序编程接口,可以大大提高不同消费电子设备的3D图形渲染速度,在嵌入式系统上实现了全面可编程的3D图形。
OpenGLES是OpenGL三维图形API的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。该API由Khronos集团定义推广,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。
OpenGLES是从OpenGL裁剪的定制而来的,去除了glBeginglEnd,四边形、多边形等复杂图元等许多非绝对必要的特性。
Android OpenGLES2.0(十八)——轻松搞定Blend颜色混合
Blend是OpenGL中的一个非常重要的部分,它可以让每个输出的源和目的颜色以多种方式组合在一起,以呈现出不同的效果,满足不同的需求。
在OpenGLES1.0中,Blend在OpenGLES固定的管线中,OpenGLES2.0相对1.0来说,更为灵活。在OpenGLES2.0中,与Blend相关的函数及功能主要有:
Blend的使用比较简单,但是如果不理解Blend的这些函数及参数的意义,使用了错误的参数,就难以获得我们所期望的混合结果了。
想要使用Blend, glEnable(GL_BLEND) 当然是必须的。与之对应的,不需要Blend的时候,我们需要调用 glDisable(GL_BLEND) 来关闭混合。
另外的四个方法,看名字差不多就能知道他们的意义了。
glBlendFunc 和 glBlendFuncSeparate 都是设置混合因子,反正就是这么个意思了。区别在于glBlendFunc是设置RGBA的混合因子,而glBlendFuncSeparate是分别设置RGB和Alpha的混合因子。设置混合因子是做什么的呢?继续看。
glBlendEquation 和 glBlendEquationSeparate 都是设置Blend的方程式,也就是设置混合的计算方式了,具体参数后面说。他们的区别在同 glBlendFunc 和 glBlendFuncSeparate 的区别一样。
颜色、因子、方程式,组合起来就是:最终颜色=(目标颜色 目标因子)@(源颜色 源因子),其中@表示一种运算符。
至于glBlendColor则是在glBlendFunc和glBlendFuncSeparate的设置中,因子可以设置和常量相关的,这个常量就是由glBlendColor设置进去的。
glBlendFuncSeparate设置混合因子,参数及它们表示的主要如下,而glBlendFunc的参数也是这些,表示的意义就是RGB和A合并为RGBA就是了。在下表中,s0表示源,d表示目的,c表示有glBlendColor设置进来的常量。
glBlendEquationSeparate的设置混合操作,参数及其意义如下表所示。通过glBlendEquationSeparate或者glBlendEquation设置的方程中,源和目的颜色分别为(Rs,Gs,Bs,As)
和(Rd,Gd,Bd,Ad)。最终混合的颜色结果为(Rr,Gr,Br,Ar)。源和目的的混合因子分别为(sR,sG,sB,sA)和(dR,dG,dB,dA)
。其中,所有的颜色分量的取值范围都为[ 0, 1 ]。 GL_MIN和GL_MAX是在OpenGLES3.0才有的 。
Mode RGB Components Alpha Component
GL_FUNC_ADD RrGrBr=sRRs+dRRd=sGGs+dGGd=sBBs+dBBd
Ar=sAAs+dAAd
GL_FUNC_SUBTRACT RrGrBr=sRRs?dRRd=sGGs?dGGd=sBBs?dBBd
Ar=sAAs?dAAd
GL_FUNC_REVERSE_SUBTRACT RrGrBr=dRRd?sRRs=dGGd?sGGs=dBBd?sBBs
Ar=dAAd?sAAs
GL_MIN RrGrBr=min(Rs,Rd)=min(Gs,Gd)=min(Bs,Bd)
Ar=min(As,Ad)
GL_MAX RrGrBr=max(Rs,Rd)=max(Gs,Gd)=max(Bs,Bd)
Ar=max(As,Ad)
目的纹理和源纹理使用的图片分别如下所示(作为源的图片为了表现混合效果,上中下三部分用了不一样的透明度,最下面部分不透明):
根据公式推敲下渲染的结果:
Android OpenGLES2.0(十七)——球形天空盒VR效果实现
在3D游戏中通常都会用到天空盒,在3D引擎中也一般会存在天空盒组件,让开发者可以直接使用。那么天空盒是什么?天空盒又是如何实现的呢?本篇博客主要介绍如何在Android中利用OpenGLES绘制一个天空盒,并实现VR效果。
虽然大多数人知道这些东西是啥,但是我觉得我还是有必要把他们的定义“搬”过来,万一有人不知道呢。
天空盒的实现应该是最简单的,但是效果可能会有些瑕疵,尤其是顶着两面的交点处总能看出点不同。天空穹和天空球效果都差不多,会比天空盒好上很多,但是相对天空盒来说,比较耗性能。
在之前的博客中Android OpenGLES2.0(六)——构建圆锥、圆柱和球体有介绍如何绘制一个球,只不过之前的球是没有贴图的,现在我们绘制一个球,并为它贴上环境的贴图。就像绘制一个地球仪一样。
首先,首先我们需要得到球的顶点坐标和纹理坐标:
相应的顶点着色器和片元着色器分别为:
准备好了顶点坐标、纹理坐标和着色器,从顶点着色器中可以看出,我们还需要几个变换矩阵,变换矩阵求取:
这样,万事俱备,我们就可以编译glprogram,并进行球体的渲染了:
其中纹理图片如下:
渲染的结果如下:
绘制出球体之后,我们需要让球与手机的姿态进行同步,也就是当手机背面超下时,我们看到的应该是地面,手机背面朝上是,我们看到的应该是天空。很明显,这就需要用到手机中的传感器了。
Android中的传感器定义如下:
虽然在API中定义了这么多的传感器,然后实际上绝大多书手机都不会具备所有的传感器。所以当我们在使用某个传感器时,一定要检测这个传感器是否存在。
根据我们的需求,我们需要获得的是手机的姿态,所以上面的传感器中,我们能使用的方案如下:
我们直接使用旋转矢量传感器来获取手机姿态。传感器的使用相对来说比较简单:
然后再监听器中处理数据就可以了:
利用旋转矢量传感器我们很方便的获得了一个旋转矩阵,将这个矩阵传递到顶点着色器我们就可以让球体随着手机的姿态变化而变化了。
修改顶点着色器中顶点的计算为:
然后获取旋转矩阵的句柄并将旋转矩阵传递进来:
这样,球的旋转就和手机姿态同步了
然而我们需要的,并不是这样的结果,仔细想想,天空球模式的话,相机应该是在球的内部,我们看天空看大地,左看右看的时候,应该是人相机在动,而不是球在动。而我们现在看到的却是球自己转动。问题出在哪儿呢?
从 gl_Position=uProjMatrix*uViewMatrix*uRotateMatrix*uModelMatrix*vec4(aPosition,1) ;中可以看到,顶点的坐标计算中,我们是用从传感器获得的旋转矩阵在模型矩阵前,这样我们的旋转操作的就是球体,修改为:
这样,我们操作的就是相机了,得到的渲染结果如下,当摄像头对的方向变话,球在屏幕上的位置也会发生变换,就像我们头转动时,看到的东西在我们眼睛中成像的位置也会发生变化。
完成上述操作,我们里成功就剩下一步之遥了。上面的操作,我们始终在球的外面看球,就如同我们在外太空看地球一样。现在我们需要回到球的内部来看球。在获取矩阵时,我们的视图矩阵求法如下:
根据上面矩阵可以看到,很简单,我们只需要将相机位置改为球的圆心就可以了,当然也可以是球内的其他位置,但是效果上肯定是不如让相机和球心重合。