NO.13 - OpenGL ES及GLKit

OpenGL ES

  • OpenGL ES是OpenGL的子集
  • 是针对嵌入式设备及移动终端设备的高级3D图形应用程序,例如iOS、Android、Windows等
  • OpenGL ES 是跨平台的,不会提供窗口相关方法,需要系统各自提供载体
OpenGL ES渲染流程
OpenGL ES渲染流程

OpenGL ES的渲染主要分为两部分:CPU和GPU

CPU部分

  • app代码通过OpenGL ES API,会调度OpenGL ES Framework
  • 通过OpenGL ES client 调度 OpenGL ES server,将顶点数据等传递到GPU

GPU部分

  • 做一些图形硬件的处理,例如光栅化、显示等
OpenGL ES 图形管道
图形管道
  • API获得顶点数据,将顶点数据从内存中拷贝至顶点缓冲区(显存)
  • 拿到数据之后,通过attribute通道传递至顶点着色器,同时,纹理坐标通过Texture通道传递到顶点着色器和片元着色器
  • 然后,图元装配,即图元的连接方式,一共有9种,常用的有6种,此步骤将顶点变换为图形
  • 光栅化:确定图形与屏幕对应的位置
  • 片元/片段/像素着色器:处理对应像素点的颜色值
  • 在将处理好的每个像素点的颜色值存储到帧缓存区,然后在显示器中显示
  • API:可以通过API操作顶点缓冲区、顶点着色器、纹理坐标、片段着色器

下面附上苹果官方文档图示,原理都是一致的,只是描述方式不同。


苹果官方图示
  • App:提供图元装配顶点信息,图片信息
  • Vertex(顶点着色器):处理顶点 -- 图形变换(旋转、缩放、平移)
  • Geometry(图元装配):图元装配 + 裁剪(超出屏幕部分被裁剪)
  • Fragment(片元着色器):纹理处理 + 雾化处理
  • Framebuffer Operation(帧缓冲区):透明度混合、模板、深度测试;最后在混合,这些操作都是在即将显示时,在帧缓冲区中完成的动作
顶点着色器 (vertex shader)
顶点着色器
  • 输入,有3种方式
    • 通过attribute通道输入顶点数据,提供每个顶点的数据
    • 通过uniform通道输入统一变量,即顶点/片元着色器中使用的不变的数据
    • 采样器:表示顶点着色器使用纹理的特殊统一变量类型
  • 输出:经过处理的最终顶点数据,有2种
    • gl_Position,是GLSL 的内建变量,是将处理后的最终顶点数据赋值给它
    • gl_PointSize,是指点的尺寸,即可以在顶点着色器中修改每个点的大小,使用率较低

顶点着色器的业务内容:
1.矩阵变换位置
2.计算光照公式⽣成逐顶点颜⾊
3.⽣成/变换纹理坐标

总结: 它可以⽤于执行⾃自定义计算,实施新的变换,照明或者传统的固定功能所不允许 的基于顶点的效果。

顶点着色器GLSL代码示例:

attribute、uniform 表示client与server之间的通道
其中的vec4、vec2都是向量类型,表示四维向量和二维向量
mat4:4*4矩阵
varying是修饰符:通过varying将纹理坐标传入到片元着色器
lowp:低精读
main中的操作
  实现了纹理坐标的桥接
  实现了顶点旋转矩阵的相乘:列向量 与 列矩阵 相乘,得到旋转后的顶点坐标
  将上述得到的顶点坐标,赋值给gl_Position
*********************************************************
attribute vec4 position;
attribute vec2 textCoordinate; 
uniform mat4 rotateMatrix; 
varying lowp vec2 varyTextCoord; 
void main()
{
    varyTextCoord = textCoordinate;
    vec4 vPos = position;
    vPos = vPos * rotateMatrix;
    gl_Position = vPos; 
}
图元装配

顶点着⾊器之后,下⼀个阶段就是图元装配.
图元(Primitive): 点,线,三⻆角形等.
图元装配: 将顶点数据计算成⼀个个图元.在这个阶段会执⾏裁剪、透视分割和 Viewport变换操作。
图元类型和顶点索确定将被渲染的单独图元。对于每个单独图元及其对应的顶点,图元装配阶段执行的操作包括:将顶点着⾊器的输出值执⾏裁剪、透视分割、视⼝变换后进⼊入光栅化阶段。

光栅化

将图元转化为一组二维片段的过程,主要是由于屏幕是2D的,所以转换的像素点也是二维的


