全部资源
  • 全部资源
  • - 交通工具
  • - 动物昆虫
  • - 场景模型
  • - 建筑模型
  • - 植物模型
  • - 武器模型
  • - 独家资源库
  • - 科幻模型
  • - 角色动画库
  • - 角色模型
  • 知识干货

【植被渲染器】Foliage Renderer中文文档

Unity的GPU的植被渲染系统Foliage Renderer中文文档封面,背景是使用Foliage Renderer渲染的森林植被效果。

Foliage Renderer 是一种基于GPU的渲染系统,用于绘制世界中的大量实例,而无需使用GameObject所带来的开销。它仅消耗少量CPU资源,将LOD计算、遮挡剔除等任务转移至GPU处理。它能显著提升Unity地形树木及细节在CPU和GPU上的渲染速度,同时增加自定义特性,使场景更加逼真。

插件的获取:

在创造趣味平台提供的Foliage Renderer v1.2+1.3及更高版本的获取地址,如果你需要尝试学习Foliage Renderer可以点击这里获取

限制与需求

Foliage Renderer需要兼容其自定义格式的着色器。尽管如此,它能自动修补许多着色器。

包内含一个支持所有特性(如风动效果、地形对齐细节、地形反照率着色、实例化)的着色器,若需更全面功能,推荐使用Better Lit Shader 2021,该着色器完全支持上述特性。对于高质量植被专用着色器,我们建议使用Vegetation Engine,它全面支持Foliage Renderer。

Foliage Renderer支持每个对象最多4个LOD。支持LODGroup的交叉淡入模式——SpeedTree交叉淡入和动画交叉淡入当前不支持。LOD必须等比例缩放。

Foliage Renderer仅支持不透明或剪裁图基底着色器,因为它不对alpha进行排序后的绘图调用进行排序。

设置步骤

为场景配置Foliage Renderer非常简单——安装包后遵循两步操作:

  1. 确保项目已构建Culling Compute Shader。它会自动生成并保存于Assets/JBooth/FoliageRenderer/Resources/。
    • 如未生成,可在Unity的文件菜单中,选择Window/Foliage Renderer/并点击Validate Culling Shader。
  1. 打开含有地形的场景,执行GameObject/Create Foliage Renderer。这会在场景中添加多个新对象和组件。
    • 每个地形对象将获得一个Terrain Foliage Provider组件。
    • 场景中会添加一个Indirect Renderer。
    • 场景中还会添加一个带有Terrain Foliage Renderer组件的Foliage Renderer对象。

自动转换着色器(Automatically Converting Shaders)

如果您的着色器与Foliage Renderer不兼容,当前场景可能看起来不太正常。选择创建的Foliage Renderer游戏对象,在顶部会有修补着色器的界面:

Foliage Renderer中文文档的自动转换着色器(Automatically Converting Shaders)界面

点击扫描按钮后,它会找到地形树和细节对象中使用的全部着色器,并列出它们以及修复或重新修补的按钮。点击按钮会创建着色器副本,文件名后附带_FR。同时,它会将对象中的所有材质映射到新着色器上。选择修补着色器时,会弹出对话框询问是否创建Shader Patch Report ScriptableObject。如果创建,报告会被放置在Assets文件夹中,记录每个着色器、新创建的替换着色器及修改的每个材质,并提供便捷方式查找或恢复到原始着色器。

修补程序会尝试修改这些新着色器以兼容Foliage Renderer。由于着色器代码可能被高度混淆,我们不能保证修补程序能自动解决所有问题,但它能解决许多问题。

注意,如果您自己编写着色器,可能希望直接包含这部分代码。包内含一个.cginc文件和Shader Graph节点以简化此过程。如果使用Better Shaders编写着色器,它已经包含了Foliage Rendering实例化的堆栈。

 

Indirect Renderer(间接渲染器)

Indirect Renderer是利用GPU驱动渲染大量对象的系统,以最小的绘图调用来实现。

该组件仅有几个选项。首先,您可以开启或关闭阴影视锥体剔除。阴影剔除仅适用于单一的方向光,如户外场景所用。如果场景中有大量其他投射阴影的光源,可能需要关闭阴影视锥体剔除。

