OpenGLES2.0是图形渲染(图形处理)库。 OpenGL ES 2.0渲染过程为:读取顶点数据-执行顶点着色器-组装图元-光栅化图元-执行片元着色器-写入帧缓冲区-显示在屏幕上。 Android openGL ES渲染示例- https://blog.csdn.net/junzia/article/category/6462864
> OpenGL 图像直接渲染到屏幕上的步骤: 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,再次从第五步开始执行。
> 相机和投影,转换矩阵 Android OpenGLES2.0(3)-等腰直角三角形和彩色三角形- http://blog.csdn.net/junzia/article/details/52817978 OpenGL ES从屏幕中心垂直到上下左右边缘的坐标映射到屏幕上.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)右上角坐标为(1).0,0.0)左下角坐标为(0).0,1.0),右下角坐标为(1.0,1.0)。
> 利用OpenGL图片处理 为保证效率,Android手机中使用了许多美容相机和图片处理应用OpenGLES处理图片。图片的颜色处理、模糊和放大镜效果. PS使用滤镜可以很容易地改变图片的整体颜色。
-- 色彩处理简单 #FF88269F,这种颜色分为四部分,每两部分组成一部分,分别表示A(透明通道),R(红色通道),G(绿色通道),B(蓝色通道)。 在GLSL在中间,颜色是向量,包含四个浮点vec四个浮点分别表示RGBA取值范围为0.0-1.0.我们先读取图片每个像素的颜色值,然后调整读取的颜色值,以便完成图片的颜色处理。 在黑白图片上,每个像素点RGB三个通道值应相等。知道了这一点,把彩色图片处理成黑白图片就很简单了。我们直接来自像素点RGB加上三个通道,然后除以3作为处理后,每个通道的值可以得到一张黑白图片。这是一种常见的黑白图片处理方法。类似的权值方法(给予)RGB三个通道的比例不同),只取绿色通道等方式。 与之类似的,冷色调的处理就是单一增加蓝色通道的值,暖色调的处理可以增加红绿通道的值。还有其他复古、浮雕等处理也都差不多。
-- 图片模糊处理 图片模糊处理相对上面的色调处理稍微复杂一点,通常图片模糊处理是采集周边多个点,然后利用这些点的色彩和这个点自身的色彩进行计算,得到一个新的色彩值作为目标色彩。模糊处理有很多算法,类似高斯模糊、径向模糊等等。
-- 放大镜效果 我们只需要将制定区域的像素点,都以需要放大的区域中心点为中心,向外延伸其到这个中心的距离即可实现放大效果。具体实现,可参考着色器中vChangeType=4时的操作。
> FBO离屏渲染 FBO可以让我们的渲染不渲染到屏幕上,而是渲染到离屏Buffer中。这样的作用是什么呢?比如我们需要处理一张图片,在上传时增加时间的水印,这个时候不需要显示出来的。再比如我们需要对摄像头采集的数据,一个彩色原大小的显示出来,一个黑白的长宽各一半录制成视频。
-- 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的视频,单独录制视频没问题,音视频混合后,出现了颜色错乱的情况,而在360 F4手机上,却都是没问题的。将视频宽高都设置为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引擎中也一般会存在天空盒组件,让开发者可以直接使用。
1.天空盒(Sky Box)是放到场景中的一个立方体,经常是由六个面组成的立方体,并经常会随着视点的移动而移动。天空盒将刻画极远处人无法达到的位置的景物。 2.天空穹(Sky Dome)与天空盒类似,只不过它将是天空盒除底面以外的五个面换成了一个曲面,可以理解成一个半球。和古人认为的天圆地方差不多。 3.天空球(Sky Sphere)就是把天空盒直接换成一个球——听说没有天空球这个说法?无所谓了,现在有了。 4.VR(Virtual Reality)虚的定义
绘制出球体之后,我们需要让球与手机的姿态进行同步,也就是当手机背面超下时,我们看到的应该是地面,手机背面朝上是,我们看到的应该是天空。很明显,这就需要用到手机中的传感器了。
-- 我们需要获得的是手机的姿态,所以上面的传感器中,我们能使用的方案如下: 1.使用旋转矢量传感器 2.使用陀螺仪加上磁场传感器 3.使用陀螺仪加上方向传感器 4.使用6自由度姿态传感器 5.或许还有其他方案
天空球模式的话,相机应该是在球的内部,我们看天空看大地,左看右看的时候,应该是人相机在动,而不是球在动。
> 搞定Blend颜色混合 Blend是OpenGL中的一个非常重要的部分,它可以让每个输出的源和目的颜色以多种方式组合在一起,以呈现出不同的效果,满足不同的需求。 颜色、因子、方程式,组合起来就是:最终颜色=(目标颜色*目标因子)@(源颜色*源因子),其中@表示一种运算符。 在OpenGLES1.0中,Blend在OpenGLES固定的管线中,OpenGLES2.0相对1.0来说,更为灵活。在OpenGLES2.0中,与Blend相关的函数及功能.