赞
踩
200+篇教程总入口,欢迎收藏:
放牛的星星:[教程汇总+持续更新]Unity从入门到入坟——收藏这一篇就够了zhuanlan.zhihu.com本文重点:
使用LOD组
不同LOD之间的交叉淡入
采样反射探针来反射环境
支持可选的菲涅尔反射
这是有关创建自定义脚本渲染管道的系列教程的第七部分。它涵盖了详细的层次结构(LOD)和简单的反射,可以为场景添加细节。
本教程是CatLikeCoding系列的一部分,原文地址见文章底部。
本教程使用Unity 2019.2.21f1创建。
1 LOD
很多小物体可以为场景添加细节,并使场景更加有趣。但是,太小而无法覆盖多个像素的细节会反而会降级为模糊的噪声。在这种视觉比例下,最好不要渲染它们,这样还可以释放CPU和GPU来渲染更重要的东西。我们可以在可以区分它们时决定是否需要剔除此类对象。这样可以进一步提高性能,但会导致物体会根据其视距大小突然出现或消失。可以添加一些中间步骤,在最终完全剔除一个对象之前,先切换到细节较少的可视化视图。通过使用LOD组,Unity可以完成所有这些事情。
1.1 LOD组 组件
你可以通过创建一个空的游戏对象并将LODGroup组件添加到场景中来将LOD组添加到场景中。默认组定义四个级别:LOD 0,LOD 1,LOD 2,最后 为Culled,这意味着什么都不会渲染。百分比表示相对于显示窗口尺寸的估计视觉尺寸的阈值。因此,将LOD 0用于覆盖超过60%的窗口的对象,通常将垂直尺寸视为最小。
但是,“Quality ”项目设置部分包含一个“ LOD Bias ”,用于调整这些阈值。默认情况下,它设置为2,这意味着它将此评估的估计视觉尺寸加倍。因此,LOD 0最终用于30%以上的所有内容,而不是60%以上的所有内容。当偏差设置为非1时,组件的检查器将显示警告。此外,还有一个“Maximum LOD Level”选项可用于限制最高LOD级别。例如,如果将其设置为1,则还将使用LOD 1代替LOD 0。
这个做法是让你制作所有可视化组对象的LOD级别子级的游戏对象。例如,对于三个LOD级别,我使用了三个大小相同的彩色球体。
必须将每个对象分配给适当的LOD级别。你可以通过在Group组件中选择一个级别块,然后将对象拖动到其“渲染器”列表中,或直接将其拖放到LOD级别块上,来执行此操作。
Unity将自动呈现适当的对象。在编辑器中选择特定对象将覆盖此行为,因此你可以在场景中看到你的选择。如果你自己选择了LOD组,则编辑器还将指示当前可见的LOD级别。
左右移动摄像机会更改每个组使用的LOD级别。或者,你可以调整LOD偏差以查看可视化效果的变化,记得保持其他所有条件不变。
1.2 添加 LOD Groups
可以将对象添加到多个LOD级别。你可以使用此选项将较小的细节添加到较高级别,而将相同的较大对象用于多个级别。例如,我用堆叠的扁平立方体制成了一个三步金字塔。基础立方体是所有三个级别的一部分。中间的立方体是LOD 0和LOD 1的一部分,而最小的顶部立方体只是LOD 0的一部分。因此,根据外观大小将细节添加到组中并将其删除,而不是替换整个对象。
可以对LOD组进行灯光映射吗?
是的。当你将LOD组贡献给GI时,它确实会包含在灯光贴图中。LOD 0用于预期的灯光映射。其他LOD级别也可以使用烘焙照明,但是场景的其余部分仅考虑了LOD 0。你还可以决定只烘焙某些级别,而让其他级别依靠光探头。
1.3 LOD转换
LOD级别的突然转换可能会在视觉上造成冲击,尤其是如果某个对象由于自身或摄像机的轻微移动而最终连续快速地来回切换时。通过将组的淡入淡出模式设置为交叉淡入淡出,可以逐步进行此过渡。这使旧的级别淡出,而新的级别同时淡入。
SpeedTree淡入淡出模式选项如何?
该模式专门用于SpeedTree树,该树使用其自己的LOD系统折叠树并在3D模型和广告牌表示之间进行转换。我们不会使用它。
跨淡入淡出时,你可以控制每个LOD级别。启用交叉渐变时,此选项变为可见。淡入淡出过渡宽度为零表示此级别与下一个较低值之间无淡入,而值为1表示其立即开始淡入淡出。值为0.5时,默认设置下,LOD 0将开始以80%交叉渐变为LOD 1。
启用交叉渐变时,两个LOD级别将同时渲染。由着色器以某种方式混合它们。Unity为LOD_FADE_CROSSFADE关键字选择一个着色器变体,因此将其的多编译指令添加到我们的Lit着色器中。对CustomLit和ShadowCaster传递都执行此操作。
通过我们已经定义的UnityPerDraw缓冲区的unity_LODFade向量,可以传达淡化对象的程度。它的X分量包含渐变因子。它的Y分量包含相同的因子,但量化为16步,我们不使用。通过在LitPassFragment的开头返回它来可视化化淡入淡出的因子。
淡出的对象从因子1开始,然后按预期减少为零。但是,我们还看到了代表较高LOD级别的纯黑色物体。之所以发生这种情况,是因为淡入的对象的淡入因子被消除了。我们可以通过返回取反的衰退因子来看到这一点。
请注意,两个LOD级别中的对象不会相互淡入淡出。
1.4 抖动
为了混合两个LOD级别,我们可以使用裁剪,应用类似于近似半透明阴影的方法。由于我们需要对表面及其阴影都进行此操作,因此我们将其为此添加一个ClipLOD函数。给它剪裁空间的XY坐标和渐变系数作为参数。然后,如果交叉淡入淡出处于活动状态,则基于淡入淡出减去抖动模式的剪辑。
为了检查裁剪是否按预期工作,我们将从垂直渐变开始,该渐变每32个像素重复一次。那应该会产生交替的水平条纹。
在LitPassFragment中调用ClipLOD,而不返回淡入因子。
并在ShadowCasterPassFragment的开始处调用它以淡入淡出阴影。
我们得到了条带化的渲染结果,但是在交叉淡入淡出时只有两个LOD级别之一出现。这是因为两者之一具有负的衰退系数。在这种情况下,我们通过添加而不是减去抖动模式来解决该问题。
现在它可以正常工作了,我们可以切换到适当的抖动模式。选择用于半透明阴影的相同对象。
1.5 动画化交叉淡化
尽管抖动创建了一个相当平滑的过渡,但是这种模式是显而易见的。就像半透明阴影一样,淡化的阴影也不稳定且分散。理想情况下,淡入淡出只是暂时的,但其他都不会改变。我们可以通过启用LOD组的“Animate Cross-fading”选项来做到这一点。这就忽略了淡入淡出的过渡宽度,而是在组通过LOD阈值时迅速进行淡入淡出。
默认动画持续时间为半秒,可以通过设置静LODGroup.crossFadeAnimationDuration属性来为所有组进行更改。
2 反射
另一个为场景增加细节和真实感的现象是环境的镜面反射(其中最明显的例子是镜子),我们尚不支持。这对于目前大多为黑色的金属表面尤为重要。为了使这一点更加明显,我在Baked Light 场景中添加了新的金属球,这些金属球具有不同的颜色和平滑度。
2.1 间接BRDF
我们已经支持基于BRDF的漫反射颜色的漫反射全局照明。现在,我们还添加了镜面反射全局光照,这也取决于BRDF。因此,让我们为BRDF添加一个IndirectBRDF函数,它具有表面和BRDF参数,以及从全局照明获得的漫反射和镜面反射颜色。最初只有它返回反射的散射光。
添加镜面反射的开始类似:只需将镜面反射GI乘以BRDF的镜面反射颜色即可。
但是粗糙度会散射这种反射,因此应减少最终看到的镜面反射。为此,我们将其除以平方粗糙度加一。因此,低粗糙度值并不重要,而最大粗糙度可使反射减半。
在GetLighting中调用IndirectBRDF,而不是直接计算漫射间接光。首先使用白色作为镜面反射GI颜色。
至少所有东西都变得更亮一点了,因为我们正在添加以前缺少的照明。金属表面的变化是巨大的:它们的颜色现在明亮而明显。
2.2 采样环境
镜面反射可反映环境,默认情况下为天空盒。可通过unity_SpecCube0将其作为立方体贴图纹理使用。这次使用TEXTURECUBE宏在GI中声明其采样器状态。
然后添加带有世界空间表面参数的SampleEnvironment函数,对纹理进行采样,并返回其RGB分量。我们通过SAMPLE_TEXTURECUBE_LOD宏对立方体贴图进行采样,该宏将贴图,采样器状态,UVW坐标和mip级别作为参数。由于它是立方体贴图,因此我们需要3D纹理坐标,因此需要UVW。首先,我们始终使用最高的Mip级别,因此我们对全分辨率纹理进行采样。
立方体贴图的采样是通过一个方向完成的,在这种情况下,该方向是从相机到从表面反射的表面的视图方向。通过调用带有负视角方向和表面法线作为参数的reflect函数来获得它。
接下来,向GI添加镜面反射颜色,并将采样的环境存储在GetGI中。
现在,我们可以将正确的颜色传递给GetLighting中的IndirectBRDF。
最后,要使其正常运行,我们必须指示Unity在设置每个对象数据时在CameraRenderer.DrawVisibleGeometry中包括反射探针。
现在,表面可以反射环境。这对于金属表面很明显,但是其他表面也可以反射它。因为它只是天空盒,所以还没有其他反射,但是我们稍后再讨论。
2.3 粗略的反射
当粗糙度使镜面反射发生散射时,它不仅降低了强度,而且使图像变得模糊,就好像没有聚焦。通过将环境图的模糊版本存储在较低的Mip级别中,Unity可以近似此效果。要访问正确的Mip级别,我们需要了解粗糙度,因此让我们将其添加到BRDF结构中。
我们可以依靠PerceptualRoughnessToMipmapLevel函数为给定的感知粗糙度计算正确的Mip级别。它在核心RP库的ImageBasedLighting文件中定义。这需要我们向SampleEnvironment添加一个BRDF参数。
也将所需的参数添加到GetGI并将其传递。
最后,在LitPassFragment中提供它。
2.4 菲涅尔反射
所有表面的一个特性是,当从掠射角度观看时,它们开始像完美的镜子,因为光线从它们身上反弹几乎不受影响。这种现象称为菲涅耳反射。实际上,它比在不同介质的边界处传输和反射光波要复杂得多,但是我们只是使用与Universal RP相同的近似值,即假定为气固边界。
我们对菲涅耳使用一个变种的Schlick近似。在理想情况下,它用纯白色代替镜面反射的BRDF颜色,但粗糙度可以防止出现反射。我们通过将表面平滑度和反射率加在一起,得出最终颜色,最大值为1。由于是灰度,因此可以在BRDF上添加单个值就足够了。
在IndirectBRDF中,我们通过获取表面法线和视图方向的点积,从1中减去该点积,并将结果提高到四次方来求出菲涅耳效应的强度。我们可以在此处使用Core RP库中便捷的Pow4函数。
然后,我们根据强度在BRDF镜面和菲涅耳颜色之间进行插值,然后使用对环境反射进行着色的结果。
2.5 菲涅尔滑动条
菲涅耳反射会增加沿几何体边缘的反射。当环境贴图正确匹配对象后面的颜色时,效果会很微妙,但是如果不是这样,则反射可能会显得怪异而分散注意力。沿结构内部球体边缘的明亮反射就是一个很好的例子。
降低平滑度可消除菲涅耳反射,但也会使整个表面变暗。同样,在某些情况下,例如在水下,菲涅耳近似法不合适。因此,我们添加一个滑块将其缩放到Lit着色器。
将其添加到LitInput的UnityPerMaterial缓冲区中,并为其创建GetFresnel函数。
还要向UnlitInput添加一个虚拟函数,以使其保持同步。
Surface现在因其菲涅耳强度而获得了发展。
我们将其设置为等于LitPassFragment中的滑块属性的值。
最后,使用它来缩放我们在IndirectBRDF中使用的菲涅耳强度。
2.6 反射探针
默认环境立方体贴图仅包含天空盒。要反射场景中的其他任何东西,我们需要通过GameObject/ Light / Reflection Probe向其添加一个反射探针。这些探针从其位置将场景渲染到立方体贴图。因此,对于靠近探针的表面,反射只会多多少少有点正确。因此,通常有必要在一个场景中放置多个探针。它们具有“Importance ”和“Box Size”属性,可用于控制每个探针影响哪个区域。
默认情况下,探针的“Type”设置为“Baked”,这意味着它渲染一次,并且将立方体贴图存储在构建中。您也可以将其设置为“Realtime”,以使地图与动态场景保持最新。使用我们的RP,就可以像渲染任何其他摄像机一样对立方体贴图的六个面中的每个面进行一次渲染。因此,实时反射探针非常昂贵。
每个对象仅使用一个环境探针,但是场景中可以有多个探针。因此,你可能必须拆分对象以获得可接受的反射。例如,理想情况下,用于构建结构的立方体应分为独立的内部和外部部分,因此每个立方体都可以使用不同的反射探针。同样,这意味着GPU批处理会被反射探针破坏。更不幸的是,网状球根本不能使用反射探针,它总是以天空盒结束。
MeshRenderer组件具有“Anchor Override”,可用于微调其使用的探针,而不必担心盒子的尺寸和位置。还有一个“Reflection Probes”选项,默认情况下设置为“Blend Probes”。这个想法是,Unity允许在最好的两个反射探针之间进行混合。但是,此模式与SRP批处理程序不兼容,因此Unity的其他RP不支持它,我们也不支持。如果你感到好奇,我的2018 SRP教程的Reflections教程中说明了如何混合探针,但是我希望一旦旧版管道删除,此功能就会消失。将来我们将研究其他反射技术。因此,仅有的两个功能模式是“Off”(始终使用天空盒)和“Simple”(选择最重要的探测器)。其他功能与简单功能完全相同。
除此之外,反射探针还具有启用盒投影模式的选项。这应该会改变确定反射以更好地匹配其有限影响区域的方式,但是SRP批处理程序也不支持此功能,因此我们也不支持它。
2.7 解码探针
最后,我们需要确保我们正确解码了立方体贴图中的数据。它可以是HDR或LDR,其强度也可以调整。这些设置可通过unity_SpecCube0_HDR向量使用,该向量位于UnityPerDraw缓冲区中的unity_ProbesOcclusion之后。
在SampleEnvironment的末尾,通过使用原始环境数据和设置作为参数调用DecodeHDREnvironment,可以获取正确的颜色。
本文翻译自 Jasper Flick的系列教程
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。