super1 发表于 2021-6-14 17:43

Inside Unreal: Nanite

UE5 EA版本发布后,官方随后计划做一系列的专题内容在线直播,本篇是关于Nanite专题录播的简要记录,直播内容较长,仅摘述了Brian大佬对Nanite的技术讲解部分,对其他内容感兴趣请移步看twitch上的回放 :
初衷与理想目标:
Nanite 是一套虚拟几何系统,用于处理大规模几何体,类似于virtual texturing system,通过 texture 来 cache 全部数据减少额外的负担:Artist 无需关心几何体面数、drawcalls与内存直接在引擎中使用高质量的美术资源无需手动优化工作,不再制作LOD或其他设置品质上无损
Nanite 的实现比 VT 要更加复杂困难:
内存管理问题几何细节直接影响渲染开销,对于内存的访问,不同于 texture 的像素访问几何体不同于 texture 可简单的用于滤波
这里类比 geometry 与 texture,geometry 的 LOD 类似于 texture 的 mipmap,但 texture 是 filterable 的,可简单的计算每个像素平均值来生成 mipmap,但geometry 就不能计算每个三角形均值来得到 lod。
用于解决几何体表示的一系列不同方法
但重要的是需要满足我们的需求
不能完全改变整个 CG 的工作流程支持导入任意的外部制作的 mesh保留 uv 和 tiling detail map仅替换 mesh,而非纹理贴图、材质(Shader)和其他工具
概括一下就是不改变工作流的前提下,仅替换 mesh 而不影响其他数据资源
经过长时间的探索,发现对于高品质的解决方案,要返璞归真到三角形上,这是计算机图形的基础。
我们要做一个三角形的基础管线,把ue4提升到一个先进的技术水准,这就是 Nanite GPU Driven Pipeline。
渲染器现在变成了 Retained Mode
全部 GPU scene,所有数据都在 GPU Memory减少场景变动的更新,一旦场景发生改变,非每帧上传更新所有的 vertex/index 数据都存在一个大内存里
每个 View
在 GPU 上执行 Instance Culling三角形光栅化
以上都完成后,就可以只渲染一个 depth only pass 来绘制整个场景,仅用一个 indirect drawcall
然后是三角形的可见性,Triangle cluster culling
将三角形组成 cluster
为每个 cluster 构建包围体
根据包围体裁剪 cluster
Frustum CullOcclusion Cull

接下来,要将 depth only rendering 也支持材质的方案是,从 material 中解耦 Visibilty,在每个像素上确定可见性,类似于深度缓冲的光栅化。不同于材质评估,这么做的原因是:
我们想要消除以下因素
在光栅化期间切换 shader 的高开销限制对材质评估的 overdraw通过 depth prepass 来避免 overdraw密集 mesh 的低效像素quad
实现 Deferred material 的方案是通过 Visibility Buffer
Visibility Buffer 的本质是将几何与材质解耦,material pass 与几何光栅化分离 ,我们执行 一次 material pass,全部的 material 是一起绘制的,不再是以前按每个物体绘制一遍,只有一个单独的 drawcall。
Material pass 写 GBuffer
与其余额延迟着色渲染器集成在一起
现在我们可以通过一次 draw 绘制全部的不透明几何体
完全 GPU Driven不再是 depth prepass,可以同时处理 material,叫做 geometry pass每个 view 光栅三角形一次

有了以上之后,比以前快多了,但还要线性地缩放 instance 数量和 triangle 数量,instance的线性缩放没问题,但 triangle 的线性缩放会有问题,无论多少数量的三角形,都无法实现目标。如果用 ray tracing 的方法,以 log N 的三角形缩放效果不错,但内存就不够用了,并且性能也不满足。
换个思考方式,既然屏幕上有这么多的像素,为什么要画更多的三角形而不是像素呢?
理想情况下,每个像素最多只画一个三角形,我们可以进行 cluster culling,我们希望每一帧都能画相同数量的 cluster。要做到完美不太现实,但一般来说,渲染几何体的裁剪应根据屏幕分辨率而不是场景的复杂性来缩放。这意味着常数时间的复杂度,常数时间实际上就是LOD。
所以,我们可以用 cluster 来做一个 LOD,为 cluster 构建一个层次结构
在 cluster 的基础上确定 LOD构建一个 LOD 的层级结构
最简单是构建一个 cluster 二叉树父节点是子节点的简化版本

