日期:2014-05-17  浏览次数:20975 次

Windows 8 Directx 开发学习笔记(七)水波纹的实现

使用DirectX实际开发中,模型的形状不可能都是一成不变,只依靠移动摄像机去实现动画。这里用实时更新顶点缓冲的方式生成一个水波模型,最终效果类似向水面扔石子时出现的水波纹。有了上一篇建立好的模型,实现这个效果仅需要更改WaterModel类和Renderer类里m_water的相关调用。

首先更改WaterModel的头文件,添加模拟变量成员和临时缓冲区指针,并增加两个方法DisturbUpdate,整个类代码如下:

class WaterModel
{
public:
    WaterModel(void);
    ~WaterModel(void);
    void Initialize(ID3D11Device* d3dDevice, int m, int n, float dx, float dt, float speed, float damping);
    void Render(ID3D11DeviceContext* d3dContext);
    void Disturb(int i, int j, float magnitude);
    void Update(ID3D11DeviceContext* d3dContext, float dt);
 
private:
    Microsoft::WRL::ComPtr<ID3D11Buffer> m_vertexBuffer;
    Microsoft::WRL::ComPtr<ID3D11Buffer> m_indexBuffer;
    uint32 m_vertexCount,m_indexCount;
 
    //二维网格的行数、列数
    int xRange;
    int zRange;
 
    // 欲计算的模拟常量.
    float mK1;
    float mK2;
    float mK3;
    float mTimeStep;
    float mSpatialStep;
   
    //顶点临时缓冲
    XMFLOAT3* mPrevSolution;
    XMFLOAT3* mCurrSolution;
};

完成后就开始更改WaterModel的构造方法,使用初始化列表初始化成员:

WaterModel::WaterModel(void):
    m_indexCount(0),m_vertexCount(0),
    mK1(0.0f),mK2(0.0f), mK3(0.0f),
    mTimeStep(0.0f),mSpatialStep(0.0f),
    mPrevSolution(0),mCurrSolution(0),
    xRange(128),zRange(128)
{
}

然后是更改Initialize方法。其中模拟常量等与算法有关的部分按照DirectX 10的例子进行计算,代码如下:

xRange = m;
zRange = n;
 
m_vertexCount = m*n;
m_indexCount = (m-1)*(n-1)*2*3;
 
mTimeStep    = dt;
mSpatialStep = dx;
 
float d = damping*dt+2.0f;
float e = (speed*speed)*(dt*dt)/(dx*dx);
mK1     = (damping*dt-2.0f)/ d;
mK2     =(4.0f-8.0f*e) / d;
mK3     = (2.0f*e) / d;

接着要为临时缓冲区分配空间并初始化,假设静止时水面的y坐标均为零。

mPrevSolution = new XMFLOAT3[m*n];
mCurrSolution = new XMFLOAT3[m*n];
 
//生成水平面
for(int row=0;row<xRange; ++row)
{
   float zPos = row*dx;
 
   for(int col=0;col<zRange; ++col)
   {
       float xPos = col*dx;
 
       mPrevSolution[xRange*row+ col] = XMFLOAT3(xPos, 0.0f, zPos);
       mCurrSolution[xRange*row+ col] = XMFLOAT3(xPos, 0.0f, zPos);
   }
}

为了让顶点缓冲区能够动态更新,需要将UsageCPUAccessFlags两个标志分别设置为D3D11_USAGE_DYNAMICD3D11_CPU_ACCESS_WRITE。另外,顶点缓冲区会在Update方法里填充,所以将CreateBuffer方法中代表数据源地址的第二个参数设为0。

D3D11_BUFFER_DESC vertexBufferDesc;
vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexBufferDesc.ByteWidth = sizeof(VertexPositionColor) * m_vertexCount;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;

DX::ThrowIfFailed(
	d3dDevice->CreateBuffer(
	&vertexBufferDesc,
	0,
	&m_vertexBuffer
	)
	);

xRangezRange现在不是常量,需要使用new的方式新建数组。

unsigned short* Indices = new unsigned short[3*2*(xRange-1)*(zRange-1)];

Indices现在是一个指针,不能用sizeof(Indices)来获得索引数组的大小,所以构造索引缓冲区的代码改为:

CD3D11_BUFFER_DESC indexBufferDesc(sizeof(unsigned short) * m_indexCount, D3D11_BIND_INDEX_BUFFER);

Initialize方法最后,删除Indices指向的索引数组,完成Initialize<