OpenGLES2.0基础(3)

来源:互联网 发布:无锡紫光软件培训中心 编辑:程序博客网 时间:2024/06/04 01:38

> 相机和投影,变换矩阵  http://blog.csdn.net/junzia

Android OpenGLES2.0(三)——等腰直角三角形和彩色的三角形- http://blog.csdn.net/junzia/article/details/52817978

 OpenGL ES坐标映射到屏幕上,从屏幕中心垂直到上下左右边缘距离都为1.0。
 矩阵常被用于图像处理、游戏开发、几何光学、量子态的线性组合及电子学等多种领域。其是需要做游戏开发、图像视频处理的程序员来说是非常重要的。

 将相机对应于OpenGL的世界,决定相机拍摄的结果(也就是最后屏幕上展示的结果),包括相机位置、相机观察方向以及相机的UP方向。
  1.相机位置:相机的位置是比较好理解的,就是相机在3D空间里面的坐标点。
  2.相机观察方向:相机的观察方向,表示的是相机镜头的朝向,你可以朝前拍、朝后拍、也可以朝左朝右,或者其他的方向。
  3.相机UP方向:相机的UP方向,可以理解为相机顶端指向的方向。比如你把相机斜着拿着,拍出来的照片就是斜着的,你倒着拿着,拍出来的就是倒着的。

 用相机看到的3D世界,最后还需要呈现到一个2D平面上,这就是投影了。Android OpenGLES的世界中,投影有两种,一种是正交投影,另外一种是透视投影。
  1.使用正交投影,物体呈现出来的大小不会随着其距离视点的远近而发生变化。Matrix.orthoM()
  2.使用透视投影,物体离视点越远,呈现出来的越小。离视点越近,呈现出来的越大。Matrix.frustumM()

  实际上相机设置和投影设置并不是真正的设置,而是通过设置参数,得到一个使用相机后顶点坐标的变换矩阵,和投影下的顶点坐标变换矩阵,我们还需要把矩阵传入给顶点着色器,在顶点着色器中用传入的矩阵乘以坐标的向量,得到实际展示的坐标向量。注意,是矩阵乘以坐标向量,不是坐标向量乘以矩阵,矩阵乘法是不满足交换律的。Matrix.multiplyMM() 

  顶点着色器是确定顶点位置的,针对每个顶点执行一次。片元着色器是针对片元颜色的,针对每个片元执行一次。而在我们的片元着色器中,我们是直接给片元颜色赋值,外部我们也只传入了一个颜色值,要使三角形呈现为彩色,我们需要在不同的片元赋值不同的颜色。

> 绘制正方形和圆形,顶点法和索引法,
  GLES20.glDrawArrays,也就是顶点法,是根据传入的定点顺序进行绘制的。还有一个方法进行绘制GLES20.glDrawElements,称之为索引法,是根据索引序列,在顶点序列中找到对应的顶点,并根据绘制的方式,组成相应的图元进行绘制。 
顶点法拥有的绘制方式,索引法也都有。相对于顶点法在复杂图形的绘制中无法避免大量顶点重复的情况,索引法可以相对顶点法减少很多重复顶点占用的空间。

