OpenGL ES 初探 - 了解OpenGL ES和GLKit

一、什么是OpenGL ES

OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。
在 OpenGL ES 苹果官方文档中有这样一段话:

The Open Graphics Library (OpenGL) is used for visualizing 2D and 3D data. It is a multipurpose open-
standard graphics library that supports applications for 2D and 3D digital content creation, mechanical
and architectural design, virtual prototyping, flight simulation, video games, and more. You use OpenGL to
configure a 3D graphics pipeline and submit data to it. Vertices are transformed and lit, assembled into
primitives, and rasterized to create a 2D image. OpenGL is designed to translate function calls into
graphics commands that can be sent to underlying graphics hardware. Because this underlying hardware is
dedicated to processing graphics commands, OpenGL drawing is typically very fast.
OpenGL for Embedded Systems (OpenGL ES) is a simplified version of OpenGL that eliminates redundant
functionality to provide a library that is both easier to learn and easier to implement in mobile graphics
hardware.
OpenGL ES 开放式图形库(OpenGL的)⽤于可视化的⼆维和三维数据。它是一个多功能开放标准图形库,支持2D和3D数字内容创建,机械和建筑设计,虚拟原型设计,⻜行模拟,视频游戏等应⽤用程序。您可以使用OpenGL配置3D图形管道并向其提交数据。顶点被变换和点亮,组合成图元,并光栅化以创建2D图像。OpenGL旨在将函数调用转换为可以发送到底层图形硬件的图形命 令。由于此底层硬件专⽤于处理理图形命令,因此OpenGL绘图通常⾮常快。
OpenGL for Embedded Systems(OpenGL ES)OpenGL的简化版本,它消除了冗余功能,提供了一个既易于学习⼜更易于在移动图形硬件中实现的库。

二、OpenGL ES 使用过程

1.顶点着色器(vertex shader)

OpenGL ES中顶点着色器通常包含如下的函数/代码段:
    1. 着⾊器程序—描述顶点上执⾏行操作的顶点着⾊器程序源代码/可执行⽂件
  • 2.顶点着⾊器输入(属性) — 用顶点数组提供每个顶点的数据

  • 3.统⼀变量(uniform)—顶点/⽚元着⾊器使⽤的不变数据

    1. 采样器—代表顶点着⾊器使⽤纹理的特殊统一变量类型.
顶点着色器的业务内容:
    1. 矩阵变换位置
  • 2.计算光照公式⽣成逐顶点颜⾊
  • 3.⽣成/变换纹理坐标

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

顶点着色器代码:
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; 
}

2.图元装配

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

3.光栅化

在这个阶段绘制对应的图元(点/线/三角形). 光栅化就是将图元转化成⼀组二维片段 的过程.⽽这些转化的⽚段将由⽚元着⾊器处理.这些⼆维⽚段就是屏幕上可绘制的像素。

光栅化.png

4.片源着色器(fragment shader)

OpenGL ES中片源着色器通常包含如下的函数/代码段:
    1. 着⾊器程序—描述⽚段上执行操作的⽚元着⾊器程序源代码/可执行⽂文件
  • 2.输⼊变量— 光栅化单元⽤插值为每个⽚段⽣成的顶点着⾊器输出

  • 3.统⼀变量(uniform)—顶点/⽚元着⾊器使⽤的不变数据

    1. 采样器—代表⽚元着⾊器使⽤纹理的特殊统⼀变量类型。
片源着色器的业务内容:
  • 1.计算颜色
    1. 获取纹理值
    1. 往像素点中填充颜⾊值(纹理值/颜色值);

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

片源着色器代码:
varying lowp vec2 varyTextCoord; uniform sampler2D colorMap;
void main() {
    gl_FragColor = texture2D(colorMap, varyTextCoord);
}

5.逐片段操作:

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

三、EGL (Embedded Graphics Library )

OpenGL ES在各个iOS、安卓、Linux等各个移动端都可以使用,但是OpenGL ES和各个平台间的对接需要由各个平台自己来提供和维护,OpenGL ES只负责底层的渲染。

  • OpenGL ES 命令需要渲染上下文和绘制表⾯才能完成图形图像的绘制.
  • 渲染上下文: 存储相关OpenGL ES状态.
  • 绘制表面: 是⽤用于绘制图元的表⾯面,它指定渲染所需要的缓存区类型,例如颜色缓存 区,深度缓冲区和模板缓存区.
  • OpenGL ES API 并没有提供如何创建渲染上下文或者上下⽂如何连接到原⽣生窗⼝口系统. EGLKhronos渲染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来呈现其内容。

四、什么是GLKit?

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

两个重要的类:
  • GLKView :提供绘制场所(View)
  • GLKViewController:(扩展于标准的UIKit设计模式. ⽤于绘制视图内容的管理与呈现.)

虽然苹果弃⽤OpenGL ES 转为Metal,但iOS开发者可以继续使用

GLKit 功能

  • 加载纹理
  • 提供高性能的数学运算
  • 提供常见的着⾊器
  • 提供视图以及视图控制器.

1.GLKit 纹理加载

1).GLKTextureInfo 创建OpenGL纹理信息
@interface GLKTextureInfo : NSObject <NSCopying>
{
@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:
从一系列文件异步加载⽴方体贴图纹理图像,并从数据中创建新纹理

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

五、案例解析

1.使用GLKit实现修改当前viewbackgroundColor

前面已经了解了GLKit中有两个重要的类GLViewGLViewController,所以我们在使用的时候就将当前的view继承与GLView或者当前VC继承于GLViewController
案例中我们使用的storyboard,所以只需将当前VCview继承于GLView即可:

GLView.png

然后来到.m文件

  • 引入相关库头文件,并定义一个EAGLContext成员变量:
#import <OpenGLES/ES3/gl.h>
#import <OpenGLES/ES3/glext.h>

@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);
    
}

ok,接下来看下效果:


效果图.png

完美!!!

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

  • 在案例1的基础上新增一个成员变量:
@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);
}

和案例一相比,这一步增加视图创建的颜色缓冲区和深度缓冲区:

3.配置视图创建的渲染缓存区.

view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
  • 创建顶点数据
-(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:@"Jessica Alba" 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];
}

ok,运行一下


女神图.png

注意:使用GLKit是一定要注意GLKViewDelegate代理的实现

觉得不错记得点赞哦!听说看完点赞的人逢考必过,逢奖必中。ღ( ´・ᴗ・` )比心

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342