十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
作为一个一个iOS底层开发小白,一直以来对于底层原理,都是一知半解的状态,希望从此时记录自己学习底层知识的过程,也希望对其他开发中可以有一个好的帮助,因为自己看其他人的博客时候,大多数作者都是基于自己认知的前提下,记录自己的知识,在我看来对于入门的人来说,非常晦涩难懂,所以希望自己的文章,可以更加帮助其他人循序渐进的了解更多关于 iOS 的知识。文章也会尽量一步步的探索更多业务开发之外的东西,对于任何方面的技能,都可以友善的帮助后来的开发者。文章中有任何错误,不恰当的地方,欢迎随时指正,多多交流才可以共同进步。想要学习 OpenGL 首先需要了解 OpenGL/OpenGL ES/Metal 三者之间的联系:
成都创新互联专注于网站建设|成都网站维护公司|优化|托管以及网络推广,积累了大量的网站设计与制作经验,为许多企业提供了网站定制设计服务,案例作品覆盖咖啡厅设计等行业。能根据企业所处的行业与销售的产品,结合品牌形象的塑造,量身建设品质网站。
通俗来讲就是, Apple 作为大厂,肯定要发展自己的底层渲染技术,所以推出了 Metal ,在推出 Metal 之前,苹果的底层渲染也是基于 OpenGL/OpenGL ES 的,在 iOS 12.0以后,苹果摒弃了 OpenGL 的相关 API ,使用 Metal 作为自己的渲染技术,但是 OpenGL 相关的 API 依然可以使用,因为在 Metal 之前,苹果提供了非常丰富的关于使用 OpenGL 相关 API ,类似于苹果推出了 swift ,但是 OC 依然是自己主流语言一样。了解了相关的背景,我们接下来要知道图形 API 究竟是用来解决什么样的问题存在的。
OpenGL/OpenGL ES/Metal 在任何项目中解决问题的本质就是利用 GPU 芯片来高效的渲染图形图像。图形 API 是 iOS 开发者唯一接近 GPU 的方式。想要了解 OpenGL 就要先学习关于 OpenGL 的专业名词,了解了这些,才可以对以后的学习,有更加深刻的认识。
状态机在 OpenGL 可以这么理解, OpenGL 可以记录自己的状态(当前所使用的颜色、是否开启了混合功能等),可以接输入(当调用 OpenGL 函数的时候,实际上可以看成 OpenGL 在接受我们的输入),如我们调用 glColor3f ,则 OpenGL 接收到这个输入后会修改自己的“当前颜色”这个状态, OpenGL 可以进入停止状态,不再接收输入。在程序退出前, OpenGL 总会先停止工作;
这里有一个 iOS 中很经常听到的概念, 离屏渲染 ,很多人知道离屏渲染会对 APP 的性能造成较大的开销,但是却不知道原理是什么,相信大家了解了上面关于 OpenGL 关于交换缓冲区的概念后,有了一个更清晰的认识,即: Off-Screen Rendering 是需要开辟新的缓冲区的,不停地切换上下文的环境则是对性能的很大的消耗,所以在 iOS 开发中,我们应当尽量的避免离屏渲染。
在学习 OpenGL 的过程中,直接非常直观的掌握并理解这些概念并不是一件容易的事,但是至少需要在入门阶段,大致的了解这些概念的意思,然后通过后续的学习,慢慢的巩固前面学到的知识,温故而知新,一步步的打开关于 iOS 底层渲染知识的大门,学习的越来越深入,慢慢的回过头看以前的知识点的时候,就会豁然开朗了。
希望能一步步的记录自己学习的过程,慢慢进步,慢慢成长,文章中有任何错误的地方,欢迎指正。希望能多多交流,共同进步。
既然有了 Metal 我们是否还有学习 OpenGL ES 的必要呢.我个人认为暂时还是有必要的.OpenGL /OpenGL ES/ Metal 在任何项目中解决问题的本质就是利用GPU芯片来⾼效渲染图形图像.所以它们底层的原理相近,首先了解OpenGL之后再去了解Metal会更加容易, 其二OpenGL是跨平台的框架.保不齐以后会在其他的地方用到,所谓技多不压身.毕竟不可能保证一直都做苹果开发吧.
理论枯燥且乏味, 但是我们联想一下flutter开发中用到的context, iOS开发中CoreGraphics里也有用到context. 是不是此时心中就有了一定的答案.我个人理解其设计模式大同小异. 顾名思义我们能够通过context拿到很多必要的状态和数据.
比如需要显示一个正方形, 则需要两个正等边三角形图元来完成
为了读取效率起见, 提前分配一块显存, 将顶点数组存放在显存中. 这部分显存就叫做 顶点缓冲区
OpenGL渲染的过程中会经历很多节点. 这些节点串起来就是管线.
常见的着⾊器主要有: 顶点着⾊器(VertexShader) , ⽚段着⾊器(FragmentShader) , 几何着⾊(GeometryShader) , 曲⾯细分着⾊器(TessellationShader)
OpenGL ES 中只⽀持 顶点着⾊器 和 片段着⾊器 .
光栅化就是把顶点数据转换为片元的过程。片元中的每一个元素对应于帧缓冲区中的一个像素。
如果把渲染比作是画画, 那么顶点着色器操作相当与确定画的内容的框架. 而之后往框架里填充内容的过程就是光栅化.
填充好内容之后就是片元着色器操作像素点填充颜色等操作
这里附上一张流程图:
纹理可以理解为图⽚。 在渲染图形时需要在顶点围成的区域中填充图⽚,使得场景更加逼真。⽽这⾥使⽤的图⽚,就是常说的纹理。只是在OpenGL,我们更加习惯叫纹理,⽽不是图⽚。
简单来说就是实现图形的底层渲染
A. 比如在游戏开发中,对于游戏场景/游戏人物的渲染
B. 比如在音视频开发中,对于视频解码后的数据渲染
C. 比如在地图引擎,对于地图上的数据渲染
D. 比如在动画中,实现动画的绘制
E. 比如在视频处理中,对于视频加上滤镜效果
OpenGL/OpenGL ES/Metal 在任何项目中解决问题的本质就是利用 GPU 芯片来高效渲染图形图像。
图形 API 是 ios 开发者唯一接近 GPU 的方式。
OpenGL 阶段:
OpenGL ES 阶段:
Metal 阶段:
固定管线/存储着⾊器
顶点数据是由CPU/GPU来处理?
顶点缓存区:区域(不在内存!-显卡显存中。)
片元着色器
像素着色器
片元函数
GPUImage
[-1,1]标准化设备坐标系(NDC)
物体/世界/照相机空间-右手系
规范化设备坐标:左手系。
x,y,z = 0,1,2
注意OpenGL中坐标系 OpenGL中的物体,世界,照相机坐标系都属于右手坐标系,而规范化设备坐标系(NDC)属于左手坐标系。笼统的说OpenGL使用右手坐标系是不合适的
OpenGL希望每次顶点着色后,我们的可见顶点都为标准化设备坐标系 (Normalized Device Coordinate, NDC)。也就是说每个顶点的x,y,z都应该在-1到1之间,超出这个范围的顶点将是不可见的。
通常情况下我们会自己设定一个坐标系范围,之后再在顶点着色器中将这些坐标系变换为标准化设备坐标,然后这些标准化设备坐标传入光栅器(Rasterizer),将他们变换为屏幕上的二维坐标和像素。
将坐标变换为标准化设备坐标,接着再转化为屏幕坐标的过程通常是分布进行的,也是类似于流水线那样。在流水线中,物体的顶点在最终转化为屏幕坐标之前还会被变换到多个坐标系系统(Coordinate System)。将物体的坐标变到几个过渡坐标系(Intermediate Coordinate System)的优点在于 在这些特定的坐标系统中,一些操作或运算更加方便和容易,这一点很快就变得明显。对我们来说比较重要的总共有5个不同的坐标系统。
这是一个顶点在最终被转化为片段之前需要经历的所有不同的状态。为了将坐标从一个坐标系变换到另一个坐标系,我们需要用到几个变换矩阵,最重要的几个分别是 模型(Model)、观察(View)、投影(Projection)三个矩阵。
物体顶点的起始坐标在局部空间(Local Space),这里称为局部坐标(Local Coordinate),他在之后在变成世界坐标(World Coordinate),观察坐标(View Coordinate),裁剪坐标(Clip Coordinate),并最后转为屏幕坐标(Screen Coordinate)
的形式结束。
物体坐标系: 每个物体都有他独立的坐标系,当物理移动或者改变方向时。该物体相关联的坐标系将随之移动或改变方向。
物体坐标系是以物体本身而言,比如,我先向你发指令,”向前走一步“,是向你的物体坐标系发指令。我并不知道你会往哪个绝对的方向移动。比如说,当你开车的时候,有人会说向左转,有人会说向东。但是,向左转是物体坐标系的概念,而向东则是世界坐标系概念。
在某种情况下,我们可以理解物体坐标系为模型坐标系。因为模型顶点的坐标都是在模型坐标系中描述的。
照相机坐标系: 照相机坐标系是和观察者密切相关的坐标系。照相机坐标系和屏幕坐标系相似,差别在于照相机坐标系处于3D空间中,而屏幕坐标系在2D平面里。
为什么要引入惯性坐标系? 因为物体坐标系转换到惯性坐标系只需要旋转,从惯性坐标系转换到世界坐标系只需要平移。
OpenGL最终的渲染设备是2D的,我们需要将3D表示的场景转换为最终的2D形式,前面使用模型变换和视觉变换将物体坐标转到照相机坐标系后,需要进行投影变换,将坐标从照相机坐标系转换为裁剪坐标系,经过透视除法后,变换到规范化设备坐标系(NDC),最后进行视口变换后,3D坐标才变换到屏幕上的2D坐标,这个过程入下图:
在上面的图中, 注意,OpenGL只定义了裁剪坐标系、规范化设备坐标系、屏幕坐标系,而局部坐标系、世界坐标系、照相机坐标系都是为了方便用户设计而自定义的坐标系,他们的关系如下图:
OpenGL 然后对裁剪坐标执行透视除法从而将他们变换到标准化设备坐标。 OpenGL 会使用glViewPort内部的参数来将标准化设备坐标映射到屏幕坐标,每个坐标关联一个屏幕上的点。这个过程称为视口变换
局部坐标系(模型坐标系)是为了方便构造模型而设立的坐标系,建立模型时我们无需关心最终对象显示在屏幕那个位置。
模型变换的主要目的是通过变换使得用顶点属性定义或者3d建模软件构造的模型,能够按照需要,通过缩小、平移等操作放置到场景中合适的位置, 通过模型变换后,物体放置在一个全局的世界坐标系中,世界坐标系是所有物体交互的一个公共坐标系
视变换是为了方便观察场景中物体而建立的坐标系,在这个坐标系中相机是个假设的概念,是为了便于计算而引入的。相机坐标系中的坐标,就是从相机的角度来解释世界坐标系中的位置
OpenGL中相机始终位于原点,指向 -Z轴,而以相反的方式来调整场景中物体,从而达到相同的观察效果。例如要观察-Z轴方向的一个立方体的右侧面,可以有两种方式:
GLShaderManager的初始化
GLShaderManager shaderManager;
shaderManager.InitializeStockShaders();