优化Unity URP项目的方法:

  • 管理光照

    • 尽可能地烘焙你场景中的光照

      烘焙是将光照渲染为纹理,然后重新投影到物体上。这种做法能够提高性能,节省Draw call的开销,提高视觉质量,并且能够渲染间接光照,添加全局光照细节。

      使用这个方法需要将光源设置为Backed,然后将场景中的物体设置为Static,这样做能将他们包含在光照贴图的计算中。

      顺带一提,还可以通过选择物体中的Mesh Renderer来调整Lightmapping,可以通过减少远处或者较小物体的设置来减少烘焙时间。

      Window -> Rendering -> Lighting中进行烘焙

    • Ligth Probes - 光照探针

      光照探针能够在烘焙过程中对场景中的光照数据进行采样,能够将反射光信息绑定到非Static物体

    • Reflect Probes - 反射探针

      反射探针是将环境的一部分投影带附加的物体上的方法,能够生成更逼真的反射。

      默认使用Skybox上的反射,但是反射探针可以让反射更接近周围的环境,最后在光滑的物体表面使用反射探针。

      可以选择不同分辨率的反射探针,可以创建更高质量的反射。如果相机离物体比较远,那么可以降低分辨率来获得更好的性能。

  • 相机设置

    • 通过禁用相机上不需要的渲染器进程来优化相机,针对不同性能设置的项目比较有用。

      禁用后处理、阴影渲染、深度纹理等功能可以减小在低端设备上的性能开销

    • 使用遮挡剔除

      通过烘焙遮挡数据,Unity可以忽略场景中被相机视野遮挡的任何部分

      要启动烘焙遮挡数据,需要将物体设置为Occluder Static或者Occludee Static

      Window -> Rendering -> Occlusion Culling中进行烘焙

  • 渲染管道设置

    • URP管道能将项目部署到不同的目标平台和设备上,URP资产包括多个我们可以控制的设置。

      Edit -> Project Settings -> Quality中可以容纳不同的URP资源,可以创建不同的预设来控制不同设备的显示效果。

  • Frame Debugger

    • Frame Debugger可以逐步查看渲染过程,在这个过程中可以找出渲染开销最多的地方进行优化

    • 设置

      在URP渲染设置的Advance中,可以关闭Debug Level,开启会损失一些性能

  • Profiler

    • 可以识别需要很长时间才能完成的脚本

URP Post-Process

URP中无法使用Post Process插件,需要使用内置的Global Volume

URP上实现自定义的Post-Process可以通过自定义的RendererFeature,需要以下脚本:

  • VolumeComponent:用来控制参数
  • RendererFeature: 向URP渲染器添加额外的渲染Pass
  • RenderPass:用来渲染指定物体

Shader Code in URP

参考文献:

Unity URP使用的Shader Code从CG转向了HLSL,核心库也是由HLSL编写的。

  • 原本的CG语义块被替换成了以下语句:
1
2
3
HLSLINCLUDE

ENDHLSL

它用来包含需要引入的文件或者我们定义的变量和函数。

  • Pass中的语句块
1
2
3
HLSLPROGRAM

ENDHLSL
  • 在上面的语句内,有下面这样的语句块,它用来保证每个Pass中的这些变量都是相同的
1
2
3
CBUFFER_START(UnityPerMaterial)

CBUFFER_END
  • 采样纹理被替换成了以下语句
1
2
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
  • 顶点着色器可以写成下面两种形式
1
2
3
4
5
6
Varyings vert(Attribute v)
{
Varyings o;
o.vertex = TransformObjectToHClip(v.vertex.xyz);
o.texcoord = v.texcoord;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Varyings UnlitPassVertex(Attributes IN) {
Varyings OUT;
// alternatively, Varyings OUT = (Varyings)0;
// to initalise all struct inputs to 0.
// otherwise, every variable in the struct must be set

//OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
// Or :
VertexPositionInputs positionInputs = GetVertexPositionInputs(IN.positionOS.xyz);
OUT.positionCS = positionInputs.positionCS;
// which also contains .positionWS, .positionVS and .positionNDC (aka screen position)

// Pass through UV/TEXCOORD0 with texture tiling and offset (_BaseMap_ST) applied :
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);

// Pass through Vertex Colours :
OUT.color = IN.color;
return OUT;
}
  • 片元着色器的写法
1
2
3
4
5
half4 frag (Varyings  i) : SV_Target
{
half4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
return baseMap * _BaseColor * IN.color;
}
  • 除此之外,可以使用hlsl文件存储我们在一个pass中定义的变量和函数,需要将这部分的代码放到下面的宏里面。
1
2
3
4
5
6
7
8
#ifndef INCLUDED
#define INCLUDED

///
///code
///

#endif //INCLUDED

可见性问题

Unity中的内置文件很多都被标记为interal,我们无法直接使用其中的函数,下面是一个获取被标记为interal的函数或者数据的方法

https://www.youtube.com/watch?v=ZiHH_BvjoGk

  • 创建一个Assembly Definition Reference
  • 选择你需要应用的文件
  • 创建一个static类用来返回其中的方法

拓展阅读