autorenew

🎉空间计算 + 人工智能 + iOS = ♾️, Let's visionOS 25 即将到来! 了解更多 →

开源框架 RealityShaderExtension,帮你将 Unity 和 Unreal 的 Shader 转到 visionOS

visionOS 上的 Shader

visionOS 上使用 Shader 有两种方式:ShaderGraphLowLevelTexture(LowLevelMesh) + ComputeShader

Unity 中也有自己的 Shader GraphUnreal蓝图 也可以对 Shader 拖拽连线,从这方面讲它们三个的操作具有很大的相似性,理论上可以很方便进行迁移。但 UnityUnreal 是高度成熟的游戏引擎,各种材质效果和配套工具非常丰富,要在苹果原生 RealityKit 达到同样效果,需要手工迁移很多 Shader。虽然 UnityPolySpatial 能自动将 Unity Shader Graph 转译为 visionOS 支持的 MaterialX 版 Shader Graph 材质以在模拟器和真机上运行,然而遗憾的是,当我们使用苹果原生开发时,PolySpatial 并不能帮上忙,所有效果还是要手工迁移。

在我的日常开发中,我经常要在 Unity 和 苹果原生之间切换,偶尔也会帮忙迁移一下 Unreal 到原生。在这个过程中经常面临的困难是:Unity 和 Unreal 中大量内置的 Shader 效果节点,在 RCP 中并没有内置,需要手工编写。于是我就经历了一系列痛苦的迁移过程,比如下图中这些复杂的连线都是手工迁移过来的:

RealityShaderExtension

相信我,经历过一次后,再也不想再重新经历第二遍编写与调试。所以我就把这些迁移好的节点整理了一下,形成开源项目 RealityShaderExtension,供其他开发者使用,可以方便地帮助大家完成从 Unity 和 Unreal 到苹果 visionOS 原生 Shader 的迁移。

RealityShaderExtension 复刻了来自 Unity28 个 Shader Graph 节点和来自 Unreal28 个 Blueprint 节点,此外还包含 20 多种颜色混合模式和 8 种颜色空间转换节点。

Shader Graph 调试

在手工迁移这些节点的过程,我遇到了非常多的 bug,总结下来有三类:

顺便来讲讲这些 bug 的应对方法。

a. 不小心写错的

最简单的当然是,花大量时间逐一检查对比节点和连线。当然我们还可以借助“假彩色图像”对输出值或中间值进行检验。

“假彩色图像”是指将输出值映射到 [0, 1] 区间,然后做为 RGB 值进行输出。

当我们在 Xcode 中编写代码时, RealityKit 自带的调试组件 ModelDebugOptionsComponent 就可以将 UV 和法线,显示为不同颜色。

在 RCP 中,右上角也有自带的 Debug Views 功能,可以将法线,UV ,粗糙度等用颜色显示出来。

除了自带方法之外,还可以手动转换为颜色进行显示,在项目的示例中,就有大量输出值被手动转换为 RGB 颜色表示出来,这样更加灵活。

b. 误用 RCP 中节点的参数

最常见的是,部分 RCP 中节点与 Unity 和 Unreal 中参数含义不同。

比如 RCP 中没有 lerp 函数,可以使用 Mix 来代替,但它们参数顺序是反过来的。还有 Step 函数,参数顺序也是反过来的:

//GLSL 
lerp(a, b, t) = b * t + a * (1-t)
step(edge, x)

//RCP 
mix(F, B, m) = F * m + B * (1-m)
step(in, edge)

比如 RCP 中的条件选择节点 MTLSelect ,当条件为 true 时,会选择参数 B;条件为 false 时,选择参数 A,这点与常规判断不同,需要特别注意。

还有一些函数名称与常见的不同,比如其他平台常见的求导函数:ddx, ddy, fwidth,在 RCP 中则是名称的全称:

c. RCP 的 bug

编写过程中,还会遇到 RCP 自身的一些 bug,有时不能自动更新效果,有时会给出错误的值。一般解决办法有:

如果你在编写 Shader graph 过程中,遇到特别奇怪的问题:所有节点和连线都是正确的,但效果就是不对。那就需要查找一下,很可能就是某个节点出错了。删除再重新创建这个节点,或者点击 Remov Override 按钮↩️,重置输入输出可能就会恢复正常。

Instancing 技术

我在创建项目中的 Node Graph 时使用了 InstancingInstancing类似于单例,它可以节省 CPU 和内存成本,因为它只在内存中加载一个实例并重复使用它。但这样也造成了在使用时,有些许不方便,所以一般有 3 种方式来使用 RealityShaderExtension 中的节点。

如果修改原始 Node Graph,所有 Instancing 的内容将会同步发生变化。

如果修改原始 Node Graph,禁用的实例化将不会发生变化,因为它们是不同的节点。

未来展望

在完成 RealityShaderExtension 中近百个节点的迁移工作后,我认为目前的 Shader Graph 使用门槛低适合入门,同时功能上基本完整,能够搭建出复杂效果。

不过 RCP 目前存在一些 bug,功能也需要进一步完善,比如:

最后,希望 RealityShaderExtension 对大家的开发有所帮助!

参考

本文作者

苹果API搬运工