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 ,并进行自定义设置。

相关推荐
虾球xz1 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
red_redemption5 小时前
自由学习记录(23)
学习·unity·lua·ab包
/**书香门第*/9 小时前
Cocos creator 3.8 支持的动画 7
学习·游戏·游戏引擎·游戏程序·cocos2d
向宇it1 天前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
Heaphaestus,RC1 天前
【Unity3D】获取 GameObject 的完整层级结构
unity·c#
芋芋qwq1 天前
Unity UI射线检测 道具拖拽
ui·unity·游戏引擎
tealcwu1 天前
【Unity服务】关于Unity LevelPlay的基本情况
unity·游戏引擎
大眼睛姑娘1 天前
Unity3d场景童话梦幻卡通Q版城镇建筑植物山石3D模型游戏美术素材
unity·游戏美术
鹿野素材屋1 天前
Unity Dots下的动画合批工具:GPU ECS Animation Baker
unity·游戏引擎
小春熙子2 天前
Unity图形学之着色器之间传递参数
unity·游戏引擎·技术美术·着色器