Foliage Renderer中文文档的Indirect Renderer(间接渲染器)组件界面

它会自动识别场景中的“Directional Light”光源,用以防止不在可见视锥体内的阴影投射物体被剔除。

Max Graphics Buffer Caches允许您通过增加更多内存使用来减少图形缓冲区的重建。重建图形缓冲区可能导致帧率不稳(卡顿),但因缓冲区按需精确分配所以占用内存较少。启用缓冲区缓存后,会保留最大的缓冲区并重复使用。

您还可以启用或禁用Hi-Z遮挡剔除。Hi-Z是一种完全在GPU上运行的遮挡剔除系统,通过使用场景的深度缓冲快速拒绝不可见的对象。当场景中有良好的遮挡物时,这可以大幅减少需要绘制的实例数量,但需要额外渲染场景的深度缓冲。在许多情况下,您可能已经为了其他效果渲染深度缓冲,因此使用Hi-Z遮挡的开销相对较低,潜在收益巨大。

Foliage Renderer中启用Hi-Z遮挡剔除效果后的俯视视角和第一人称视角下的渲染效果

HiZ渲染设置

HiZ遮挡剔除的设置根据渲染管线而异:

  • 内置渲染管线
    • 在Indirect Renderer组件中启用HiZ剔除
  • HDRP
    • 在Indirect Renderer组件中启用HiZ剔除
  • URP
    • 项目中的每个Scriptable Renderer Data对象都需要添加HiZ URP渲染器特性。别担心,我们让这个过程变得简单!
    • 编辑器加载时,HiZ URP渲染器特性会自动添加到项目质量设置中分配的每个渲染管线所绑定的Scriptable Renderer Data上。
    • 如果渲染器特性没有自动分配,或者您修改了质量设置,或其他特殊情况,可以手动触发此分配。在Unity的文件菜单中,选择Window/Foliage Renderer并点击Validate URP Data Assets。
    • 最后,确保URP设置为渲染深度缓冲。完成后,您可以在间接渲染器上启用Hi-Z。

【植被渲染器】Foliage Renderer中文文档次世代模型库

【植被渲染器】Foliage Renderer中文文档次世代模型库

最后,系统会显示统计信息,包括内存使用量、绘图调用次数等。点击“捕获后剔除数据”按钮可展示在视锥体和Hi-Z剔除后,该帧实际绘制了多少实例。通常情况下,这仅占区域总实例的一小部分。

 

Indirect Camera(间接摄像机)

Indirect Camera组件负责向Indirect Renderer订阅或取消订阅摄像机。

Terrain Foliage Renderer(地形植被渲染器)

Terrain Foliage Renderer会自动将场景中的Terrains转换为使用Foliage Renderer,方法是在这些对象上添加Terrain Foliage Provider组件。它还允许您为树木和细节的渲染提供默认设置,以及系统中每个单独树木或细节的覆盖设置。

【植被渲染器】Foliage Renderer中文文档次世代模型库

在Terrain上,您可以为树木和细节设置默认值。默认情况下,它们及其阴影的绘制距离是从Unity地形和场景设置中读取的,但您可以使用自己的值覆盖这些值,甚至可以针对每个实例进行。阴影最大LOD可用于从与可见模型不同的LOD渲染阴影。这对于复杂对象(如树木)特别有利,尤其是因为阴影缓冲区通常比全屏分辨率低,因此微三角形问题比主场景更严重。

【植被渲染器】Foliage Renderer中文文档次世代模型库

在默认设置下方,您可以选择单个树木或细节,并覆盖它们的设置。例如,您可以较早地剔除较小对象的实例或它们的阴影。这提供了大量的优化控制。

Detail Streaming(细节对象流式加载)

Unity Terrains上的细节对象不是放置的对象,而是由位掩码控制的。当Unity渲染细节对象时,它会选择靠近摄像机的地形区域(补丁),并将这些位掩码转换为散落在附近地形上的对象。

