大多数浏览器和
Developer App 均支持流媒体播放。
-
Metal 光线追踪指南
了解如何通过 Metal 光线追踪技术,提高游戏和 App 的视觉质量。我们将带您了解 Metal 光线追踪 API 的基本知识,探索最新的增强功能和技术,使您能创建更大、更复杂的场景,减少内存使用和构建时间,高效渲染头发、毛皮等视觉内容。
章节
- 0:09 - Intro
- 1:50 - Build your scene
- 9:48 - Scale with instancing
- 17:34 - Build with parallelization
- 18:34 - Refitting
- 19:47 - Compaction
- 21:14 - Intersect rays
- 26:16 - Debug and profile
- 30:42 - Wrap-Up
资源
- Accelerating ray tracing using Metal
- Metal for Accelerating Ray Tracing
- Rendering reflections in real time using ray tracing
相关视频
Tech Talks
WWDC22
WWDC21
WWDC20
-
下载
♪ ♪
Pawel Szczerbuk:大家好 我是 Pawel Szczerbuk GPU 软件工程师 Metal 可以帮助你 扩展你的光线追踪应用 以创建复杂 细致的场景 光线追踪是制作渲染中 确保图像保真的基本技术 而游戏中的光线追踪技术 聚焦于确保图像高帧率 同时改善视觉效果 这张迪士尼影片中莫阿娜岛场景是用 Metal 光线追踪技术进行渲染的 今天 我将为你讲解 如何使用 Metal 光线追踪技术 我将着重介绍一些 令人兴奋的新功能 你可以用它来加速 游戏和制作渲染器中的光线追踪
光线追踪应用可模拟 各光线在场景中的弹射 要使用 Metal 光线追踪技术 进行渲染 第一步是要定义场景的几何体 然后 Metal 会建立一个加速结构 这个加速结构中包含了你的 几何体 你可利用 GPU 加速 来高效查询交点 在一个 GPU 函数中 创建一条光线与场景相交 在着色器中创建一个求交器对象 将光线和加速结构都传给这个对象 会返回一个相交结果 其中包含你可能需要的所有信息 以便你对像素进行着色 或是进行进一步的处理 这些部分共同协作 就能帮你建立场景 使用实例化来增加视觉复杂性 使光线相交 同时还有一些优秀的工具 可供你使用 以帮助你更好地开发光线追踪应用 这一切都要从建立场景开始 Metal 光线追踪 API 支持几种不同类型的几何体 所有这些几何体 都存储在一个加速结构中
加速结构通过几何体的递归分区 来加速光线追踪过程 这可以快速消除 任何不与光线相交的几何体 你可以通过三个步骤 来设置一个加速结构 创建一个加速结构描述符 你可以在这里设置实际的几何体 有了描述符之后 你就可以分配加速结构 然后构建这个加速结构 一个加速结构描述符 包含一个或多个几何体描述符 Metal 中有三种几何体描述符 三角形是大家众所周知 并喜爱的图元 在计算机图形学中 几乎任何建模都能用到它 包围盒图元则完全 由你的自定义相交函数来定义 当光线射到一个封闭的包围盒时 Metal 会调用这个相交函数 今年还新增了一个新的图元:曲线 非常适合渲染头发和毛皮 要使用三角形创建加速结构 要先为单个几何体 创建三角形几何体描述符 你需要设置一个顶点缓冲区 索引缓冲区以及所需三角形的数量 包围盒几何体描述符 也需要进行相似设置 只是不需要设置顶点 完成后 就得到包围住 几何体的包围盒 此外 你还要有一个相交函数 当光线射到你的包围盒图元时 Metal 就会调用该函数
关于如何设置相交函数的更多详情 请参考 2020 年的 “利用 Metal 探索光线追踪” 讲座
头发 毛皮和植被等几何体 可由数以千计 甚至百万计的图元组成 它们通常被建模为 细腻 平滑的曲线 现在你不必用三角形模拟这些曲线 Metal 新的曲线图元可直接用于建模 即使摄影机放大 这些曲线也依然保持平滑 与三角形相比 曲线的内存占用更为紧凑 并有助于加快构建加速结构
一条完整的曲线 由一系列相连的曲线段组成 一条曲线上的每个曲线段 都是独立的图元 Metal 会为每个曲线段 分配一个唯一的图元 ID 每个曲线段由一系列控制点定义 这些控制点控制着曲线的形状 这些控制点的插值运算 由一组基础函数完成 根据所选基础函数的不同 每个曲线段可有 2-4 个控制点 Metal 提供了四种 不同的曲线基础函数: 贝塞尔曲线 Catmull-Rom 曲线 B 样条曲线 线性曲线 这些基底函数各有优点 请根据你的用例选择最合适的函数 Metal 还需要一个 控制点索引缓冲区 每个曲线段 在该缓冲区中有一个索引 来表示该曲线段的第一个控制点 比如 假设你有四个控制点 你可以使用第一个控制点的索引 来定义一个曲线段 在索引缓冲区中 添加其索引值 0 本示例使用 基础函数 Catmull-Rom 因此 实际定义的曲线段仅在 控制点 1 和控制点 2 之间 要连接另一个曲线段 你只需再添加一个控制点即可 这个新增的曲线段 使用了 1 至 4 号控制点 在索引缓冲区中 添加索引值 1 依靠索引缓冲区 这两个曲线段共享了 3 个控制点 这是曲线图元 能够节省内存的原因之一 根据需要 你可不断重复此操作 直到完成整条曲线 要开始一条新的曲线 你只需添加新的控制点 该控制点与之前的控制点不能重叠 并将相应的索引值 添加到索引缓冲区中 到目前为止 我所描述的曲线 都只是抽象的数学对象 为了渲染这些曲线 需要使其具有某种三维形状 每个控制点都有一个半径 它们根据曲线长度插值计算得出 默认情况下 曲线以三维圆柱体截面进行渲染 这非常适用于近观的曲线 而对于那些只能远观的曲线 Metal 也支持平直曲线 当你不需要完全三维的几何体时 这样做可以提高性能
与三角形和包围盒相类似 曲线几何体也有 相应的曲线几何体描述符 其中的缓冲区包含控制点 相应半径和控制点索引缓冲区 可在控制点缓冲区中 设置控制点数量 还可设置实际曲线段的数量 这应与索引缓冲区中的 索引数量相同 指定你正在使用的曲线类型 本示例中使用圆形贝塞尔曲线 每个曲线段有 4 个控制点 这就是你需要对曲线几何体描述符 所做的全部设置
现在你已经设置了 你的几何体描述符 你可以设置加速结构描述符 对于三角形 包围盒和曲线等 图元几何体 使用图元加速结构描述符 将几何体描述符 添加到加速结构描述符中 可以将多个几何体描述符 添加到单个加速结构中 以合并几何体 当你的加速结构描述符已准备好时 你可为你的加速结构分配内存 Metal 允许你完全控制 内存的分配时间和位置
这个操作分为两个部分 首先计算构建所需的对象的大小 Metal 设备提供了一种方法 来计算加速结构所需的 内存分配大小 虽然可以直接从 Metal 设备 分配加速结构的存储空间 但从堆内存中分配它们 将使你能够在后续 减少资源管理的开销 堆内存可能会有额外的 大小和对齐要求 你可以使用 Metal 设备的 另一个方法进行查询 有了这些内存大小 你现在就可以分配内存 来存储加速结构 这个存储由一个 MTLAccelerationStructure 对象来表示 要分配其中的一个对象 对堆内存或 Metal 设备调用 makeAccelerationStructure 方法 并传递大小的参数值 你还需要分配一些临时内存 Metal 在构建加速结构时 将使用该内存 由于只有 GPU 需要访问此内存 你可以在 Metal 设备中 分配私有存储模式缓冲区来实现
现在 你已经准备好 要来实际构建加速结构了 安排构建操作 然后 Metal 将在 GPU 上 为你构建加速结构 你可以使用加速结构 命令编码器来实现
该编码器提供了几种方法 供你构建和修改加速结构 在本例中 调用构建方法 其中包含目标加速结构 描述符和临时缓冲区等 Metal 将为你的几何体 构建图元加速结构 你可以在随后的 GPU 命令中使用 刚才介绍的就是 如何使用图元加速结构 来呈现场景中的几何体 为了帮助你扩展到更大的场景 Metal 还支持实例加速结构 在单个的图元加速结构中 存储一个复杂 详细的场景 比如前面提到的莫阿娜岛的场景 将需要占用大量内存 但是在这个复杂的场景中 有成千上万棵树 数百万片叶子和其他物体 其中的结构有重复 可用来高效地渲染场景 场景中所有独特的对象 包括山脉 珊瑚和树木 都可以表示为各种图元加速结构 这些可以合并到一个表示整个场景的 实例加速结构中 一个图元加速结构中包含有几何体 一个实例加速结构 包含了对其他加速结构的引用 引用中会变换位置 大小和方向 以构成完整的场景 每个实例都有一个变换矩阵 用于在场景中 放置其所引用的加速结构 构建实例加速结构 与构建图元加速结构类似 首先你要创建一个描述符 这次不再是创建几何体 而是创建一个 包含每个实例信息的缓冲区 比如它所引用的加速结构 用于将其放置在场景中的变换矩阵 然后在 GPU 上构建加速结构 方式与构建图元加速结构相同
为了创建描述符 构造一个 MTLInstanceAccelerationStructureDescriptor 并设置它所包含的实例数 然后提供一个图元加速结构数组 这个数组可以被实例引用 然后指定实例缓冲区中 将包含哪种类型的实例描述符 Metal 有几种实例描述符 类型可供你选择 具体取决于你的用例 你将通过两个步骤 来配置加速结构中的实例
首先 分配一个缓冲区 来存储单实例数据 该缓冲区的大小取决于实例的数量 以及每个实例描述符的大小 但它的分配方式 与其他的 Metal 缓冲区都相同 分配完缓冲区后 将其分配给实例加速结构描述符
接下来 在实例缓冲区中填充 加速结构中 所有实例的详细信息 对于每个实例 创建一个描述符 并指定该实例所引用的加速结构 你将通过你在实例加速结构描述符 设置的数组索引来识别加速结构 每个实例还含有一个变换矩阵 可见性遮罩和其他属性 具体取决于 所使用的实例描述符类型
最后一步是构建实际的加速结构 这与图元加速结构的构建过程相同 进行构建之前的所有步骤 都可以在 CPU 上运行 但如果实例的数量过多 填充实例缓冲区的过程 可能会导致计算量过大 由于实例描述符 存储在普通的 Metal 缓冲区中 你可以从 GPU 填充这些描述符 来加速此步骤 这里适合使用 GPU 加速 前提是你在将工作交给 GPU 之前 就已知该加速结构 将包含多少个实例 但比如 你如果想剔除实例 需要在 CPU 上先剔除实例 以便在描述符上 设置最终的实例数量 今年新推出的功能 是你可以使用 新的间接实例加速结构描述符 在 GPU 上推动这一过程 通过使用该间接描述符 你可以在 GPU 上剔除实例 填充实例缓冲区 并设置最终的实例数量 基于 GPU 进行加速结构构建时 创建一个间接实例加速结构描述符 在描述符上设置最大实例数量 并设置从 GPU 向其写入 最终实例数量的缓冲区 然后简单地设置实例描述符缓冲区 你就可以开始在 GPU 上配置实例
你将在实例缓冲区中 使用不同类型的描述符 间接实例描述符 与直接实例描述符类似 不同之处在于你可以将 正在实例化的加速结构分配给 描述符 以进行识别 以上介绍了 构建实例加速结构的方法 到目前为止 我介绍了 两级实例化模型 在该模型中 莫阿娜岛场景中的森林 由成千上万个不同树木的实例组成 但深入看 树本身就包含一个树干和 一片叶子的多个相同副本 你可以基于这种结构 使用新的多级实例化功能 在多级实例化中 一个实例加速结构可以包含 图元加速结构 以及其他实例加速结构 比如在此场景中 一棵棕榈树可以表示为 一个实例加速结构 其中包含有树干和叶子的实例 而整个场景可以包含棕榈树的实例 莫阿娜岛的场景是多级实例化 强大功能的很好示例 使用两级实例化时 向场景中添加一种树 可能需要添加数百甚至数千个 树组成部分的副本 但是通过多级实例化 你可以添加一个复杂树的实例 该实例由其各部分的重复实例定义 这样 在整个莫阿娜岛场景中 就节省了数百万个实例 但多级实例化 不仅适用于制作渲染器 对于实时性 App 比如游戏 也很有价值 游戏也使用两级加速结构模式 通过游戏对象的实例构建游戏世界 但是 游戏与制作渲染器不同 制作渲染器使用深层次结构 来复用对象 而游戏会使用 游戏对象的长实例列表 游戏还会为其动态内容 重建每帧的实例加速结构 并且 过多的实例数量意味着 重建需要消耗大量 GPU 时间
但在游戏中 有很多静态内容 并不需要每帧更新 因此 你可以将游戏世界 分为静态和动态加速结构 将加速结构更新限制为 仅对有变化的内容进行 这意味着你只需要 重新构建动态内容 其数量通常远少于静态内容的数量 在将静态和动态内容分开时 还必须考虑平衡结构的层次深度 以及遍历光线带来的额外开销 在有加速结构构建 和光线追踪的单帧中 使用 3 级实例化 可减少构建的时间 这样对跟踪时间影响较小 从而在总体上减少帧时间 多级实例化是一种减少内存使用量 加快重建速度的强大工具 你还可以通过其他方式 优化 Metal 光线追踪 App 其中之一的方法是 进行“并发构建”
一个典型的 App 需要构建或更新 许多的加速结构 这些结构用于表示不同场景 和场景的不同部分 通过并发构建操作 你可以大大减少启动时间
如有可能 将多个构建操作 编码到同一个命令编码器 进行并发运行 从而实现批量构建 你需要尽可能多地进行并发构建 同时确保你的内存 足够支撑该工作量 还要记住 在完成加速结构构建后 临时缓冲区就不再被占用 所以 在完成一批加速结构构建后 在下一批构建过程中 可重复使用该临时缓冲区 有时 要减少 重建加速结构所需时间 最好的方法是完全避免重建 而这正是“改造”加速结构的 意义所在 当 Metal 构建一个加速结构时 它会将附近的图元归入 一个盒子层次结构中 如果图元发生移动 则这些盒子不再能准确地表示场景 需要更新加速结构 但是如果几何体只发生了轻微变化 则原有层次结构可能依然合理 Metal 可以改造现有的加速结构 以反映几何体中图元的新位置 无需从头开始 建立一个新的加速结构 比起从头开始重建加速结构 这样做的成本更低 改造操作和构建操作类似 也需要一个临时缓冲区 临时改造缓冲区的大小 与之前用于分配加速结构的 结构体相同 改造操作在 GPU 上运行 并使用加速结构命令编码器 进行编码 改造可原位进行 或在其他加速结构中进行
最后 压缩技术是 减少内存中加速结构大小的好方法 在初次构建加速结构时 Metal 无法确切知道所需的内存量 因此 只能进行保守估计 构建完加速结构后 Metal 可以计算出加速结构 所需的最小内存量 使用压缩技术 你可以最小的内存大小 分配新的加速结构 然后使用 GPU 复制当前的加速结构 到新的加速结构中 这对于图元加速结构尤其有价值 要使用压缩技术 先编码一个命令 来计算 GPU 上压缩后 加速结构的大小 当执行该命令时 Metal 会将压缩后的大小 写入你所提供的缓冲区中 当你读取到压缩后的大小 你就可以使用该大小 分配一个新的加速结构 然后编码一个“复制并压缩”操作 将旧的加速结构复制到新的结构中 完成此命令缓冲区的操作后 你可以释放原有的加速结构 要了解更多 优化 Metal 光线追踪 App 的详情 请查阅 2022 年的“最大限度提升 Metal 光线追踪性能”一讲 在本讲中 我介绍了如何设置实例化 如何使用新的多级实例化功能 以及如何处理大规模实例化等内容 现在 该介绍场景中的光线相交了 在 Metal 中 光线相交的 GPU 函数 是作为指令的一部份来执行的 在 Apple 芯片上 你可以 在计算和渲染命令中做光线相交 在 AMD 和 Intel 上 你可以在计算命令中相交光线 为相交光线做准备时 将你的加速结构与命令编码器绑定 现在 你可以使用 GPU 函数 为加速结构做光线相交 声明该函数 且函数应包括 加速结构参数 创建一个求交器对象 你可以对求交器对象进行属性设置 配置光线相交 以获得最佳性能 要将光线与你的场景相交 只需创建一个光线 并对求交器对象调用相交方法 将光线和加速结构作为参数传入 结果将返回相交的所有必要信息 比如与光线相交的图元类型 到交点的距离 图元 ID 等等
要获取三角形交点的相关更多信息 在求交器和 “intersection_result” 类型中 添加 “triangle_data” 标签 在返回的相交结果中 你就能获得 三角形的重心坐标值 以上涵盖了图元加速结构与 光线的内容 光线与实例加速结构的相交与之类似 用绑定图元加速结构相同的方式 来绑定实例加速结构 并确保调用 “useResource” 或 “useHeap” 以使实例加速结构中 引用的加速结构在 GPU 上可用 你只需要对 GPU 函数稍作修改 就可以使光线与一个实例加速结构相交 首先 在加速结构类型中 添加 “instancing” 标签 然后 在求交器 和 “intersection_result” 上 添加 “instancing” 标签 和 “max_levels” 标签 “max_levels” 标签用于指定 加速结构中的实例级别数 比如 表示莫阿娜岛场景的 加速结构 是一个三级加速结构 第一级是包含了整个场景的 实例加速结构 第二级包含珊瑚 树木和地形的实例 第三级包含树木的各部分实例 比如树叶 花朵和树干 当一条光线与该场景相交时 它不仅与单个图元相交 还与包含该图元的实例相交 如果一条光线与一片树叶相交 则也会与树的实例 和树中的树叶实例相交 Metal 通过记录每个相交实例的 ID 来帮你跟踪记录数据 在本例中 第一个相交的实例 是 ID 为 6 的树 第二个相交的实例 是 ID 为 1 的树叶 光线也可能只与一个实例相交 比如如果光线与地形相交 Metal 只会记录地形实例的 ID 你可以在相交结果中找到 相交的实例数和相交实例的 ID 以上介绍了图元加速结构 和实例加速结构与光线相交的方法 在使用曲线图元时 需要记住以下事项: 默认情况下 Metal 假定 你在进行光线相交时 不使用曲线图元 你可以对求交器对象 设置几何体类型 来告知 Metal 你正在使用曲线图元 设置了几何体类型后 你就可以相交曲线了 与之前相同 你可在相交结果中 查找相交信息 如你使用了 “curve_data” 标签 则相交结果中也将包含曲线参数 你可将该数值 插入曲线的基底函数中 计算曲线上与光相交的点 在 Metal 着色语言中 这些函数已实现 你可以在 “Metal 着色 语言规范” 中了解更多信息 在许多应用中 曲线几何体 通常只表示为一种类型的曲线 比如 你场景中的所有曲线 可能都表示为三次贝塞尔曲线 且有圆形横截面 在本例中 你可以 对求交器对象设置曲线属性 来告知 Metal 你的场景 使用了什么类型的曲线 这样 在使用曲线图元时 即可获得最佳性能 以上介绍了让光线与 你的场景相交的方法 你可以使用 Xcode 来调试和 分析光线追踪工作负载
在处理难以调试的问题时 你也可以使用 “着色器验证” 工具 它会在着色器中进行运行时检查 帮你捕捉可能导致崩溃或破坏的问题 “着色器验证”工具 现已包含 Metal API 中的所有内容 包括最新的光线追踪功能 此外 “着色器验证” 工具大大 降低了对着色器编译时间的影响 这在处理长而复杂的着色器时 比如光线追踪 App 中 常见的那些着色器 非常有帮助 另一个可以帮助你的工具 是先进的 “加速结构查看器” 在你进行相交测试时 它能让你检查场景相交情况 当打开“加速结构查看器”时 左侧会显示概览 便于你浏览 加速结构中的各个构件 及其细小的几何体图元 此处 列出了构成三角形几何体的 各个三角形 右侧有一个视口 我可以用各种突显模式 来检查加速结构 比如“轴对齐的包围盒遍历” 突显模式 可以将具有更深层次 遍历的区域进行可视化 这相当于为我们 节约了昂贵的相交测试费用 将指针移到场景上时 检查器会更新一个光线 在指定的方向上击中的交点数量 另一个例子是 “加速结构”突显模式 它用不同颜色来显示加速结构 “加速结构查看器” 支持新的多级实例化功能 和曲线几何体 当我在视口中移动摄影机时 可以找到一些树的实例加速结构 和一些植被的曲线 要识别一个加速结构 我可以在视口中点击它 概览中就会定位到该结构 现在 仔细观察这些棕榈叶的 加速结构 在这个加速结构中 棕榈叶由曲线组成 我可以将视口 切换到“图元”突显模式 来显示曲线段 为了更好地检查曲线段 我会稍微放大一些 与之前在场景中 选择加速结构相类似 可在此点击选择每个曲线段 在检查光线追踪工作负载时 你还可使用另一个有用的工具 就是“着色器调试器” 它可以帮你排除着色器代码中的问题 这里是一个分发计算用于在着色器中 执行相交测试 要开始调试着色器 我可以点击“着色器调试”按钮 在弹出框中选择一个线程 然后点击“调试”按钮
等待数据收集完成后 我可以在着色器执行过程中 检查任意点每个变量的值 仔细看一下图元 ID 的值 为了提供更多的调试信息 “着色器调试器”同时还提供了 来自相邻线程的数据 在此 可将指针悬停在数值视图上 检查来自相同线程组的图元 ID
性能是另一个 对于任何 App 而言重要的指标 “性能分析”时间轴中提供了 光线追踪工作负载性能的概览 使你能并行检查和关联 各种性能指标 此外 还可以更改“调试”导航器 以查看工作负载中所有管线状态 通过着色器性能分析数据 导航器会将最耗时的管线状态 列在前面 展开管线状态 可查看着色器代码 打开一个着色器 可看到每行着色器的性能分析结果 了解每个着色器会在何处 以及如何使用其执行时间 将指针移到侧边栏的饼图上时 会显示一个弹框 列出该行代码的更详细的 开销明细 以上这些工具 支持 Metal 所有新的光线追踪功能 可在你开发 Metal App 时 为你提供出色的 调试和性能分析辅助功能
Metal 光线追踪技术 还支持更多的功能 比如: 图元和实例运动: 可用于制作渲染器中的动画场景 自定义相交函数: 用于自定义增强版的光线相交 比如 alpha 测试 相交查询 便于从基于 查询的 API 中进行移植 Metal 光线追踪 API 语言和工具 支持实时渲染 App 比如游戏和制作渲染器 你可以使用最新的 Metal 光线追踪 API 使用图元加速结构 包括 曲线几何体来构建你的场景 实例化 特别是新的 多级实例化功能 让你可扩展到更大 更复杂的场景 你的 GPU 函数可直接调用 Metal 光线追踪 API 最后 Xcode 开发工具 可帮你调试和分析你的 App 请务必观看之前的光线追踪讲座 其中我们已经更详细地 介绍了许多这些主题内容 以及我们的示例代码和文档 感谢你的观看 ♪ ♪
-
-
3:06 - Create triangle geometry descriptor
// Create geometry descriptor: let geometryDescriptor = MTLAccelerationStructureTriangleGeometryDescriptor() geometryDescriptor.vertexBuffer = vertexBuffer geometryDescriptor.indexBuffer = indexBuffer geometryDescriptor.triangleCount = triangleCount
-
3:20 - Create bounding box geometry descriptor
// Create geometry descriptor: let geometryDescriptor = MTLAccelerationStructureBoundingBoxGeometryDescriptor() geometryDescriptor.boundingBoxBuffer = boundingBoxBuffer geometryDescriptor.boundingBoxCount = boundingBoxCount
-
6:42 - Create curve geometry descriptor
let geometryDescriptor = MTLAccelerationStructureCurveGeometryDescriptor() geometryDescriptor.controlPointBuffer = controlPointBuffer geometryDescriptor.radiusBuffer = radiusBuffer geometryDescriptor.indexBuffer = indexBuffer geometryDescriptor.controlPointCount = controlPointCount geometryDescriptor.segmentCount = segmentCount geometryDescriptor.curveType = .round geometryDescriptor.curveBasis = .bezier geometryDescriptor.segmentControlPointCount = 4
-
7:29 - Create primitive acceleration structure descriptor
// Create acceleration structure descriptor let accelerationStructureDescriptor = MTLPrimitiveAccelerationStructureDescriptor() // Add geometry descriptor to acceleration structure descriptor accelerationStructureDescriptor.geometryDescriptors = [ geometryDescriptor ]
-
8:08 - Query for acceleration size and alignment requirements
// Query for acceleration structure sizes let sizes: MTLAccelerationStructureSizes sizes = device.accelerationStructureSizes(descriptor: accelerationStructureDescriptor) // Query for size and alignment requirement in a heap let heapSize: MTLSizeAndAlign heapSize = device.heapAccelerationStructureSizeAndAlign(size: sizes.accelerationStructureSize)
-
8:39 - Allocate acceleration structure and scratch buffer
// Allocate acceleration structure from heap var accelerationStructure: MTLAccelerationStructure! accelerationStructure = heap.makeAccelerationStructure(size: heapSize.size) // Allocate scratch buffer let scratchBuffer = device.makeBuffer(length: sizes.buildScratchBufferSize, options: .storageModePrivate)!
-
8:40 - Encode the acceleration structure build
let commandEncoder = commandBuffer.makeAccelerationStructureCommandEncoder()! commandEncoder.build(accelerationStructure: accelerationStructure, descriptor: accelerationStructureDescriptor, scratchBuffer: scratchBuffer, scratchBufferOffset: 0) commandEncoder.endEncoding()
-
11:30 - Create instance acceleration structure descriptor
var instanceASDesc = MTLInstanceAccelerationStructureDescriptor() instanceASDesc.instanceCount = ... instanceASDesc.instancedAccelerationStructures = [ mountainAS, treeAS, ... ] instanceASDesc.instanceDescriptorType = .userID
-
12:07 - Allocate the instance descriptor buffer
let size = MemoryLayout<MTLAccelerationStructureUserIDInstanceDescriptor>.stride let instanceDescriptorBufferSize = size * instanceASDesc.instanceCount let instanceDescriptorBuffer = device.makeBuffer(length: instanceDescriptorBufferSize, options: .storageModeShared)! instanceASDesc.instanceDescriptorBuffer = instanceDescriptorBuffer
-
12:33 - Populate instance descriptors
var instanceDesc = MTLAccelerationStructureUserIDInstanceDescriptor() instanceDesc.accelerationStructureIndex = 0 // index into instancedAccelerationStructures instanceDesc.transformationMatrix = ... instanceDesc.mask = 0xFFFFFFFF
-
14:06 - Configure indirect instance acceleration structure descriptor
var instanceASDesc = MTLIndirectInstanceAccelerationStructureDescriptor() instanceASDesc.instanceDescriptorType = .indirect instanceASDesc.maxInstanceCount = ... instanceASDesc.instanceCountBuffer = ... instanceASDesc.instanceDescriptorBuffer = ...
-
14:29 - Populate indirect instance descriptor
device MTLIndirectAccelerationStructureInstanceDescriptor *instance_buffer = ...; // ... acceleration_structure<> as = ...; instance_buffer[i].accelerationStructureID = as; instance_buffer[i].transformationMatrix[0] = ...; instance_buffer[i].transformationMatrix[1] = ...; instance_buffer[i].transformationMatrix[2] = ...; instance_buffer[i].transformationMatrix[3] = ...; instance_buffer[i].mask = 0xFFFFFFFF;
-
19:22 - Update geometry using refitting
// Allocate scratch buffer let scratchBuffer = device.makeBuffer(length: sizes.refitScratchBufferSize, options: .storageModePrivate)! // Create command buffer/encoder ... // Refit acceleration structure commandEncoder.refit(sourceAccelerationStructure: accelerationStructure, descriptor: asDescriptor, destinationAccelerationStructure: accelerationStructure, scratchBuffer: scratchBuffer, scratchBufferOffset: 0)
-
20:24 - Use compaction to reclaim memory
// Use compaction to reclaim memory // Create command buffer/encoder ... sizeCommandEncoder.writeCompactedSize(accelerationStructure: accelerationStructure, buffer: sizeBuffer, offset: 0, sizeDataType: .ulong) // endEncoding(), commit command buffer and wait until completed ... // Allocate new acceleration structure using UInt64 from sizeBuffer ... compactCommandEncoder.copyAndCompact(sourceAccelerationStructure: accelerationStructure, destinationAccelerationStructure: compactedAccelerationStructure)
-
21:36 - Set acceleration structure on the command encoder
encoder.setAccelerationStructure(primitiveAccelerationStructure, bufferIndex:0)
-
21:48 - Intersect rays with primitive acceleration structure
// Intersect rays with a primitive acceleration structure [[kernel]] void trace_rays(acceleration_structure<> as, /* ... */) { intersector<> i; ray r(origin, direction); intersection_result<> result = i.intersect(r, as); if (result.type == intersection_type::triangle) { float distance = result.distance; // shade triangle... } }
-
22:24 - Use triangle_data tag to get triangle barycentric coordinates
// Intersect rays with a primitive acceleration structure [[kernel]] void trace_rays(acceleration_structure<> as, /* ... */) { intersector<triangle_data> i; ray r(origin, direction); intersection_result<triangle_data> result = i.intersect(r, as); if (result.type == intersection_type::triangle) { float distance = result.distance; float2 coords = result.triangle_barycentric_coord; // shade triangle... } }
-
22:51 - Set instance acceleration structure on the command encoder
encoder.setAccelerationStructure(instanceAccelerationStructure, bufferIndex:0) encoder.useHeap(accelerationStructureHeap);
-
23:07 - Intersect rays with instance acceleration structure
// Intersect rays with an instance acceleration structure [[kernel]] void trace_rays(acceleration_structure<instancing> as, /* ... */) { intersector<instancing, max_levels<3>> i; ray r(origin, direction); intersection_result<instancing, max_levels<3>> result = i.intersect(r, as); if (result.type == intersection_type::triangle) { float distance = result.distance; // shade triangle... } }
-
24:43 - Find intersected instance information in the intersection result
// Intersect rays with an instance acceleration structure [[kernel]] void trace_rays(acceleration_structure<instancing> as, /* ... */) { intersector<instancing, max_levels<3>> i; ray r(origin, direction); intersection_result<instancing, max_levels<3>> result = i.intersect(r, as); if (result.type == intersection_type::triangle) { float distance = result.distance; for (uint i = 0; i < result.instance_count; ++i) { uint id = result.instance_id[i]; // ... } // shade triangle... } }
-
25:02 - Intersect rays with curve primitives
// Intersect rays with curve primitives [[kernel]] void trace_rays(acceleration_structure<> as, /* ... */) { intersector<> i; i.assume_geometry_type(geometry_type::curve | geometry_type::triangle); ray r(origin, direction); intersection_result<> result = i.intersect(r, as); if (result.type == intersection_type::curve) { float distance = result.distance; // shade curve... } }
-
25:26 - Find curve parameter in the intersection result
// Intersect rays with curve primitives [[kernel]] void trace_rays(acceleration_structure<> as, /* ... */) { intersector<curve_data> i; i.assume_geometry_type(geometry_type::curve | geometry_type::triangle); ray r(origin, direction); intersection_result<curve_data> result = i.intersect(r, as); if (result.type == intersection_type::curve) { float distance = result.distance; float param = result.curve_parameter; // shade curve... } }
-
26:04 - Set geometry type on the intersector for better performance
// Intersect rays with curve primitives [[kernel]] void trace_rays(acceleration_structure<> as, /* ... */) { intersector<curve_data> i; i.assume_geometry_type(geometry_type::curve | geometry_type::triangle); i.assume_curve_type(curve_type::round); i.assume_curve_basis(curve_basis::bezier); i.assume_curve_control_point_count(3); ray r(origin, direction); intersection_result<curve_data> result = i.intersect(r, as); if (result.type == intersection_type::curve) { float distance = result.distance; float param = result.curve_parameter; // shade curve... } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。