>  绘制立方体,索引法
深度测试GLES20.glEnable(GLES20.GL_DEPTH_TEST)
在绘制前清除深度缓存导致GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT)
 (1)什么是深度? 
 深度其实就是该象素点在3d世界中距离摄象机的距离(绘制坐标),深度缓存中存储着每个象素点(绘制在屏幕上的)的深度值! 
 深度值(Z值)越大,则离摄像机越远。 
 深度值是存贮在深度缓存里面的,我们用深度缓存的位数来衡量深度缓存的精度。深度缓存位数越高,则精确度越高,目前的显卡一般都可支持16位的Z Buffer,一些高级的显卡已经可以支持32位的Z Buffer,但一般用24位Z Buffer就已经足够了。 
 (2)为什么需要深度? 
 在不使用深度测试的时候,如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这样的效果并不是我们所希望的。而有了深度缓冲以后,绘制物体的顺序就不那么重要了,都能按照远近(Z值)正常显示,这很关键。 
 实际上,只要存在深度缓冲区,无论是否启用深度测试,OpenGL在像素被绘制时都会尝试将深度数据写入到缓冲区内,除非调用了glDepthMask(GL_FALSE)来禁止写入。这些深度数据除了用于常规的测试外,还可以有一些有趣的用途,比如绘制阴影等等。 
 (3)启用深度测试 
 使用 glEnable(GL_DEPTH_TEST); 
 在默认情况是将需要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,如果比深度缓存中的值小,那么用新像素的颜色值更新帧缓存中对应像素的颜色值。 但是可以使用glDepthFunc(func)来对这种默认测试方式进行修改。 
 其中参数func的值可以为GL_NEVER(没有处理)、GL_ALWAYS(处理所有)、GL_LESS(小于)、GL_LEQUAL(小于等于)、GL_EQUAL(等于)、GL_GEQUAL(大于等于)、GL_GREATER(大于)或GL_NOTEQUAL(不等于),其中默认值是GL_LESS。 
一般来将,使用glDepthFunc(GL_LEQUAL);来表达一般物体之间的遮挡关系。 
 (4)启用了深度测试,那么这就不适用于同时绘制不透明物体。

>  绘制圆锥、圆柱和球体
  OpenGL ES2.0中物体的绘制重点就是在于把这个物体表面分解成三角形,分解成功后,绘制自然就不成问题了。圆锥我们很容易就能想到把它拆解成一个圆形和一个锥面,锥面的顶点与圆形的顶点,除了锥面的中心点的坐标有了“高度”,其他的完全相同。
  把圆柱拆解成上下两个圆面,加上一个圆筒。如同拆圆的思路来理解圆柱,想想正三菱柱、正八菱柱、正一百菱柱……菱越多,就越圆滑与圆柱越接近了,然后再把每个菱面(矩形)拆解成两个三角形就OK了.

> 纹理贴图,游戏中的皮肤或衣服
 基于OpenGL开发的应用和游戏,可不仅仅是那些规则形体和一些简单的色彩构成,而是各种不规则的形体构成了现实世界或者卡通世界的人和事物,他们都是外面穿着漂亮“衣服”的。本篇博客就是来讲解这些“衣服”的基础的。这些衣服就是纹理贴图。

 一般说来,纹理是表示物体表面的一幅或几幅二维图形,也称纹理贴图(texture)。当把纹理按照特定的方式映射到物体表面上的时候,能使物体看上去更加真实。当前流行的图形系统中,纹理绘制已经成为一种必不可少的渲染方法。在理解纹理映射时,可以将纹理看做应用在物体表面的像素颜色。在真实世界中,纹理表示一个对象的颜色、图案以及触觉特征。纹理只表示对象表面的彩色图案,它不能改变对象的几何形式。更进一步的说,它只是一种高强度的计算行为。——百度百科

 纹理贴图的技术,把一个纹理(对于2D贴图,可以简单的理解为图片),按照所期望的方式显示在诸多三角形组成的物体的表面。
  启用纹理映射后,如果想把一幅纹理映射到相应的几何图元,就必须告诉GPU如何进行纹理映射,也就是为图元的顶点指定恰当的纹理坐标。纹理坐标用浮点数来表示,范围一般从0.0到1.0,左上角坐标为(0.0,0.0),右上角坐标为(1.0,0.0),左下角坐标为(0.0,1.0),右下角坐标为(1.0,1.0)。