光栅化
片源着色器(fragment shader)
片源着色器
  • 输入同顶点着色器一样,有3种方式
    • 由顶点着色器桥接传递过来的纹理坐标等
    • 通过uniform通道输入统一变量,即顶点/片元着色器中使用的不变的数据
    • 采样器:表示顶点着色器使用纹理的特殊统一变量类型,例如纹理就是通过采样器传递
  • 输出:某个像素点经过片元着色器处理后的结果。

片元着色器的业务内容:
1.计算颜色
2.获取纹理值
3.往像素点中填充颜色值(纹理值/颜色值)

总结: 它可以⽤于图片/视频/图形中每个像素的颜⾊填充(⽐如给视频添加滤镜,实际上就是将视频中每个图⽚的像素点颜⾊填充进行修改.)

片元着色器GLSL代码示例

varying:必须和顶点着色器中一模一样,这样才能传递纹理坐标
sampler2D 采样器类型
texture2D(纹理采样器,纹理坐标):获取对应位置/坐标的颜色值,简称获得纹素
gl_FragColor(内建变量):将最终的颜色值赋值给它
*********************************************************
varying lowp vec2 varyTextCoord; 
uniform sampler2D colorMap;
void main() {
gl_FragColor = texture2D(colorMap, varyTextCoord); }
逐片段操作

这个过程都是GPU内部处理的,开发者并不需要关心,将处理好的数据存储到帧缓存区,最后读取帧缓存区将图形显示到屏幕上

逐片段操作
  • 像素归属测试: 确定帧缓存区中位置(Xw,Yw)的像素⽬前是不是归属于OpenGL ES所 有. 例如,如果一个显示OpenGL ES帧缓存区View被另外一个View 所遮蔽.则窗口系统可以确定被遮蔽的像素不属于OpenGL ES上下文.从而不全显示这些像素.而像素归属测试是OpenGL ES 的⼀部分,它不由开发者开人为控制,⽽是由OpenGL ES内部进⾏.
  • 裁剪测试: 裁剪测试确定(Xw,Yw)是否位于作为OpenGL ES状态的⼀部分裁剪矩形范围内.如果该⽚段位于裁剪区域之外,则被抛弃.
  • 深度测试: 输⼊片段的深度值进步⽐较,确定⽚段是否拒绝测试
  • 混合: 混合将新生成的⽚段颜色与保存在帧缓存的位置的颜色值组合起来.
  • 抖动: 抖动可⽤于最⼩化因为使⽤有限精度在帧缓存区中保存颜⾊值⽽产生的伪像.
OpenGL ES的应用

图片滤镜

  • 获取图片中的每一个像素点
  • 像素点做饱和度处理
  • 得到新的颜色
  • 将新的颜色翻入帧缓存区
  • 最后进行显示

视频滤镜
原理以及处理方式是一样的(GLSL代码),视频也是一帧一帧处理的,而一帧就是一张图片

  • 获得视频MP4文件
  • 拿到h264(视频压缩文件) -
  • 将视频解码(解压),还原成一帧一帧的图片
  • 针对一帧一帧的图片进行处理

EGL(Embedded Graphics Library)

  • OpenGL ES 命令需要渲染上下⽂和绘制表面才能完成图形图像的绘制
    渲染上下⽂: 存储相关OpenGL ES状态,是一个状态机
  • 绘制表面:⽤于绘制图元的表面,需要指定渲染的缓存区,例如颜⾊缓、深度和模板
  • OpenGL ES API并没有提供如何创建渲染上下文或者上下文如何连接到原生窗口系 统. EGLKhronos 渲染API(如OpenGL ES) 和原⽣窗⼝系统之间的接⼝. 唯⼀支持 OpenGL ES 却不支持EGL的平台是iOS. Apple 提供⾃己的EGL API的iOS实现,称为EAGL
  • 因为每个窗⼝系统都有不同的定义,所以EGL提供基本的不透明类型—EGLDisplay, 这 个类型封装了所有系统相关性,用于和原生窗⼝系统接⼝

GLKit

了解GLKit框架前,有必要先看下GLKit的苹果官方文档GLKitAPI

GLKit框架的设计目标是为了简化基于OpenGL / OpenGL ES的应用开发。它的出现加快OpenGL ESOpenGL应用程序开发。 使用数学库,背景纹理加载,预先创建的着色器效果,以及标准视图和视图控制器来实现渲染循环。
GLKit框架提供了功能和类,可以减少创建新的基于着⾊器的应用程序所需的⼯作量,或者⽀持依赖早期版本的OpenGL ESOpenGL提供的固定函数顶点或⽚段处理的现有应用程序。

