Unity SRP 管线【第七讲:URP LOD实现以及Reflections反射探针】

目录

  • [一、URP LOD 组件](#一、URP LOD 组件)
    • [1、LOD Group的使用](#1、LOD Group的使用)
    • 2、LOD切换原理
      • [Cross Fade(淡入淡出)模式](#Cross Fade(淡入淡出)模式)
        • [Animated Cross-Fading](#Animated Cross-Fading)
        • [如果未设置Clip,并且Fade Transition Width不为0](#如果未设置Clip,并且Fade Transition Width不为0)
        • LOD物体烘培
      • [SpeedTree 模式](#SpeedTree 模式)
  • 二、反射探针
    • [1. 获取反射探针数据](#1. 获取反射探针数据)
    • [2. 环境光照明 IBL](#2. 环境光照明 IBL)
    • [3. 反射探针(Reflection Probes)](#3. 反射探针(Reflection Probes))
    • [4. Box Projection 盒体投影](#4. Box Projection 盒体投影)

中文版:https://edu.uwa4d.com/lesson-detail/282/1314/0?isPreview=0

英文原版:https://catlikecoding.com/unity/tutorials/custom-srp/lod-and-reflections/

一、URP LOD 组件

1、LOD Group的使用

1. 首先该组件需要将子类模型置于该组件物体子节点下

2. 可在单个LOD中设置其level的模型,并可设置它的距离范围,即可在不同距离下显示不同的模型

若模型变化时间不符合预期,有如下几种方式调节:

  1. 重新继续Bound,Unity会根据三种LOD模型计算该模型的包围盒,从而在计算物体占屏幕
    2. Project Settings > Quality > Level of Detail > LOD Bias, 该变量增大物体的评估高度,从而导致LOD切换时占比与实践物体占比不同。将 LOD Bias设为1,可以使组件阈值与实际大小同步。
  2. LOD过渡类型:
    • Cross Fade(淡入淡出),Fade Transition Width可调节过渡区域占比

2、LOD切换原理

Cross Fade(淡入淡出)模式

当启用Cross Fade(淡入淡出)模式,相邻两个LOD对象会同时渲染出来,着色器将以某种方式进行混合。Unity通常使用屏幕抖动或者混合来实现Cross Fade。

在URP通用管线中,LOD现只用于SpeedTree7XXX.shader,即大面积树木的渲染,其余Shader并未使用,但并不代表不可以自定义。

我们可以通过UnityPerDraw下的变量float4 unity_LODFade; 取得LOD信息

cpp 复制代码
CBUFFER_START(UnityPerDraw)
	float4 unity_LODFade; 
	// x is the fade value ranging within [0,1]. 
	// y is x quantized into 16 levels
CBUFFER_END
  • x分量存储过渡因子(逐渐远离消失的LOD对象,x分量从1变换到0;渐入的LOD对象,x分量从0变换到 -1
  • y分量存储了相同的因子,只不过被量化为16步

即,若渐出LOD的x值为0.4,则渐入LOD的值为 -0.6

我们可以通过一个Noise图来决定使用哪一级LOD的选择。

通过如下内置函数,即可快速得到一个Noise图

float dither = InterleavedGradientNoise(positionCS.xy, 0);

使用屏幕抖动实现LOD混合

cpp 复制代码
void ShadowCasterPassFragment (Varyings input) {
	UNITY_SETUP_INSTANCE_ID(input);
	ClipLOD(input.positionCS.xy, unity_LODFade.x);

	...
}

void ClipLOD (float2 positionCS, float fade) {
	#if defined(LOD_FADE_CROSSFADE)
		float dither = InterleavedGradientNoise(positionCS.xy, 0);
		clip(fade + (fade < 0.0 ? dither : -dither));
	#endif
}
Animated Cross-Fading

启用Animated Cross-Fading后,不再通过距离去设置渐入渐出的比例,当物体组比例超过LOD阈值就通过动画快速交叉渐变。

默认的动画持续时间为半秒,可以通过设置static LODGroup.crossFadeAnimationDuration来更改所有组的动画持续时间。然而,在unity2022中,当不在播放模式下,转换速度更快。

如果未设置Clip,并且Fade Transition Width不为0

则,当距离处于交叉切换之间,会使两个物体同时被渲染出来。

因此,如果未实现LOD交叉切换算法,请不要使用CrossFade选项。

LOD物体烘培

LOD0会被用于光照映射(Lightmapping)。其他LOD级别也会得到烘焙照明(Baked Light),但场景的其余部分只考虑LOD 0。你也可以决定只烘焙一些级别,让其他级别依靠光探针。

SpeedTree 模式

这种模式是专门针对SpeedTree树的,它使用自己的LOD系统来折叠树,并在3D模型和广告牌表示之间进行转换。

二、反射探针

1. 获取反射探针数据

  1. 如果未定义LightMap(烘培光照),则会使用球谐函数作为基础环境光颜色。此球谐函数即为环境球的球谐采样结果。
  2. 若想使用IBL作为镜面反射,需要添加反射探针标志。
cpp 复制代码
perObjectData |= PerObjectData.ReflectionProbes;

2. 环境光照明 IBL

在Unity中,IBL环境光照贴图保存在UnityInput.hlsl

cpp 复制代码
// Unity specific
TEXTURECUBE(unity_SpecCube0);
SAMPLER(samplerunity_SpecCube0);
TEXTURECUBE(unity_SpecCube1);
SAMPLER(samplerunity_SpecCube1);

在GlobalIllumination.hlsl中,使用函数

cpp 复制代码
half3 CalculateIrradianceFromReflectionProbes(half3 reflectVector, float3 positionWS, half perceptualRoughness)

即可得到环境光照。函数中使用

cpp 复制代码
half4 encodedIrradiance = half4(SAMPLE_TEXTURECUBE_LOD(unity_SpecCube0, samplerunity_SpecCube0, reflectVector, mip));

得到CubeMap Mipmap插值采样结果,并根据是否使用HDR解码。

cpp 复制代码
#if defined(UNITY_USE_NATIVE_HDR)
        irradiance += weightProbe0 * encodedIrradiance.rbg;
#else
        irradiance += weightProbe0 * DecodeHDREnvironment(encodedIrradiance, unity_SpecCube0_HDR);
#endif // UNITY_USE_NATIVE_HDR

SAMPLE_TEXTURECUBE_LOD函数根据使用的API不同,实现各不相同

IBL在URP Shader中,通过如下函数得到包括环境光照在内的所有全局光照。

cpp 复制代码
lightingData.giColor = GlobalIllumination(brdfData, brdfDataClearCoat, surfaceData.clearCoatMask,
                                          inputData.bakedGI, aoFactor.indirectAmbientOcclusion, inputData.positionWS,
                                          inputData.normalWS, inputData.viewDirectionWS);
cpp 复制代码
half3 reflectVector = reflect(-viewDirectionWS, normalWS);
half NoV = saturate(dot(normalWS, viewDirectionWS));
half fresnelTerm = Pow4(1.0 - NoV);

half3 indirectDiffuse = bakedGI;
half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, positionWS, brdfData.perceptualRoughness, 1.0h);

half3 color = EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm);
  • bakedGI值为LightMap烘培光照数据,或者是环境球谐的采样结果。
  • 通过GlossyEnvironmentReflection计算基于环境的镜面反射光照
  • 最后通过EnvironmentBRDF函数获取混合结果(漫反射颜色 * 漫反射系数 + 镜面反射颜色 * 镜面反射系数)。
cpp 复制代码
half3 EnvironmentBRDF(BRDFData brdfData, half3 indirectDiffuse, half3 indirectSpecular, half fresnelTerm)
{
    half3 c = indirectDiffuse * brdfData.diffuse;
    c += indirectSpecular * EnvironmentBRDFSpecular(brdfData, fresnelTerm);
    return c;
}

3. 反射探针(Reflection Probes)

在场景中未使用反射探针时,场景中具有反射(包括镜面反射Specular和金属反射Metallic)的物体会使用天空贴图(包括天空面 Sky Plane / 天空盒 Sky Box / 天空球 Sky Dome)的信息来制作反射效果。

默认的 environment cube map 只包含天空盒。为了反射场景中的其他东西,我们必须通过GameObject / Light / reflection probe为其添加反射探针。这些探测器在它们的位置处看向场景,将场景渲染为立方体贴图。因此,只有在靠近探测器的表面上,反射才会显得或多或少正确。因此,通常需要在一个场景中放置多个探头。它们具有Importance和Box Size属性,可用于控制每个探针影响的区域。

探测器的Type默认设置为Baked,这意味着它只渲染一次,CubeMap在构建时渲染。你也可以将其设置为实时,这将使地图与动态场景保持同步。它像任何其他相机一样被渲染,使用我们的RP,对立方体地图的六个面分别渲染一次。所以实时反射探测器很昂贵。

每个对象只使用一个环境探针,但场景中可以有多个探针。因此,你可能不得不 split 对象以获得 acceptable(理想的) 的反射。例如,理想情况下,用于构建结构的立方体应该分成单独的内部和外部部分,这样每个部分都可以使用不同的反射探头。此外,这意味着GPU batching 会被反射探针破坏。不幸的是,网格球根本不能使用反射探针,所以渲染出来的总是天空盒。

MeshRenderer组件有一个Anchor Override(直接将带有Probe的物体拖上去就好),可以用来微调它们使用的探针,而不必担心盒子的大小和位置。还有一个Reflection Probes,默认设置为Blend Probes。我们的想法是,Unity允许在最好的两个反射探针之间进行混合。然而,这种模式与SRP批处理程序不兼容,所以Unity的其他rp不支持它,我们也不支持。如果你很好奇,我在2018年SRP教程的反射教程中解释了如何混合探针,但我希望这个功能在遗留管道被删除后消失。

我们将在将来研究其他反射技术。所以仅有的两个功能模式是Off,它总是使用天空盒子,和Simple,它选择最重要的探测器。其他的功能和Simple完全一样。

除此之外,反射探针还可以选择启用box projection mode。这将改变如何确定反射以更好地匹配其有限的影响区域,但SRP批处理程序也不支持这一点,因此我们也不支持它。

:物体在选择反射探针时,确定物体是否在探针包围盒内,如果在,则加入列表;再根据所有影响物体的反射探针的权重,只保留权重最大的一批(若有2个权重为5,3个权重为3,则只保留权重为5的探针),再根据交叉体积权重计算保留的反射探针的系数。
特别的,当大包围盒反射探针完全覆盖小包围盒反射探针,且物体处于小包围盒反射探针内,则默认最小包围盒探针为最重要探针。

解码探针

最后,我们必须确保正确地解释来自CubeMap的数据。它可以是HDR或LDR,其强度也可以调节。这些设置是通过unity_SpecCube0_HDR矢量提供的,它在UnityPerDraw缓冲区中的unity_ProbesOcclusion之后。

cpp 复制代码
// Reflection Probe 0 block feature
// HDR environment map decode instructions
real4 unity_SpecCube0_HDR;
real4 unity_SpecCube1_HDR;

解码函数:

cpp 复制代码
float3 SampleEnvironment (Surface surfaceWS, BRDF brdf) {
	...
	return DecodeHDREnvironment(environment, unity_SpecCube0_HDR);
}

4. Box Projection 盒体投影

引自:技术美术杂谈 反射探针(Reflection Probe)

在未开启盒体投影时,反射贴图的图像通常是通过从无限远处投射的。

开启盒体投影允许我们使用探针包围盒尺寸Probe Size 、探针包围盒偏移 Probe Offset 以及镜头与反射物体的距离作为参数,控制反射探针所生成的反射贴图的尺寸和效果。

通常情况下,Unity默认开启反射探针的盒体投影支持。如果想自定义不同画面质量是否支持盒体投影效果,可以在编辑面板 Edit →项目设置 Project Settings →图像设置 Graphics →画面分级设置 Tier Settings 中取消勾选使用默认设置 Use Defaults ,并进行自定义设置。

相关推荐
一个小狼娃16 小时前
Android集成Unity避坑指南
android·游戏·unity
极客柒16 小时前
Unity 协程GC优化记录
java·unity·游戏引擎
黄思搏16 小时前
Unity SpriteRenderer 进度条 Shader 实现
unity·游戏引擎
猫屋小鱼丸18 小时前
手把手教你在unity中实现一个视觉小说系统(一)
unity
国服第二切图仔21 小时前
Rust开发实战之简单游戏开发(piston游戏引擎)
开发语言·rust·游戏引擎
HahaGiver6661 天前
Unity与Android原生交互开发入门篇 - 打开Unity游戏的设置
android·unity·交互
@LYZY1 天前
Unity TextMeshPro 文本对齐方式详解
unity·游戏引擎·textmeshpro·tmp
在路上看风景1 天前
2.1 ShaderLab - 渲染状态
unity
AA陈超1 天前
虚幻引擎5 GAS开发俯视角RPG游戏 P07-06 能力输入的回调
c++·游戏·ue5·游戏引擎·虚幻
一线灵2 天前
跨平台游戏引擎 Axmol-2.9.1 发布
游戏引擎