OBJ文件动态载入Unity中的一个算法小问题

FBX、OBJ等格式的3D文件,拖入Unity工程时,都经历了一个内部处理过程以转换成Unity能使用的格式。

通常游戏项目的模型文件都是作为静态资源打包进安装包的。但假设面临在程序运行时,灵活动态获取模型文件,并马上使用的需要,该怎么办呢?

Unity在这块并没有做很好的支持,不要和我说什么AssetBundle,那个也是要预先在Editor里手工处理好才能下载使用的。我是指直接丢过来一个OBJ,程序该如何解析?

 

基本的原理是:利用Unity Mesh类,准备好顶点、法线、面等信息数据,写入Mesh.vertices、Mesh.normals、Mesh.trangles等即可。

这方面有很多老外已经写好了示例工程,网上搜搜都能很容易获取。

(2018.9.12 补充说明:

鉴于经常有人在评论中询问具体的demo和代码,我把找到的参考资料链接贴于此。

OBJ文件格式详解

解析OBJ模型并将其加载到Unity3D场景中

Loading 3d models at runtime in Unity3d

GitHub Project unity-obj-loader

新发现的两个带代码的文章:

unity 加载obj文件的方法

Obj格式解析以及在Unity3D下导入测试

 

但以上不是这篇文章的重点。

 

故事开始在我下载到一个老外的工具工程后,研读了下他的代码,发现其存在两个内部缺陷:

1.对于顶点数超出65000个的情况,没有做很好的处理,仅仅是报错。

        而我要处理的模型有16万顶点,33万面。

2.直接将faces的vertices依次写入Mesh.vertices,然后trangles直接写成{1,2,3,4,5,6,......n}这样的顺序整形数组。

        熟悉3D渲染的应该都知道,模型的顶点索引是记录一份Vector3[],而面的表示只是用前面的顶点索引信息记录一份int[],节省内存空间。作者这种偷懒的解析法,造成了约6倍的重复顶点数,进一步对性能产生压力。

 

为了消除vertices中的重复数据,并重新映射trangles,我一开始写了这么一个算法:

 

List<Vector3> refinedVertexesList = new List<Vector3>();  //新顶点索引容器
List<Vector3> refinedNormalsList = new List<Vector3>();
List<int> trangles = new List<int>();
for (int j = 0; j < tvertices.Length; j++)
{
    if (!refinedVertexesList.Contains(tvertices[j]))
    {
        refinedVertexesList.Add(tvertices[j]);
        refinedNormalsList.Add(tnormals[j]);
        trangles.Add(refinedVertexesList.Count - 1);
    }
    else
    {
        trangles.Add(refinedVertexesList.IndexOf(tvertices[j]));
    }
}

 

大概的思想就是:

新建一个顶点索引列表,

遍历面队列中记录的每个顶点,

假如没加入新索引,就加入,然后修正面index,

假如已经加入了,就只是修正面index。

 

结果一运行,发现程序卡在那了,经测试,整个运算过程处理一个模型要十五分钟左右。。。

 

主要是List的Contains和IndexOf效率都极低,处理6万个顶点,一趟下来就是6万*6万=36亿的次数量级。

然后我试了各种其他容器,Dictionary的问题在于Add极慢,而HashSet虽然Add和Contains都快,但没法得到索引。

 

最后灵机一动,回归最原始的数组,然后就搞定了:

 

List<Vector3> refinedVertexesList = new List<Vector3>();  //新顶点索引容器
List<Vector3> refinedNormalsList = new List<Vector3>();
int[] trangles = new int[od.allFaces.Count];
{
    int[] vis = new int[vertices.Count];
    for (int k = 0; k < od.allFaces.Count; k++) //给总顶点索引中此object faces用到的vertexes做标记
    {
        vis[od.allFaces[k].vi] = 1;
    }

    int[] newVis = new int[vertices.Count];     //用于记录原index对应的新index
    for (int k = 0; k < vis.Length; k++)
    {
        if (vis[k] != 0)
        {
            refinedVertexesList.Add(vertices[k]);
            refinedNormalsList.Add(normals[k]);
            newVis[k] = refinedVertexesList.Count - 1;
        }
    }

    for (int k = 0; k < od.allFaces.Count; k++) //更新faces索引
    {
        trangles[k] = newVis[od.allFaces[k].vi];
    }
}

当然肯定有更刁钻的优化算法,但这个已经将耗时控制在0.1s以下,符合需求了。

 

原理是建立两个最大容量的数组,一个用于记录顶点是否被使用,一个用于记录新旧索引的映射关系。过程中得到新的一份顶点索引和面信息,就ok了。

如此用几十K的内存空间,换到十几分钟的CPU计算时间。

 

算法优化的本质,是操作元素的数量在2万-6万间变动,那么直接按6万的量来开辟空间,多费一点内存,省下了高速Contains()类容器的Add()时间。

所以没事乱用些不合适的容器,是个很傻的行为。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
### 回答1: Unity是一款流行的游戏引擎,可以动态载入多种模型,包括OBJ、FBX等格式。这种动态载入的机制非常适合需要频繁变换场景或者模型的游戏,因为它可以节省存储空间,并且可以更好地优化游戏性能。 Unity载入OBJ、FBX等多种模型可以使用AssetBundles(资产包)。AssetBundles可以将游戏资源文件按需加载,只有当需要用到资源时才会进行加载,可以大大减少游戏的启动时间和占用内存,提高游戏流畅度和稳定性。 在Unity,可以使用AssetBundle.LoadFromMemoryAsync() API来加载AssetBundles。这个API可以异步加载AssetBundles,并将其作为Unity的资源文件来使用。同时,在实际开发,还可以使用AssetBundleVariant来实现不同模型的差异化处理,从而对不同的游戏画面或者场景进行定制化。 最后,值得一提的是,Unity的AssetBundles机制还支持数据压缩和加密,可以更好地保护游戏的模型、图像等资源文件的安全。总之,Unity动态载入模型的机制非常强大,适合开发需要不断更新游戏内容的游戏。 ### 回答2: Unity作为一款游戏引擎,其支持从外部动态载入不同类型的模型,包括obj、fbx等多种格式的模型。这种功能对游戏制作过程动态化、优化和扩展非常有帮助。 动态载入是指在运行时,将一些游戏资源从外部文件加载进来。Unity支持动态载入的模型格式非常丰富,包括目前最流行的3D模型格式obj和fbx等。obj通常用于简单的模型,例如在一些场景需要放置一些简单的道具和器材。而fbx则是一种相对复杂的模型格式,支持更多的属性和特性。在一些大型游戏,fbx一般被用来加载角色、怪物、武器等复杂的三维模型。 通过动态载入模型,可以随时根据游戏需要加载和卸载资源,使游戏播放流畅、运行效率高。同时,还可以加快游戏制作的速度和进度,让开发者可以更灵活地进行游戏制作。此外,动态载入模型还可以让游戏的扩展更加自由,开发者可以随时添加和替换场景的道具、人物等物品,使游戏具有更高的自由度和可玩性。 总之,动态载入多种模型格式是Unity一个非常实用的功能,让游戏制作变得更加的灵活和高效。无论是游戏开发者还是游戏玩家,都可以从受益良多。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值