如果转换为原始位置数据,整个地形的细节对象可能会占用大量内存(演示场景中约为28MB)。因此,默认情况下,Foliage Renderer会在这些区域进入视图时从Unity Terrain流式加载这些数据,从而大幅度减少内存使用。然而,由于Unity永远不会修复Terrain API,获取此数据的API调用会分配托管内存数组,且不处于渲染兼容格式,实际上并未正确处理2022年新增的地形对齐细节。FR会自动实时转换和修正这些数据,但如果您正将渲染距离推得很远并且有足够的内存备用,您可能希望在加载时一次性完成而不是使用流式系统。

【植被渲染器】Foliage Renderer中文文档次世代模型库

当设置为使用流式加载时,细节数据会缓存直到超出由“Detail Caching Distance”属性指定的视图区域之外。当设置为AtLoad时,所有细节都会加载且永不卸载。

 

Terrain Foliage Provider(地形植被提供程序)

Terrain Foliage Provider是一个位于Terrain对象上的组件,它从Terrain中提取细节和树木原型,并将其转换为Terrain Foliage Renderer显示的Draw数据。

Foliage Renderer中的Terrain Foliage Provider(地形植被提供程序)组件截图

默认情况下,树木位置和反照率渲染在标记为刷新时更新。这通常仅在引发Terrain的OnTerrainChanged回调且TerrainChangedFlags包含相关更改标志时发生。可以通过调用Terrain Foliage Provider上的Refresh()手动执行此操作。

Provider提供了一些选项,您可以使用它们微调何时为渲染器更新信息。

  • Update On Transform Changed:当为真时,树木原型将在下一个指定的更新时机(见下文)刷新。默认情况下为假。
  • Tree Timing:枚举,指示应在哪个更新阶段(或根本不处理)处理树木。默认设置为LateUpdate。
  • Detail Timing:枚举,指示应在哪个更新阶段(或根本不处理)处理细节。默认设置为Update。

Material options(材质选项)

“Material Options”展开项为您提供控件,用于向地形上渲染的实例的材质发送额外数据。结合自定义着色器,这可以成为增强场景的强大系统。

【植被渲染器】Foliage Renderer中文文档次世代模型库

  • 地形纹理设置:这些选项允许您提供来自地形的高度和法线贴图,或生成地形的反照率贴图,并使用Unity的标准命名约定发送给您的材质。让我们看看如何使用这些设置。

【植被渲染器】Foliage Renderer中文文档次世代模型库

这是一个由几十块岩石组成的示例网格,可以轻松放置在平坦的地形上。然而,如果您尝试将此作为地形上的细节对象使用,它会看起来像这样:

【植被渲染器】Foliage Renderer中文文档次世代模型库

岩石浮动,因为它们不遵循地形。在Unity 2022中,Unity添加了一个将对象对齐到地形的功能 – 但是,这仅在对象的枢轴点处发生,这意味着远离该中心的岩石仍会浮动,或者最终位于地形内部。

【植被渲染器】Foliage Renderer中文文档次世代模型库

使用来自地形的高度数据,我们可以使网格符合地形的精确几何形状。这使得可以使用这样的网格,以及大量原本不适合地形的摄影测量几何体。

【植被渲染器】Foliage Renderer中文文档次世代模型库

请注意,然而,无法知道Unity地形何时会降低其细节级别,且着色器将始终从高度贴图采样。在足够远的距离上,这可能导致当低分辨率地形覆盖它们时,物体在小块区域被地形覆盖。

  • 额外纹理和数据配置

您也可以将地形的法线或反照率信息传递给树木和细节对象上的材质。需要注意的是,反照率信息直接由漫反射纹理生成,因此无法考虑地形上可能使用的自定义着色器,这些着色器可能包含用于色调调整等的控制选项。

但是,此区域还有另一个控制项,允许您将与地形关联的任意纹理传递给地形上的对象。例如,您可能会传递使用MicroSplat烘焙出的地形反照率贴图,以将草丛融合到地形颜色而不是使用内置的。这将完全支持着色或您可能想要烘焙并用于着色器的任何其他数据。

