但是需要注意的是,OpenGL 和 OpenGL ES 还是有很大的不同,比如 #version 的声明,所以一些图形学的概念可以参考 openGL 中的教程,但是涉及到具体的 Api 差异或者版本问题,最好直接看 OpenGL ES 的官方文档;
这里是 openGL 和 openGL ES 相关文档的归档:
https://www.khronos.org/registry/OpenGL/specs/es/
image.png
3. version
按照 LearnOpenGL 中的示例代码,编译着色器时如果使用到了#version 330 core,跑在 iOS 设备上,必定是会报错的,报错信息大概如下:
ERROR: 0:1: '' : version '330' is not supportedERROR: 0:1: '' : syntax error: #version
那为什么会报错呢?原因是因为 OpenGL ES 中的 GLSL 最高版本为 320,且使用 GLSL ES 时,需要这样:
#version number es
而 OpenGL 中,3.3 版本发生了重大更新,所以一般使用 OpenGL 3.3 版本,对应的 GLSL 为 330,具体可以上官网查询。而 OpenGL 中版本声明一般这么写:
#version number core
关键就在于版本号和 es 、core 的区别;
来看看官方文档中的描述吧~~~
这里是 3.1.0 的版本:GLSL_ES_3.1.0
最新版本为 3.2.0,iOS 中支持的 GLSL ES 最高版本为 3.0.0,但是 version 部分基本没什么变化,所以直接那 3.1.0 中的文档来看了;
关于 #version 的描述如下:
image.png
比较重要的几个结论:
必须声明在第一行,且必须在所有预处理命令之前,且必须在注释之前;如果没有声明 version,则默认为 1.0.0 版本;和 OpenGL 中的 GLSL 不同, #version number 后面必须加 es,以此来表明该 GLSL 为 ES 版本;
再来看看 iOS 中的 OpenGL ES,iOS 支持 openGL ES 3.0,相关文档如下:
image.png
这里其实有待商榷,Apple 官方文档中没有找到 OpenGL ES 的具体版本,只知道是 OpenGL ES 3.0 以上的。可以做下测试,如果着色器代码中声明 #version 320 es 被支持,也就是最新版本的 GLSL ES,是不是可以理解为:iOS 的 OpenGL ES 虽然被遗弃了,但是仍然是最新的?
测试 #version 320 es 和 #version 310 es结果如下:
image.png
测试 #version 330 es,结果如下:
image.png
也就是说,iOS 中的 GLSL ES 版本为 3.0.0。所以,iOS 对应的 OpenGL ES 也为 3.0.0,并不是 2019 年修订的 3.2 版本。
4. Apple 抛弃 OpenGL
已经有现成的 openGL ES 了,得罪开发者,而且自己也需要额外的工作量来开发新的 Metal 框架,同时还需要底层 GPU 支持自己的 Metal 框架,这么吃力不讨好的事情, Apple 为什么这么做?
其实这里可以拆解成几个问题:
OpenGL ES 是否已经不能满足 Apple 的需求了?是否真的得罪开发者?Apple 是否在为自己的独立 GPU 步道?
首先第一个问题,查阅资料之后可以确定,openGL ES 已经无法满足 Apple 了。因为:
OpenGL ES 由 khronos 制定、发布、更新,但是该组织由很多公司组成,各公司为了自己的利益在 openGL/openGL ES 中增加扩展,需要得到的部分公司同意之后才能发布到 release 版本,所以效率极慢;openGL 和 openGL ES 已经很久没有更新了,而且官方已经使用 Vulken 来替代 openGL 了。嵌入式设备端, Vulken 已经支持嵌入式设备,并在安卓和 Flutter 上应用,只是 Apple 有了 Metal,所以官方并没有对其进行支持。如果想在 MacOS/iOS 上使用 Vulken,只能使用 khronos 开发的扩展来进行支持,本质上是对 Metal 的封装;OpenGL 拥有很多历史包袱且支持跨平台,那么性能必定上无法满足 Apple 对高性能的追求。加上更新缓慢,Apple 研发自己的图形 Api 势在必行;
所以,对于第一个问题可以做出解答:
OpenGL/OpenGL ES 已经有点更不上现在 GPU 的发展,在性能和、更新周期、Api 设计上皆无法满足 Apple 的需求了,所以 Metal 势在必行;
第二个问题,Apple 是否对开发者不友好?这个要从几点来说明:
Apple 从来都是以自身的发展方向作为技术选型的主要考虑,包括后面的自研芯片,因特尔的芯片因为无法跟上自己的需求被 Apple 抛弃转向自研 M1 芯片,Metal 同样如此;DirectX 和 Metal 一样都是属于面向 GPU 的图形 Api,windows 早就这么做了,为什么现在 Apple 这么做就不行?OpenGL/OpenGL ES 官方都已经抛弃并转向 Vulken 了,这个时候怪 Apple 对开发者不友好?谁让你在 Metal 都已经武装完成之后才出来 Vulken,难不成 Apple 还要一直等你或者受制于 khronos?
开发者可以使用该层架构提供的 Api 来快速完成 UI 相关的业务,就算是新手也可以使用几行代码就可以展示一个 Label、Button 或者是列表;
Unity 3D 没有使用过,个人理解,Unity 3D 类似于 UIKit 和 AppKit 框架,或者说是介于 UIKit 和 OpenGL/Metal 中间的一个框架。开发者可以使用 Unity 3D 提供的 Api 做出很多酷炫的动画和图形,从而简化游戏开发或者是重度绘制 App 的开发流程。
图形 Api(graphics Api)是针对 GPU 的,由 GPU 厂商进行支持;GPU 厂商支持的越多,你的标准就更容易流行并被开发者接纳,亦或是像苹果一样,依靠自身的生态链自成一派;现在的图形 Api 只有三种:Metal、OpenGL、Vulken;图形 Api 之上还有中间层,也就是对图形 Api 的封装和渲染流程的封装,有:skia(flutter)、Core Animation/Core Graphic、Canvans??类似的,游戏开发中的游戏引擎也是处于这一部分,比如 Unity3D、UE4 或 cocos2dx 都是给予对图形 Api 的封装而实现的;在中间层之上,还有 UI Framework ,比如 UIKit 就是使用 Core Animation 或者 Core Graphic 实现了一套 UI。类似的还有 Android 和 Flutter 上层的 UI Framework;openGL 是最初的图形 Api,在最早也是所有图形绘制框架的基础架构,跨平台支持很好。但是随着嵌入式设备的普及而嵌入式的 GPU 又没有 PC 端那么强悍,所以只能在 openGL 上拉出一个子集来支持,也就是 openGL ES;openGL 虽然历史悠久,跨平台支持很好,但是存在着很多历史包袱,另外因为跨平台,if else 自然就会消耗性能,最后,因为 khronos 是有很多公司组成的一个组织,大家都会为了子集的利益各自为战,这就导致 openGL 更新缓慢,适应不了一些公司的发展,所以就出现了 DirectX 和 Metal;Vulken 本质上是为了 替换 openGL,所以一开始就是跨平台的。但是 Apple 已经有了自己的 Metal 框架,所以暂时没有对 Vulken 进行支持,这也就让 Vulken 的跨平台意义大打折扣,也就让 Vulken 和 DirectX 成为了竞争对手。当前除了 Apple 在 iOS/MacOS 上已经完全抛弃了 openGL,其他平台暂时还处于一种比较纠结和共存的状态,但是大部分 GPU 厂商已经支持 Vulken 了,这算是一个比较好的地基;Windows PC 端,大部分游戏应用都是给予 DirectX 开发,少部分已经支持 Valken,比如 Dota2;安卓端,仍然是 openGL ES 为主,Flutter 已经是 Vulken 和 Metal 为主了?openGL 用于支持低端机型?vulkan 相对于 openGL,Api 上更偏向于底层,这意味着可以使用 vulkan 更加自由的操作 GPU 以此来为上层提供更丰富和搞笑的功能。于此同时,开发者也需要做更多的工作,所以也就出现了图形 Api 入门程序中,绘制三角形时,vulkan 的代码远超过 openGL/openGL ES;
7. iOS 和 OpenGL ES
几个结论:
iOS 上仍然可以使用 OpenGL ES 进行开发,只不过版本最高只能使用 OpenGL ES 3.0;iOS 上如果实在对图形 Api 有需求,趁早转 Metal;iOS 中做一些渲染优化时,最好直接参考 Apple 的官方文档。因为 Metal 和 OpenGL ES 的流程、设计思路都不一样了;要深刻认识封装、封装颗粒、Api 通用性设计的重要性;
比如如果 App 中有图形 Api 的需求,就应该基于 OpenGL ES 进行封装,产生一套自有的中间框架,而且 Api 应当尽量屏蔽硬件层的逻辑。如果这样做了,除非 Metal 不支持,后续更换 Metal,只需要将 OpenGL ES 的调用替换成 Metal 即可,相对而言会轻松一些;