2012-06-02 52 views
0

我有一个地形网格,每个顶点的Z值需要更新每一帧。我现在的方法是这样的:更新Direct3D网格的顶点缓冲区的最快方法?

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代表地形高度创造了一个float数组。基本上,它扫描整个TerrainHeight数组,并将每个值复制到网格中相应的PositionNormalTextured的Z值。我使用LockFlags.NoOverwrite来避免创建数组的新副本,尽管这看起来没有LockFlags.Discard更快。

它需要长或更长的时间比它计算的CPU,这使我相信应该有一个更快的方式,在新的领域进行更新的网格。我一直无法在Google上找到关于此的信息。有更好的方法来更新顶点缓冲区吗?万一它很重要,网格的大小是用户设置,可能包括超过一百万个顶点(这是用多个网格完成的),但默认设置是32k个顶点,这是单个D3D网格的最大值。

+0

你应该避免每帧更新大的顶点缓冲区。特别是当它包含复制数据时。您的szenario是否允许在例如顶点着色器的高度图? –

回答

0

看样子你不了解DiscardNoOverwrite标志的后果。阅读部分使用动态顶点和索引缓冲区性能优化在DirectX SDK的帮助。假设你正在使用动态顶点缓冲区,那么Discard的意思是“我正在替换整个缓冲区”,NoOverwrite的意思是“我正在写入缓冲区的一个未使用部分,我保证不会更改我已经使用过的任何部分”。

无论哪种标志​​,你必须写地形的顶点,甚至还没有在新的框架改变了的人的每一个组件。

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

我建议你网格z值分离到自己的顶点缓冲 - 假设你没有在位置更新x和y,你的正常(这可能是如果Z修改不正确的)或你的纹理坐标。这样,你可以使用你的PositionNormalTextured(没有位置z)顶点缓冲区在每一帧都不触摸,并且使用Discard标志从开头开始每帧填充你的动态z位置缓冲区,而不需要每个Z值的步幅。由于步伐不复存在,您可以使用平面内存拷贝来做到这一点。

你会与z位置值与SetStreamSource(1, ZPositionVB, ...)提供你的顶点着色器。您需要调整顶点声明以读取流1中的z位置值,并使用顶点着色器在变换前合并z位置值。如果有一些不适合C#,我们表示道歉。

+0

我不知道你能做到这一点!这听起来似乎是一种更有效的方法,尽管我想我必须手动计算法线,也许我可以在顶点着色器中做到这一点。谢谢! – HypnoToad