OpenGL ES 是以手持和嵌入式为目标的高级的3D图形应用程序编程接口(API),OpenGL ES 是目前智能手机中占据统治地位的图形API,支持的平台有iOS、Andriod,BlackBerry,bada,Linux,Windows。
OpenGL 和 OpenGL ES 其实是一套标准,是跨平台的,并没有提供载体。所以在对应的平台需要提供对应的载体用于实现。
Run 执行,我们写的代码调用 OpenGL ES framework 也就是CPU(客户端),然后CPU去调用GPU(服务端)处理(例如光栅化等)
OpenGL ES framework 都是在CPU 上做 客户端
光栅话的 是在GPU上做, 服务端
通过 图元装配 将其图元组装,连接方式产考文章
8、OpenGL - 渲染框架
5、OpenGL 的基本连接方式,5.1中介绍了OpenGL图元的9中连接方式
GLSL -> 语法
上图中,API 到 顶点缓存区/数组对象、顶点着色器、纹理坐标、片段着色器 的箭头表示可以通过API直接调用这些缓存区或着色器。
后面的箭头,表示执行顺序
着色器程序 — 描述顶点上执行操作的顶点着色器程序源代码 / 可执行文件
顶点着色器输入【属性】— 用顶点数组提供每个顶点的数据
统一变量【uniform】— 顶点/片元着色器使用功能的不变数据
采样器 — 代表顶点着色器使用纹理的特殊统一变量类型
可以通过三种方法在iOS中将数据传给顶点着色器
内建变量(已经创建好了,不需要我们自己创建),最终输出结果
片元着色器是没有办法直接接收传递的属性的,需要一个桥接
传递属性 -> 顶点着色(桥接)-> 片元着色器
为什么片元着色器不能直接接收传递属性,参阅:8、OpenGL 渲染架构 1、渲染架构
顶点着色器业务:
矩阵变换位置
计算光照公式生成顶点颜色
生成 / 变换纹理坐标
总结:她可以用于执行自定义计算,实施新的变换,照明或者传统的固定功能所不允许的基于顶点的效果。
顶点着色器 下一步是图元装配 - 光栅话 - 片元 - 深度测试
我们写代码的时候,写的是顶点着色器,之后写片元着色器。但是并不是顶点着色器之后下一步就是片元着色器。只是图元装配、光栅化等是我们无法操作的。我们能写代码的只有顶点着色器和片元着色器。
图元[Primitive]:点、线、三角形等
图元装配:将顶点数据计算成一个个图元,在这个阶段会执行裁剪、透视分割和Viewport变换操作。
图元类型和顶点确定将被渲染的单独图元。对于每个单独图元及其对应的顶点,图元装配阶段执行的操作包括:
将顶点着色器的输出值执行裁剪、透视分割、视口变换后进入光栅化阶段
在这个阶段对应的图元【点/线/三角形】,光栅化就是将图元转化成一组二维片段的过程,而这些转化成一组二维片段的过程。而这些转化将有片元着色器处理,这些二维片段就是屏幕上可绘制的像素
图元装配 点 光栅化
线光栅化
三角形光栅化
片元着色器 / 片段着色器
着色器程序 — 描述片段上执行操作的片元着色器程序源代码 / 可执行文件
业务:
总结:它可以用于图片/视频、图形中每个像素的颜色填充(比如给视频添加滤镜,实际上就是将视频中每个图片的像素点颜色填充进行修改)
例如:有100个像素点就会执行100次,性能方面由GPU 执行,有许多的计算单元,可以执行真正的高并发,所以不会出现CPU上面的那些问题。
gl_Position
gl_fragColor 片元着色器对某个像素进行处理之后的结果
图形图像都是一个一个像素点组成的
图片滤镜 图片中每一个像素点,像素点做饱和度处理,新的颜色 帧缓冲区 显示
视频滤镜,原理以及处理方式与图片是一模一样,因为视频也是由一帧一帧的图片组成
像素归属测试:确定帧缓冲区中对应位置上的像素目前是不是归属OpenGL ES 所有(OpenGL ES内部操作,不用开发者控制)
裁剪测试:裁剪测试确定对应位置(Xw,Yw)是否位于作为OpenGL ES状态的一部分裁剪矩形范围内,如果该片段位域裁剪区域之外,则被抛弃
深度测试:输入片段的深度值进行比较,确定片段是否拒绝测试
混合:混合将新生成的片段颜色与保存在帧缓存的位置的颜色值组合
抖动:抖动可用于最小化因为使用有限精度在帧缓存区中保存颜色值而产生的伪像
Embedded Graphics Library
OpenGL ES 命令需要渲染上下文和绘制表面才能完成图形图像的绘制
渲染上下文:存储相关OpenGL ES 状态
绘制表面:是用于绘制图元的表面,它指定渲染所需要的缓存区类型,例如颜色缓存区,深度缓存区和模块缓存区。
OPenGL ES API 并没有提供如何创建渲染上下文或者上下文如何连接到原生窗口系统。 EGL 是Khronos 渲染 API (如 OpenGL ES)和原生窗口系统之间的接口。唯一支持OpenGL ES却不支持EGL 平台是iOS.Apple 提供自己的EGL API 的iOS实现,称为EAGL
因为每个窗口系统都有不同的定义,所以EGL 提供基本的不透明类型 — EGLDisplay,这个类型封装了所有系统相关性,用于和原生窗口系统接口
由于OpenGL ES 是基于C的API,因此它非常便携且受到广泛支持,作为C API,它与Objective-C Cocoa Touch 应用程序无缝集成。OpenGL ES规范没有定义窗口层;相反托管操作系统必须提供函数来创建一个接受命令的OpenGL ES渲染上下文和一个帧缓冲区,其中写入任何绘图命令的结果。在iOS上使用功能OpenGL ES需要使用iOS类来设置和呈现绘图表面,并使用平台中立的API 呈现其内容。
Update 更新内容数据
Display 重新渲染
GLKit 框架的设计目标是为了简化基于OpenGL / OpenGL ES 的应用开发。它的出现加快 OPenGL ES 或 OPenGL 应用程序开发。使用数学库,北京纹理加载,预先创建的着色器效果,以及标准视图和视图控制器来实现渲染循环。
GLKit 框架提供了功能和类,可以减少创建新的基于着色器的应用程序所需的工作量,或者支持依赖早期版本的OPenGL ES 或者 OPenGL 提供的固定函数顶点或片段处理的现有应用程序
GLKit 提供绘制场所(view)
GLKitViewController (扩展于标准的UIKit 设置模式,用于绘制视图内容的管理与呈现)
苹果弃用OPenGL ES,但是iOS 开发者可以继续使用
4.1、使用GLKit
GLkit 已经做了深度测试(Depth)和颜色、帧缓存区等。同时写了gkCrear、glunifrom等等。如果不用GLKit 这些是需要我们自己去写的
功能
加载纹理
提供高性能的数学运算
提供常见的着色器
提供视图以及视图控制器
GLKit 只能在有限的情况下使用,不能自定义编程,只能传参数进行调用
5.1.1、
GLKTextureInfo 创建OPenGL 纹理信息
属性
name:OPenGL 上下文纹理名称
target:纹理绑定的目标
height: 加载的纹理高度
width: 加载纹理的宽度
textureOrigin:加载纹理中的原点位置
alphaState:加载纹理中 alpha 分量状态
containsMipmaps: 布尔值,加载的纹理是否包含mip 贴图
5.1.2、
GLTextureLoader 简化从各种资源文件中加载纹理
初始化
-initWithSharegroup:初始化一个新的纹理加载到对象中
-initWithShareContext:初始化一个新的纹理加载对象
从文件中加载处理
+texttureWithContentsOfFile:options:error: 从文件加载2D 纹理图像并从数据中创建新的纹理
-texttureWithContentsOfFile:options:queue:completionHandler: 从文件中异步加载2D纹理图像,并根据数据创建新纹理
从URL 加载纹理
-texttureWithContentsOfURL:options:error: 从URL 加载2D纹理图像并从数据创建新纹理
-texttureWithContentsOfURL:options:queue:completionHandler: 从URL异步加载2D纹理图像,并根据数据创建新纹理
从内存中创建纹理
+texttureWithContentsOfData:options:error: 从内存空间加载2D纹理图像,并根据数据创建新纹理
-texttureWithContentsOfData:options:queue:completionHandler: 从内存空间异步加载2D纹理图像,并根据数据创建新纹理
从CGImage创建纹理
-texttureWithCGImage:options:error: 从Quartz图像,加载2D纹理图,并根据数据创建新纹理
-texttureWithCGImage:options:queue:completionHandler: 从Quartz图像,异步加载2D纹理图像,并根据数据创建新纹理
从URL 加载多维创建纹理
+cabeMapWithContentsOfURL:options:error:从单个URL加载立方体贴图纹理图像,并根据数据创建新纹理
-cabeMapWithContentsOfURL:options:queue:completionHandler: 从单个URL异步加载立方体贴图纹理图像,并根据数据创建新纹理
从文件加载多维数据创建纹理
+cubeMapWithContentsOfFile:options:error:从单个文件加载立方体贴图纹理图像,并根据数据创建新纹理
-cubeMapWithContentsOfFIle:options:queue:completionHandler: 从单个文件异步加载立方体贴图纹理图像,并根据数据创建新纹理
+cubeMapWithContentsOfFiles:options:error:从一系列文件加载立方体贴图纹理图像,并根据数据创建新纹理
-cubeMapWithContentsOfFIles:options:queue:completionHandler: 从一系列文件异步加载立方体贴图纹理图像,并根据数据创建新纹理
5.2.1、
初始化视图
-initWithFrame:context: 初始化新视图
代理
delegate 视图代理
配置帧缓存区对象
drawableColorFormat 颜色渲染缓存区格式
drawableDepthFormat 深度渲染缓存区格式
drawableStencilFormat 模板渲染缓存区格式
drawableMultisample 多重采样缓存区格式
帧缓存区属性
drawableHeight 底层缓存区对象的高度(以像素为单位)
drawableWidth 底层缓存区对象的宽度(以像素为单位)
绘制视图的内容
Context 绘制驶入内容时使用功能的OPenGL ES 上下文
- bindDrawable 将底层 FrameBuffer 对象绑定的OPenGL ES
enableSetNeedsDiplay 布尔值,指定视图是否响应使得视图内容无效的消息
- display 立即重绘视图内容
Snapshot 绘制视图内容并将其作为新图像对象返回
删除视图FrameBuffer
- deleteDrawable 删除与视图关联的可绘制对象
GLKViewDelegate 用于GLKView 对象回调方法
绘制视图的内容
- glkView:drawInRect: 绘制视图内容(必须实现代理)
GLKviewController 管理OPenGL ES 渲染循环的视图控制器
更新
-(void)update 更新视图内容
-(void)glkViewControllerUpdate:
配置帧速率
preferredFramesPerSecond 视图控制器调用视图以及更新视图内容的速率
framesPerSencond 视图控制器调用视图以及更新其内容的实际速率
配置GLKViewController 代理
Delegate 视图控制器的代理
5.2.2、
GLKViewController 管理OPenGL ES 渲染循环的视图控制器
控制帧更新
Paused 布尔值,渲染循环是否已暂停
pausedonWillResignActive 布尔值,当前程序重新激活活动状态时视图控制器是否自动暂停渲染循环
resumeOnDidBecomeActive 布尔值,当前程序变为活动状态时视图控制是否自动恢复呈现循环
获取有关View 更新信息
frameDisplayed 视图控制器自创建以来发送的帧更新数
timeSinceFrstResume 自视图控制器第一次恢复发送更新事件以来经过的时间量
timeSinceLastResume 自上次视图控制器恢复发送更新事件以来经过的时间量
timeSinceLastUpdate 自上次视图控制器调用委托方法以及经过的时间量
glkViewControllerUpdate:
timeSinceLastDraw 自上次视图控制器调用视图display方法以来经过的的时间量
GLKViewControllerDelegate 渲染循环回调方法
处理更新事件
-glkViewControllerUpdate: 在显示每个帧之前调用
暂停 / 恢复通知
-glkViewController:willPause: 在渲染循环暂停或恢复之前调用
GLKBaseEffect 一种简单光照 / 着色系统,用于基于着色器OPenGL 渲染
命名 Effect
laber 给 Effect(效果)命名
配置模型视图转换
Tranform 绑定效果时应用于顶点数据的模型视图,投影和纹理变换
配置关照效果
lighingType 用于计算每个片段的光照策略,GLKLightingType
GLKLinghtingType
GLKLightingTypePerVertex 表示在三角形中每个顶点执行关照计算,然后在三角形进行插值
GLKLightingTypePixel 表示光照计算在输入在三角形内插入;并且在每个片段执行关照计算
配置光照
lightModelTwoSided 布尔值,表示为基元的两侧计算光照
Material 计算渲染图元光照使用的材质属性
lightModelAmbientColor 环境颜色,应用效果渲染的所有图元
light0 场景中第一个光照属性
light1 场景中第二个光照属性
light2 场景中第三个光照属性
配置纹理
texture2d0 第一个纹理属性
texture2d1 第二个纹理属性
textureOrder 纹理应用与渲染图元的顺序
配置雾化
Fog 应用场景的雾属性
配置颜色信息
colorMaterialEnable 布尔值,表示计算光照与材质交互时是否使用颜色顶点属性
useConstantColor 布尔值,指示是否使用功能常量颜色
constantColor 不提供每个顶点颜色数据时常用常量颜色
准备绘制效果
- prepareToDraw 准备渲染效果
GLKit 在使用的的时候
RGBA8888,各占8位
24占24位
glKview
这些自己去了解一下就行,并不需要刻意记录。
渲染上下文,存储OpenGL ES状态
绘制表面 是用于绘制图元的表面,他指定渲染所需要的缓存区类型,例如颜色缓存区,深度缓冲区
GLKit 处理3件事情
GLKit 处理纹理对象,初始化等,还封装了许多着色器。
详细代码参见Demo
Demo地址 -> OpenGL_ESDemos -> 001--test
跟创建普通的iOS项目没有任何区别,自己创建就行了,不多说什么
#import "ViewController.h"
#import
#import
#import
@interface ViewController ()
{
EAGLContext * context;
GLKBaseEffect * cEffect;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1、OPenGL ES 相关初始化
[self setUpConfig];
// 2、加载顶点 / 纹理坐标数据
[self setUpVertexData];
// 3、加载纹理数据(使用GLBaseEffect)
[self setUpTexture];
}
-(void)setUpConfig
{
//1.初始化上下文&设置当前上下文
/*
EAGLContext 是苹果iOS平台下实现OpenGLES 渲染层.
kEAGLRenderingAPIOpenGLES1 = 1, 固定管线
kEAGLRenderingAPIOpenGLES2 = 2,
kEAGLRenderingAPIOpenGLES3 = 3,
*/
context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
//判断context是否创建成功
if (!context) {
NSLog(@"Create ES context Failed");
}
//设置当前上下文
[EAGLContext setCurrentContext:context];
//2.获取GLKView & 设置context
GLKView *view =(GLKView *) self.view;
view.context = context;
/*3.配置视图创建的渲染缓存区.
(1). drawableColorFormat: 颜色缓存区格式.
简介: OpenGL ES 有一个缓存区,它用以存储将在屏幕中显示的颜色。你可以使用其属性来设置缓冲区中的每个像素的颜色格式。
GLKViewDrawableColorFormatRGBA8888 = 0,
默认.缓存区的每个像素的最小组成部分(RGBA)使用8个bit,(所以每个像素4个字节,4*8个bit)。
GLKViewDrawableColorFormatRGB565,
如果你的APP允许更小范围的颜色,即可设置这个。会让你的APP消耗更小的资源(内存和处理时间)
(2). drawableDepthFormat: 深度缓存区格式
GLKViewDrawableDepthFormatNone = 0,意味着完全没有深度缓冲区
GLKViewDrawableDepthFormat16,
GLKViewDrawableDepthFormat24,
如果你要使用这个属性(一般用于3D游戏),你应该选择GLKViewDrawableDepthFormat16
或GLKViewDrawableDepthFormat24。这里的差别是使用GLKViewDrawableDepthFormat16
将消耗更少的资源
*/
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;//精度越高,闪烁越少出现
// 5、设置背景颜色
glClearColor(1, 0, 0, 1);
}
//2 加载顶点 / 纹理坐标数据
-(void)setUpVertexData
{
// 1、顶点数据(顶点坐标,纹理坐标)存在内存
// 2个三角形,有6个顶点
/*
纹理坐标系取值范围[0,1];原点是左下角(0,0);
故而(0,0)是纹理图像的左下角, 点(1,1)是右上角.
*/
GLfloat vertextData[]={
0.5, -0.5, 0.0f, 1.0f, 0.0f, //右下
0.5, 0.5, 0.0f, 1.0f, 1.0f, //右上
-0.5, 0.5, 0.0f, 0.0f, 1.0f, //左上
0.5, -0.5, 0.0f, 1.0f, 0.0f, //右下
-0.5, 0.5, 0.0f, 0.0f, 1.0f, //左上
-0.5, -0.5, 0.0f, 0.0f, 0.0f, //左下
};
/*
顶点数组: 开发者可以选择设定函数指针,在调用绘制方法的时候,直接由内存传入顶点数据,也就是说这部分数据之前是存储在内存当中的,被称为顶点数组
顶点缓存区: 性能更高的做法是,提前分配一块显存,将顶点数据预先传入到显存当中。这部分的显存,就被称为顶点缓冲区
*/
// 2开辟顶点缓冲区
// 1)、创建顶点缓冲区标识符ID
GLuint bufferID;
glGenBuffers(1, &bufferID);
// 2、绑定顶点缓冲区
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
// 3、顶点缓冲区存在 GPU
glBufferData(GL_ARRAY_BUFFER, sizeof(vertextData), vertextData, GL_STATIC_COPY);
//3.打开读取通道.
/*
(1)在iOS中, 默认情况下,出于性能考虑,所有顶点着色器的属性(Attribute)变量都是关闭的.
意味着,顶点数据在着色器端(服务端)是不可用的. 即使你已经使用glBufferData方法,将顶点数据从内存拷贝到顶点缓存区中(GPU显存中).
所以, 必须由glEnableVertexAttribArray 方法打开通道.指定访问属性.才能让顶点着色器能够访问到从CPU复制到GPU的数据.
注意: 数据在GPU端是否可见,即,着色器能否读取到数据,由是否启用了对应的属性决定,这就是glEnableVertexAttribArray的功能,允许顶点着色器读取GPU(服务器端)数据。
(2)方法简介
glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
功能: 上传顶点数据到显存的方法(设置合适的方式从buffer里面读取数据)
参数列表:
index,指定要修改的顶点属性的索引值,例如
size, 每次读取数量。(如position是由3个(x,y,z)组成,而颜色是4个(r,g,b,a),纹理则是2个.)
type,指定数组中每个组件的数据类型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT。
normalized,指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE)
stride,指定连续顶点属性之间的偏移量。如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0
ptr指定一个指针,指向数组中第一个顶点属性的第一个组件。初始值为0
*/
// 顶点坐标数据
glEnableVertexAttribArray(GLKVertexAttribPosition);
// 读取数据
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (GLfloat*)NULL+0);
// 打开纹理的数据通到
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (GLfloat*)NULL+3);
}
#pragma mark -- GLKViewDelegate
//绘制视图的内容
/*
GLKView对象使其OpenGL ES上下文成为当前上下文,并将其framebuffer绑定为OpenGL ES呈现命令的目标。然后,委托方法应该绘制视图的内容。
*/
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClear(GL_COLOR_BUFFER_BIT);
// 2、准备绘制
[cEffect prepareToDraw];
// 3、开始绘制
glDrawArrays(GL_TRIANGLES, 0, 6);
}
-(void)setUpTexture
{
// 1、获取图片路径
NSString * filePath = [[NSBundle mainBundle] pathForResource:@"WX20200723-181603@2x" ofType:@"png"];
// 2、设置纹理的参数
// 纹理坐标原点:左下角,图片显示原点在左上角
// 调整一下,让图片是正着显示的
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
GLKTextureInfo * textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
// 3、使用苹果GLKit 提供GLBaseEffect 完成着色器工作(顶点/片元)
cEffect = [[GLKBaseEffect alloc] init];
cEffect.texture2d0.enabled = GL_TRUE;
cEffect.texture2d0.name = textureInfo.name;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}