0

我有一个地形网格,每帧只需要更新每个顶点的 Z 值。我当前的方法如下所示:

int stepping = CustomVertex.PositionNormalTextured.StrideSize / 4;

//ZPtr points to the Z value of the first PositionNormalTextured in the mesh.  
//This way we don't have to dereference ->Z for each vertex.
float* ZPtr = &(((CustomVertex.PositionNormalTextured*)
    TerrainMesh.LockVertexBuffer(LockFlags.NoOverwrite).InternalDataPointer)->Z);

float* DPtr = TerrainHeight; //point to begin scanning result
float* EndPtr = DPtr + TerrainMesh.NumberVertices; //point to stop scanning result

do { *ZPtr = *DPtr; ZPtr += stepping; } while (++DPtr < EndPtr); //copy data
TerrainMesh.UnlockVertexBuffer(); //unlock

这里,TerrainHeight 是一个使用 Marshal.AllocHGlobal 创建的浮点数组,表示地形高度。基本上,它扫描整个 TerrainHeight 数组并将每个值复制到网格中相应 PositionNormalTextured 的 Z 值。我使用 LockFlags.NoOverwrite 来避免创建数组的新副本,尽管这似乎并不比 LockFlags.Discard 快。

更新网格所需的时间与在 CPU 中计算新地形所需的时间一样长或更长,这让我相信应该有更快的方法。我一直无法在 Google 上找到有关此的信息。有没有更好的方法来更新顶点缓冲区?如果重要的话,网格的大小是用户设置的,可能包括超过一百万个顶点(这是通过多个网格完成的),但默认设置是 32k 顶点,这是单个 D3D 网格的最大值。

4

1 回答 1

0

看来您不了解DiscardNoOverwrite标志的后果。阅读DirectX SDK 帮助中性能优化下的使用动态顶点和索引缓冲区部分。假设您使用的是动态顶点缓冲区,则意味着“我正在替换整个缓冲区”并且意味着“我正在写入缓冲区的未使用部分,并且我保证不会更改我已经使用的任何部分”。DiscardNoOverwrite

使用任一标志,您都必须编写地形顶点的每个组件,即使是在新框架中未更改的组件。

如果您不使用动态顶点缓冲区,那么当您尝试锁定下一帧的顶点缓冲区时,如果您的 GPU 仍在使用它,您可能会遇到停顿。在这种情况下,您将需要使用多个顶点缓冲区,并在地形高度发生变化的每一帧中使用不同的缓冲区进行锁定、更新、解锁和渲染。您还必须使用所有地形顶点数据初始化所有这些缓冲区。

我建议将网格的 z 值分离到它们自己的顶点缓冲区中——假设你没有更新 x 和 y 的位置、你的法线(如果 z 改变可能不正确)或你的纹理坐标。这样你就可以使用你的PositionNormalTextured(没有位置 z 的)顶点缓冲区,每帧都保持不变,并从头开始使用Discard标志填充你的动态 z 位置缓冲区,而不需要跨到每个 Z 值。由于步幅消失了,您可以使用平面内存副本来做到这一点。

您将为顶点着色器提供 z 位置值SetStreamSource( 1, ZPositionVB, ... )。您需要调整顶点声明以从流 1 中读取 z 位置值,并调整顶点着色器以在转换之前组合 z 位置值。

如果其中一些不适合 C#,我们深表歉意。

于 2012-06-23T00:43:34.463 回答