热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析UnityURP/SRP渲染管线:匠心打造的全面指南

本文深入探讨了Unity中的URP、SRP和HDRP渲染管线,详细解析了它们之间的关系及各自的特点。首先介绍了SRP的基本概念及其在Unity渲染架构中的作用,随后重点阐述了URP和HDRP的设计理念与应用场景。文章还分析了SRP诞生的背景,解释了为何Unity需要引入这一灵活的渲染框架,以满足不同项目的需求。通过对比URP和HDRP,读者可以更好地理解如何选择合适的渲染管线,以优化项目的性能和视觉效果。

 

目录

SRP/URP/HDRP之间的关系

SRP是什么?

URP是什么?

HDRP是什么?

2. 为什么诞生SRP?

3. URP 和 内置渲染管线 性能对比

1. 首先来说说光照处理部分

2. 接下来看看SRP Batcher(重点)

SRP Batcher 是什么?

SRP Batcher是怎么优化的?

内置渲染管线和URP的CPU原理图对比:

SRP Batcher 兼容性

3. URP的增加功能

1.渲染管线扩展/自定义渲染器(待补充)

1. 第一人称对象

2. 卡通描边

3. 对象遮挡

4. 模糊清晰混合

2. 摄像机堆叠(待更新)

URP源码解析(待更新)

参考文章:

额外内容




SRP/URP/HDRP之间的关系

下图是各个管线的关系图

根据上图所示,URP是Unity可编程渲染管线(SRP)的一种,所以了解URP之前需要先了解SRP是什么。(在文章最下面,额外内容里附了张渲染管线流程图)


SRP是什么?

SRP全称为Scriptable Render Pipeline(可编程渲染管线/脚本化渲染管线),是Unity提供的新渲染系统,可以在Unity通过C#脚本调用一系列API配置和执行渲染命令的方式来实现渲染流程,SRP将这些命令传递给Unity底层图形体系结构,然后再将指令发送给图形API。

说白了就是我们可以用SRP的API来创建自定义的渲染管线,可用来调整渲染流程或修改或增加功能。

它主要把渲染管线拆分成二层:


  • 一层是比较底层的渲染API层,像OpenGL,D3D等相关的都封装起来。

  • 另一层是渲染管线上层,上层代码使用C#来编写。在C#这层不需要关注底层在不同平台上渲染API的差别,也不需要关注具体如何做一个Draw Call


URP是什么?

它的全称为Universal Render Pipeline(通用渲染管线), 它是Unity官方基于SRP提供的模板,它的前身是LWRP(Lightweight RP即轻量级渲染管线), 在2019.3开始改名为URP,它涵盖了范围广泛的不同平台,是针对跨平台开发而构建的,性能比内置管线要好,另外可以进行自定义,实现不同风格的渲染,通用渲染管线未来将成为在Unity中进行渲染的基础 。

平台范围:可以在Unity当前支持的任何平台上使用

 


HDRP是什么?

它的全称为High Definition Render Pipeline(高清晰度渲染管线),它也是Unity官方基于SRP提供的模板,它更多是针对高端设备,如游戏主机和高端台式机,它更关注于真实感图形和渲染,该管线仅于以下平台兼容:


  • Windows和Windows Store,带有DirectX 11或DirectX 12和Shader Model 5.0
  • 现代游戏机(Sony PS4和Microsoft Xbox One)
  • 使用金属图形的MacOS(最低版本10.13)
  • 具有Vulkan的Linux和Windows平台

在此文章对HDRP不过多描述。

 


2. 为什么诞生SRP?

内置渲染管线的缺陷


  • 定制性差:过去,Unity有一套内置渲染管线,渲染管线全部写在引擎的源码里。大家基本上不能改动,除非是买了Unity源码客户,当然大部分开发者是不会去改源码,所以过去的管线对开发者来说,很难进行定制。
  • 代码脓肿,效果效率无法做到最佳:内置渲染管线在一个渲染管线里面支持所有的二十多个平台,包括非常高端的PC平台,也包括非常低端的平台,很老的手机也要支持,所以代码越来越浓肿,很难做到使效率和效果做到最佳。

目的:


  • 为了解决仅有一个默认渲染管线,造成的可配置型、可发现性、灵活性等问题。决定在C++端保留一个非常小的渲染内核,让C#端可以通过API暴露出更多的选择性,也就是说,Unity会提供一系列的C# API以及内置渲染管线的C#实现;这样一来,一方面可以保证C++端的代码都能严格通过各种白盒测试,另一方面C#端代码就可以在实际项目中调整。

 

在看URP 和 内置渲染管线 性能对比之前最好先了解DrawCall,Batches,SetPassCalls分别是什么值。

