UE5-Nanite构建

16 篇文章 2 订阅
11 篇文章 1 订阅

在MeshBuilderModule.cpp的FMeshBuilderModule::BuildMesh中执行到StaticMeshBuilder.cpp的BuildNanite会执行nanite的构建NaniteBuilderModule.Build

 

执行到NaniteBuilder.cpp的FBuilderModule::Build

分线程构建三角形

首先从Sections中拿到材质索引设置到MaterialIndices中。执行BuildNaniteData,根据三角形的数据执行ClusterTriangles,分多个线程来构建三角形数据。

 

并通过Partitioner.BuildLocalityLinks构建三角形中心点以及拿到周边最近的点。

然后再ClusterTriangles中把三角形写入cluster

 

模糊法构建簇

然后在BuildNaniteData中根据mesh的数据构建DAG(BuildDAG

DAG的簇组织方式是针对网格计算中大规模、异构、动态环境下的任务调度问题解决方案,提出了一种基于模糊聚类的启发式算法。许多以前的调度算法需要搜索和比较目标系统中的每个处理单元,以便为任务选择合适的处理单元。尽管这些方法可以获得令人满意的制造跨度,但毫无疑问,它会增加整个运行时间。本文定义了一组描述目标系统中处理单元综合性能的特征。在这些特征的基础上,采用模糊聚类的方法对目标系统即处理单元网络进行预处理,以实现处理器网络的合理聚类。在调度阶段,首先选择综合性能较好的集群。不需要在每个调度步骤中搜索目标系统中的每个处理单元。因此,它大大降低了选择哪个处理单元来执行当前任务的成本。就绪任务优先级的设计不仅考虑了关键路径上节点的执行所产生的影响,还考虑了异构资源对任务调度的影响。最后对算法的性能进行了分析,并与其他算法进行了比较,测试结果表明,目标系统越大,算法的性能越好。

 

执行到ClusterDAG.cpp的BuildDAG,这里会拿到前一步拿到的Clusters,构建出LevelClusters,首先执行DAGReduce,在DAGReduce中首先对Clusters根据GUID排序,然后对Cluster里的内容执行Merged.Simplify坍塌的减面。剔除不需要的三角面后再进入Clusters中。

 

并建立FClusterGroup,把cluster放入FClusterGroup中。并组织FClusterGroup的数据结构,包括lod,父级等。

 

粗糙Nanite代理网格体

然后如果要有一个粗略的替代nanite网格体的数据的话,再BuildNaniteData中执行BuildCoarseRepresentation,首先通过FindDAGCut会从FClusterGroup中找到最高容错的簇进行替换,然后通过CoarseRepresentation.Simplify做三角形简化,然后将简化后的三角形信息写入到Verts中

 

Vertex是一个静态网格顶点数据FStaticMeshBuildVertex。

然后执行NaniteEncode.cpp的BuildMaterialRanges,主要是填充每个三角形的三个顶点的索引以及材质索引信息。

 

然后根据材质索引排序,为的是后面让一样的材质的索引范围确定到FMaterialRange

 

 

然后再BuildCoarseRepresentation中找到Sections数据对应的材质范围,如果截面的源数据中没有足够的三角形,则可以从粗网格中删除截面。并跟新节点数据。

 

最后在BuildCoarseRepresentation中执行CalcTangents来执行Mikktspace的标准来计algorithm线贴图的切线空间。

 

然后在BuildNaniteData中更改节点的数据为粗糙的三角形数据。

 

Nanite编码

然后再BuildNaniteData中执行Encode,会到NaniteEncode.cpp的Encode。

移除老数据

里面首先会执行RemoveDegenerateTriangles,这里主要是让簇里的索引和材质等都用老的数据代替。

 

材质索引范围

然后是BuildMaterialRanges,这个在前面的粗糙三角形替代体执行的时候已经讲过了,核心是获取三角形每个顶点的材质索引范围。

然后是ConstrainClusters,首先他用另一个线程来执行Stripifier.ConstrainAndStripifyCluster

 

然后对每个簇判断如果顶点数大于256个,就执行二分的方式来构建ClusterA和ClusterB放入Clusters数组。BuildClusterFromClusterTriangleRange这里就是通过给到一个区间来构建一个簇的。也就是nanite的簇的顶点数是不能大于256个的。

 

三角形的位置量化信息

然后是CalculateQuantizedPositionsUniformGrid,这里首先会拿到系统设置的位置信息的精度,如果网格更密集,我们需要更高的分辨率。使用簇大小的几何平均值作为密度的代理。也就是位精度是集群所需的平均值。

 

 

并且在精度上不会完全按照用户的设置,因为ue认为对磁盘大小的节省贡献甚微(在测试项目中约为0.4%)而且会产生效果上的问题。

 

然后用另一个线程来执行网格体的bound范围以及三角形的位置量化信息。

 

最终通过CalculateQuantizedPositionsUniformGrid返回位置精度信息。

打印材质范围

然后是PrintMaterialRangeStats,这里是打印材料范围统计信息。不细说了。

三角形信息编码

然后是CalculateEncodingInfos(这个方式主要对代理三角形的顶点,索引,材质,uv等的编码),这里是首先要修改FPageSections数据结构里面的数据,FPageSections里是关于页里的簇的三角形索引以及材质信息。然后要修改FEncodingInfo,其中ColorBits是拿到当前簇的顶点颜色后得到每个颜色通道的最大值和最小值

 

然后存在ColorBits中,ColorBits中记录索引,索引使用每个索引的ceil(log2(NumClusterices))位存储在密集压缩比特流中。

 

然后记录uv,块压缩纹理坐标纹理坐标相对于簇最小/最大UV坐标存储。UV接缝会产生非常大的稀疏边界矩形。为了缓解这种情况,从编码空间中排除边界矩形的U和V中的最大间隙。解码非常简单:UV+=(UV>=间隙)?GapRange:0;生成排序的U和V数组。

 

分配簇到分页

然后是AssignClustersToPages,首先对ClusterGroups内的子对象执行排序SortGroupClusters,排序规则是拿(1.0f, 1.0f, 1.0f)与Clusters的两个数据的点乘比较,得到点乘的排序。

 

然后对ClusterGroups执行CalculateClusterGroupPermutation生成群集组的排列,首先按mip级别排序,然后按移动变量顺序x、y和z排序。按mip级别排序首先确保形成的页面之间不存在循环依赖关系。

得到这个排序后的clustergroup后,设置group的起始位置和结束位置

 

然后累加去重算bounds

 

层级关系组合

然后是BuildHierarchies构建层级关系,将组零件指定给它们所属的网格,

 

构建每个mesh的每个lod的叶子节点

 

层级的子节点组织

然后如果只有一片叶子。需要使用特殊情况,因为根应该始终是内部节点。

 

不然的话就开始构建nanite的网格层级数据。

Nanite网格包含多层次细节的簇数据。来自不同细节层次的集群在大小上可能存在巨大差异,这对于构建良好的层次结构来说已经是一个挑战。除了可见性界限外,层次结构还跟踪子节点的保守LOD错误度量。只要子对象可见,运行时遍历就会下降到子对象中,并且保守的LOD错误并不比我们所寻找的更详细。当混合来自不同LOD的聚类时,必须非常小心,因为不太详细的聚类很容易导致边界和错误度量膨胀。

目前看来,为每个LOD级别构建单独的层次结构,然后构建这些层次结构的层次结构可以提供最佳和最可预测的结果

但是这些层次结构的根都共享相同的可见性和LOD边界,或者至少足够接近,我们可以在不损失太多的情况下创建一个共享的保守边界。这使得围绕根节点的大量工作变得相当冗余。

 

其中BuildHierarchyTopDown是一个自上而下的拆分方法构建层次结构。

到目前为止,它只关注最小化最坏情况下的树深度/延迟。它通过构建一个完整的树来实现这一点,其中最多有一个部分填充的级别。最多有一个节点被部分填充。

构建完层级结构后,对每个层级的node执行PackHierarchyNode写入Resources.HierarchyNodes中。

写入页

最后是WritePages,这里首先会创建FixupChunk这个Chunk,首先写入NumHierarchyFixups到头文件,然后构建FClusterFixup然后写入到FixupChunk当作额外的fixup信息。然后对每个页里面的FixupChunk的头文件中的cluster数量来拿到每个Fixup的依赖数据。

 

然后利用多线程写入几何数据EncodeGeometryData

 

在EncodeGeometryData里面通过BitWriter_PositionBitWriter_Attribute两个字节写入工具FBitWriter写入位置信息以及ColorBit信息。

然后再WritePages中通过内存拷贝把簇索引,材质信息,三角形索引,剔除字节遮罩StripBitmaskData,顶点引用,位置信息,nanite属性

然后最后写入到Resources.StreamableClusterPages中。

 

软光栅化

然后用另一个线程执行ImposterAtlas.Rasterize

 

这里分成tile来光栅化

再FImposterAtlas::Rasterize里首先转到Imposter空间,imposter是ue用于实现多个不同得角度下的公告牌面片实现的效果,然后把簇得顶点位置转到Imposter空间。

然后簇得三角形拿到上面顶点得位置执行RasterizeTri执行软件得光栅化处理。

 

完成整个nanite得构建。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值