[性能优化&工具类] 批量Mesh网格压缩

问题描述:

对于3D游戏工程来说,美术资源的存储几乎占据了绝大多数的空间,而对于一个3d 模型文件,MeshFilter(网格过滤器)负责存储物体的网格 以及贴图。依靠MeshRender(网格渲染器)跟据MeshFilter的信息去绘制此物体。Mesh 属性存储众多物理信息,比如顶点的位置、法线、切线。能否在不破坏原有模型外观的情况下尽量减少模型所占体积呢。

解决思路:

这里可以根据模型精度制定压缩规则,使用 Dictionary<VertexAttribute, VertexAttributeFormat> 字典存储(K值对应,顶点依赖属性。V表示它的精度)
比如低模的文件,远景或者边缘物体,小尺寸物体可以不需要添加UV。
根据不同的规则对文件夹下的模型进行分类,依照压缩类型,使用上述字典DescriptorReplaceMap来存储需要替换的顶点属性格式,然后将例如位置、法线和切线的属性格式从Float32或Float16进行替换。这样可以在保持Mesh数据结构不变的情况下,减少存储这些属性所需的内存空间。

压缩过程

根据压缩规则OptimizeType进行分类

OptimizeType 压缩方式

 [SerializeField]
public enum OptimizeType
{
   
    None,
    CompressByUnityDefault,                     //使用unity默认压缩方式
    CompressAllToFloat16,                       //不建议使用,可以使用Unity自带配置
    CompressPosNormalAndTangentTo8Channel,  //基本等同于CompressAllToFloat16,建议高模
    [Obsolete("法线精度较低,不适用高模、低模")]
    CompressPosNormalAndTangentTo6Channel,  //会较大缺少精度
    CompressPosNormalTo6Channel,  //会删除切线并保留较高精度的法线,建议低模+地形
    CompressNormal,                 //不压缩position, 删除切线并保留较高精度的法线(建议精度较大的不分块地形(与剔除UV联用))
    CompressPosToFloat32,                    //只保留position
    [Obsolete("此类型暂时无法使用")]
    CompressPosNormalTo4Channel,    //将法线压缩到pos的w分量,目前unity的pos.w只能存放符号位
}

填充字典 DescriptorReplaceMap

/// <summary>
/// get optimize mesh
/// worldVertexs : 为空时正常压缩,不为空时将会存储相对坐标
/// </summary>
        public static Mesh GetOptimizeMesh(Mesh originMesh, OptimizeType optimizeType, bool withoutUVs = false, Vector3[] worldVertexs = null)
        {
   
            if (originMesh == null || originMesh?.tangents.Length == 0)
            {
   
                Debug.LogWarning("mesh is null or has been optimize");
                return null;
            }
            DescriptorReplaceMap.Clear();
            switch (optimizeType)
            {
   
                case OptimizeType.None:
                    break;
                case OptimizeType.CompressByUnityDefault:
                    DescriptorReplaceMap.Add(VertexAttribute.Position, VertexAttributeFormat.Float32);
                    DescriptorReplaceMap.Add(VertexAttribute.Normal, VertexAttributeFormat.Float16);
                    DescriptorReplaceMap.Add(VertexAttribute.Tangent, VertexAttributeFormat.Float16);
                    break;
                case OptimizeType.CompressAllToFloat16:
                    DescriptorReplaceMap.Add(VertexAttribute.Position, VertexAttributeFormat.Float16);
                    DescriptorReplaceMap.Add(VertexAttribute.Normal, VertexAttributeFormat.Float16);
                    DescriptorReplaceMap.Add(VertexAttribute.Tangent, VertexAttributeFormat.Float16);
                    break;
                case OptimizeType.CompressPosNormalAndTangentTo8Channel:
                    DescriptorReplaceMap.Add(VertexAttribute.Position, VertexAttributeFormat.Float16);
                    //DescriptorReplaceMap.Add(VertexAttribute.Position, VertexAttributeFormat.Float32);
                    DescriptorReplaceMap.Add(VertexAttribute.Normal, VertexAttributeFormat.Float16);
                    break;
                case OptimizeType.CompressPosNormalAndTangentTo6Channel:
                    DescriptorReplaceMap.Add(VertexAttribute.Position, VertexAttributeFormat.Float16);
                    DescriptorReplaceMap.Add(VertexAttribute.Normal, VertexAttributeFormat.UInt16);
                    break;
                case OptimizeType.CompressPosNormalTo6Channel:
                    DescriptorReplaceMap.Add(VertexAttribute.Position, VertexAttributeFormat.Float16);
                    DescriptorReplaceMap.Add(VertexAttribute.Normal, VertexAttributeFormat.Float16);
                    break;
                case OptimizeType.CompressNormal:
                    DescriptorReplaceMap.Add(VertexAttribute.Position, VertexAttributeFormat.Float32);
                    DescriptorReplaceMap.Add(VertexAttribute.Normal, VertexAttributeFormat.Float16);
                    break;
                case OptimizeType.CompressPosToFloat32:
                    DescriptorReplaceMap.Add(VertexAttribute.Position, VertexAttributeFormat.Float32);
                    break;
                case OptimizeType.CompressPosNormalTo4Channel:
                    DescriptorReplaceMap.A
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值