WWDC Quick Look 💓 By SwiftGGTeam
Enhance your app with Metal ray tracing

Enhance your app with Metal ray tracing

观看原视频

Highlight

Metal ray tracing 新增三大能力:从 render pipeline 直接发射光线(无需 compute pass)、intersection query 替代 intersector 对象进行内联求交、以及 motion blur 和 extended limits 支持生产级渲染。

核心内容

你想在渲染中加入光线追踪效果,比如反射或阴影。去年的 API 要求你额外创建一个 compute pass,把 G-Buffer 数据写出到内存,再读回来做光追。这增加了内存带宽开销,还打断了渲染管线。

今年 Metal 允许你直接从 render pipeline 发射光线,不需要离开 render pass。

详细内容

从 Render Pipeline 发射光线

02:21

去年的 workflow:render pass → compute pass(光追)→ 可能还要再开 render pass。中间数据要在内存中来回传递。

今年的 workflow:一个 render pass 内直接发射光线,中间数据留在 tile 内存。

设置方式:

// 定义 intersection 函数
[[intersection(triangle, triangle_data, instancing)]]
bool sphereIntersection(...)

// 在 render pipeline descriptor 中链接函数
let linkedFunctions = MTLLinkedFunctions()
linkedFunctions.functions = ["sphereIntersection"]
renderPipelineDescriptor.fragmentLinkedFunctions = linkedFunctions
// 创建 intersection function table
let functionTable = renderPipelineState.makeIntersectionFunctionTable(
    descriptor: tableDescriptor,
    stage: .fragment
)

// 填充 table
let functionHandle = renderPipelineState.functionHandle(
    name: "sphereIntersection",
    stage: .fragment
)
functionTable.setFunction(functionHandle, index: 0)

04:48

关键点:

  • linked functions 添加到 render pipeline 的 fragment stage
  • makeIntersectionFunctionTable 新增 stage 参数
  • function handle 也是 stage-specific
  • 绑定到 render encoder 的 buffer index

在 fragment shader 中发射光线:

fragment float4 rayTracedFragment(...) {
    // 绑定加速结构和 intersection function table
    intersector<instancing, triangle_data> intersector;
    intersector.set_acceleration_structure(accelerationStructure);
    intersector.set_intersection_function_table(intersectionFunctionTable);

    // 发射光线
    ray ray;
    ray.origin = worldPosition;
    ray.direction = reflect(viewDirection, normal);

    auto intersection = intersector.intersect(ray, primitiveAccelerationStructure);

    // 用交点结果着色
    return shadeIntersection(intersection);
}

05:57

关键点:

  • 加速结构和 function table 都绑定到 render encoder
  • shader 中使用方式和 compute kernel 相同
  • 可以利用 tile memory 优化,避免写出 G-Buffer

Intersection Query:更灵活的光线遍历

07:23

去年的 intersector 对象封装了完整的遍历逻辑。对于简单的自定义求交(比如 alpha test),创建一个完整的 intersection function 可能太重。

intersection query 允许你在 shader 中内联控制遍历过程:

// 用 intersection query 做 alpha test
intersection_query<instancing, triangle_data> query;
query.reset(ray, accelerationStructure, intersectionFunctionTable);

while (query.next()) {
    switch (query.get_candidate_type()) {
        case intersection_query::candidate_type::triangle: {
            // 获取三角形交点信息
            float2 barycentrics = query.get_candidate_triangle_barycentric_coord();
            float distance = query.get_candidate_ray_distance();

            // 采样 alpha texture
            float alpha = sampleAlphaTexture(barycentrics, distance);

            // 如果通过 alpha test,提交交点
            if (alpha > 0.5) {
                query.commit_triangle_intersection();
            }
            break;
        }
        default:
            break;
    }
}

// 获取最终交点
if (query.get_committed_intersection_type() == ...)

10:36

关键点:

  • query.next() 遍历所有候选交点
  • get_candidate_type() 判断交点类型
  • 自定义逻辑决定是否 commit_triangle_intersection()
  • 遍历结束后查询 get_committed_intersection_type()

