【渲染流水线】[应用阶段]-[裁剪]以UnityURP为例
【从UnityURP开始探索游戏渲染】专栏-直达
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默认未采用)。
- 基础算法:AABB与视锥体相交测试
二、裁剪过程的位置:C++引擎层与URP C#定制
- 主要位于C++引擎层:视锥体剔除由Unity引擎核心C++代码处理,实现高效、低层的包围盒测试和视锥计算,确保平台兼容性和性能。URP作为上层框架,依赖此基础剔除机制。
- URP C#端定制:
- URP在C#中扩展了裁剪相关逻辑,例如通过
ForwardsLights.cs等脚本实现光照专用的剔除算法(如zBinning)。用户可通过URP源代码定制Tile划分规则或光源影响测试,但这些定制聚焦于优化光照而非几何裁剪本身。 - UI裁切组件(如Mask或ClipRect)完全在C#端实现,通过Shader参数控制裁剪区域,适用于UI渲染管线的特定需求。
- URP在C#中扩展了裁剪相关逻辑,例如通过
裁剪过程以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开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,)
【渲染流水线】[应用阶段]-[裁剪]以UnityURP为例的更多相关文章
- Shader 入门笔记(二) CPU和GPU之间的通信,渲染流水线
渲染流水线 1)应用阶段(CPU处理) 首先,准备好场景数据(摄像机位置,视锥体,模型和光源等) 接着,做粗粒度剔除工作. 最后,设置好每个模型的渲染状态(使用的材质,纹理,shader等) 这一阶段 ...
- 《UnityShader入门精要》学习笔记之渲染流水线
第一种分类方式: 图形管道(如下7步): 顶点数据 : 由3D模型传递的三角形网格 顶点着色 : 编写CG程序对各个顶点进行着色 生成几何图元 : 连接特定的顶点生成几何图元,例如连接三个顶点生成一个 ...
- Unity Shader 之 渲染流水线
Unity Shader 之渲染流水线 什么是渲染流水线 一个渲染流程分成3个步骤: 应用阶段(Application stage) 几何阶段(Geometry stage) 光栅化阶段(Raster ...
- Unity Shader入门精要学习笔记 - 第2章 渲染流水线
来源作者:candycat http://blog.csdn.net/candycat1992/article/ 2.1 综述 渲染流水线的最终目的在于生成或者说是渲染一张二维纹理,即我们在电脑屏 ...
- Unity 渲染流水线 :CPU与GPU合作创造的艺术wfd
前言 对于Unity渲染流程的理解可以帮助我们更好对Unity场景进行性能消耗的分析,进而更好的提升场景渲染的效率,最后提升游戏整体的性能表现 Unity的游戏画面的最终的呈现是由CPU与GPU相互配 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第五章:渲染流水线
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第五章:渲染流水线 学习目标 了解几个用以表达真实场景的标志和2D图像 ...
- 移动端 像素渲染流水线与GPU Hack
什么是 像素渲染流水线 web页面你所写的页面代码是如何被转换成屏幕上显示的像素的.这个转换过程可以归纳为这样的一个流水线,包含五个关键步骤: 1.JavaScript:一般来说,我们会使用JavaS ...
- GPU渲染流水线的简单概括
GPU流水线 主要分为两个阶段:几何阶段和光栅化阶段 几何阶段 顶点着色器 --> 曲面细分着色器(可选)----->几何着色器(可选)----->裁剪-->屏幕 ...
- OpenGL渲染流水线
其实OpenGL的流水线,对我学习来说只能算是一个概念性的东西.毕竟OpenGL也在发展,流水线也不会是一成不变的. 不过理解流水线的过程,重点在于理解每一步的作用,进而可以如何衔接起来,完成整个绘制 ...
- 大数据学习day16------第三阶段-----scala04--------1. 模式匹配和样例类 2 Akka通信框架
1. 模式匹配和样例类 Scala有一个十分强大的模式匹配机制,可以应用到很多场合:如switch语句.类型检查等.并且Scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配 1.1 模式匹 ...
随机推荐
- Docker拉取镜像时的疑惑
Docker拉取镜像时的疑惑 不知道你在拉取镜像的时候会不会有这样的疑惑--我不是只拉取了一个镜像嘛,为什么会出现这么多的东西 Docker中Layer(层)的概念 在 Docker 中,镜像(Ima ...
- prometheus 日常配置记录
通用模糊匹配 irate(node_network_transmit_bytes_total{device!~"lo|bond[0-9]|cbr[0-9]|veth.*"}[5m] ...
- centos7.5安装mariadb
一.安装mysql 这里我们基础系统是centos7.5 [root@monitor ~]# yum install -y mariadb-server [root@monitor ~]# syste ...
- Golang基础笔记一之变量声明和类型介绍
本文首发于公众号:Hunter后端 原文链接:Golang基础笔记一之变量声明和类型介绍 这一篇笔记主要介绍 Golang 的基础内容,包括 Golang 的运行,变量声明以及 Golang 里的各种 ...
- C#学习日志
C#入门篇 EanoJiang/CSharp-: C#入门教程,自用 程序思维题: 两根不均匀的香,烧完一根是1h,怎么用来计时15min呢? 思路:一根香从两头同时点燃烧完是30min,只需再对半即 ...
- MongoDB入门实战教程:学习总结目录
2021年Edison总结了MongoDB的入门实战教程,2022年整理了一份目录索引,希望对你有帮助. 1 MongoDB学习路径 在去年学习<MongoDB高手课>的途中,我总结了一个 ...
- C# 关于 &符号字符串转对象
string respDataJson = "A=&B=2&C=asdfasdf"; string respDataJson = "A=&B=2& ...
- 用commitlint规范团队的git提交信息
前言在一个团队中,每个人的git的commit信息都不一样,五花八门,没有一个机制很难保证规范化,如何才能规范化呢?可能你想到的是git的hook机制,去写shell脚本去实现.这当然可以,其实Jav ...
- 前端开发系列122-进阶篇之Floating point addition
本文简单说明 JavaScript 中常见的进制转换函数以及浮点数计算的注意点. 如何把任意进制的数据转换为十进制? 假设我们有二进制数据110,如果要把该数据转换为十进制数据可以参考下面的处理过程. ...
- 一个遵循 Material Design 设计规范、开源免费的 Blazor 组件库
前言 今天大姚给大家分享一个遵循 Material Design 设计规范.开源免费(MIT License)的 Blazor 组件库:MASA Blazor. Blazor 介绍 Blazor 是基 ...