携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情
Mesh Collider的烘焙
物理引擎运行时通常分为Broad phase和Narrow phase这两个步骤。Broad phase收集可能发生的潜在碰撞,然后将之发送到Narrow phase,在这里进行真正的碰撞计算。当生成Mesh Collider中使用的运行时mesh时,重要的是要为mesh生成合适的三角形。物理引擎使用的模型通常要经过一个烘焙过程,在这个过程中会创建物理查询所需的优化空间结构。
烘焙选项
对于预先制作的物理模型,Unity的默认Cooking选项就是最佳的选择,默认的选项为Everyting,这包含:
- Cook for Faster Simulation 为物理模拟优化物理模型,如果不使用可以缩短烘焙时间。
- Enable Mesh Cleaning 去除退化的三角形以及其他几何瑕疵,这有助于更好的计算碰撞检测。
- Weld Colocated Vertices 焊接重叠的顶点
- Use Fast Midphase 针对当前平台使用最佳的加速结构和算法
程序生成物理模型的烘焙时间优化
由于这些烘焙选项都会增加烘焙时间,对于程序在运行时生成的物理模型,如果能保证不产生退化三角形就可以关闭Enable Mesh Cleaning,如果能保证不产生位置重叠的顶点就可以关闭Weld Colocated Vertices。
使用Job System优化运行时烘焙
对于程序生成物理模型这种需要在运行时进行的烘焙,可以使用Job System进行加速,让烘焙在主线程之外的其他线程进行,并且可以多个线程同时烘焙多个模型。
- 首先,定义一个BakeMeshJob
using Unity.Burst;
using Unity.Jobs;
using Unity.Collections;
using UnityEngine;
[BurstCompile]
public struct BakeMeshJob : IJobParallelFor
{
[ReadOnly]
[DeallocateOnJobCompletion]
public NativeArray<int> MeshIds;
public void Execute(int index)
{
Physics.BakeMesh(MeshIds[index], false);
}
}
这个Job使用Mesh ID来调用Physics.BakeMesh来烘焙物理模型。因此在启动job时需要指定Mesh ID的数组。
- 调度Job
var ids = new NativeArray<int>(1, Allocator.TempJob);
ids[0] = meshes[0].GetInstanceID();
var bakeJobHandle = new BakeMeshJob {
MeshIds = ids
}.Schedule(1, 1, bakeJobHandle);
- 等待Job完成 一般在使用Job System时,我们会在一帧的末尾去调度Job,而在下一帧的开始去等待Job完成,这样可以缩短等待的时间,因为一帧末尾调度Job会让Job线程去执行该Job,同时,该帧在主线程上已经计算完毕了,下面就是等待GPU渲染完成,因此Job的执行和GPU的执行可以看成是同步的,等GPU渲染完毕进入下一帧,Job也已经完成或者差不多该完成了,此时再去执行Complete确认他完成,就可以减少等待的时间。
void Update()
{
//一帧的开始,先确认Job完成
bakeJodHandle.Complete();
//其他逻辑
}
Prebake Collision Meshes
这是Unity Player Setting中的一个优化选项:
注意,这儿的Prebake并不是上面说的烘焙Mesh Collider。Unity使用的物理引擎是Physx,Physx对于mesh collider需要计算一个physx mesh内部的表示,默认情况下,是在load关卡时计算的。如果使用LoadLevelAsync异步载入,则这些计算不会影响帧率,但是会增加一些load时间。如果勾选此选项,则Unity会在Build时进行这个计算,特别是当有多个物体使用相同的物理模型,且物理模型最终只会均匀缩放,这些物体就可以共享物理模型,开启选项后,在build时就可以让这些物体引用同一个physx mesh。从而大大节省loading时间。