说明
以前在写计算几何时,写到过 S04E11: 李萨如曲线体网格生成,其中在一个圆环上叠加 2 个周期的正弦波动(即 2*2π),即可生成 Meta Logo 的 Mesh 网格几何体。
实际上,借用同样的思路只用编写 Shader,也能将一个圆环变成一个 Meta Logo。刚好此次 Xcode 15 中的 Reality Composer Pro 支持了 Shader Graph 功能,让我们能够通过拖拖拽拽就能编写好一个 Shader,那就来试玩一下吧!
准备工作
不过首先,我们需要一个圆环体,这里不得不吐槽一下,以前在 SceneKit 编辑器界面可以直接拖拽生成圆环体的,现在 Reality Composer Pro 却只有立方体球体等几种。这里不得不借助 Blender 生成一下基础的圆环体,记得将主环段数设置的稍微大一些,这样变形后的几何体也会平滑。导出为 .usdc
文件即可。
在 Xcode 中打开 Reality Composer Pro,创建一个新的工程。 将模型导入,并拖动到 3D 场景中,因为没有材质,所以整个模型显示为紫红色条纹,我们需要创建一个新的材质。 将材质应用到圆环几何体上,然后选中材质,打开 Shader Graph 面板,可以愉快地编写 Shader 了。 添加新的 Shader 节点的方法是:在空白处双击,或从任一输入或输出拖拽出来,即可弹出选择面板。推荐采用拖拽方式,因为这样可以自动匹配类型,比如从输出拖拽时,输出类型是 vector3
的话,弹出界面中会筛选出可以接受 vector3
类型的节点。
另外需要提前注意的是:圆环模型大环在 XY 平面上,半径 2m,小环是绕 Z 轴旋转,半径 0.25m。要得到 Meta Logo 我们需要编写 Shader 将圆环上的点沿 Z 轴进行偏移。
基本思路
如果直接对所有点沿着 Z 轴进行偏移的话,因为不同位置的偏移量不一致,会导致圆环变得扁平。 这时我们就需要先沿旧法线将小环收缩成一条线并添加 Z 坐标扭曲,然后重新计算扭曲后的法线,再沿新的法线恢复小环,就能让小环恢复成圆形。整体流程和效果如下图所示: 接下来,我们按照五个主要流程来讲解计算的原理。
1.收缩小环
要收缩小环其实非常简单,添加一个 GeometryModifier,然后将所有的点沿法线移动 -0.25,即可将小环缩小到 0。用伪代码表达:
scss
newPosition = position + normal * (-0.25)
当然,如果此时直接缩小到 0 就看不到,为了方便观察可以先移动 -0.245 但是,因为最终返回的是偏移量 Position Offset,也就是将原有的点移动多少而不是每个点的具体位置,这样会导致后续计算出现困难,所以我们还需要做一些小的修改:先加上原始位置,得到偏移后的坐标,中间计算可以在此基础上进行,最终输出时再减去原始位置,得到最终偏移量做为输出。
因为在下一步中,我们需要对 Z 坐标进行单独操作,这时我们就需要用到另外两个节点:Separate3 和 Combine3 ,前者可以将三维向量分解为三个单独的量,后者则是将三个单独的量组合为一个三维向量。 选中前面多个节点,点击右键,选择:Compose Node Graph 来压缩节点整理一下,将压缩后的命名为:CenterLine 。至此,收缩小环的工作完美完成了!
2.扭曲大环
要扭曲大环,我们需要知道每一个点在大环上的位置,或者说角度。这里我们使用 atan2
函数来求夹角,将 XY 坐标输入就得到该点相对于 X 轴的夹角,输出范围为 (-π,π]。 接下来对 Z 坐标添加 2 个周期的扭曲,波动幅度也是 2m,用伪代码表达就是:
ini
angle = atan2(x, y)
z = sin(2*angle) * 2
可以看到大环发生了明显的扭曲,转动不同位置观察,就会发现已经和 Meta Logo 比较相似了。
3.计算扭曲切线
要获取扭曲后的法线,需要先计算扭曲后大环的切线。这里我们补充一点数学知识:如果已知一条曲线的数学表达式,要求任意点的切线,只需要对其表达式求导即可。那么收缩后的图形的表达式为:
matlab
// 大环半径为 2 米
x = sin(angle) * 2
y = cos(angle) * 2
z = sin(2*angle) * 2
//对其求导后
x' = cos(angle)
y' = -sin(angle)
z' = cos(2*angle) * 2 //复合函数求导链式法则:f[(g(x))]' = f'[g(x)] * g'(x),也就是说:sin'(2x) = cos(2x) * 2;sin'(3x) = cos(3x) * 3;sin'(4x) = cos(4x) * 4
最后要对求出的切线长度进行归一化 Normalize ,以便后续处理,顺便将节点整理压缩一下,如下图:
4.计算扭曲法线
我们先用切线和旧的法线,进行叉乘得到副切线。将副切线与切线再次进行叉乘,即可得到新的法线方向。这里需要副切线,是因为我们扭曲 Z 坐标时,副切线不变,它实际相当于旋转轴。 这里需要注意叉乘的顺序:叉乘遵守右手螺旋法则,右手手指从第一个向量出发,向第二个向量弯曲,大拇指方向就是新向量的方向。 用伪代码表示如下:
ini
bitangent = cross(tangent, oldNormal)
newNormal = cross(bitangent, tangent)
修改 Shader Graph 如下,最后记得将求出的新法线,赋值给 GeometryModifier 的 Normal 属性,以便显示出正确的光照效果。
5.恢复小环
恢复小环也很简单,将挤在中心线上的点沿新的法线移动 0.25 米即可,用伪代码表达:
ini
newPosition = centerOffset + newNormal * 0.25
还要记得,将前面的 -0.245
改为 -0.25
,以便获得更完美的效果。整理好的 Shader Graph 如下: 展开后完全版 Shader Graph 如下:
6.其他
最后,我们选中物体,将其绕 X 轴旋转 90 度,这样看上去更像 Meta Logo 了。因为我们的 Shader Graph 是作用在 Geometry 上,也就是模型空间坐标系,所以外部旋转并不会影响 Shader 的坐标系。 相信大家也发现了,当我们选中物体时,显示的高亮轮廓仍然是原来的圆环的形状,这是因为 Shader 代码工作在 GPU 上,我们只更改了物体的显示效果。当物体与其他物体(包括鼠标,眼动等交互操作)交互时,实际是在 CPU 上处理的物理碰撞计算,这些我们并没有专门处理。
最后的最后,如果想要显示更多周期的波动,只需要将下图中红色框中倍数进行更改即可,比如改为 3个周期:
总结
此次 Reality Composer Pro 的更新非常强大,尤其是对 Shader Graph 的支持让人惊喜,从使用者的角度,使用 Shader Graph 比直接编写 Shader 代码要容易很多,可以有效降低入门门槛。缺点方面:偶尔会遇到一些类型的 bug;另外缺少 Debug 功能让人编写代码时难以调试。
总之,功能强大,简洁好用,推荐初学者都来试试!
本文工程代码已开源,github 地址: github.com/XanderXu/Sh...