2.Square Grid

1.Procedural Mesh Job Framework

2.A Grid of Quads


1.Procedural Mesh Job Framework

csharp 复制代码
1).通用顶点

定义一个通用的Vertex结构体来保存顶点数据
csharp 复制代码
将顶点结构放在一个命名空间namespace(ProceduralMeshs)中
csharp 复制代码
2).网格流

a.为了存储网格数据, 我们需要定义"顶点缓冲区和索引缓冲区"

b.这次, 我们通过一个接口ProceduralMeshes.IMeshStreams负责"设置顶点缓冲区和索引缓冲区", 对外隐藏细节
csharp 复制代码
它的首要职责是初始化网格数据, 我们通过一个Setup方法, 以网格数据, 顶点数和索引数作为参数
csharp 复制代码
将顶点的数据写入到顶点缓冲区中
csharp 复制代码
将索引写入到索引缓冲区中, 使用三角形比使用单独的索引更方便, 我们将定义一个SetTriangle方法, 以三角形索引和一个

int3顶点索引三元组作为参数
csharp 复制代码
该接口最直接的是单流方法, 我们将此类型命名为SingleStream, 它必须是"一个结构体才能与Burst作业"配合使用; 我们

将流实现分在ProceduralMeshes.Streams嵌套空间中
csharp 复制代码
添加Setup使用它来定义"网格的缓冲区", 立即设置子网格, 暂不考虑其边界
csharp 复制代码
我们引入一个新的私有结构体Stream0, 这样外部的Vertex应对需求变化, 增加新的字段时, 不会影响到这里的逻辑; 通过附

加StructLayout(LayoutKind.Sequential)属性来确保字段顺序固定
csharp 复制代码
SetVertex将顶点数据写入到顶点缓冲区中
csharp 复制代码
如果SetVertex方法变得复杂多, 比如: 将部分数据存储为16位值, 则需要转换; 这种情况下Burst可能不会对该方法进行内

联, 而是每次设置顶点时插入一条调用指令, 该方法速度较慢; 必须指定该方法可以被Burst进行内联, 可以给方法添加特性

[MethodImpl(MethodImplOptions.AggressiveInlining)]
csharp 复制代码
最后, 我们可以通过将索引数据重新解释为int3 triangle数据
csharp 复制代码
3).网格生成器

我们通过另一个接口Procedural.IMeshGenerator负责"生成网格的代码", 它定义了Job执行的代码, 因此需要有一个带有

index参数的Execute方法; 还需要一个IMeshStreams类型的参数
csharp 复制代码
生成网格, 需要知道网格的顶点数目和索引数目
csharp 复制代码
调度任务时还需要知道任务长度(数目)
csharp 复制代码
定义方形网格, 我们通过SquGrid实现该接口, 其中的参数目前保持默认值


csharp 复制代码
4).网格Job

定义一个Job System中Job来生成网格, 引入ProceduralMeshes.MeshJob类型; 这是一个泛型IJobFor结构体, 具有网格相

关的参数: IMeshGenerator和IMeshStreams参数; 其Execute方法调用直接转发给生成器, 传入索引和流


csharp 复制代码
由于我们生成网格时写入流而不从中读取, 因此可以添加WriteOnly属性, 间接将只写状态应用于IMeshStreams所实现包含

的原生数组
csharp 复制代码
最后创建一个静态方法ScheduleParallel, 该方法返回的参数是任务句柄, 该方法需要网格数据和任务依赖项作为参数
csharp 复制代码
5).创建程序化网格脚本(Component)
csharp 复制代码
Awake中创建网格对象, 生成网格, 并将其赋给MeshFilter; 将网格生成的代码放在单独的GenerateMesh方法
csharp 复制代码
分配网格可写的网格数据, 调用MeshJob中的ScheduleParallel方法, 使用SquareGrid和SingleStram类型


csharp 复制代码
6.生成一个四边形

由于我们未设置 SquareGrid.JobLength的数目, 因此Job没有被调度, 因此得到是一个空网格
csharp 复制代码
设置Job的数目是1
csharp 复制代码
进入播放模式时, 我们会收到一个无效操作异常, 提示两个容器可能是同一个对象; 提示两个容器可能是同一个对象, 这指

的是SingleStream的两个原生数组; Unity抱怨它们可能存在别名, 这意味着这些原生数组可能代表重叠的数据

原因是所有网格数据都是一个单一的非托管内存块, 我们的任务试图同时访问该数据的两个子部分------顶点部分和三角形索引部

分------而Unity不允许这样做, 因为这可能导致错误的结果
csharp 复制代码
通常, Unity的安全检查是有效的, 应当予以遵守; 但在本例中, 我们确定顶点和索引数据绝不会重叠; 因此, 我们将通过将

Unity.Collections.LowLevel.Unsafe命名空间中的"NativeDisableContainerSafetyRestriction"属性附加到两个原生数组

字段来禁用安全检查
csharp 复制代码
为了测试我们的框架, 我们首先生成一个四边形; 顶点数目为4
csharp 复制代码
四边形网格生成器中, 创建一个通用的顶点数据, 设置其法线和切线向量, 这些向量对所有顶点都是相同的; 由于所有值都

初始化为零, 我们只需设置非零向量即可

注:

"我们跳过了逐顶点处理的常规循环, 直接一次性生成一个四边形(两个三角形)的所有顶点数据; 为了实现这种高效操作," 

"我们关闭了Unity的安全检查系统"


csharp 复制代码
设置索引的数目为6, Excute的末尾设置两个三角形
csharp 复制代码
我们运行的时候还会出现一个参数异常的错误, 当在 SingleStream.Setup 中设置子网格时会发生这种情况; 当我们调用

