【渲染流水线】[应用阶段]-[定制裁剪]以UnityURP为例
除了常见的包围盒裁剪(Frustum Culling)和遮挡剔除(Occlusion Culling),还存在以下裁剪算法及其应用场景:
【从UnityURP开始探索游戏渲染】专栏-直达
1. 层级剔除(Layer Culling)
原理:基于Unity的Layer层级系统,动态禁用特定层级的渲染。例如:
csharp
Camera.main.cullingMask &= ~(1 << LayerMask.NameToLayer("UI"));// 剔除UI层
业务逻辑:用于场景切换时隐藏非活动层(如隐藏后台场景的NPC层)。
2. 距离剔除(Distance Culling)
原理:根据物体与摄像机的距离动态关闭渲染。URP中可通过
LOD Group组件实现:csharp
LODGroup group = GetComponent<LODGroup>();
group.SetLODs(new LOD[] { new LOD(0.5f, renderers) });// 50%视距时切换LOD
业务逻辑:优化开放世界地形渲染,远处物体使用低模或代理网格。
3. 视口裁剪(Viewport Culling)
原理:仅渲染摄像机视口矩形内的内容。通过
Camera.rect控制:csharp
camera.rect = new Rect(0, 0, 0.5f, 1);// 仅渲染左半屏
业务逻辑:分屏游戏或多画中画场景中减少冗余渲染。
自定义裁剪逻辑实现
1. 基于Shader的裁剪
实现:在顶点着色器中手动丢弃片元:
hlsl
if (worldPos.y < _CutoffHeight) discard; // 自定义高度裁剪
应用:动态地形破坏效果中隐藏地下部分。
2. 脚本驱动的动态剔除
示例:结合业务逻辑的裁剪系统:
csharp
void Update() {
Renderer renderer = GetComponent<Renderer>();
renderer.enabled = CheckBusinessLogic();// 自定义条件判断
}
案例:剧情触发时才显示特定物体(如任务道具)。
3. 混合剔除策略
组合方案:在URP的
RenderObjects特性中叠加多重条件:csharp
RenderObjects renderFeature = scriptableRenderer.GetFeature<RenderObjects>();
renderFeature.settings.filterSettings.LayerMask = customMask;// 混合层级+距离
性能优化建议
数据准备:将裁剪数据预加载至显存(如通过GraphicsBuffer)减少CPU-GPU传输。
GraphicsBuffer 是 Unity 中用于直接操作显存数据的底层 API,通过结构化缓冲区高效存储 GPU 可访问的数据(如裁剪信息、动态网格数据等)
使用
GraphicsBuffer预加载裁剪数据到显存的核心步骤示例1. 创建GraphicsBuffer
csharp
// 定义裁剪数据(如包围盒的8个顶点)
Vector3[] boundsVertices = CalculateBoundsVertices();// 自定义计算逻辑// 创建GraphicsBuffer(显存缓冲区)
GraphicsBuffer gpuBuffer = new GraphicsBuffer(
GraphicsBuffer.Target.Structured,// 缓冲类型
boundsVertices.Length,// 元素数量sizeof(float) * 3// 每个元素大小(Vector3=3*float)
);
2. 上传数据到显存
csharp
// 将CPU数据上传至GPU显存
gpuBuffer.SetData(boundsVertices); // 绑定到Shader(通过全局变量或MaterialPropertyBlock)
Shader.SetGlobalBuffer("_BoundsBuffer", gpuBuffer);
3. Shader中读取裁剪数据
hlsl
StructuredBuffer<float3> _BoundsBuffer; // 接收显存数据 // 在顶点着色器中判断裁剪
v2f vert (appdata v) {
if (ShouldCull(_BoundsBuffer, v.vertex)) // 自定义裁剪逻辑
clip(-1); // 丢弃片元
// ...正常渲染逻辑
}
4. 释放资源(关键!)
csharp
void OnDestroy() {
gpuBuffer?.Release();// 必须手动释放显存
}
优化技巧
批量处理:合并多个物体的裁剪数据到同一Buffer,减少API调用:
csharp
GraphicsBuffer combinedBuffer = new GraphicsBuffer(...);
combinedBuffer.SetData(CombineAllBounds(objects));
动态更新:仅当数据变化时重新上传:
csharp
if (boundsChanged) {
gpuBuffer.SetData(newData);
}
ComputeShader加速:复杂裁剪逻辑可移至ComputeShader:
hlsl
// ComputeShader内并行处理裁剪
[numthreads(64,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID) {
if (_BoundsBuffer[id.x].y < _CutoffHeight) {
_VisibilityBuffer[id.x] = 0; // 标记不可见
}
}
性能对比
| 方法 | CPU-GPU传输量 | 显存占用 | 适用场景 |
|---|---|---|---|
| 传统每帧SetData | 高 | 低 | 数据频繁变化 |
| GraphicsBuffer预加载 | 低 | 中 | 静态/半静态裁剪数据 |
| ComputeShader | 极低 | 高 | 大规模动态裁剪 |
调试建议
- 使用
RenderDoc验证显存数据是否正确上传 - 通过
Profiler监控Graphics.Blit和SetData的调用开销 - 在URP的
Frame Debugger中检查裁剪结果 - 调试工具:使用
Frame Debugger验证裁剪效果,避免过度剔除。
以上方案可根据项目需求在URP的Forward Renderer Asset中配置或通过C#脚本扩展
【从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也在发展,流水线也不会是一成不变的. 不过理解流水线的过程,重点在于理解每一步的作用,进而可以如何衔接起来,完成整个绘制 ...
- UML和模式应用4:初始阶段(4)--需求制品之用例模型模板示例
1. 前言 UP开发包括四个阶段:初始阶段.细化阶段.构建阶段.移交阶段: UP每个阶段包括 业务建模.需求.设计等科目: 其中需求科目对应的需求制品包括:设想.业务规则.用例模型.补充性规格说明.词 ...
随机推荐
- springboot添加ssl实现https访问(http自动跳转)
目录 Step1 Step2 Step3 Step4 Step5 Tips: 直接docker部署的时候端口号问题 Step1 去搞一个证书,我的是腾讯买域名送的 Step2 下载jks格式的 Ste ...
- Java学习篇(二)—— C++和Java的区别之程序内存分布
上一篇介绍了C++和Java编译的区别和Java独有的网络编程,线程管理.这一篇主要介绍一下两者在程序运行时的内存空间. 内存分布 项目 C++ 程序 Java 程序(使用 JVM) 编译结果 直接生 ...
- sql注入绕过某waf
简单布尔判断 直接输入and 1=1拦截 使用mysql黑魔法 and{a 1=1} and{a 1=2}不拦截 本地mysql测试语句正常执行 简单延时判断 and sleep(1) 简单测试后在( ...
- FastAPI如何玩转安全防护,让黑客望而却步?
扫描二维码 关注或者微信搜一搜:编程智域 前端至全栈交流与成长 发现1000+提升效率与开发的AI工具和实用程序:https://tools.cmdragon.cn/ 1. FastAPI安全基础架构 ...
- Window 下bat管理员身份运行文件
@echo off%1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe&qu ...
- C# 组合键 判断,文本框不接受纯回车enter
//TextBox 必须是多行文本框 private void Txt_Billcode_KeyDown(object sender, KeyEventArgs e) { if (e.Control ...
- java -- 监听器、国际化
监听器 监听器: 主要是用来监听特定对象的创建或销毁.属性的变化的! 是一个实现特定接口的普通java类! Servlet中哪些对象需要监听? request / session / servletC ...
- mybatis-plus单表操作
查询统计 QueryWrapper queryWrapper = new QueryWrapper<>().groupBy("type_id").select(&quo ...
- 前端开发系列029-基础篇之Canvas绘图(压缩)
写这篇文章的原因是因为今天早上的时候,突然遇到个需求需要等比例调整照片的大小(主要是想把图片等比例的缩小),我在Mac上通过图片处理软件捣鼓的时候发现比较麻烦,就随手百度了一个在线修改图片尺寸的网站, ...
- PyCharm新建项目中无法调用Anaconda3中的包
最近为了做一个新的Python项目,就在PyCharm上新建了一个项目,结果发现以前引入的包竟然在新项目中报错,提示没有发现这个包,因为我用的是Anaconda3来管理我的第三方包,所以就首先查看了A ...