【渲染流水线】[应用阶段]-[裁剪]以UnityURP为例
前情提要
【渲染流水线】主线索引-从数据到图像以UnityURP为例-CSDN博客
Unity URP中的裁剪过程涉及多个阶段和算法,尤其在应用阶段(CPU端)主要通过视锥体剔除实现,核心算法包括包围盒测试和空间分区优化。裁剪过程主要发生在引擎的C++层面,但URP在C#端提供了定制化扩展点,例如光照剔除的优化。以下详细分析裁剪过程。
(对渲染的探索是个持续不断完善的过程,记录这个过程将零散的内容整理起来,其中肯定会有理解偏差和问题,如果哪里有问题,欢迎在评论区探讨和指出)
一、应用阶段的裁剪算法
包围盒裁剪算法:
- 视锥体剔除使用轴对齐包围盒(AABB)测试对象是否在视锥体内,Unity引擎默认采用此算法快速筛选可见对象。引擎会计算一个模型的包围盒,包围盒信息存放在U3D的Mesh.bounds中。包围盒是长方体,称之为AABB盒。盒子有8个点定,有任意一个顶点在摄像机范围内(视锥体或正交范围内)就不会被剔除,否则直接剔除。例如,每个GameObject的包围盒与摄像机视锥体进行相交检测,剔除不可见物体以提高渲染效率。
其他算法:
- 空间分区算法:如zBinning和Tile-based划分,将场景划分为多个区块(Tile),用于光源剔除(例如forward+算法中的光照影响范围测试),这虽然不是直接几何裁剪,但优化了后续渲染阶段的负载。
- 层次结构加速:Unity引擎底层可能使用Bounding Volume Hierarchy(BVH)或四叉树加速包围盒测试,尤其在复杂场景中减少计算量,但这些实现细节未完全公开。
- 遮挡剔除算法(如软件光栅化或Early-Z技术)主要在光栅化阶段执行,非应用阶段焦点。
包围盒裁剪是应用阶段的核心,但结合空间分区可提升效率;其他如UI裁切(如RectMask2D或模板缓冲)属于特定情境的实现,非全局几何裁剪。
下面讨论包围盒裁剪算法的原理与实现
基础算法:AABB与视锥体相交测试
Unity URP在应用阶段(CPU端)的裁剪核心是轴对齐包围盒(AABB)与视锥体的相交检测。具体流程如下:
- AABB生成:每个GameObject的Renderer组件会自动计算其AABB,包含
center
和size
属性,定义物体在局部空间的边界。 - 视锥体平面方程:摄像机视锥体由6个平面(近、远、左、右、上、下)定义,每个平面通过法线向量和距离原点参数表示。Unity通过
GeometryUtility.CalculateFrustumPlanes
获取这些平面。 - 相交测试:对每个AABB的8个顶点,检查是否所有顶点均在任一视锥体平面的外侧。若存在至少一个顶点在所有平面内侧,则物体可见;否则被剔除。
优化策略:层次化与空间分区
为提高效率,Unity引擎采用以下优化:
- 层级包围盒(BVH):对复杂场景构建层次化包围盒树,优先测试父节点AABB,快速剔除整组不可见物体。
- 动态更新机制:仅对位置或旋转变化的物体重新计算AABB,静态物体通过预计算缓存结果。
URP的定制化扩展
URP在C#层通过以下方式增强裁剪:
- 光照剔除集成:结合Tile-based分块(如16x16像素区块),将光源AABB与区块边界比对,仅保留影响当前区块的光源。
- 动态分辨率适配:根据目标渲染分辨率动态调整视锥体参数,确保裁剪精度与性能平衡。
数学原理与代码实现
关键数学逻辑包括:
- 平面测试公式:对平面方程
Ax + By + Cz + D = 0
,顶点(x,y,z)
代入后若结果大于0则位于外侧。 - AABB顶点计算:通过
center ± size/2
生成8个顶点,转换到世界空间后测试。
示例代码片段(简化版):bool IsVisible(Bounds bounds, Plane[] frustumPlanes) {foreach (var plane in frustumPlanes) {if (plane.GetDistanceToPoint(bounds.ClosestPoint(plane.normal * -1)) < 0)return false; // 完全在平面外侧}return true; }
性能与局限性
- 优势:AABB计算简单,适合快速剔除;层级结构减少测试次数。
- 局限:旋转物体会导致AABB膨胀(需使用OBB优化,但URP默认未采用)。
二、裁剪过程的位置:C++引擎层与URP C#定制
主要位于C++引擎层:
- 视锥体剔除由Unity引擎核心C++代码处理,实现高效、低层的包围盒测试和视锥计算,确保平台兼容性和性能。URP作为上层框架,依赖此基础剔除机制。
URP C#端定制:
- URP在C#中扩展了裁剪相关逻辑,例如通过
ForwardsLights.cs
等脚本实现光照专用的剔除算法(如zBinning)。用户可通过URP源代码定制Tile划分规则或光源影响测试,但这些定制聚焦于优化光照而非几何裁剪本身。 - UI裁切组件(如Mask或ClipRect)完全在C#端实现,通过Shader参数控制裁剪区域,适用于UI渲染管线的特定需求。
裁剪过程以C++引擎层为主,URP的C#定制用于补充优化(如光源管理),而非取代核心几何剔除。
三、Unity URP裁剪过程详细分析
Unity URP的裁剪是多阶段流水线,应用阶段为核心起点:
视锥体剔除(应用程序阶段):
- Unity引擎在CPU端遍历场景对象,使用包围盒算法测试每个对象的AABB与摄像机视锥体的相交性。可见对象进入渲染队列,不可见对象被剔除。
- 优化机制:zBinning将视锥体深度(Z轴)划分为多个bin,结合Tile-based空间分区(XY平面),加速光源影响计算。例如,在URP的
PreSetup
方法中,为每个区块生成位图标记潜在受影响光源,减少冗余光照计算。
后续裁剪阶段(后面会讨论的阶段):
- 视口裁剪(几何阶段):在GPU端执行,裁剪超出视口的三角形图元,确保只渲染可见像素区域。
- 背面裁剪与遮挡剔除(光栅化阶段):GPU处理背面剔除(基于法线方向)和遮挡测试(如Early-Z技术),后者使用深度缓冲优化像素级渲染。
URP定制流程:
- 光照剔除:URP在C#中实现forward+算法,通过Tile和zBinning分块光源,仅计算影响当前区块的光源。例如,点光源和聚光灯的AABB与区块边界比较,标记影响位图。
- UI裁切:独立于几何裁剪,使用ClipRect坐标或模板缓冲实现像素级遮罩,适用于Canvas元素。
整个过程以视锥体剔除为起点,URP的光照优化增强性能,但核心裁剪依赖引擎层
接下来:【渲染流水线】[应用阶段]-[遮挡剔除]以UnityURP为例-CSDN博客
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)