SetSubMesn的时候, 会立即验证三角形索引并重新计算边界, 这肯定会失败, 因为任务尚未运行, 索引缓冲区包含的是任意

数据; 我们必须提供MeshUpdateFlags来指示SetSubMesh不应对数据执行任何操作 

"DontRecalculateBounds和DontValidateIndices(不要校验索引)" 
csharp 复制代码
7.Bounds 边界

我们的网格现在缺少的就是有效的边界, 网格生成器应该提供这些边界, 因此向IMeshGenerator接口添加一个属性来获取它
csharp 复制代码
SquareGrid中实现
csharp 复制代码
要设置边界, 为网格向MeshJob.SchduleParallel添加一个参数, 将其设为第一个参数; 可以在创建Job后立即设置网格边界
csharp 复制代码
还需要设置子网格的边界, 需要将边界作为第二个参数添加到IMeshStreams.Setup中
csharp 复制代码
调整SingleStream.Setup, 设置子网格的边界和顶点数
csharp 复制代码
直接使用网格边界赋值表达式的结果作为 Setup 的参数
csharp 复制代码
8).16位索引

将三角形索引从32位减少到16位, 这样索引缓冲区的大小减半; 在ProceduralMeshes.Streams命名空间中定义一个新的类型

"TriangleUInt16"
csharp 复制代码
更改三角形索引元素类型和索引缓冲区格式, 即可将SingleStream切换为16位索引
csharp 复制代码
9).多顶点流

创建一个新的类MultiStream, 将单一流替换为用于各个顶点属性的四个流

2.A Grid of Quads

csharp 复制代码
现在我们已经有了一个可用的框架, 接下来我们将生成一个由多个四边形组成的网格, 这些四边形被排列成一个规则的方形网

格

csharp 复制代码
1).网格分辨率

IMeshGenerator中添加一个属性Resolution, 表示网格的分辨率, 用于生成Resolution * Resolution的方形网格
csharp 复制代码
SquareGrid中实现该属性
csharp 复制代码
顶点数量, 索引数量, 任务长度现在取决于分辨率的平方
csharp 复制代码
向MeshJob.ScheduleParallel添加分辨率参数, 创建作业后使用它来设置生成器的分辨率
csharp 复制代码
然后向ProceduralMesh添加一个分辨率滑块, 并在生成网格时使用


csharp 复制代码
2).生成所有四边形

在Execute开始时确定正确的顶点和三角形索引, 传递给Execute的作业索引代表"四边形索引"; 其第一个顶点索引是该索引

的四倍, 第一个三角形索引是该索引的两倍
csharp 复制代码
设置顶点数据
csharp 复制代码
设置索引数据
csharp 复制代码
Execute中传递的参数是四边形的索引, 我们还需要确定当前的四边形在整个范围的具体位置, 比如说: 第几行, 第几列

因此将一维索引转换为二维索引
csharp 复制代码
使用一个float4值定义四边形所需的全部四个左边, 包括x, x + 1, y, y + 1; 刚开始使用0.9, 在四边形之间流出可见的

间隙
csharp 复制代码
对坐标进行重排操作来设置正确位置
csharp 复制代码
3).一个平面

网格通常用于平面, 因此调整网格, 使其位于XZ平面; 将y重命名为z, 同时闭合四边形的间隙
csharp 复制代码
通过将网格方向分配给顶点位置的XZ分量而非XY分量来改变其朝向
csharp 复制代码
更改法线向量, 使其指向上方
csharp 复制代码
让平面以原点为中心, 比如: (0, 2) -> (-0.5, 0.5)
csharp 复制代码
调整边界
csharp 复制代码
4).以行为单位生成四边形

我们当前的任务是独立生成网格中的每个四边形, 创建单个四边形的工作量不大, 每个四边形都必须单独计算, Unity的任务

框架还会增加额外的开销; 我们可以通过在单次调用中合并生成多个四边形来提高效率, 将单行的所有四边形一起生成是最合

理的做法; "这将使任务长度等于分辨率, 而不再是其平方"
csharp 复制代码
让每次Execute调用负责处理沿X轴的一整行四边形, 那么Z表示"第几行"
csharp 复制代码
现在我们不再使用固定的X偏移, 而是为整个行引入一个循环, 该循环包含了填充流的代码
csharp 复制代码
每次循环迭代后, 我们必须将顶点索引增加四, 将三角形索引增加二
csharp 复制代码
Burst可以检测循环内永不改变的代码, 并自动将其提取到循环外; 然而, 它不会拆分向量, 因此我们可以通过手动将坐标向

量拆分为独立的X和Z对来进行一些优化; Z坐标的计算是常量, 因此将被提升到循环外
相关推荐
咸鱼永不翻身8 小时前
Unity视频资源压缩详解
unity·游戏引擎·音视频
在路上看风景8 小时前
4.2 OverDraw
unity
在路上看风景9 小时前
1.10 CDN缓存
unity
ellis197019 小时前
Unity插件SafeArea Helper适配异形屏详解
unity
nnsix20 小时前
Unity Physics.Raycast的 QueryTriggerInteraction枚举作用
unity·游戏引擎
地狱为王20 小时前
Cesium for Unity叠加行政区划线
unity·gis·cesium
小贺儿开发1 天前
Unity3D 八大菜系连连看
游戏·unity·互动·传统文化
在路上看风景1 天前
25. 屏幕像素和纹理像素不匹配
unity
ۓ明哲ڪ1 天前
Unity功能——创建新脚本时自动添加自定义头注释
unity·游戏引擎
熬夜敲代码的小N1 天前
Unity大场景卡顿“急救包”:从诊断到落地的全栈优化方案
java·unity·游戏引擎