DrawCall,Batches,SetPass calls文章:https://blog.csdn.net/qq_30259857/article/details/110062397

 


3. URP 和 内置渲染管线 性能对比

主要提速的有两个方面

1. 光照处理(包括阴影)

2. SRP Bacher (SRP 批处理)(重点)

其他可以看看官网图,下面是官网的对比表链接和图。

https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@8.2/manual/universalrp-builtin-feature-comparison.html

 


1. 首先来说说光照处理部分

如上图所示,老的渲染管线使用Multi-Pass的Forward Rendering,就是多Pass的正向渲染。最大的问题是如果要在场景里要加很多动态光的话,每一个动态光都有可能会增加一个Pass,这个动态光所影响的物体要多画一遍。

这就导致如果游戏里想要有多个动态光的话,可能这个场景会被画很多遍,性能会很差。它带来的问题是所有的游戏几乎都不会用多个动态光,因为实在太费性能了。

在过去制作移动的游戏的过程中,大家的标准做法都是烘焙Lightmap。

现在URP就解决了这个问题。实现了一个单PASS的正向渲染。可以支持多盏动态光,但是全部动态灯光都会放在一个Pass里渲染,这样带来的问题是要限制灯光的数量,因为每次Draw Call去画的时候,传给GPU的参数是有限的。

如果灯光数量特别多,参数太多,那就会无法在一次Draw Call里完成很多个灯光。所以我们有一些限制,在轻量级渲染管线LWRP里,目前是支持1盏平行光,每个对象可能只能接受4个动态光。每个摄像机也有一些限制,这是为了我们可以把所有的计算放在一个Pass里面。

接下来看看内置渲染管线URP各种情况下的光照处理实验对比

以下是分别在四种情况下对比所得出的结论


  1. 无光源。 (没区别)
  2. 一个平行光,无阴影。(没区别)
  3. 一个平行光,一个点光源,无阴影。
    结论:内置渲染管线跟只有一个平行光时比起来Batches将近增加了一倍,而URP的Batches和SetPass calls跟一个平行光时一样,一点都没有增加。
  4. 一个动态光,有阴影。
    结论:在阴影的处理方面URP性能比内置渲染管线好很多。
     

URP光照处理最终结论:


1. 性能上阴影处理方面比内置渲染管线好很多。

2. URP平行光基础上添加动态光没有带来额外的Batches和SetPass calls性能开销。


下面有图有真相

1. 无光源情况下的对比


内置渲染管线:

URP:

结论:Batches,SetPass Calls 基本是一样的,没区别。


2. 一个平行光,无阴影下的对比


内置渲染管线:

URP:

结论:Batches,SetPass Calls 基本是一样的,没区别。


3. 一个平行光,一个点光源,无阴影下的对比


内置渲染管线:

URP:

结论:可以发现内置渲染管线的Batches将近增加了一倍,而URP的Batches和SetPass calls跟一个平行光时一样,一点都没有增加。


4. 一个平行光,有阴影下的对比


内置渲染管线:

URP:

结论:内置渲染管线处理阴影面数增加了45k,Batches增加了759,URP面数增加了11K,Batches增加了188。处理阴影性能上URP好很多。



2. 接下来看看SRP Batcher(重点)


SRP Batcher 是什么?


官网解释:SRP Batcher 是一个底层渲染循环,可通过许多使用同一着色器变体的材质来加快场景中的 CPU 渲染速度。

个人解释:可以把它理解为渲染状态批处理就可以,它的主要目的是减少渲染状态设置的开销,也就是把使用同一着色器变体材质的物体批处理成一个渲染状态,还有就是把物体属性用专用代码快速更新。

上面解释都提到了变体,那么变体怎么理解呢?

看官网描述没太看懂,所以亲自试验了一下,一个SRP Batch 属于一个变体材质,因为SRP Batcher把一个变体材质的物体批处理成一快了。

根据大概试验首先是不同Shader就是不同的变体,如果一样的Shader但图中的参数不一样,也会产生变体,假设有两个物体,用的Shader都是"Lit",第一个物体的SurfaceType属性为 Opaque, 而第二个物体SurfaceType设置为Transparent时,就有两个变体材质。如果图中的参数一样那就是一个变体材质,SRP Batch就是根据变体材质来批处理成一个SRP Batch。

主要参考来自以下文章:

https://docs.unity.cn/cn/2019.3/Manual/SRPBatcher.html

 https://connect.unity.com/p/srp-batcher-jia-su-xuan-ran


过去,Unity 中,可以在一帧内的任何时间修改任何材质的属性。但是,这种做法有一些缺点。当Draw Calls使用新材质时,需要进行很多处理。场景内的材质越多,设置GPU数据所需的CPU资源就越多。解决此问题的传统方法是减少 DrawCall 的数量以优化 CPU 渲染成本,因为 Unity 在发出 DrawCall 之前必须进行很多设置。实际的 CPU 成本来自该设置,而不是来自 GPU DrawCall 本身(DrawCall 只是 Unity 需要推送到 GPU 命令缓冲区的少量字节)。