GLKit功能

  • 提供高性能的数学运算(Math libraries):提供常用的向量,四元数和矩阵运算。

  • 加载纹理(Texture loading):允许加载各种纹理,且可以后台加载,通过GLKTextureLoader类来加载

  • 提供常见的着色器(effect):包含以下3种着色器

    • GLKBaseEffect
    • GLKReflectionMapEffect
    • GLKSkyboxEffect
  • 提供视图视图以及视图控制器:GLKViewGLKViewController

    • GLKView:提供绘制场所,继承自UIView
    • GLKViewController:⽤于绘制视图内容的管理与呈现,继承自UIViewController)

使用GLKit视图呈现OpenGL ES 内容
下图来自Apple官方文档Drawing with OpenGL ES and GLKit

GLKit呈现图片过程

通过GLKit展示图片,主要有以下三个步骤

  • GLKView的创建和配置
  • 使用GLKView对象绘制图形,并存储到帧缓存区
  • 从帧缓存区中读取数据,显示到屏幕上

GLKit 常用API

GLKit文理加载
1.GLKTextureInfo 创建OpenGL纹理信息
@interface GLKTextureInfo : NSObject 
{
@private
    GLuint                      name;
    GLenum                      target;
    GLuint                      width;
    GLuint                      height;
    GLuint                      depth;
    GLKTextureInfoAlphaState    alphaState;
    GLKTextureInfoOrigin        textureOrigin;
    BOOL                        containsMipmaps;
    GLuint                      mimapLevelCount;
    GLuint                      arrayLength;
}

@property (readonly) GLuint                     name;
@property (readonly) GLenum                     target;
@property (readonly) GLuint                     width;
@property (readonly) GLuint                     height;
@property (readonly) GLuint                     depth;
@property (readonly) GLKTextureInfoAlphaState   alphaState;
@property (readonly) GLKTextureInfoOrigin       textureOrigin;
@property (readonly) BOOL                       containsMipmaps;
@property (readonly) GLuint                     mimapLevelCount;
@property (readonly) GLuint                     arrayLength;

@end

GLKTextureInfo对象里面有很多属性,常用的如下:

name : OpenGL 上下文中纹理名称
target: 纹理绑定的目标
height : 加载的纹理⾼度
width : 加载纹理的宽度
textureOrigin: 加载纹理中的原点位置
alphaState: 加载纹理中alpha分量状态
containsMipmaps: 布尔值,加载的纹理是否包含mip贴图

2.GLTextureLoader简化从各种资源文件中加载纹理

GLTextureLoader初始化方式:

- initWithSharegroup: 初始化一个新的纹理加载到对象中
- initWithShareContext:初始化一个新的纹理加载对象

常见的加载方式如下:

  • 从⽂件中加载纹理

+ textureWithContentsOfFile:options:errer:从⽂件加载2D纹理图像并从数据中
创建新的纹理
- textureWithContentsOfFile:options:queue:completionHandler:从⽂件中异步
加载2D纹理图像,并根据数据创建新纹理

  • 从URL加载纹理

- textureWithContentsOfURL:options:error:从URL加载2D纹理图像并从数据创
建新纹理
- textureWithContentsOfURL:options:queue:completionHandler:从URL异步
加载2D纹理图像,并根据数据创建新纹理.

  • 从内存中表示创建纹理

+ textureWithContentsOfData:options:errer:从内存空间加载2D纹理图像,并根据数据创建新纹理
- textureWithContentsOfData:options:queue:completionHandler:从内存空间异步加载2D纹理图像,并从数据中创建新纹理

  • 从CGImages创建纹理

- textureWithCGImage:options:error:从Quartz图像加载2D纹理图像并从数据创建新纹理
- textureWithCGImage:options:queue:completionHandler:从Quartz图像异步加载2D纹理理图像,并根据数据创建新纹理

  • 从URL加载多维创建纹理

+ cabeMapWithContentsOfURL:options:errer:从单个URL加载⽴方体贴图纹理图像,并根据数据创建新纹理
- cabeMapWithContentsOfURL:options:queue:completionHandler:从单个
URL异步加载⽴方体贴图纹理图像,并根据数据创建新纹理

  • 从⽂件加载多维数据创建纹理