要手动设置反照率功能,我们可以这样做:

【植被渲染器】Foliage Renderer中文文档次世代模型库

添加条目后,您可以为其提供一个配置,告诉系统您的着色器具有哪些纹理属性。您可以从项目窗口的右键创建菜单(Create/FoliageRenderer/Create Neighbor Property Config)制作配置。

【植被渲染器】Foliage Renderer中文文档次世代模型库

活动属性在使用此配置时设置为1,可以通过关键字或分支进行分支。纹理将设置在您在其他字段中提供的属性上。如果您不需要相邻数据,可以设置基本的着色器属性名称。并命名相邻数据的约定,允许您跨地形接缝混合数据。

随附的示例着色器可以在您希望使用任意纹理代替内置地形反照率贴图着色地形对象时使用此配置。一个例子可能是指定某个区域花朵的颜色,传递风向数据,或几乎任何其他您可能想要的数据。

如果您有兴趣编写使用这些特性的自定义着色器,您可以查看位于Shaders/BetterShadersSource目录中的Stackable_TerrainAlignedDetail.surfshader。它既包含将细节对齐到地形的特性,也包含基于提供的贴图调整它们的特性。

【植被渲染器】Foliage Renderer中文文档次世代模型库

您可能希望分配的另一个配置是Terrain Extra Data Config。这允许您将一些关键的TerrainData值传递给着色器和材质。每种数据类型:大小、高度、位置和Y偏移;可以设置为Disabled、Basic或Use Neighbors模式。这些数据将被分配到什么着色器属性取决于您接下来分配的Terrain Neighbor Property Config。

  • 禁用(Disabled):不会向材质传递任何数据。
  • 基本(Basic):相应的Neighbor Property Config ‘Basic’ 属性名称将被使用。
  • 使用相邻(Use Neighbors):将使用相邻属性名称,并分配任何可用的相邻TerrainData。

Menu Options(菜单选项)

一些有用的功能可通过菜单访问。您可以在“Window > FoliageRenderer >”中找到它们:

【植被渲染器】Foliage Renderer中文文档次世代模型库

  • Force Scene View Refresh【强制场景视图刷新】:启用或禁用以控制场景视图更改时更新IndirectRenderer。
  • Scene View Refresh Rate【场景视图刷新率】:切换场景视图刷新率至适合您的FPS值,以在Unity编辑器中工作时最小化不必要的GPU开销。(默认情况下,Unity编辑器大约以100 FPS运行,但速率不一致。)
  • Validate Culling Shader【验证剔除着色器】:此选项将运行自动构建Foliage Renderer剔除着色器的验证过程,以防万一。
  • Validate URP Data Assets【验证URP数据资产】:此选项仅在您处于URP项目中时出现!类似于验证剔除着色器,它运行尝试将Hi Z URP Renderer Feature附加到与您项目的质量设置相连的每个ScriptableRenderPipelineAsset的验证过程。

 

Benchmarking(基准测试)

我们经常听到的一个说法是,人们将FR添加到场景中并查看FPS,然后说“FPS没有提高”。这通常是因为人们不了解他们实际的性能瓶颈是什么,间接渲染器的作用,以及GPU和CPU之间的区别。他们还倾向于在没有游戏玩法的大场景中进行测试,此时CPU有足够的时间来做渲染工作。

最基本的渲染形式会对它遇到的每个网格和材质提交一个绘制调用。所以,一棵树的树皮和叶子分别使用不同材质,就会产生两个绘制调用。一千棵树就是两千个绘制调用,以此类推。Unity地形使用一种实例化形式来减少这个数量——允许最多合并1023个这样的绘制调用(尽管在某些硬件上,这个数量只有512)。所以,如果有1万棵草被绘制,就需要10到20个绘制调用来绘制它们。而使用间接实例化,可以在单个绘制调用中提交的项目数量是没有限制的,因此所有这些都可以在一个绘制调用中渲染。