选择 intersector 还是 intersection query:

场景推荐方案
有现有 intersector 代码继续用 intersector
从其他 API 移植 query 代码用 intersection query
简单自定义求交(如 alpha test)intersection query
复杂自定义求交逻辑intersector
性能敏感,需要对比两种都试

12:21

User Instance ID 和 Instance Transform

13:52

新 API 允许你为每个 instance 指定自定义 ID,并在 intersection 中读取 instance 的变换矩阵。

// 设置 user instance ID
let instanceDescriptor = MTLAccelerationStructureMotionInstanceDescriptor()
instanceDescriptor.userID = 42 // 自定义值
instanceDescriptor.transformationMatrix = transform
// 在 intersection function 中读取
uint userID = intersection.user_instance_id;
float4x4 transform = intersection.instance_transform;

14:48

关键点:

  • user_instance_id 是 32 位自定义值
  • 可以用来索引材质表、编码自定义数据
  • instance_transform 直接读取 instance 的变换矩阵
  • 不需要自己维护外部映射表

Extended Limits:更大的场景

18:29

生产级场景可能超出默认的加速结构限制。extended limits 模式增加了以下上限:

  • primitive 数量
  • geometry 数量
  • instance 数量
  • mask 大小

启用方式:

// 构建加速结构时启用
let descriptor = MTLPrimitiveAccelerationStructureDescriptor()
descriptor.usage = .extendedLimits

// shader 中标记
intersector<extended_limits, instancing> intersector;

19:18

关键点:

  • extended limits 可能影响性能
  • 只在需要时启用
  • 加速结构和 intersector 都要标记

Motion Blur:运动模糊

19:39

真实相机的曝光不是瞬时的。物体在曝光期间移动会产生模糊。Metal 现在支持在 ray tracing 中模拟这个效果。

原理:每根光线随机采样一个时间值,Metal 在对应时间点的场景状态中求交。

// 生成随机时间值(在曝光区间内)
float time = randomInRange(exposureStart, exposureEnd);

// 把时间和光线一起传给 intersector
ray.time = time;
auto intersection = intersector.intersect(ray, accelerationStructure);

22:38

支持两种动画:

Instance motion:整个物体刚性变换。成本低,适合平移/旋转。

// 提供关键帧变换矩阵
let motionDescriptor = MTLAccelerationStructureMotionInstanceDescriptor()
motionDescriptor.motionTransformsStartIndex = 0
motionDescriptor.motionTransformsCount = 2

Primitive motion:每个顶点独立运动。成本高,适合形变动画(如角色蒙皮)。

// 为每个关键帧提供顶点 buffer
let keyframeData = MTLMotionKeyframeData(buffer: vertexBuffer, offset: 0)
let geometryDescriptor = MTLMotionTriangleGeometryDescriptor()
geometryDescriptor.vertexBuffers = [keyframeData0, keyframeData1]

25:56

关键点:

  • instance motion 比 primitive motion 快
  • 两种动画可以同时使用
  • Metal 自动在关键帧之间插值
  • shader 中标记 instance_motionprimitive_motion

核心启发

  1. 从 render pipeline 直接做光追反射。不再需要 compute pass 和 G-Buffer 的来回传递,利用 tile memory 优化。入口 API:renderPipelineDescriptor.fragmentLinkedFunctions + intersector in fragment shader。

  2. 用 intersection query 做 alpha test。几行代码替代完整的 intersection function,适合简单的自定义求交。入口 API:intersection_query + commit_triangle_intersection()

  3. 用 user instance ID 简化材质查找。把材质索引直接编码到 instance ID,intersection 中直接读取。入口 API:MTLAccelerationStructureMotionInstanceDescriptor.userID

  4. 为生产渲染启用 extended limits。大场景需要更大的加速结构限制。入口 API:descriptor.usage = .extendedLimits

  5. 用 motion blur 提升真实感。每根光线随机采样时间值,Metal 自动处理插值。入口 API:ray.time + instance_motion/primitive_motion tag。

关联 Session

评论

GitHub Issues · utterances