专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

案例:利用GLKit实现旋转立方体

效果:

51_1.png

GLKit框架

51_2.png

GLKit功能

1、 加载纹理
2、 提供高性能的数学计算
3、 提供常见着色器
4、 提供视图以及视图控制器

以下介绍GLK库的常用类,扩展查询阅读GLK官方文献资料

1. 加载纹理

GLKTextureLoader:GLK纹理加载器,可简化从各种图像文件格式中加载OpenGL或OpenGL ES纹理数据的过程。

初始化

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

加载

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

纹理信息GLKTextureInfo

加载后返回的纹理信息

  • name:OpenGL上下⽂中纹理名称,标识符的作用
  • target:纹理绑定的⽬标
  • height:加载的纹理高度
  • width:记载的纹理宽度
  • textureOrigin:记载的纹理原点的位置
  • alphaState:加载的纹理中alpha组件状态
  • containsMipmaps:加载的纹理是否包含mip贴图(BOOL)

2.GLKBaseEffect基础着色器

一个简单的照明和着色系统,可用于基于着色器的OpenGL渲染

  • label:给Effect(效果)命名
  • transform:绑定效果后,将模型视图,投影和纹理转换应用于顶点数据。
  • lightingType:⽤于计算每个⽚段的光照策略
GLKLightingType
//表示在三⻆形中每个顶点执⾏光照计算,然后在三⻆形进⾏插值
GLKLightingTypePerVertex
//表示光照计算的输⼊在三⻆形内插⼊,并且在每个⽚段执⾏光照计算
GLKLightingTypePerPixel

  • material:计算渲染图元光照使⽤的材质属性
  • lightModelAmbientColor:环境颜⾊,应⽤效果渲染的所有图元.
  • light0,light1,light2:场景中最多三个光照属性
light0.enabled:是否打开光照
light0.diffuseColor:光照漫反射颜色
light0.position:光照位置

  • texture2d0,texture2d1:最多两个纹理属性
  • textureOrder:纹理应⽤于渲染图元的顺序
  • fog:应⽤于场景的雾属性
  • colorMaterialEnable:布尔值,表示计算光照与材质交互时是否使⽤颜⾊顶点属性
  • useConstantColor:布尔值,指示是否使⽤常量颜⾊(黑色)
  • constantColor:不提供每个顶点颜⾊数据时使⽤常量颜⾊
  • - prepareToDraw:准备渲染效果函数

3.视图和视图控制器

GLKView

使用OpenGL ES绘制内容的默认视图,通过直接代表开发者管理帧缓冲区对象,简化了创建OpenGL ES应用程序所需的工作量,当需要更新时,只需要draw到帧缓冲区即可。

初始化
// 初始化frame,context上下文
-initWithFrame:context:

代理
必须用到的代理方法是 
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect

配置帧缓冲区对象
  • drawableColorFormat:颜⾊渲染缓存区格式
GLKViewDrawableColorFormatRGBA8888,
GLKViewDrawableColorFormatRGB565,
GLKViewDrawableColorFormatSRGBA8888,

  • drawableDepthFormat:深度渲染缓存区格式
深度缓冲区的精度
GLKViewDrawableDepthFormatNone,
GLKViewDrawableDepthFormat16,
GLKViewDrawableDepthFormat24,

  • drawableStencilFormat:模板渲染缓存区的格式
  • drawableMultisample:多重采样缓存区的格式
帧缓冲区属性

创建帧缓冲区的宽高,一般与屏幕对应

  • drawableHeight:底层缓存区对象的⾼度(以像素为单位)
  • drawableWidth底层缓存区对象的宽度(以像素为单位)
绘制视图的内容
  • context:绘制视图内容时使用的OpenGL ES上下文
  • -bindDrawable:将底层FrameBuffer对象绑定到OpenGLES
  • enableSetNeedsDisplay:布尔值,指定视图是否响应使得视图内容⽆效的消息
  • -display:⽴即重绘视图内容
  • snapshot:绘制视图内容并将其作为新图像对象返回
删除视图FrameBuffer对象

-deleteDrawable删除与视图关联的可绘制对象

GLKViewController

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

注意:
当您将view属性设置为指向GLKView对象时,如果视图尚无委托,则视图控制器将自动设置为视图的委托。

更新
  • -(void)update:更新视图内容
  • -(void)glkViewControllerUpdate:同上
