网格操作非常缓慢

4

我的程序在运行时接收一个网格。该网格可能由超过200k个顶点组成。 我需要将其反转(内部变为外部)。我已经反转了索引,但我还需要翻转法线。到目前为止,我使用了以下循环:

Vector3[] newnormals = new Vector3[mesh.normals.Length];
for (int i=0;i<mesh.normals.Length;i++)
{
    newnormals[i] = -mesh.normals[i];
}

revMesh.normals = newnormals;

其中 "mesh" 是原始网格,当然 "revMesh" 则是反向网格。 我不知道为什么,但这个循环非常慢。在我的 i7 上需要很多秒才能完成。如果我用以下内容代替它:

revMesh.RecalculateNormals();

执行时间降至100毫秒以下。

我的例程为什么效率如此低下?有没有什么方法可以加快速度?

2个回答

4
也许在每次迭代中获取mesh.normals[i]会导致速度变慢,可以尝试以下方法:
Vector3[] newnormals= revMesh.normals;
for (int i=0;i<newnormals.Length;i++)
    newnormals[i] = -newnormals[i];
revMesh.normals = newnormals;

请注意:如果要更改法线(normals),重要的是要从网格中复制法线。一旦复制并更改了法线,就可以将其重新分配回网格。参考文献链接:https://docs.unity3d.com/ScriptReference/Mesh-normals.html。还可以尝试以下方法(未测试,但我认为可以):
using System.Linq;
revMesh.triangles = revMesh.triangles.Reverse().ToArray();

现在你快了一点 ;) 不过这仍然值得怀疑,是否能将时间从“几秒钟”缩短到“少于100毫秒”... - derHugo
@derHugo 刚刚在我发布答案几秒钟后看到了你的评论。 - Mohammad Zamanian
2
@RufusL 不行。像Unity中的大多数这样的值,它不是一个字段,而是一个属性。因此,您例如不能执行mesh.normals[0] = new Vector3(x,y,z);这样的操作,而必须始终复制整个数组,修改它并将整个数组作为状态写回。请注意:要更改法线,重要的是从网格中复制法线。一旦复制并更改了法线,就可以将法线重新分配回网格。 (https://docs.unity3d.com/ScriptReference/Mesh-normals.html) - derHugo
性能飙升!执行时间降至95毫秒。RecalculateNormals()大约为65毫秒,我仍然不知道它如何如此快...但95毫秒绝对可以接受。非常感谢! - kefren
1
@derHugo 谢谢,我会相信你的话。但我还不确定它是属性有什么区别 - 引用类型不总是引用类型吗?在“正常”的C#中,如果我有一个类Foo,其中有一个int[] Bar {get; set;}属性,并且我分配int[] x = fooInstance.Bar;,那么修改x[n]也将修改fooInstance.Bar[n]。这必须是属性getter返回数组的副本而不是引用。 - Rufus L
@RufusL 没错 ;) - derHugo

2
主要问题在于读取mesh.normals并不能直接访问该数组。Unity会创建该数组的副本并将其传递给您。在每次循环迭代中,您都会读取mesh.normals两次,因此对于具有200k法线的网格,您将创建400k个该数组的副本。这可能会非常慢。

如果向Unity请求一个数组的副本,然后对其进行处理,并在完成后将数据传回Unity,速度会快得多。

Unity的内部类有一些属性,每次读取时都返回新的副本。如果不确定,请查阅手册。


非常感谢您。您的解释很清晰。我是一名老式汇编语言程序员,有时很难理解类似C#这样的高级语言底层发生了什么,但现在您已经解释了,我明白了问题所在。非常感谢! - kefren
@kefren 这是一个有见地的问题。考虑到你的知识,这个性能问题一定看起来非常奇怪! - rutter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接