运行时 LOD
找到目标LOD的树视点相关取决于感知差异
Streaming
整个树不需要一次加载进内存可以标记任意树的切割作为叶子,并且丢弃剩余部分在渲染期间按需请求数据
类似于 Virtual Texturing

注:这里右侧 LOD1 的两个叶子 LOD0 就不需要先加载到内存,直到 View dependent 满足条件再加载
现在我们有了细粒度的 view dependent lod,并且是随屏幕分辨率缩放的。
那么我们需要画多少个三角形呢?这个数应该是达到零细节损失。
那么三角形需要是多小的呢?这个数是无法感知的。
我们可以用一个比像素大的三角形来填充像素缩放细节吗?
取决于是否平滑,三角形是自适应的,如果某些物体是平的,那么三角形就可以覆盖到这里,它们也不需要像素大小的三角形但通常来说不行,像素大小的特性需要像素大小的三角形来表示,它是内容相关的。
注:这一段没听清,希望有人来斧正,或以后补正。
软光栅可以打败硬件光栅吗?Yes!
软光栅可以替代 Primitive Shader,完成 vertex shader & pixel shader pass

大三角形用 硬件光栅 更快(其他情况用 软光栅更快?)根据性能选择使用 SW or HW
性能测试,视频后面有更详细的。
副作用:资源数据量也是惊人。。。还有人在提mobile??
另一个新技术是 Virtual Shadow Maps,基于物理的SMRT算法,通过ray tracing 可产生sharp shadows or soft shadows,视频中没有讲太多,细节直接看文档和代码吧:
Limits:
Nanite 目前专注在刚体的支持上
>90%的几何体支持移动物体
还不支持的
半透和mask材质非刚性变换,骨骼动画,等Tessellation and displacement
聚合体(Aggregates)支持不好
由小物件组成的带孔的物体草体、树叶、头发

Ylisar 发表于 2021-6-14 17:51

一种材质一个drawcall是吗?

JamesB 发表于 2021-6-14 17:58

第一时间翻译[赞],前两天看 Brian 的 Twitter 时看到他关于这个讲解的直播预告,甚是期待,今天还没来得及看,翻译都已经出来了[捂脸]。只是原本预期 Brian 能讲更多的技术细节却没有讲得更透彻,比如 Pixel Scale、Cluster Hierarchy 等,有些许失望。

FeastSC 发表于 2021-6-14 18:07

对,每种 material 是一个低分辨率 quad,根据material range map处理一个quad内的材质

Arzie100 发表于 2021-6-14 18:09

是的,似乎准备的非常仓促,开局一张图,基本全靠说 [捂脸],工程细节没有交代,还没有大佬您的 cull rasterize流程分析的透彻,期待一下 siggraph 吧,有一些思路还是交代了,怎么想到 cluster lod & streaming 这个方案的,以及 rasterize 神乎其技

pc8888888 发表于 2021-6-14 18:18

嗯确实,这么短短的1、2个小时恐怕也很难讲的太细节,不过还是难免会抱着些许期待。

量子计算9 发表于 2021-6-14 18:24

为啥cluster culling没有进行背面剔除

Mecanim 发表于 2021-6-14 18:32

背面剔除是在Rasterize阶段做的

Doris232 发表于 2021-6-14 18:39

到光栅化阶段做,是不是因为三角形的背面剔除会比cluster的背面剔除要高效?不过这两个也差了两个数量级呀,并且IO也会增加

yukamu 发表于 2021-6-14 18:46

只能放在Rasterize里做效率才最高呀,放在 cluster culling 里做只能串行遍历三角形,rasterize里是逐线程光栅化三角形
页: [1]
查看完整版本: Inside Unreal: Nanite