+ cubeMapWithContentsOfFile:options:errer:从单个文件加载⽴方体贴图纹理对象,并从数据中创建新纹理理
- cubeMapWithContentsOfFile:options:queue:completionHandler:从单个⽂件异步加载⽴方体贴图纹理对象,并从数据中创建新纹理
+ cubeMapWithContentsOfFiles:options:errer:从⼀系列文件中加载⽴方体贴图纹理图像,并从数据总创建新纹理
- cubeMapWithContentsOfFiles:options:options:queue:completionHandler:
从一系列文件异步加载⽴方体贴图纹理图像,并从数据中创建新纹理

GLKit OpenGL ES视图渲染

GLKView使用OpenGL ES绘制内容的视图默认实现

  • 初始化视图
    - initWithFrame:context:初始化新视图
    delegate 视图的代理
  • 配置帧缓存区对象
    drawableColorFormat颜⾊渲染缓存区格式
    drawableDepthFormat深度渲染缓存区格式
    drawableStencilFormat 模板渲染缓存区的格式
    drawableMultisample多重采样缓存区的格式
  • 帧缓存区属性
    drawableHeight底层缓存区对象的⾼度(以像素为单位)
    drawableWidth 底层缓存区对象的宽度(以像素为单位)
  • 绘制视图的内容
    context绘制视图内容时使⽤用的OpenGL ES上下文
    - bindDrawable将底层FrameBuffer对象绑定到OpenGL ES
    enableSetNeedsDisplay 布尔值,指定视图是否响应使得视图内容⽆效的消息
  • display ⽴即重绘视图内容
    snapshot绘制视图内容并将其作为新图像对象返回
  • 删除视图FrameBuffer对象
    - deleteDrawable删除与视图关联的可绘制对象

GLKViewDelegate⽤于GLKView对象回调方法

绘制视图的内容
- glkView:drawInRect:绘制视图内容 (必须实现代理)

** GLKViewController管理OpenGL ES渲染循环的视图控制器**

  • 更新
    - (void) glkViewControllerUpdate:
  • 配置帧速率
    preferredFramesPerSecond视图控制器调⽤视图以及更新视图内容的速率
    framesPerSencond 视图控制器器调用视图以及更新其内容的实际速率
  • 配置GLKViewController代理
    delegate视图控制器的代理
  • 控制帧更新
    paused 布尔值,渲染循环是否已暂停
    pausedOnWillResignActive布尔值,当前程序重新激活动状态时视图控制器是
    否⾃动暂停渲染循环
    resumeOnDidBecomeActive布尔值,当前程序变为活动状态时视图控制是否⾃动恢复呈现循环
  • 获取有关View 更更新信息
    frameDisplayed视图控制器⾃创建以来发送的帧更新数
    timeSinceFirstResume自视图控制器第一次恢复发送更新事件以来经过的时间量
    timeSinceLastResume自上次视图控制器恢复发送更新事件以来更新的时间量
    timeSinceLastUpdate⾃上次视图控制器调⽤委托⽅法以及经过的时间量
    glkViewControllerUpdate:timeSinceLastDraw自上次视图控制器调用视图display方法以来经过的时间量

GLKViewControllerDelegate渲染循环回调⽅法

处理更新事件
- glkViewControllerUpdate:在显示每个帧之前调用
暂停/恢复通知
- glkViewController : willPause:在渲染循环暂停或恢复之前调⽤

GLKBaseEffect 一种简单光照/着⾊系统,⽤于基于着⾊器OpenGL渲染

  • 命名Effect
    labelEffect(效果)命名
  • 配置模型视图转换
    transform绑定效果时应⽤于顶点数据的模型视图,投影和纹理变换
  • 配置光照效果
    lightingType ⽤于计算每个⽚段的光照策略,GLKLightingType
  • GLKLightingType
    GLKLightingTypePerVertex表示在三⻆形中每个顶点执行光照计算,然后在三⻆形进⾏插值
    GLKLightingTypePerPixel表示光照计算的输⼊入在三⻆形内插⼊,并且在每个片段执行光照计算
  • 配置光照
    lightModelTwoSided 布尔值,表示为基元的两侧计算光照
    material 计算渲染图元光照使用的材质属性
    lightModelAmbientColor 环境颜⾊色应用效果渲染的所有图元.
    light0 场景中第一个光照属性 light1 场景中第二个光照属性 light2 场景中第三个光照属性
  • 配置纹理
    texture2d0第⼀个纹理属性 texture2d1第二个纹理属性
    textureOrder纹理应用于渲染图元的顺序
  • 配置雾化
    fog 应⽤于场景的雾属性
  • 配置颜⾊信息
    colorMaterialEnable 布尔值,表示计算光照与材质交互时是否使⽤颜色顶点属性
    useConstantColor 布尔值,指示是否使用常量颜色
    constantColor不提供每个顶点颜⾊数据时使⽤常量颜⾊
  • 准备绘制效果
    - prepareToDraw准备渲染效果

