DirectX11编程5 渲染动态顶点
环境:VS2017 语言:C++
总起:
第六章剩下的Demo差别不是很大,基本没什么新的知识点,不过我都将其实现了,今天就专门针对Waves这个Demo说一些特别的东西。
工程在此:https://github.com/anguangzhihen/Dx11。
如果遇到工程不能运行的情况,请留言,我会尽快查明原因。
Waves Deme:
该Demo主要是实现了一个动态的顶点变化,以模拟出水波的效果。
如果有一些Shader基础的同学,可能会想到Shader中有顶点动画,没错,跟这个类似,但是Shader中的顶点动画是在Shader中处理的,而这边的动态顶点渲染是CPU处理好再传到GPU中的。
我们先来看一下效果:
因为我们还没有学习光影,所以使用的是线框模式,不然估计很难看清。
所以我们先来说一下线框模式的设置:
// 申明光栅化阶段状态变量
ID3D11RasterizerState* mWireframeRS;
// 初始化,创建光栅化阶段的描述
D3D11_RASTERIZER_DESC wireframeDesc;
ZeroMemory(&wireframeDesc, sizeof(D3D11_RASTERIZER_DESC));
wireframeDesc.FillMode = D3D11_FILL_WIREFRAME;
wireframeDesc.CullMode = D3D11_CULL_BACK;
wireframeDesc.FrontCounterClockwise = false;
wireframeDesc.DepthClipEnable = true;
// 创建线框模式
HR(md3dDevice->CreateRasterizerState(&wireframeDesc, &mWireframeRS));
// 设置为线框模式,该方法需要在每帧ClearRT之后执行
md3dImmediateContext->RSSetState(mWireframeRS);
通常来说Dx是一个状态机,它会随着我们设置的状态的变化而变化。
ID3D11RasterizerState便是代表了在光栅化阶段的一个状态机集合。
D3D11_RASTERIZER_DESC便是这个状态的描述,以下是描述中各个参数的含义:
FillMode 填充模式,可以选择D3D11_FILL_WIREFRAME线框模式、D3D11_FILL_SOLID实体模式,默认为实体模式。线框模式就是文章一开始给出的效果。
CullMode 剪裁模式:
1. D3D11_CULL_NONE 禁用剪裁;
2. D3D11_CULL_BACK 剪裁背面的三角形(默认);
3. D3D11_CULL_FRONT 剪裁正面的三角形。
FrontCounterClockwise bool形,false为顺时针可见(默认),true为逆时针。
DepthClipEnable 是否启用深度剪裁。
了解了线框模式之后,我们来了解一下如何做到顶点动态移动(这里用到了一个Waves类用来计算当前顶点的位置,具体实现请参见代码,这边不展开叙述)。
首先是创建顶点缓存:
// 创建CPU可写的缓存
D3D11_BUFFER_DESC vbd;
ZeroMemory(&vbd, sizeof(vbd));
vbd.Usage = D3D11_USAGE_DYNAMIC; // 指定缓存为动态的
vbd.ByteWidth = sizeof(Vertex) * mWaves.VertexCount();
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // 指定CPU可写
HR(md3dDevice->CreateBuffer(&vbd, 0, &mWaveVB));
接着是在Update中动态设置顶点:
// 每0.25秒更新产生一次波纹
static float t_base = 0.0f;
if ((mTimer.TotalTime() - t_base) >= 0.25f)
{
t_base += 0.25f;
DWORD i = 5 + rand() % 190;
DWORD j = 5 + rand() % 190;
float r = MathHelper::RandF(1.0f, 2.0f);
mWaves.Disturb(i, j, r);
}
// 每帧更新Wave
mWaves.Update(dt);
// 创建mappedData用以设置顶点信息
D3D11_MAPPED_SUBRESOURCE mappedData;
HR(md3dImmediateContext->Map(mWaveVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData));
// 更新顶点信息
Vertex* v = reinterpret_cast<Vertex*>(mappedData.pData);
UINT length = mWaves.VertexCount();
for (UINT i = 0; i < length; i++)
{
v[i].Pos = mWaves[i];
v[i].Color = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
}
// 将新的顶点信息进行设置
md3dImmediateContext->Unmap(mWaveVB, 0);
接下来说一下ID3D11DeviceContext::Map函数中参数的含义:
pResource 我们想要访问的资源,可以是Dx11的buffer或者图片等。
Subresource 子资源的编号。
MapType 通用标志位:
1. D3D11_MAP_WRITE_DISCARD, 告诉硬件舍弃当前的内存,返回一块新的内存,以防止继续渲染老的顶点数据;
2. D3D11_MAP_WRITE_NO_OVERWRITE, 告诉硬件我们只写入没有初始化的数据。
pMappedResource 返回的一个D3D11_MAPPED_SUBRESOURCE指针,用于读写内存。
使用返回出来的指针更新顶点之后,一定要用ID3D11DeviceContext::Unmap来更新顶点数据。
最后再强调一下,这种方式在CPU和GPU之间互相传传递了数据,所以效率比较低,如果能使用顶点Shader、几何Shader等GPU的方式实现动态效果的话,尽量使用GPU的方式。