> 利用OpenGL进行图片处理
  为了保证效率,Android手机中许多美颜相机、图片处理应用,都用到了OpenGLES来处理图片。图片色彩处理、模糊和放大镜效果.
 PS利用滤镜可以非常方便的更改图片整体色彩。

 -- 简单色彩处理
 #FF88269F,把这个色彩分为4部分,每两位组成一部分,分别表示A(透明通道)、R(红色通道)、G(绿色通道)、B(蓝色通道)。
 在GLSL中,颜色是用包含四个浮点的向量vec4表示,四个浮点分别表示RGBA四个通道,取值范围为0.0-1.0。我们先读取图片每个像素的色彩值,再对读取到的色彩值进行调整,这样就可以完成对图片的色彩处理了。 
  黑白图片上,每个像素点的RGB三个通道值应该是相等的。知道了这个,将彩色图片处理成黑白图片就非常简单了。我们直接出处像素点的RGB三个通道,相加然后除以3作为处理后每个通道的值就可以得到一个黑白图片了。这是均值的方式是常见黑白图片处理的一种方法。类似的还有权值方法(给予RGB三个通道不同的比例)、只取绿色通道等方式。 与之类似的,冷色调的处理就是单一增加蓝色通道的值,暖色调的处理可以增加红绿通道的值。还有其他复古、浮雕等处理也都差不多。

 -- 图片模糊处理
 图片模糊处理相对上面的色调处理稍微复杂一点,通常图片模糊处理是采集周边多个点,然后利用这些点的色彩和这个点自身的色彩进行计算,得到一个新的色彩值作为目标色彩。模糊处理有很多算法,类似高斯模糊、径向模糊等等。

 -- 放大镜效果
 我们只需要将制定区域的像素点,都以需要放大的区域中心点为中心,向外延伸其到这个中心的距离即可实现放大效果。具体实现,可参考着色器中vChangeType=4时的操作。


> FBO离屏渲染
  FBO可以让我们的渲染不渲染到屏幕上,而是渲染到离屏Buffer中。这样的作用是什么呢?比如我们需要处理一张图片,在上传时增加时间的水印,这个时候不需要显示出来的。再比如我们需要对摄像头采集的数据,一个彩色原大小的显示出来,一个黑白的长宽各一半录制成视频。 

图像直接渲染到屏幕上的步骤:
1.编写Shader。(检查支持、权限什么的就不再提了)
2.创建GL环境,直接使用GLSurfaceView,GLSurfaceView内部实现了创建GL环境。
3.GL环境创建后,编译Shader,创建GL Program。获取可用Texture,设置渲染参数。(onSurfaceCreated中)
4.设置ViewPort。(onSurfaceChanged中)
5.清屏(onDrawFrame中)
6.启用必要的属性,useProgram,绑定纹理,传入参数(顶点坐标、纹理坐标、变换矩阵等)。(onDrawFrame中)
7.绘制。(onDrawFrame中)
8.下一帧数据,requestRender,再一次从第5步开始执行。

FBO离屏渲染我们需要改动的地方为:
  获取可用的Texture,不再只获取一个,针对我们假设的需求可以获取两个。一个是作为数据源的texture,另外一个是用来作为输出图像的texture,这时候这个texture相当于是一块还没画东西的画布。获取一个可用的FrameBuffer,方法名和获取可用Texture类似,为glGenFrameBuffers。
  绘制前先绑定FrameBuffer、RenderBuffer、Texture,并将RenderBuffer和Texture挂载到FrameBuffer上。

FBO是一个容器,自身不能用于渲染,需要与一些可渲染的缓冲区绑定在一起,像纹理或者渲染缓冲区。 
Render Buffer Object(RBO)即为渲染缓冲对象,分为color buffer(颜色)、depth buffer(深度)、stencil buffer(模板)。 

> 流畅的播放逐帧动画
  在当前很多直播应用中,拥有给主播送礼物的功能,当用户点击赠送礼物后,视频界面上会出现比较炫酷的礼物特效。这些特效,有的是用粒子效果做成的,但是更多的时用播放逐帧动画实现的
  用Android的Animation的确很容易就实现了逐帧动画。但是用Android的Animation实现动画,当图片要求较高时,播放会比较卡。
  用OpenGL来播放PNG逐帧动画,虽然比用Animation会有一些改善,但是并不能解决动画播放卡顿的问题。(当初天真的以为Animation播放动画是因为Animation用CPU绘制导致卡顿,然后改成用GPU来做,发现然并卵,这才把视线放到PNG解码上了。) 

