WWDC Quick Look 💓 By SwiftGGTeam
Build real-time neural rendering pipelines with Metal

Build real-time neural rendering pipelines with Metal

观看原视频

Highlight

Metal 4 提供三层机器学习能力:MetalFX 提供开箱即用的神经降噪和上采样、ML Command Encoder 可在渲染管线中直接运行预训练模型、TensorOps API 让着色器内联运行小型神经网络,充分利用 M5 和 A19 Pro GPU 的神经加速器。

核心内容

实时渲染的机器语言困境

做实时渲染的开发者都会遇到一个矛盾:要画质就要多采样,要多采样帧率就扛不住。

比如路径追踪器(Path Tracer),要达到无噪点的图像,每像素需要数百次采样。但实时视口只能承受每像素 1 次采样——结果就是满屏噪点。

传统方案是各种降噪算法,但它们要么效果有限,要么计算成本太高。

Apple 的三层解决方案

Metal 4 提供了三层递进的机器学习能力:

第一层:MetalFX —— 黑盒解决方案,直接调用就能用

第二层:ML Command Encoder —— 把训练好的模型跑在渲染管线里

第三层:TensorOps API —— 在着色器里手写小型神经网络

MetalFX:即时可用的神经降噪

MetalFX Denoising 是一个神经上采样器和降噪器的组合,专门为 Apple silicon 优化。

Maxon 的 Redshift Live(Cinema 4D 的实时路径追踪视口)就采用了这个方案。开启后,原本充满噪点的单采样图像瞬间变得干净,还能保持实时帧率。

要获得最好的效果,有三个关键实践:

  1. 保持输入干净 —— 辅助输入(如 diffuse albedo)应该是无噪点的,这是降噪器最强的信号

  2. 存储观众看到的 —— 镜面反射存储反射几何体的属性,玻璃通过菲涅尔项混合反射和折射

  3. 运动矢量要正确 —— MetalFX 期望去抖动的运动矢量,没有次像素偏移

ML Command Encoder:替换后处理管线

降噪只是起点。后处理管线(色调映射、色彩分级、胶片模拟)同样可以用机器学习。

传统管线是多个阶段串联,每个阶段有自己的参数,整体可能变得很复杂。神经网络的思路是:学习整个色彩变换。

以 HDRNet 为例,它用下采样图像做全局和局部分析,为 16x16 图块生成局部变换,最后用边缘感知技术应用到全图。

工作流是这样的:在 PyTorch 中训练网络,导出为 MTLPackage,然后在 Metal 4 中加载和执行。

TensorOps:着色器内联微型网络

最激进的方案是在着色器里直接运行一个小型神经网络。

这类网络只有几千个参数,针对特定任务训练,甚至可以每帧在线训练。

举例:基于图像的光照(Image Based Lighting)传统需要预计算 irradiance map,但场景是动态的(日夜循环),预计算可能过时。

解决方案是用一个小型 MLP 在线学习这个信号,每帧运行几次训练迭代,模型就能实时适应新的世界状态。

详细内容

MetalFX 降噪集成

02:16

MetalFX Denoising 的集成很直接:路径追踪器生成颜色和一些辅助输入,MetalFX 输出降噪后的图像,之后继续你的后处理流程。

需要的辅助输入包括 diffuse albedo、深度等,如果你的渲染器已经产生这些,直接用就行。

去抖动运动矢量计算

08:46

正确计算运动矢量对时间稳定性至关重要。下面是计算静态物体相机-only 运动矢量的代码:

#include <metal_stdlib>
using namespace metal;

// 计算相机-only 运动矢量
float4 clipCurrent = viewProjCurrent * float4(worldPos, 1.0);
float2 ndcCurrent = clipCurrent.xy / clipCurrent.w;

float4 clipPrevious = viewProjPrevious * float4(worldPos, 1.0);
float2 ndcPrevious = clipPrevious.xy / clipPrevious.w;

float2 motion = ndcPrevious - ndcCurrent;

// 获取当前帧和上一帧的次像素偏移
float2 jitterCurrent = getJitter(frameIndex);
float2 jitterPrevious = getJitter(frameIndexPrevious);
motion -= jitterPrevious - jitterCurrent;

关键点:

  • 通过当前和上一帧的视图投影矩阵变换世界位置
  • 计算两个 NDC 坐标的差值得到运动矢量
  • 减去抖动偏移,得到去抖动的干净运动矢量

对于移动或变形的物体,需要存储每个顶点的上一帧世界位置,或蒙皮两次,计算真实运动矢量。

ML Command Encoder 部署流程

12:03

部署神经网络需要两个步骤:

设置阶段:

  1. 加载 MTLPackage
  2. 用函数描述符指定网络函数
  3. 创建机器学习管线描述符

执行阶段:

  1. 创建编码器
  2. 创建参数表,绑定输入和输出
  3. 分发命令缓冲区

更新后的渲染管线:路径追踪 → MetalFX 降噪 → 神经色调映射,全部在同一个命令缓冲区中编码,同一帧内执行。

TensorOps 构建内联网络

17:56

神经网络由三部分组成:输入层、隐藏层、输出层。

以天空探头为例,这是一个 3-4-4-3 的 MLP(多层感知器):输入 3 个浮点数编码方向,输出 3 个浮点数表示该方向的平均光照颜色。

在着色器中评估网络的流程:

  1. 准备输入张量(最好是 2D 矩阵以批量处理)
  2. 使用 matmul 2D 张量操作进行矩阵乘法
  3. 应用激活函数
  4. 重复到输出层

SIMD 群组执行模式:

19:29

使用 SIMD-group 执行范围时,所有参与的线程协同工作在同一个矩阵乘法上,还能访问协作张量。

协作张量的存储分布在线程组的多个线程之间,避免了昂贵的主存往返。用协作张量作为第一次乘法的输出,结果会留在快速的线程存储内存中,然后原地应用激活函数。

在线训练循环

16:06

在线训练改变了传统渲染循环:

  1. 生成一个希望采样的方向
  2. 在模型上运行推理获取结果
  3. 计算天空照明问题的解析解
  4. 使用解析解计算误差
  5. 运行反向传播逐步改进模型

这个流程可以在每一帧重复多次,模型就能实时适应新的世界条件,并用这个信息立即进行着色。

核心启发

  1. 动态 irradiance probe —— 为你的实时渲染器构建一个在线学习的 irradiance 网络。当场景光照变化时(开关灯、日夜循环),网络自动适应,无需重新预计算。入口:TensorOps API + 自定义训练循环

  2. 神经材质近似 —— 训练一个小型 MLP 学习复杂材质的响应。比如多层涂层、次表面散射等昂贵计算,可以用一个在着色器内联运行的微型网络近似。入口:ML Command Encoder 部署自定义网络

  3. 自适应后处理 —— 收集你项目中美术手动调色的图像对(HDR 原图 + 调色后结果),训练一个神经色调映射器。部署后,渲染器直接输出 HDR,网络自动完成风格化后处理。入口:PyTorch 训练 + MTLPackage 导出

  4. 实时 AI 辅助布光 —— 用小网络学习特定场景的布光偏好。美术调整几盏灯后,网络学习这个风格,然后可以快速应用到其他场景或自动生成类似布光方案。入口:TensorOps 在线学习

  5. 降噪强度自适应 —— 根据场景内容自动调整 MetalFX 的降噪强度。比如用一个小网络分析图像,为不同区域生成最优的 denoiser strength mask。入口:TensorOps + MetalFX strength mask

关联 Session

评论

GitHub Issues · utterances