复制网格一千次并且没有大的性能影响动画?

时间:2016-09-21 05:39:30

标签: javascript 3d three.js webgl

我有一个演示,其中我使用了数百个具有完全相同的几何和纹理的立方体,例如:

texture  = THREE.ImageUtils.loadTexture ...
material = new THREE.MeshLambertMaterial( map: texture )
geometry = new THREE.BoxGeometry( 1, 1, 1 )

cubes = []
for i in [0..1000]
  cubes.push new THREE.Mesh geometry, material

... on every frame

for cube in cubes
  // do something with each cube

创建所有立方体后,我开始在屏幕上移动它们。

所有这些都具有相同的纹理,相同的大小,它们只是改变位置和旋转。这里的问题是,当我开始使用数百个立方体时,计算机开始受到渲染。

有什么方法可以告诉Three.js / WebGL所有这些对象都是同一个对象,它们是不同位置的相同副本吗?

我准备了一些关于BufferGeometry和Geometry2能够在这种情况下提高性能的东西,但我不确定在这种情况下最好的是什么。

谢谢

2 个答案:

答案 0 :(得分:3)

  

有什么方法可以告诉Three.js / WebGL所有这些对象都是   同一个对象,它们只是位于不同位置的相同副本?

不幸的是,在这方面没有什么可以自动确定和优化渲染调用。那将是非常棒的。

  

我读过一些关于BufferGeometry和Geometry2能够在这种情况下提高性能但我不确定在这种情况下最好的情况。

所以,这里的细节是这样的:正常的THREE.Geometry - 类three.js提供了为开发人员提供方便,但与WebGL处理数据的方式有点差异。这就是DirectGeometry(以前称为Geometry2)和BufferGeometry的用途。 BufferGeometry表示WebGL如何保持drawcalls的数据:它包含几何体的每个属性的类型化数组。每次Geometry设置为true时,都会自动从BufferGeometry转换为geometry.verticesNeedsUpdate

如果你没有改变任何属性,这个转换将在每个几何体(你有1个)时发生一次,所以这是完全正确的,并且移动到缓冲区 - 几何形状不会有帮助(简单来说)因为你已经在使用它了。)

您面对几百个几何的主要问题是渲染场景所需的drawcall数。一般来说,THREE.Mesh的每个实例都代表一个drawcall。那些抽奖活动很昂贵:输出数十万个三角形的单个抽屉完全没有问题,但是成千上万个每个100个三角形的抽屉会很快成为一个严重的性能问题。

现在,有多种方法可以使用three.js减少drawcalls的数量。第一个是(如评论中已经提到的)将多个网格/几何组合成一个网格(最后,网格只是三角形的集合,所以不要求它们形成单个网格/#34;身体"或类似的东西)。这在你的情况下并不实用,因为这将涉及通过JS应用每个立方体的位置和旋转,并相应地在每个帧上更新顶点数组。

您真正需要的是一个称为几何实例的WebGL功能。 这不像常规网格和几何图形那么容易使用,但也不是太复杂。

通过实例化,您可以在一个drawcall中创建大量对象。所有渲染的对象将共享一个几何体(您的立方体几何体及其顶点,法线和uv坐标)。当您添加名为InstancedBufferAttribute的特殊属性时,会发生实例化,这些属性可以包含每个实例的独立值。因此,您可以为位置和旋转添加两个每实例属性(如果您愿意,可以添加单个实例转换矩阵)。

这些例子应该是您正在寻找的: http://threejs.org/examples/?q=instancing

现在实例化的唯一困难是材料:您需要提供一个自定义顶点着色器,它知道如何将每个实例属性应用于原始几何体的顶点位置(这也可以是在例子的代码中看到。)

答案 1 :(得分:3)

你有一个webgl标签,所以我要给出一个非三个js的答案。

处理这个的最好方法是分配一个由模型变换矩阵数据组成的浮点纹理数组(或者只需要vec3位置,如果你需要的话)。然后分配包含所有多维数据集数据的网格块。您需要添加我称为modelTransform index的其他属性。对于网格块中的每个“多维数据集实例”,请在模型转换数据纹理中写入与正确偏移对应的正确modelTransform index值。

在每个帧上,为所有立方体计算正确的模型变换数据,并使用正确的偏移量等写入模型变换数据纹理。将纹理上传到每帧的GPU上。

在顶点着色器中,从modelTransform index属性和浮动纹理访问模型转换数据。休息是一样的。

这是我在我的引擎中使用的,它适用于像立方体这样的小物体。但请注意,在60 FPS上更新150000多维数据集可能会占用JS的大部分CPU资源。无论您采用哪种实例方案,这都是不可避免的。

如果每个立方体的运动/动画是固定的,那么更好的方法是为每个立方体实例上传速度属性和初始创建时间戳属性。在每个帧上,将当前时间发送为统一,并将位置计算为"pos += attr_velocity * getDeltaTime(attr_initTime, unif_currentTime);"。这会一起跳过CPU上的工作,并允许您渲染更多数量的立方体。