为Android下OpenGLES实现逐帧动画的方案比较:待选方案
1.直接2D纹理逐帧加载PNG
2.使用ETC压缩纹理替代PNG
3.使用ETC2压缩纹理替代PNG
4.使用PVRTC压缩纹理替代PNG
5.使用S3TC压缩纹理替代PNG

  摄像头的数据回调时间并不是确定的,就算你设置了摄像头FPS范围为30-30帧,它也不会每秒就一定给你30帧数据。Android摄像头的数据回调,受光线的影响非常严重,这是由HAL层的3A算法决定的,你可以将自动曝光补偿、自动白平光等等给关掉,这样你才有可能得到稳定的帧率。 
而我们录制并编码视频的时候,肯定是希望得到一个固定帧率的视频。所以在视频录制并进行编码的过程中,需要自己想些法子,让帧率固定下来。最简单也是最有效的做法就是,按照固定时间编码,如果没有新的摄像头数据回调来就用上一帧的数据。 

使用MediaCodec和MediaMuxer的过程中遇到的问题,总结下需要注意主要有以下几点:
1.MediaCodec是Android4.1新增API,MediaMuxer是Android4.3新增API。
2.颜色空间。按照Android本身的意思,COLOR_FormatYUV420Planar应该是所有硬件平台都支持的。但是实际上并不是这样。所以在设置颜色空间时,应该获取硬件平台所支持的颜色空间,确保它是支持你打算使用的颜色空间,不支持的话应该启用备用方案(使用其他当前硬件支持的颜色空间)。
3.视频尺寸,在一些手机上,视频录制的尺寸可以是任意的。但是有些手机,不支持的尺寸设置会导致录制的视频现错乱。博主在使用Oppo R7测试,360*640的视频,单独录制视频没问题,音视频混合后,出现了颜色错乱的情况,而在360F4手机上,却都是没问题的。将视频宽高都设置为16的倍数,可以解决这个问题。
4.编码器格式设置,诸如音频编码的采样率、比特率等,取值也需要结合硬件平台来设置,否则也会导致崩溃或其他问题。这个其实和颜色空间的选择一样。
5.网上看到许多queueInputBuffer中设置presentationTimeUs为System.nanoTime()/1000,这样做会导致编码出来的音视频,在播放时,总时长显示的是错误的。应该记录开始时候的nanoTime,然后设置presentationTimeUs为(System.nanoTime()-nanoTime)/1000。
6.录制结束时,应该发送结束标志MediaCodec.BUFFER_FLAG_END_OF_STREAM,在编码后区获得这个标志时再终止循环,而不是直接终止循环。

> Obj格式3D模型加载
 遇到复杂的形体,比如说一个人、一朵花,怎么办呢?自然是通过其他工具类似于Maya、3DMax等3D建模工具,做好模型导出来,然后用OpenGLES加载导出的模型文件。模型的加载大同小异

  一个炫酷的模型,往往是包含很多数据的,主要的数据类型如下:
1.顶点数据(Vertex data):
 v 几何体顶点(Geometric vertices)
 vt 贴图坐标点(Texture vertices)
 vn 顶点法线(Vertex normals)
 vp 参数空格顶点 (Parameter space vertices)

2.自由形态曲线(Free-form curve)/表面属性(surface attributes):
 deg 度(Degree)
 bmat 基础矩阵(Basis matrix)
 step 步尺寸(Step size)
 cstype 曲线或表面类型 (Curve or surface type)
 
3.元素(Elements):
 p 点(Point)
 l 线(Line)
 f 面(Face)
 curv 曲线(Curve)
 curv2 2D曲线(2D curve)
 surf 表面(Surface)
 