当一帧画面被渲染时,GPU和CPU都必须完成它们的工作才能开始渲染下一帧。CPU需要提交所有的绘制调用,而GPU需要渲染它们,但GPU可以在第一个绘制调用提交后就开始渲染,同时CPU继续提交新的绘制调用。

【植被渲染器】Foliage Renderer中文文档次世代模型库

在上面所示的Unity Profiler图表中,你可以看到CPU只是在等待GPU完成。在这里,它花费了13毫秒等待GPU完成渲染。这是13毫秒,本来可以用来运行游戏逻辑、物理等。通常,在帧率相同的场景中,你会看到CPU在使用Unity地形渲染时等待GPU的时间要长得多,仅仅因为Unity地形在提交额外的绘制调用上花费了大量的CPU时间。在这种情况下,虽然FR更快,但帧率是一样的,因为你的场景并没有使用大量的CPU时间。

此外,人们通常在为Unity地形设计的场景中进行测试。这些场景常常对草地的绘制距离很短,因为当推向远距离时,一切渲染都会变慢。包含的示例场景将地形细节的绘制距离设置为250米,这是Unity地形允许的最大值,尽管FR可以做得更高。在这个示例中,帧率是使用Unity渲染器的两倍多,因为Unity渲染器为了达到那样的视角距离提交了太多的绘制调用。在这个场景的一个视角中,有超过20万个实例——Unity地形会将这些实例批处理为200到400个绘制调用之间,而FR只需要22个绘制调用就能做到。FR的剔除系统还将实际的绘制实例数减少到了2.9万个。这样的节省对CPU和GPU都有很大帮助。

【植被渲染器】Foliage Renderer中文文档次世代模型库

扩展Foliage Renderer

你可能希望绘制网格而不依赖于Terrain和TerrainFoliageProvider组件来绘制细节。

系统的核心是IndirectRenderer组件,它可以用来渲染各种东西,不仅仅是地形对象。要使用它渲染对象,你只需使用唯一的ID注册和注销List。

Draw类封装了给定Prefab(myPrefab)要绘制的网格、材质和RenderParams。

例如,假设我们有一个Transform列表(myTransforms),我们想在其上绘制一个Prefab。为了渲染这些对象,我们需要做几件事:

  1. 创建一个NativeList(myMatrices)

a. 对myTransforms列表进行循环,并使用每个Transform的位置、旋转和局部缩放来添加一个新的Matrix4x4…Matrix4x4.TRS(myTransform[i].position, myTransform[i].rotation, myTransform[i].localScale)

  1. 创建一个List来添加你的Draws。

a. List myDraws = new List(); 你可能希望把这个放在添加draws的类上,而不是反复创建。

  1. 计算所有Draws的世界边界(myBounds)。

a. 循环遍历Transforms,找到每个轴上的最小和最大维度。

b. 做一些数学运算来得到中心点。

c. 使用这些数据创建一个Bounds。

  1. 你可能希望暴露或定义一些自定义的RenderParams。

a. 这是Unity的一个类,你可以在Unity的文档中找到关于RenderParams的完整介绍。

  1. 你可能希望暴露或定义一些自定义的DrawOptions。

a. DrawOptions是FoliageRenderer特有的一个类,它重现了Unity地形细节绘制选项的功能(Unity密封了那个类……不要问我为什么,没有任何道理可言)。

  1. 使用静态Draw.AddToDrawList函数来填充myDraws。

a. Draw.AddToDrawList(

prefab: myPrefab, // 你想绘制的预制体

List drawList: myDraws, // 你想添加到的可绘制对象列表

NativeArray mtxArray: myMatrices, // 绘制实例的位置/旋转/缩放

RenderParams renderParams, // 渲染参数(阴影等)

Bounds worldBounds: myBounds, // 所有实例的边界

DrawOptions drawOptions); // 要使用的自定义绘制选项

  1. 现在一切都准备好了。你现在可以通过调用…IndirectRenderer.Register(string id, // 你的绘制组件的唯一ID)来展示你的对象。
  2. 并且你可以随时通过…

IndirectRenderer.Unregister(string provider, // 用于注册你的绘制组件的相同唯一ID)来停止绘制对象。

标签

评论