这是官网说的提速效果:


Unity 2018引入了可编程渲染管线SRP,其中包含新的底层渲染循环SRP Batcher批处理器,它可以大幅提高CPU在渲染时的处理速度,根据场景内容的不同,提升效果为原来的1.2~4倍不等。



SRP Batcher是怎么优化的?


SRP Batcher使材质数据持久保留在 GPU 内存中。如果材质内容不变,SRP Batcher 不需要设置缓冲区并将缓冲区上传到 GPU。还有 SRP Batcher 会使用专用的代码路径来快速更新大型 GPU 缓冲区中的 Unity 引擎属性,如下图。

上面的功能能解决什么问题呢?也就是CPU不需要再设置渲染状态和一大堆渲染数据设置,只需要物体跟缓冲区的数据绑定就可以了。

SRP Batcher 正是通过批处理一系列 Bind 和 Draw GPU 命令来减少 DrawCall 之间的 GPU 设置。



内置渲染管线和URP的CPU原理图对比:


内置渲染管线:(红框部分就是SRP Batcher优化的性能部分)

 

URP:

在把材质数据和物体数据上传好后的流程图:(GPU没有详细画,主要看CPU)

 

上面流程图中绑定的意思是大家都知道Shader里有很多变量,如纹理贴图,Property定义的变量以及内置变量等,个人理解是把缓冲区里存的渲染数据设置给了Shader变量。

 

再看看OpenGL API调用情况

内置渲染管线

URP

可以发现内置渲染管线有 glUniform4fv API,这是设置一大堆着色器数据的函数,也是耗时的部分,而在URP取而代之的是Bind接口,省去了设置着色器数据的开销。


 

根据上面内容我们可以知道SRP Batcher并没有减少DrawCall,而是优化了DrawCall之前的设置开销。

 

SRP Batch值我们可以在Frame Debug窗口可以看得到。

Statistics窗口上的SetPass calls值,其实就是SRP Batch数量 加上 未能批处理的DrawCall数量。

在下图中可以看到SRP Batch批处理了 189个 DrawCalls.

 


SRP Batcher 兼容性

为了使 SRP Batcher 代码路径能够渲染对象:


  • 渲染的对象必须是mesh或者skinned mesh。该对象不能是粒子。
  • 着色器必须与 SRP Batcher 兼容。HDRP 和 URP 中的所有光照和无光照着色器均符合此要求(这些着色器的“粒子”版本除外)。

为了使着色器与 SRP Batcher 兼容:


  • 必须在一个名为“UnityPerDraw”的 CBUFFER 中声明所有内置引擎属性。例如:unity_ObjectToWorld 或 unity_SHAr
  • 必须在一个名为 UnityPerMaterial 的 CBUFFER 中声明所有材质属性。

Property定义的属性也是属于PerMaterial.

 

可以在 Inspector 面板中查看着色器的兼容性状态。

可以在 Inspector 面板中检查特定着色器的兼容性。

可以在 Inspector 面板中检查特定着色器的兼容性。

 

在看看时间对比

内置渲染管线(未开启任何合批)

内置渲染管线(开启静态批处理)

URP未开启SRP Batcfh

URP环境下(开启SRP Batcher)

 

 


3. URP的增加功能

主要有以下功能

1. 渲染管线扩展/自定义渲染器

2. 摄像机堆叠 (待更新)

3. Shader Graphic(Shader图形编程)(待更新)

 


1.渲染管线扩展/自定义渲染器(待补充)

官网案例Github地址:https://github.com/Unity-Technologies/UniversalRenderingExamples

以下是官网案例效果(最后一个是自己实现)


1. 第一人称对象

此Demo主要演示的是摄像机堆叠功能,摄像机堆叠功能下面会单独专门讲。


2. 卡通描边

展示一种创建卡通样式轮廓效果的设置,示例中有两种方法,一种是后处理方法,另一种是船体网格方法。一个示例使用自定义RendererFeature,并且都使用自定义着色器

此Demo有两个渲染器

我们来看看第二个渲染器

主要的卡通描边效果就是在上图中的 Sobel Outlines (Blit)实现,


3. 对象遮挡

当一个对象被另一个对象遮挡时,可以创建效果。

如下图所示,一个角色被建筑挡住时用指定的材质效果来显示角色被遮挡的部分。


 

以下是自己实现的扩展功能


4. 模糊清晰混合

此效果是在模糊的基础上把某层的物体清晰显示的效果。

 