4.自由形态曲线(Free-form curve)/表面主体陈述(surface body statements):
 parm 参数值(Parameter values )
 trim 外部修剪循环(Outer trimming loop)
 hole 内部整修循环(Inner trimming loop)
 scrv 特殊曲线(Special curve)
 sp 特殊的点(Special point)
 end 结束陈述(End statement)

5.自由形态表面之间的连接(Connectivity between free-form surfaces):
 con 连接 (Connect)

6.成组(Grouping):
 g 组名称(Group name)
 s 光滑组(Smoothing group)
 mg 合并组(Merging group)
 o 对象名称(Object name)

7.显示(Display)/渲染属性(render attributes):
 bevel 导角插值(Bevel interpolation)
 c_interp 颜色插值(Color interpolation)
 d_interp 溶解插值(Dissolve interpolation)
 lod 细节层次(Level of detail)
 usemtl 材质名称(Material name)
 mtllib 材质库(Material library)
 shadow_obj 投射阴影(Shadow casting)
 trace_obj 光线跟踪(Ray tracing)
 ctech 曲线近似技术(Curve approximation technique)
 stech 表面近似技术 (Surface approximation technique)

> 利用EGL后台处理图像
  用到了GLSurfaceView控件来提供GL环境。怎样完全抛开GLSurfaceView来进行图像处理呢,use EGL?
 OpenGL(全写Open Graphics Library)是指定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库。
 EGL是介于诸如OpenGL 或OpenVG的Khronos渲染API与底层本地平台窗口系统的接口。它被用于处理图形管理、表面/缓冲捆绑、渲染同步及支援使用其他Khronos API进行的高效、加速、混合模式2D和3D渲染。

Android中EGL的使用步骤为: 
1. 取得EGL实例 
2. 选择Display 
3. 选择Config 
4. 创建Surface 
5. 创建Context 
6. 指定当前的环境为绘制环境

> 3D模型贴图及光照处理(obj+mtl)
 模型加载以obj文件为入口,解析obj文件,从中获取到mtl文件相对路径,然后解析mtl文件。将材质库拆分为诸多的单一材质。obj对象的 加载,根据具使用材质不同来分解为多个3D模型。

> 球形天空盒VR效果实现
 在3D游戏中通常都会用到天空盒,在3D引擎中也一般会存在天空盒组件,让开发者可以直接使用。

  天空盒(Sky Box)是放到场景中的一个立方体,经常是由六个面组成的立方体,并经常会随着视点的移动而移动。天空盒将刻画极远处人无法达到的位置的景物。
  天空穹(Sky Dome)与天空盒类似,只不过它将是天空盒除底面以外的五个面换成了一个曲面,可以理解成一个半球。和古人认为的天圆地方差不多。
  天空球(Sky Sphere)就是把天空盒直接换成一个球——听说没有天空球这个说法?无所谓了,现在有了。
  VR(Virtual Reality)虚的定义

  绘制出球体之后,我们需要让球与手机的姿态进行同步,也就是当手机背面超下时,我们看到的应该是地面,手机背面朝上是,我们看到的应该是天空。很明显,这就需要用到手机中的传感器了。

  我们需要获得的是手机的姿态,所以上面的传感器中,我们能使用的方案如下:
 1.使用旋转矢量传感器
 2.使用陀螺仪加上磁场传感器
 3.使用陀螺仪加上方向传感器
 4.使用6自由度姿态传感器
 5.或许还有其他方案

  天空球模式的话,相机应该是在球的内部,我们看天空看大地,左看右看的时候,应该是人相机在动,而不是球在动。

> 搞定Blend颜色混合
 Blend是OpenGL中的一个非常重要的部分,它可以让每个输出的源和目的颜色以多种方式组合在一起,以呈现出不同的效果,满足不同的需求。
 颜色、因子、方程式,组合起来就是:最终颜色=(目标颜色*目标因子)@(源颜色*源因子),其中@表示一种运算符。 
 在OpenGLES1.0中,Blend在OpenGLES固定的管线中,OpenGLES2.0相对1.0来说,更为灵活。在OpenGLES2.0中,与Blend相关的函数及功能.