当使用大量的3D形状填充时,FMX ViewPort3D 性能下降。

3

我在一个多设备应用程序中有一个ViewPort3D元素,其中填充了大量的TRectangle3D元素(数量从1到10000不等),这些元素都需要应用LightMaterialSource并且需要动态渲染,因为我还使用以下过程旋转相机:

procedure TForm3.Viewport3D1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
var
  I: IViewport3D;
begin
  if ssRight in shift then
  begin
    I:=ViewPort3D1;
    with tdummy(I.CurrentCamera.Parent) do RotationAngle.X:=RotAng.X - Y;
    with tdummy(I.CurrentCamera.Parent.Parent) do RotationAngle.Y:=RotAng.Y + X;
  end;
end;

然而,当要渲染的矩形数量接近几十个时,ViewPort3D 的性能开始明显下降。随着更多矩形添加到视口中,摄像机旋转变得越来越慢和不响应,直到成为幻灯片。

有没有方法可以改善 ViewPort3D 的性能,而无需删除这些矩形?

我尝试将 Multisample 属性设置为“none”:ViewPort3d1.Context.SetMultisample(TMultisample.None),以及从所有矩形中移除 MaterialSource。虽然这在一定程度上有助于性能提升,但并未完全解决问题。

2个回答

1

10k drawed cube

在我的测试中,10k。

Context.Draw/Fill.cube

在渲染事件中绘图效率更高,但当相机角度改变时性能显著下降。
TRectangle3D创建10k个物体后,在Windows上显示时会出现严重的性能问题。
如果在TRectangle3D创建之后设置visible=false,则渲染速度非常快,
但是,当visible=true时,当相机角度改变时性能会严重下降。
据我所见,减速的原因是CPU上的操作,即GPU不是这里减速的部分,但当我检查代码时,有大量的事件类型消息通知到on mousemove对象。
我的建议是,如果要使用大量可见对象, 如果对象超出相机视野,请隐藏对象(visible=false),在每个mousemove事件中循环隐藏对象,查看对象是否在相机区域内,这将有助于提高性能。
这里不仅存在10k个对象的问题,还有许多缺点。 基本上,这可以用作简单工作的简单绘图3D引擎。
据我所知,在Java中,就像viewport3d对象一样,我们可以将Unity引擎图形窗口添加到Java应用程序的表单中,
我不知道Unity引擎的图形窗口是否可以添加到Delphi中,您可以向Embercadero请求支持,但对于高级图形来说,使用像虚幻引擎,Unity专业优化引擎等更为合理。

不幸的是,对象几乎从不超出相机视野,因此隐藏它们并不能帮助解决问题,尽管这是一个好主意。 我注意到,当我旋转相机时,任务管理器中的GPU使用率可能会飙升到50%(该PC安装的GPU也非常强大),而CPU保持在正常的8%。 - Crockinline
你的处理器型号是什么? 它有多少个CPU核心? 例如,如果你有12个核心,100%的CPU使用率, 100/12=8.33每个核心的CPU使用率, 如果你的应用程序在CPU使用率上不超过8.33,那么这意味着代码正在单线程上运行,这是限制性能的关键点,也就是瓶颈。请检查它。 - user6050132

0

每个高级FMX 3D对象都会产生一个“绘制调用”(drawcall)。这个过程会重新处理所有内容,从CPU(网格准备)到GPU(显示)-> 您始终需要最小化绘制调用次数。

因此,显示10000个FMX矩形永远不是解决方案。:)

您只需创建1个Tmesh子类,它将在一次调用中绘制您的10000个“手工制作”的矩形。

请查看TMesh.Data(data.points和data.triangleindice),以了解如何在直接模式下绘制3D对象。或者更简单地说,请查看源代码中的“TPlane”是如何构建的。

作为一般计划和基本的“3D制作”方法,制作“复杂”的3D FMX对象是可能的,但您必须处理顶点/索引,以便在一次调用中尽可能多地绘制物体。

例如,以下是实现它的代码:

将TMesh放在您的视口上,并调用此过程:


Procedure PopulateMesh_RectangleMap(aMesh : TMesh; const xCount : integer = 100; const yCount : integer=100; const rectWidth : single = 2.0; const rectHeight : single= 1.0);
var lv,li : integer;
    lsx, lsy, xpos, ypos : single;
    i,j : integer;
    a,b,c,d : TPoint3d; //4 corner of a rect.
begin
  Assert(assigned(aMesh));

  lsx := rectWidth;
  lsy := rectHeight;
  li := 0;
  lv := 0;

  //mem. allocation.
  aMesh.Data.Clear;
  aMesh.Data.VertexBuffer.Length := 4 * xCount * yCount;     //4 vertices by rect, need k*k rects.
  aMesh.Data.IndexBuffer.Length := 6 * xCount * yCount;     // 6 indices for 2 triangles desc (to make 1 rect) for k*k rects.

  for i := 0 to xcount-1 do
  for j := 0 to ycount-1 do begin

    xpos := -xcount/2 + i + i*lsx;
    ypos := -ycount/2 + j + j*lsy;

    a := point3d(xpos - lsx/2,ypos - lsy/2,0);
    b := point3d(xpos + lsx/2,ypos - lsy/2,0);
    c := point3d(xpos + lsx/2,ypos + lsy/2,0);
    d := point3d(xpos - lsx/2,ypos + lsy/2,0);

    aMesh.Data.VertexBuffer.Vertices[li+0] := a;
    aMesh.Data.VertexBuffer.Vertices[li+1] := b;
    aMesh.Data.VertexBuffer.Vertices[li+2] := c;
    aMesh.Data.VertexBuffer.Vertices[li+3] := d;

    aMesh.Data.IndexBuffer.Indices[lv+0] := li+0;
    aMesh.Data.IndexBuffer.Indices[lv+1] := li+1;
    aMesh.Data.IndexBuffer.Indices[lv+2] := li+2;
    aMesh.Data.IndexBuffer.Indices[lv+3] := li+2;
    aMesh.Data.IndexBuffer.Indices[lv+4] := li+3;
    aMesh.Data.IndexBuffer.Indices[lv+5] := li+0;

    inc(li,4);
    inc(lv,6);
  end;
  aMesh.Data.BoundingBoxNeedsUpdate; //We touch data. Update Mesh container.
  aMesh.Width := lsx*3;              //keep proportion.
  aMesh.Height := lsy*3;
  aMesh.Repaint;
end;

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