配置帧速率
  • preferredFramesPerSecond:视图控制器调⽤视图以及更新视图内容的速率(例如:60帧/s)
  • framesPerSencond:视图控制器调⽤视图以及更新其内容的实际速率
代理
  • -glkViewControllerUpdate:在显示每帧之前调用
  • -glkViewController:willPause:在渲染循环暂停或恢复之前调⽤
控制帧更新
  • paused:布尔值,渲染循环是否已暂停
  • pausedOnWillResignActive:布尔值,当前程序重新激活活动状态时视图控制器是否自动暂停渲染循环
  • resumeOnDidBecomeActive:布尔值,当前程序变为活动状态时视图控制是否⾃动恢复呈现循环

案例:旋转立方体

1.创建OpenGL上下文

// 创建上下文
EAGLContext *context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
// 设置为 当前上下文
[EAGLContext setCurrentContext:context];

2.初始化GLKView

CGRect frame = CGRectMake(0, 100, self.view.frame.size.width, self.view.frame.size.width);
self.glkView = [[GLKView alloc]initWithFrame:frame context:context];
self.glkView.delegate = self;
self.glkView.backgroundColor = [UIColor clearColor];
[self.view addSubview:self.glkView];

// glkView 深度测试精度
self.glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
// 深度缓冲区的范围
glDepthRangef(1, 0);

3.获取图片纹理信息

// 图片地址
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"timg1.jpg" ofType:@""];
UIImage *image = [UIImage imageWithContentsOfFile:filePath];

// 图片变成纹理(位图),获取纹理信息TextureInfo
// 纹理坐标的原点是在左下角,而图片显示的原点在左上,这里需要进行调整,以免渲染出来的结果是倒的
NSDictionary *dic = @{GLKTextureLoaderOriginBottomLeft:@(YES)};
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:[image CGImage] options:dic error:nil];

4.初始化着色器

// effect 着色器
self.baseEffect = [[GLKBaseEffect alloc]init];
self.baseEffect.texture2d0.name = textureInfo.name;
self.baseEffect.texture2d0.target = textureInfo.target;

// 添加光照
self.baseEffect.light0.enabled = YES;
self.baseEffect.light0.diffuseColor = GLKVector4Make(1, 1, 1, 1); // 漫反射颜色
self.baseEffect.light0.position = GLKVector4Make(-0.5, -0.5, 5, 1); // 光照位置

5.顶点&纹理坐标,法线数据

// 开辟顶点数据空间(数据结构SenceVertex 大小 * 顶点个数kCoordCount)
self.vertices = malloc(sizeof(VertexData) * kCoordCount);

// 前面
self.vertices[0] = (VertexData){{-0.5, 0.5, 0.5}, {0, 1}, {0, 0, 1}};
self.vertices[1] = (VertexData){{-0.5, -0.5, 0.5}, {0, 0}, {0, 0, 1}};
self.vertices[2] = (VertexData){{0.5, 0.5, 0.5}, {1, 1}, {0, 0, 1}};
self.vertices[3] = (VertexData){{-0.5, -0.5, 0.5}, {0, 0}, {0, 0, 1}};
self.vertices[4] = (VertexData){{0.5, 0.5, 0.5}, {1, 1}, {0, 0, 1}};
self.vertices[5] = (VertexData){{0.5, -0.5, 0.5}, {1, 0}, {0, 0, 1}};

// 上面
self.vertices[6] = (VertexData){{0.5, 0.5, 0.5}, {1, 1}, {0, 1, 0}};
self.vertices[7] = (VertexData){{-0.5, 0.5, 0.5}, {0, 1}, {0, 1, 0}};
self.vertices[8] = (VertexData){{0.5, 0.5, -0.5}, {1, 0}, {0, 1, 0}};
self.vertices[9] = (VertexData){{-0.5, 0.5, 0.5}, {0, 1}, {0, 1, 0}};
self.vertices[10] = (VertexData){{0.5, 0.5, -0.5}, {1, 0}, {0, 1, 0}};
self.vertices[11] = (VertexData){{-0.5, 0.5, -0.5}, {0, 0}, {0, 1, 0}};

