6.13 动态顶点缓冲

到目前为止,我们一直使用的是静态缓冲(static buffer),它的内容是在初始化时固定下来的。相比之下,动态缓冲(dynamic buffer)的内容可以在每一帧中进行修改。当实现一些动画效果时,我们通常使用动态缓冲区。例如,我们要模拟一个水波效果,并通过函数f(x ,z ,t)来描述水波方程,计算当时间为t时,xz平面上的每个点的高度。在这一情景中,我们必须使用“山峰与河谷”中的那种三角形网格,将每个网格点代入f(x, z , t)函数得到相应的水波高度。由于该函数依赖于时间t(即,水面会随着时间而变化),我们必须在很短的时间内(比如1/30秒)重新计算这些网格点,以得到较为平滑的动画。所以,我们必须使用动态顶点缓冲区来实时更新三角形网格顶点的高度。

前面提到,为了获得一个动态缓冲区,我们必须在创建缓冲区时将Usage标志值指定为D3D11_USAGE_DYNAMIC;同时,由于我们要向缓冲区写入数据,所以必须将CPU访问标志值指定为D3D11_CPU_ACCESS_WRITE

D3D11_BUFFER_DESC vbd;
vbd.Usage = D3D11_USAGE_DYNAMIC;
vbd.ByteWidth = sizeof(Vertex) * mWaves.VertexCount();
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
vbd.MiscFlags = 0;
HR(md3dDevice->CreateBuffer(&vbd, 0, &mWavesVB));

然后,使用ID3D11Buffer::Map函数获取缓冲区内存的起始地址指针,并向它写入数据:

HRESULT ID3D11DeviceContext::Map(
    ID3D11Resource *pResource ,
    UINT Subresource,
    D3D11_MAP MapType,
    UINT MapFlags,
    D3D11_MAPPED_SUBRESOURCE *pMappedResource);

1.pResource:指向要访问的用于读/写的资源的指针。缓冲是一种Direct3D 11资源,其他类型的资源,例如纹理资源,也可以使用这个方法进行访问。

2.Subresource:包含在资源中的子资源的索引。后面我们会看到如何使用这个索引,而缓冲不包含子资源,所以设置为0。

3.MapType:常用的标志有以下几个:

4.MapFlags:可选标志,这里不使用,所以设置为0;具体细节可参见SDK文档。

5.pMappedResource:返回一个指向D3D11_MAPPED_SUBRESOURCE的指针,这样我们就可以访问用于读/写的资源数据。

D3D11_MAPPED_SUBRESOURCE结构定义如下:

typedef struct D3D11_MAPPED_SUBRESOURCE{
    void  *pData;
    UINT Row Pitch;
    UINT DepthPitch;
}D3D11_MAPPED_SUBRESOURCE

1.pData:指向用于读/写的资源内存的指针,你必须将它转换为资源中存储的数据的格式。

2.RowPitch:资源中一行数据的字节大小。例如,对于一个2D纹理来说,这个大小为一行的字节大小。

3.DepthPitch:资源中一页数据的大小。例如,对于一个3D纹理来说,这个大小为3D纹理中一个2D图像的字节大小。

RowPitchDepthPitch的区别是针对2D和3D资源(类似于2D和3D数组)而言的。顶点/索引缓冲本质上是1D数组,RowPitchDepthPitch的值是相同的,都等于顶点/索引缓冲的字节大小。

下面的代码展示如何在水波演示程序中更新顶点缓冲:

D3D11_MAPPED_SUBRESOURCE mappedData;
HR(md3dImmediateContext->Map(mWavesVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData));

Vertex* v = reinterpret_cast<Vertex*>(mappedData.pData);
for(UINT i = 0; i < mWaves.VertexCount(); ++i)
{
	v[i].Pos = mWaves[i];
	v[i].Color = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
}

md3dImmediateContext->Unmap(mWavesVB, 0);

当你完成缓冲区的更新操作之后,必须调用ID3D11Buffer::Unmap函数。

当使用动态缓冲区时,必然会有一些额外开销,因为这里存在一个从CPU内存向GPU内存回传数据的过程。 所以,在实际工作中应尽可能多使用静态缓冲区,少使用动态缓冲区。在Direct3D的最新版本中已经引入了一些新特性用于减少对动态缓冲区的需求。例如:

1.可以在顶点着色器中实现简单动画。

2.通过渲染到纹理(render to texture)和顶点纹理推送(vertex texture fetch)功能,可以实现完全运行在GPU上的水波模拟动画。

3.几何着色器为GPU提供了创建和销毁图元的能力,在以前没有几何着色器时,这些工作都是由CPU来完成的。

索引缓冲区可以是动态的。不过,在水波演示程序中,三角形的拓扑结构始终不变,只有顶点高度会发生变化;所以,这里只需要让顶点缓冲区变为动态缓冲区。

本章的水波演示程序使用了一个动态缓冲区来实现简单的水波效果。本书不会将重点放在水波模拟算法的实现细节上(有兴趣的读者可以参见[Lengyel02]),我们只是用它来说明动态缓冲区的用法:在CPU上更新模拟数据,然后调用Map/Unmap方法更新顶点缓冲区。

注意:在水波演示程序中,我们以线框模式渲染水波;是因为我们现在还没有讲到灯光的用法,在实体填充模式下,很难看出水波的运动效果。注意:我们再次强调,这个示例应该在GPU上使用更高级的方式实现,比如渲染到纹理和顶点纹理推送。但是由于我们还没有讲到些技术,所以现在只能在CPU上实现,暂时使用动态顶点缓冲区来更新顶点。

图6.19
6.19 水波演示程序截图
文件下载(已下载 1289 次)

发布时间:2014/8/3 下午8:53:48  阅读次数:5920

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号