注意
1、GLKit中最多只有3个光照,2个纹理
2、常量颜色:黑色

GLKit加载图片案例

首先实现一个修改viewbackgroundColor

创建一个iOS项目,并将系统创建的ViewController的父类由UIViewController修改为GLKViewController,其中的view的父类由UIView修改为GLKView

ViewController.h文件中导入GLKit框架的头文件#import
ViewController.h文件中导入OpenGL ES相关头文件#import #import

  • 定义一个EAGLContext成员变量
#import 
#import 

@interface ViewController ()
{
    EAGLContext *context;
    
@end
  • viewDidLoad方法:
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    //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.设置背景颜色
    glClearColor(0, 0, 1, 1.0);
    
}
  • 实现GLView代理方法
//绘制视图的内容
/*
 GLKView对象使其OpenGL ES上下文成为当前上下文,并将其framebuffer绑定为OpenGL ES呈现命令的目标。然后,委托方法应该绘制视图的内容。
*/
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClear(GL_COLOR_BUFFER_BIT);
    
}

运行看下结果,如下图:


GLKit修改view的backgroundColor

使用GLKit加载图片纹理并显示到view

在上面的基础上添加一个GLKBaseEffect成员变量*

@interface ViewController ()
{
    EAGLContext *context;
    GLKBaseEffect *cEffect;
}
@end
  • 初始化OpenGL ES
-(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
     将消耗更少的资源
     
     */
    
    //3.配置视图创建的渲染缓存区.
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
    
    //4.设置背景颜色
    glClearColor(0, 0, 1, 1.0);
}
  • 创建顶点数据
-(void)setUpVertexData
{
    //1.设置顶点数组(顶点坐标,纹理坐标)
    /*
     纹理坐标系取值范围[0,1];原点是左下角(0,0);
     故而(0,0)是纹理图像的左下角, 点(1,1)是右上角.
     */
    GLfloat vertexData[] = {
        
        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).将顶点数组的数据copy到顶点缓存区中(GPU显存中)
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
    
    //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);
 
}
  • 为了创建显示图片的顶点数据,确定图片显示到view的那个位置。
    图片是正方形,我们由两个三角形拼起来,所以需要6个顶点
  • 我们定义顶点坐标使用的是一维数组,我们在前面就讲过,在OpenGL中习惯使用一维数组,所以OpenGL ES也是一样的。
  • 加载图片纹理并显示
-(void)setUpTexture
{
//1.获取纹理图片路径
    NSString *filePath = [[NSBundle mainBundle]pathForResource:@"tutu" ofType:@"jpg"]
    
    //2.设置纹理参数
    //纹理坐标原点是左下角,但是图片显示原点应该是左上角.
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
    
    GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
    
    //3.使用苹果GLKit 提供GLKBaseEffect 完成着色器工作(顶点/片元)
    cEffect = [[GLKBaseEffect alloc]init];
    cEffect.texture2d0.enabled = GL_TRUE;
    cEffect.texture2d0.name = textureInfo.name;
}
  • 实现GLKViewDelegate代理方法
//绘制视图的内容
/*
 GLKView对象使其OpenGL ES上下文成为当前上下文,并将其framebuffer绑定为OpenGL ES呈现命令的目标。然后,委托方法应该绘制视图的内容。
*/
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    //1.
    glClear(GL_COLOR_BUFFER_BIT);
    
    //2.准备绘制
    [cEffect prepareToDraw];
    
    //3.开始绘制
    glDrawArrays(GL_TRIANGLES, 0, 6);
    
}
  • viewDidLoad调用
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //1.OpenGL ES 相关初始化
    [self setUpConfig];
    
    //2.加载顶点/纹理坐标数据
    [self setUpVertexData];
    
    //3.加载纹理数据(使用GLBaseEffect)
    [self setUpTexture];
}

最后运行看下效果,如下图:


效果图

你可能感兴趣的:(NO.13 - OpenGL ES及GLKit)