2. 摄像机堆叠(待更新)

 

 


URP源码解析(待更新)

详解属性:

渲染器列表

HDR(High Dynamic Range Imaging):高动态范围成像,

Anti Aliasing(MSAA):抗锯齿

 


参考文章:

Unity轻量级渲染管线LWRP源码及案例解析 - 上:https://connect.unity.com/p/unityqing-liang-ji-xuan-ran-guan-xian-lwrpyuan-ma-ji-an-li-jie-xi-shang

Unity轻量级渲染管线LWRP源码及案例解析 - 下:https://connect.unity.com/p/unityqing-liang-ji-xuan-ran-guan-xian-lwrpyuan-ma-ji-an-li-jie-xi-xia

SRP Batcher:加速渲染:https://connect.unity.com/p/srp-batcher-jia-su-xuan-ran

URP扩展案例 Github地址: https://github.com/Unity-Technologies/UniversalRenderingExamples

Unite Now - (中文字幕)使用URP提升游戏画面效果:https://www.bilibili.com/video/BV1fK4y1a78s

关于静态批处理/动态批处理/GPU Instancing /SRP Batcher的详细剖析:https://zhuanlan.zhihu.com/p/165574008

关于静态批处理/动态批处理/GPU Instancing /SRP Batcher的详细剖析:https://zhuanlan.zhihu.com/p/98642798

Batch, Draw Call, Setpass Call:https://zhuanlan.zhihu.com/p/76562300

Unity3D优化技巧系列(一):Draw Call优化:https://gameinstitute.qq.com/community/detail/113025

Unity ConstantBuffer的一些解析和注意:https://zhuanlan.zhihu.com/p/137455866

 


额外内容

渲染流水线图:

 


推荐阅读
  • 我有一个SpringRestController,它处理API调用的版本1。继承在SpringRestControllerpackagerest.v1;RestCon ... [详细]
  • 本文介绍了如何在 Node.js 中使用 `setDefaultEncoding` 方法为可写流设置默认编码,并提供了详细的语法说明和示例代码。 ... [详细]
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
  • Python自动化测试入门:Selenium环境搭建
    本文详细介绍如何在Python环境中安装和配置Selenium,包括开发工具PyCharm的安装、Python环境的设置以及Selenium包的安装方法。此外,还提供了编写和运行第一个自动化测试脚本的步骤。 ... [详细]
  • 本文探讨了如何利用HTML5和JavaScript在浏览器中进行本地文件的读取和写入操作,并介绍了获取本地文件路径的方法。HTML5提供了一系列API,使得这些操作变得更加简便和安全。 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 解决TensorFlow CPU版本安装中的依赖问题
    本文记录了在安装CPU版本的TensorFlow过程中遇到的依赖问题及解决方案,特别是numpy版本不匹配和动态链接库(DLL)错误。通过详细的步骤说明和专业建议,帮助读者顺利安装并使用TensorFlow。 ... [详细]
  • 本文介绍如何从字符串中移除大写、小写、特殊、数字和非数字字符,并提供了多种编程语言的实现示例。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • 深入解析ESFramework中的AgileTcp组件
    本文详细介绍了ESFramework框架中AgileTcp组件的设计与实现。AgileTcp是ESFramework提供的ITcp接口的高效实现,旨在优化TCP通信的性能和结构清晰度。 ... [详细]
  • 优化Flask应用的并发处理:解决Mysql连接过多问题
    本文探讨了在Flask应用中通过优化后端架构来应对高并发请求,特别是针对Mysql 'too many connections' 错误的解决方案。我们将介绍如何利用Redis缓存、Gunicorn多进程和Celery异步任务队列来提升系统的性能和稳定性。 ... [详细]
  • 本文将详细介绍如何在没有显示器的情况下,使用Raspberry Pi Imager为树莓派4B安装操作系统,并进行基本配置,包括设置SSH、WiFi连接以及更新软件源。 ... [详细]
  • 远程过程调用(RPC)是一种允许客户端通过网络请求服务器执行特定功能的技术。它简化了分布式系统的交互,使开发者可以像调用本地函数一样调用远程服务,并获得返回结果。本文将深入探讨RPC的工作原理、发展历程及其在现代技术中的应用。 ... [详细]
  • 本文详细探讨了Java中的ClassLoader类加载器的工作原理,包括其如何将class文件加载至JVM中,以及JVM启动时的动态加载策略。文章还介绍了JVM内置的三种类加载器及其工作方式,并解释了类加载器的继承关系和双亲委托机制。 ... [详细]
  • 本文探讨了如何通过一系列技术手段提升Spring Boot项目的并发处理能力,解决生产环境中因慢请求导致的系统性能下降问题。 ... [详细]
author-avatar
哲亚Zoe
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有