更新Direct3D网格顶点缓冲区的最快方法?

时间:2012-06-02 23:31:46

标签: c# .net directx direct3d mesh

我有一个地形网格,每个顶点只需要更新每个顶点的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中新地形的时间长或长,这让我相信应该有更快的方法。我一直无法在谷歌上找到关于此的信息。有更好的方法来更新vertexbuffer吗?如果它很重要,网格的大小是用户设置的,可能包含超过一百万个顶点(这是通过多个网格实现的),但默认设置是32k顶点,这是单个D3D网格的最大值。

1 个答案:

答案 0 :(得分:0)

您似乎无法理解DiscardNoOverwrite标志的后果。阅读DirectX SDK帮助中的性能优化下的使用动态顶点和索引缓冲区部分。假设您正在使用动态顶点缓冲区,则Discard表示“我正在替换整个缓冲区”,NoOverwrite表示“我正在写入缓冲区的未使用部分,我保证不会改变我已经使用的任何部分“。

使用任一标记,您必须编写地形顶点的每个分量,甚至是新帧中未更改的分量。

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

我建议将网格的z值分成它们自己的顶点缓冲区 - 假设你没有更新位置的x和y,你的法线(如果z改变可能是不正确的)或你的纹理坐标。这样你可以使用你的PositionNormalTextured(没有位置z)顶点缓冲区来触及每一帧,并使用Discard标志从头开始每帧填充动态z位置缓冲区,而不是每个Z值。由于步幅消失,你可以使用平坦的记忆副本。

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

如果其中某些内容不适合C#,请道歉。