autorenew

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

一个三维描边效果,带你入门 visionOS 上的 Shader Graph 效果

说明

本文来自 Let’s Vision 2025 大会上的演讲《跨入 visionOS Shader 大门,实现高级视觉效果》,总结文章分为两篇:《一个三维描边效果,带你入门 visionOS 上的 Shader Graph 效果》和《开源框架 RealityShaderExtension,帮你将 Unity 和 Unreal 的 Shader 转到 visionOS》。

Shader 是什么

Shader 是跑在 GPU 上一段小程序。我们平时写的代码是跑在 CPU 上的,OS 替我们完成 GPU 操作

而 Shader 运行在 GPU 上,用来控制每一个顶点/每一个像素的色彩和光照效果。正是因为 Shader 运行在 GPU 上,看上去语法与逻辑都比较奇怪,限制也比较多。

visionOS 上的 Shader

visionOS 中的 Shader,有三种:

在 visionOS 上,我们可以把 GPU 看作一个黑盒,Shader Graph 允许我们控制的只有两个:Geometry Modifier 和 Surface Shader

Compute Shader 更加灵活可以与 CPU 同步数据,数据回到 CPU 后就可以做任意操作:保存或显示

visionOS Shader 示例

Shader Graph 适合入门

最适合初学者入门的,还是 ShaderGraph。

在 RealityComposer Pro 中使用 ShaderGraph 有以下优点:

Shader 的前置知识

不过在开始演示之前,还需要了解一点基础知识,很多人入门 Shader 困难,就是因为不了解这些知识。

首先就是,Shader 中能拿到的输入有什么:位置,法线,贴图坐标。下面左图中的 ” 刺 ” 就代表了法线,线条交汇处就是顶点的位置。右图是法线的另一种表现形式:将 xyz 坐标转换为 RGB 颜色显示出来。

下图中展示了不同的 uv 贴图方式,将二维坐标 xy 转换为颜色 RG,一个点对应一个 uv 坐标。我们注意到,同样形状的模型可以有多种贴图方式。

如果你还想了解更多模型知识,可以看 RealityMoreShape 这个开源框架。它用 Swift 代码实现构建 3D 模型的顶点,法线,uv

Shader 用来做什么

简单的说:Geometry Modifier 用来调整物体形状,而 Surface Shader 用来调整表面颜色,它们之间由 GPU 自动插值处理:

Shader Graph 使用演示

法线外扩

首先,我们将法线外扩,实现一个 ” 变胖 ” 效果。我们将模型复制一份,对复制出来的材质进行调整:

下图左为原始模型,中间为放大 0.2 倍的模型,右边将顶点沿法线移动 0.5 后的效果对比。

我们可以看到直接放大与法线外扩的效果不同,图中假设模型原点在底部,而直接放大导致离原点越远的部分偏移越多,而法线外扩会产生 ” 均匀的变胖 ” 效果,这个可以拿来做描边效果叫:法线外扩描边法

剔除正面,显示描边

我们将 ” 胖模型 ” 的材质进行修改,将面剔除(右侧的 Face Culling)方式改为 Front,这样就能露出内部的原始模型,同时将 Surface Shader 颜色修改为红色,即可产生描边效果:

产生周期性变化

这样的效果似乎有点单调,我们可以让描边动起来,产生随时间变大变小的效果。先添加一个 Time 节点,再用 Sin 函数产生周期性变化,最后 Remap 一下,将范围从 -1~1 改为 0~1 区间,再与外扩量相乘:

边缘渐变效果

最后,我们再给描边添加一个渐变的效果,让描边在靠近边缘处逐渐变淡,同时用 Power 函数调整变淡的速度,让它更快衰减:

为什么向量点乘可以实现渐变效果呢?这和模型的法线分布有关,模型边缘处法线几乎垂直,此时与视线向量 ViewDirection 点乘,就可以判断是否到边缘了,用来产生渐变效果

缺点

需要说明的是,法线外扩法也有缺点:法线不连续时,会产生断裂

当模型的法线不连续时,就需要平滑法线,一般通过代码在 CPU 处理 3D 模型,或者在建模软件中处理好。

如果形状非常简单,也可以简单处理:将点的位置按比例放大(直接放大)

数学方法总结

总结一下,这个 Demo 用到数学方法:

用 AI 辅助编写 Shader 的尝试

对于 Compute Shader 的 MSL 代码,AI 工具理解的比较好,也能顺利编写代码。

而对于包含了 Shader Graph 的 usda 文件,AI 也可以读取 Shader 相关逻辑,但理解比较困难,修改也比较难。

参考

本文作者

苹果API搬运工