大家好,今天小编来为大家解答深入iOS开发:OpenGL ES 3.0绘制三角形、屏幕旋转及架构设计技巧解析这个问题,很多人还不知道,现在让我们一起来看看吧!
OpenGL ES程序流水线图
着色器是一个语法类似于C语言的小程序片段。不同的图形接口有不同的着色器语言。对于OpenGL来说,是GLSL(OpenGL Shader Language),Metal也有自己的着色器语言。就我的经验而言,Metal 编程模型更加直观,而OpenGL 则过于陈旧,难以理解。
2.OpenGL ES 3.0绘制三角形
在开始具体操作之前,首先了解OpenGL ES程序的运行流程。 ES采用服务器/客户端编程模型。 CPU是客户端,调用的函数被发送到GPU(服务器端),由GPU转换为底层图形硬件支持的绘图命令。
OpenGL 旨在将函数调用转换为可发送到底层图形硬件的图形命令。由于该底层硬件专用于处理图形命令,因此OpenGL 绘图通常非常快。
程序运行过程
从iOS OpenGL ES 3编程1:"Hello world"可以看出,绘制三角形的操作应该在清除缓冲区操作之后进行。前面已经介绍过,具体的操作都是由着色器来实现的,所以我们先来了解一下着色器。
2.1.着色器
着色器不在Xcode 中编译,而是以源代码字符串的形式存在。当应用程序运行时,ARM(在iOS上)处理器会将其编译成与当前图形硬件兼容的可执行文件。该过程类似于C语言程序的编译和链接过程。尽管OpenGL ES 标准提供了加载已编译着色器二进制数据的功能,但iOS 不支持这种方法。这个问题稍后会描述。
1. 顶点着色器
顶点被变换和照亮,组装成基元,并光栅化以创建2D 图像。
#version300eslayout(location=0) 在vec4 位置;voidmain(){ gl_Position=位置;}
OpenGL ES 3.0着色器编写比2.0多了一项要求:在开头声明版本信息,#version 300 es,300表示使用OpenGL ES 3.0。将其更改为310 表示OpenGL ES 3.1。 Nexus 6P 支持3.1。目前所有iOS 设备最多仅支持3.0。由于3.0向后兼容2.0,意味着用2.0语法编写的着色器也可以正常使用。
in表示输入参数,vec4是类型,表示向量(x,y,z,w),同样vec3表示向量(x,y,z),以此类推。位置是参数名称。数据一般是从CPU上传到GPU,可以看作是CPU和GPU之间的通信端口。 layout(location=0)指定属性索引为0。ES 3.0最多支持16个属性。默认情况下,它们按递增的自然顺序排列。您可以使用位置来修改它们的顺序。这也是后续CPU向GPU上传数据的基础。
ES 3.0 有三个参数修饰符,in、uniform 和out。其中uniform与ES 2.0相同,代表不可变数据。数据在顶点着色器和片段着色器之间共享。每个顶点和片段着色器都可以访问相同的值。其余的对应关系是:
in==属性,表示输入数据
out==Varying,表示渲染管线后续操作的输出数据。
gl_Position是GLSL的内置变量,表示顶点坐标,数据类型为vec4。此外,还有几个内置变量,将在后续文档中介绍。
2. 片段着色器
#version300es precision highpfloat;out vec4 o_color;voidmain(){ o_color=vec4(1.0,1.0,0,1.0);//RGBA}
比顶点着色器多了一项要求:如果使用浮点数,则必须指定浮点精度。精度越高,对应的颜色过渡越细腻,计算时间就越长,漂亮的东西价格也就越高。由于ES 3.0不再提供gl_FragColor内置变量,因此在使用完全符合3.0语法的GLSL时,使用gl_FragColor会导致编译错误。为了表示顶点对应的像素颜色值,这里声明了一个vec4类型变量o_color。
现在着色器内容的编码已经完成,下面介绍如何使用它们。
2.2.编译和使用着色器
前面提到,shader 以源代码字符串的形式保存,并在App 运行过程中进行编译。那么,下面介绍编译shader的步骤。
2.2.1.编译着色器
需要编译两个着色器:顶点(GL_VERTEX_SHADER)和片段(GL_FRAGMENT_SHADER)。 Shader源码中可能存在写入错误导致编译失败,因此需要进行编译检查。 OpenGL ES不会主动提示编译结果,需要你主动查询。
编译着色器的过程与编译C代码类似:
创建着色器
指定着色器源代码
编译源代码
检查编译错误
适当时,删除编译的着色器数据
示例代码如下:
GLuintcompileShader(char*shaderContent, GLenum shaderType){//1GLuint shader=glCreateShader(shaderType);//2glShaderSource(shader,1, shaderContent,NULL);//3glCompileShader(shader);//4GLintcompileStatus; glGetShaderiv(着色器, GL_COMPILE_STATUS ,compileStatus);if(compileStatus==GL_FALSE) { GLint infoLength; glGetShaderiv(着色器, GL_INFO_LOG_LENGTH, infoLength);if(infoLength 0) { GLchar *infoLog=malloc(sizeof(GLchar) * infoLength); glGetShaderInfoLog(shader, infoLength, NULL, infoLog);printf("%s -n%sn", C_STRING(shaderType), infoLog);free(infoLog); } }返回着色器;}
对于错误输出,也可以直接使用字符串数组,省去分配堆内存的麻烦。
GLint shaderCompileLogLength;glGetShaderiv(着色器,GL_INFO_LOG_LENGTH,shaderCompileLogLength);charcompileMessage[shaderCompileLogLength];glGetShaderInfoLog(着色器,shaderCompileLogLength,NULL,compileMessage);printf("s-nsn",C_STRING(shaderType),compileMessage) ;
删除编译器一般是在释放绘图资源时进行的。将之前保存的着色器句柄传递给void glDeleteShader(GLuint Shader);那是。该函数不会立即删除着色器,而是将指定着色器标记为已删除。着色时只有当设备没有与任何程序对象关联(Attach)时,内存才会被清除。
2.2.2.使用着色器
着色器不能单独作用于OpenGL,而是通过一个中介者来组织和使用,这个中介者就是程序对象(程序)。 OpenGL ES规定一个程序必须配备一对着色器,而且只能配备一对,即有效程序=顶点着色器+片段着色器。
看到程序执行结果后,很多人会想,为什么只指定了几个顶点及其颜色,图形就会显示出过渡颜色。
OpenGL ES 规范没有定义窗口层;相反,托管操作系统必须提供函数来创建OpenGL ES 渲染上下文(接受命令)和帧缓冲区(将任何绘图命令的结果写入其中)。在iOS 上使用OpenGL ES 需要使用iOS 类来设置和呈现绘图表面,并使用平台无关的API 来渲染其内容。
3.OpenGL ES处理屏幕旋转
iPhone等设备的屏幕旋转会导致上一节创建的图形超出屏幕范围。具体情况是纵向启动应用程序,然后水平转动屏幕,或反之亦然,如下图。
从竖屏转横屏时存在偏移
横屏转竖屏时有偏移
显然,这不是我们想要的,需要修复。
3.1.维修简单
[self.view addSubview:view];添加我们自定义的GLView作为子视图,在屏幕旋转时会出现上述的偏移问题。一个简单的解决方案是在Storyboard 中将View 类设置为我们自定义的GLView 或设置self.view=view;在控制器中。这两种说法具有相同的效果。
这种storyboard设置的方法需要我们重写initWithCoder:而我们重写的是initWithFrame:导致代码没有被执行。类似的逻辑必须在initWithCoder:中实现才有效,这就造成了代码冗余。
无论是Storyboard、Xib还是initWithFrame:和self.view=view;视图在显示时都会执行layoutSubviews,所以在这个交集处绘制是一个不错的选择。将initWithFrame:中的绘图代码迁移到layoutSubviews中,并删除View中的其他代码。然后运行应用程序。可以发现,旋转屏幕时,画面正常。
但是,调用[self.view addSubview:view]的问题;仍然存在。这就需要ViewController通知View重新布局子视图来触发layoutSubviews。对此问题进行进一步分析。
首先,控制器覆盖- viewWillTransitionToSize: 和TransitionCoordinator:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator {NSLog(@"size=%@, 图层矩形=%@",NSStringFromCGSize(size),NSStringFromCGRect(self.view.layer.bounds)); [self.viewlayoutIfNeeded];}
size是旋转后的屏幕尺寸,view.layer.bounds是旋转前的屏幕尺寸,layoutIfNeeded不会导致我们自定义的View执行layoutSubviews。
layoutIfNeeded 无效
同样,[self.view setNeedsLayout];不触发layoutSubviews。既然我们的View是Controller的View的子视图,那么如果我们遍历子视图并一一发送刷新通知会发生什么呢?
for(UIView*subviewinself.view.subviews) { [子视图layoutIfNeeded];}
执行发现而不触发layoutSubviews。改成[subview setNeedsLayout];此时就触发了layoutSubviews,但是结果还是错误。
遍历通知子视图进行刷新
END,本文到此结束,如果可以帮助到大家,还望关注本站哦!
【深入iOS开发:OpenGL ES 3.0绘制三角形、屏幕旋转及架构设计技巧解析】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
要学习 iOS 游戏开发吗?这本书应该能帮到我!
有20位网友表示赞同!
OpenGL ES 3 一直是我的心仪目标,看看能不能从这本书入手。
有18位网友表示赞同!
屏幕旋转挺重要的功能啊,这个教程讲了吗?
有6位网友表示赞同!
架构设计是游戏开发的基础吧,期待了解更多有用的技巧。
有7位网友表示赞同!
终于找到一篇关于iOS OpenGL ES 3 的详细教程!
有12位网友表示赞同!
希望这本书介绍一些常见的三角形绘制方法。
有9位网友表示赞同!
我之前对图形渲染还不太了解,这本书会不会比较入门级别?
有5位网友表示赞同!
游戏开发需要不断学习新技术,正好来看看这本书的内容。
有6位网友表示赞同!
看了标题感觉这本书内容很全面。
有18位网友表示赞同!
想让手机上的APP界面看起来更漂亮,这个教程可能非常有用!
有11位网友表示赞同!
平时写Android 开发的代码,有机会来了解一下iOS 的编程。
有5位网友表示赞同!
三角形绘制?架构设计? 这都是游戏开发的关键要素啊!
有18位网友表示赞同!
现在很多手机游戏都使用了 OpenGL ES 3.0 吧?
有7位网友表示赞同!
学习 iOS 游戏开发需要哪些基础知识呢?
有6位网友表示赞同!
希望这本书能帮助我更好地理解iOS 开发的原理。
有13位网友表示赞同!
感觉这个教程很适合刚入门的人!
有16位网友表示赞同!
我要开始我的游戏开发之旅,这本教材很棒!
有16位网友表示赞同!
期待能在这本书中学习到最新的 iOS 开发技巧。
有13位网友表示赞同!
希望这本书有具体的代码示例和调试技巧。
有6位网友表示赞同!