最近,斗破苍穹电视剧上映的,里面的梗就不用多提, 三十年河东,三十年河西,莫欺少年穷
指不定口碑低开高走呢。
人生也是一样, 无法预料,学历代表你的过去,能力代表你的现在,学习代表你的将来.
所以学无止境,需精益求精,不断的学习,才能蹒跚前行。
概述
OpenGL ES (Open Graphics Library for Embedded Systems)是访问类似 iPhone 和 iPad 的现代嵌入式系统的 2D 和 3D 图形加速硬件的标准。
把程序提供的几何数据转换为屏幕上的图像的过程叫做渲染。
GPU 控制的缓存是高效渲染的关键。容纳几何数据的缓存定义了要渲染的点、线段和三角形。
OpenGL ES 3D 的默认坐标系、顶点和矢量为几何数据的描述提供了数学基础。
渲染的结果通常保存在帧缓存中。有两个特别的帧缓存,前帧缓存和后帧缓存,它们控制着屏幕像素的最终颜色。
OpenGL ES 的上下文保存了 OpenGL ES 的状态信息,包括用于提供渲染数据的缓存地址和用于接收渲染结果的缓存地址。
OpenGL ES 是 OpenGL 的子集,它移除了 OpenGL 中冗余的函数,使其更易学也更容易在移动图形硬件中实现。
OpenGL ES 是基于 C 语言的 API ,所以可以无缝移植到 Objective—C 中,然后通过创建上下文来接收命令和操纵帧缓存。
在 iOS 中,使用 OpenGL ES 时,还可以使用 GLKit 框架中的 GLKView 将 OpenGL ES 绘制的内容渲染到屏幕上,并且可以使用 GLKViewController 来管理 GLKView 视图。另外,还可以使用 CAEAGLLayer 图层将动画与视图相结合。
但是,需要注意的是,当应用处于后台状态时,不能调用 OpenGL ES 中的函数,否则应用便会被终止,而且 OpenGL ES 中的上下文也不支持在同一时刻被不同的线程访问。
作为新手入门,我打算先了解一下 OpenGL 的一些基本概念
渲染管道 (Graphics Pipeline)
管道,英文名叫Pipeline,相信用过FaceBook图片加载库的同学对这个管道并不陌生,因为SimpleImageDrawee里面也是用的管道来对图片进行的一个处理。由于其底层也是C,因此我可以大胆的猜想,FaceBook图片加载库的设计思路可能有参考OpenGL(这当然纯属臆想^_^)。
管道用正确的计算机语言来描述就是:
显卡执行的、从几何体到最终渲染图像的、数据传输处理计算的过程。
即是管道,那就得有先后顺序。整体是从上游流到下游。
在OpenGL ES1.x中,它是固定管道,整体是封闭的,中间的各道工艺按固定的流程顺序走。看下图:
从上图可以看出,这些工艺顺序是固定的。整个过程又可以分成三部分:处理顶点
、处理片元
、验证片元信息并存入内存
。
Rasterizer:光栅化处理,当顶点处理完后,会交给rasterizer来进行光栅化处理,结果会把顶点的坐标信息转换成能在屏幕显示的像素信息即片元(fragments)。生成片元后,接下来就是对fragments片元的各种验证,即过滤掉无用的片元,裁剪掉不在视野内的片元,最终把有效片元存储入内存中。
OpenGL 组成
OpengGL 主要包括三个要素: 原件(Primitives) 缓冲区(Buffers) 光栅化(Rasterisation)
原件(图元装配)
在顶点着色器程序输出顶点坐标之后,各个顶点被按照绘制命令中的图元类型参数,以及顶点索引数组被组装成一个个图元。
原件 包括 点, 线 三角形。有的OpenGL版本还包括四边形,但在OpengGL ES中只有这三种原件,
OpenGL 绘制的都是图形,包括形状和填充,基本形状是三角形。其它的复杂图元都是基于这些基本图元来绘成的
每个形状都有顶点,Vertix,顶点的序列就是一个图形。
图形有所谓的正反面,如果我们看向一个图形,它的顶点序列是逆时针方向,那我们看到的就是正面。
在 图元装配(Primitive Assembly)
阶段,那些经过顶点着色器(VertexShader)处理过的顶点数组或缓冲区的数据(VertexArrays/Buff Objects),被组装到一个个独立的几何图形中(eg:点,线,三角形等)。
对装配好的每一个图元,都必须确保它在世界坐标系(即能显示在屏幕的可见区域)中,而对于不在世界坐标系中的图元,就必须进行裁剪,使其处在世界坐标系中才能流到下一道工序(光栅化处理)。
在这里注意下还有一个剔除操作(Cull),前提是这个功能的开关是打开的:
1 | GLES20.glEnable(GLES20.GL_CULL_FACE); //剔除的是图元的背影,阴影,背面等。 |
缓存区
缓冲区包括三个概念: 帧缓冲区
(Frame Buffers) 渲染缓冲区
(Render Buffers) 缓冲区对象
(Buffer Objects)
帧缓冲区
是GL储存渲染结果的地方,为GPU指定存储渲染结果的区域
,它可以包含多个图像,也就是包含多个渲染缓冲区
, 它存储着 OpenGL ES 绘制每个像素点最终的所有信息:颜色,深度和模板值。更通俗点,可以理解成存储屏幕上最终显示的一帧画面的区域.渲染缓冲区 也就是一个图像,也被称作颜色缓冲区.因为它本质上是存储要显示的颜色.例如颜色图像,深度图像,模版图像(决定每一个位置是可以的掩膜)等。多个纹理对象或多个渲染缓存对象,可通过连接点(attachment points)连接到帧缓存对象上。
缓冲区对象 就是程序员提供给OpenGL的数据,分为结构类和索引类。前者被称为 “数组缓冲区对象” 或者 “顶点缓冲对象”(“Array Buffer Object or Vertex Buffer Object”),即用来描述模型的数组,如顶点数组,纹理数组等;后者被称为 “索引缓冲区对象”(“Index Buffer Object”),是对上述数组的索引。
可以同时存在很多帧缓冲区,并且可以通过 OpenGL ES 让 GPU 把渲染结果存储到任意数量的帧缓冲中。但是,只有将内容绘制到视窗体提供的帧缓冲中,才能将内容输出到显示设备。视图系统提供的帧缓冲通常由两个缓存对象组成,一个前端缓存,一个后端缓存。
前帧缓存决定了屏幕上显示的像素颜色。程序的渲染结果通常保存在后帧缓存在内的其他帧缓存,当渲染后的后帧缓存包含一个完成的图像时,前后帧缓存会立即互换,前帧缓存变成新的后帧缓存,后帧缓存变成新的前帧缓存。
但是前后帧我们无法去操纵,它是由系统控制的。我们只能显式的告诉系统,要展示哪个帧缓存了,然后由系统去完成前后帧的切换。
Rasterizer/Rasterization (光栅化)
光栅化的过程就是根据缓冲区对象里提供的数据 经过渲染 从3D模型得到2D模型的过程。得到的2D图像会根据帧缓冲区的配置来决定是直接送到屏幕显示 或是 做别的用处。
简单点,就是把矢量图形转化成像素点儿的过程。我们屏幕上显示的画面都是由像素组成,而三维物体都是点线面构成的。要让点线面,变成能在屏幕上显示的像素,就需要Rasterize这个过程。就是从矢量的点线面的描述,变成像素的描述。如上图
在光栅化阶段,基本图元被转换为供片段着色器使用的片段(Fragment
),Fragment 表示可以被渲染到屏幕上的像素,它包含位置,颜色,纹理坐标等信息,这些值是由图元的顶点信息进行插值计算得到的。这些片元接着被送到片元着色器中处理。 这是从顶点数据到可渲染在显示设备上的像素的质变过程
。
在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。
流水线
从2.0版本开始,OpenGL支持可变的流水线,也就是说,程序员 可以通过 Shader(一种程序,一段包含着色信息的源代码字符串)来控制GPU渲染的过程。
Shader 也叫着色器,用来描述如何绘制(渲染), GLSL 是 OpenGL 的 编程语言,全称全称就叫 OpenGL Shader Language。
Shader 分为 Vertex Shader
和 Fragment Shader
。这两种Shader是成对出现的,GPU先执行前者,后执行后者。
Vertex Shader(顶点着色器)
Vertex Shader 对于3D模型网格的每一个顶点执行一次,主要是确定改顶点的最终位置。Vertex Shader 还会准备并输出一些变量,传给 Fragment Shader 使用。 简单点就是 控制顶点的绘制,指定坐标、变换等。
Fragment Shader(片元着色器)
Fragment Shader(也叫片段着色器) 对于最终得到的2D图像的每一个像素处理一次。3D物体的表面最终显示成什么样将有它决定。例如为模型的可见表面添加纹理,处理光照 阴影的影响等等,都在这里做。 Fragment Shader 的输出是一个 RGBA 格式的像素颜色值。 简单点就是 控制形状内区域渲染,纹理填充内容。 片元着色器的主要作用是计算每一个片元最终的颜色值(或者丢弃该片段)。
在片段着色器之前的阶段,渲染管线都只是在和顶点,图元打交道。在 3D 图形程序开发中,贴图是最重要的部分,程序可以通过 GL 命令上传纹理数据至 GL 内存中,这些纹理可以被片段着色器使用。片段着色器可以根据顶点着色器输出的顶点纹理坐标对纹理进行采样,以计算该片段的颜色值。
另外,片段着色器也是执行光照等高级特效的地方,比如可以传给片段着色器一个光源位置和光源颜色,可以根据一定的公式计算出一个新的颜色值,这样就可以实现光照特效。
着色器的使用流程
着色器源代码和OpenGL源代码不是一起编译的,所以要特别强调我刚才说的“着色器是一段包含着色信息的源代码字符串”。所以,OpenGL源代码肯定是和工程一起编译的,但是着色器源代码是在运行期编译的。你可能会问,着色器的源代码是一个字符串怎么编译呢?所以OpenGL ES提供了一套运行期动态编译的流程:
(1)创建着色器:glCreateShader
(2)指定着色器源代码字符串:glShaderSource
(3)编译着色器:glCompileShader
(4)创建着色器可执行程序:glCompileShader
(5)向可执行程序中添加着色器:glAttachShader
(6)链接可执行程序:glLinkProgram
纹理
纹理是一个用来保存图像的色值的 OpenGL ES 缓存。
现实生活中,纹理最通常的作用是装饰我们的物体模型,它就像是贴纸一样贴在物体表面,使得物体表面拥有图案。
但实际上在 OpenGL 中,纹理的作用不仅限于此,它可以用来存储大量的数据。一个典型的例子就是利用纹理存储画笔笔刷的 mask 信息。
坐标系
弄清楚坐标系很重要,它可以把几何图像和代数方程关联起来,也是是我们图像绘制的 布局的开始。
OpenGL 渲染管线整个流程中,涉及了多个坐标系变化,看起来非常繁琐。但是针对 2D 图像处理,我们其实不需要关心这些变化,我们只需要了解 标准化设备坐标
即可。
iOS 手机坐标系
二维坐标系,原点在左上角,x 轴向右,y 轴向下,x y 取值范围为屏幕分辨率:
OpenGL 坐标系
三维坐标系,原点在中间,x 轴向右,y 轴向上,z 轴朝向我们,x y z 取值范围都是 [-1, 1]:
纹理(texture) 坐标
为了能够把纹理映射到三角形,我们需要指定三角形的每一个顶点各自对应纹理的哪个部分。这样每一个顶点就会关联着一个纹理坐标,用来标明该从纹理图像的那一个人部分采样(采集片段颜色)。之后再图形的其他片段上进行片段插值。
纹理坐标是二维坐标系,使用纹理坐标获取纹理颜色叫做采样,其原点在左下角,s(x)轴向右,t(y)轴向上,x y 取值范围都是 [0, 1]。
下面的图片展示了我们是如何把纹理坐标映射到三角形上:
OpenGL 有关概念
OpenGL 提供一组API,各显卡制造商和系统制造商来实现这组API。
EGL 是另一组API,主要是系统制造商实现,它负责OpenGL和原生窗口系统之间的接口,用于把OpenGL 渲染的结果显示在屏幕上。
EGAL 是Apple公司对EGL做了修改,自己实现的一套屏幕显示接口,一般只用于iOS和 OS X 系统中。
关系如下图所示
参考文章:
https://www.2cto.com/kf/201706/647894.html
https://blog.csdn.net/wangyuchun_799/article/details/7736928