// 下面
self.vertices[12] = (VertexData){{0.5, -0.5, 0.5}, {1, 1}, {0, -1, 0}};
self.vertices[13] = (VertexData){{-0.5, -0.5, 0.5}, {0, 1}, {0, -1, 0}};
self.vertices[14] = (VertexData){{0.5, -0.5, -0.5}, {1, 0}, {0, -1, 0}};
self.vertices[15] = (VertexData){{-0.5, -0.5, 0.5}, {0, 1}, {0, -1, 0}};
self.vertices[16] = (VertexData){{0.5, -0.5, -0.5}, {1, 0}, {0, -1, 0}};
self.vertices[17] = (VertexData){{-0.5, -0.5, -0.5}, {0, 0}, {0, -1, 0}};

// 左面
self.vertices[18] = (VertexData){{-0.5, 0.5, 0.5}, {1, 1}, {-1, 0, 0}};
self.vertices[19] = (VertexData){{-0.5, -0.5, 0.5}, {0, 1}, {-1, 0, 0}};
self.vertices[20] = (VertexData){{-0.5, 0.5, -0.5}, {1, 0}, {-1, 0, 0}};
self.vertices[21] = (VertexData){{-0.5, -0.5, 0.5}, {0, 1}, {-1, 0, 0}};
self.vertices[22] = (VertexData){{-0.5, 0.5, -0.5}, {1, 0}, {-1, 0, 0}};
self.vertices[23] = (VertexData){{-0.5, -0.5, -0.5}, {0, 0}, {-1, 0, 0}};

// 右面
self.vertices[24] = (VertexData){{0.5, 0.5, 0.5}, {1, 1}, {1, 0, 0}};
self.vertices[25] = (VertexData){{0.5, -0.5, 0.5}, {0, 1}, {1, 0, 0}};
self.vertices[26] = (VertexData){{0.5, 0.5, -0.5}, {1, 0}, {1, 0, 0}};
self.vertices[27] = (VertexData){{0.5, -0.5, 0.5}, {0, 1}, {1, 0, 0}};
self.vertices[28] = (VertexData){{0.5, 0.5, -0.5}, {1, 0}, {1, 0, 0}};
self.vertices[29] = (VertexData){{0.5, -0.5, -0.5}, {0, 0}, {1, 0, 0}};

// 后面
self.vertices[30] = (VertexData){{-0.5, 0.5, -0.5}, {0, 1}, {0, 0, -1}};
self.vertices[31] = (VertexData){{-0.5, -0.5, -0.5}, {0, 0}, {0, 0, -1}};
self.vertices[32] = (VertexData){{0.5, 0.5, -0.5}, {1, 1}, {0, 0, -1}};
self.vertices[33] = (VertexData){{-0.5, -0.5, -0.5}, {0, 0}, {0, 0, -1}};
self.vertices[34] = (VertexData){{0.5, 0.5, -0.5}, {1, 1}, {0, 0, -1}};
self.vertices[35] = (VertexData){{0.5, -0.5, -0.5}, {1, 0}, {0, 0, -1}};

6.将顶点数据从内存Copy到帧缓冲区

// 数据copy到帧缓冲区
// 分配
glGenBuffers(1, &_vertexBuffer);
// 绑定
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData) * 36, self.vertices, GL_STREAM_DRAW);

// 10.把数据从帧缓冲区 读取到GLKit中的着色器
// 顶点数据
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), NULL+offsetof(VertexData, positionCoord));

// 纹理
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), NULL+offsetof(VertexData, textureCoord));

// 法线
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), NULL+offsetof(VertexData, normal));

7.添加定时器

// 添加定时器
- (void)addCADisplayLink{
    self.angle = 0;
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeUpdate)];
    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)timeUpdate{
    // 1.计算旋转的角度
    self.angle = (self.angle + 1) % 360;
    //  渲染 会调用到代理中
    [self.glkView display];
}

8.代理方法进行渲染

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // 打开深度测试
    glEnable(GL_DEPTH_TEST);
    // 准备渲染
    [self.baseEffect prepareToDraw];

    // 旋转
    GLKMatrix4 modelviewMatrix = GLKMatrix4Rotate(GLKMatrix4Identity, GLKMathDegreesToRadians(self.angle), 0.3, 0.4, 0.7);
    self.baseEffect.transform.modelviewMatrix = modelviewMatrix;

    // 正式绘制,
    //三角形绘制方式/从0顶点开始/顶点数
    glDrawArrays(GL_TRIANGLES, 0, kCoordCount);
}

推荐

CADisplayLink

GLKit绘制立方体+旋转

文章永久链接:https://tech.souyunku.com/31374

未经允许不得转载:搜云库技术团队 » 案例:利用GLKit实现旋转立方体

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们