粒子系统类,粒子系统是游戏里细小元素的控制系统,虽然感觉上它对游戏的影响不大,但是其实有了它能给游戏增色不少。粒子系统控制着细小元素的生死,运动,纹理。对它的编写让我知道,游戏里的这一片从天空飘落的雪花其实是之前那一朵已经融化在地上的雪花。
这个类我还没有编写完整,因为我发现如果要真正实现那种很美的效果我还要多加学习啊。使用广告版技术让人觉得它真的是个粒子,开启alpha通道能让粒子与背景融为一体,开启光照能让粒子煜煜生辉,给纹理混合上颜色才能让粒子真正的变幻幻幻(不是打错字)起来。
particleSysclass.h
#pragma once
#include <d3d11.h>
#include <d3dcompiler.h>
#include <D3DX11.h>
#include <xnamath.h>
#include<time.h>
#pragma comment(lib,"d3dx11.lib")
#pragma comment(lib,"d3d11.lib")
#pragma comment(lib,"d3dcompiler.lib")
class particleSysclass
{
public:
particleSysclass();
~particleSysclass(); private:
ID3D11ShaderResourceView* m_texture;
struct vertex
{
XMFLOAT3 pos;
XMFLOAT2 tex;
};
struct particle
{
XMFLOAT3 pos;
float size;
bool alive;
};
struct constantBuffer
{
XMMATRIX world;
XMMATRIX view;
XMMATRIX pro;
}; vertex *m_vertexarray;
particle *m_particlearray;
int m_particlecountMAX;
int m_particlecountCUR;
XMFLOAT3 m_sourcepos; ID3D11Buffer *m_vertexBuffer, *m_indexBuffer;
ID3D11Buffer *m_constantBuffer;
int m_vertexCount, m_indexCount;
ID3D11SamplerState *m_samplerstate;
XMMATRIX m_worldMatrix; public:
bool Initialize(ID3D11Device* device, LPCWSTR texture, int maxcount);
void Render(ID3D11DeviceContext* context, XMMATRIX& viewmatrix, XMMATRIX& pro,
ID3D11VertexShader* vertexshader, ID3D11PixelShader* pixelshader);
void Shutdown(); private:
void Emitparticle();
void Killparticle();
void Updatebuffer(ID3D11DeviceContext* context);
void ChangeparticlePos();
bool Initbuffer(ID3D11Device* device);
void Loadtexture(ID3D11Device* device,LPCWSTR texture);
};
可以看到,粒子系统类与之前的模型类非常相像只是多了:
- 粒子数据结构,粒子数组指针,粒子最大数量,粒子当前数量(m_particlecountCUR)
- 私有方法:生成粒子,粒子湮灭,更新缓存,改变例子位置(运动),初始化缓存
particleSysclass.cpp
#include "particleSysclass.h" particleSysclass::particleSysclass()
{
m_worldMatrix = XMMatrixIdentity();
} particleSysclass::~particleSysclass()
{
} bool particleSysclass::Initialize(ID3D11Device* device, LPCWSTR texture, int maxcount)
{
m_particlecountMAX = maxcount; Loadtexture(device,texture); Initbuffer(device); m_particlearray = new particle[m_particlecountMAX]; srand((unsigned)time(NULL));
for (int i = ; i < m_particlecountMAX; i++)
{
m_particlearray[i].alive = true;
m_particlearray[i].pos.x = (((float)rand() - (float)rand()) / RAND_MAX) * ;
m_particlearray[i].pos.y = (((float)rand() - (float)rand()) / RAND_MAX) * ;
m_particlearray[i].pos.z = (((float)rand() - (float)rand()) / RAND_MAX) * ; m_particlearray[i].size = 0.05;
} return false;
} void particleSysclass::Shutdown()
{
if (m_particlearray)
{
delete m_particlearray;
}
if (m_samplerstate)
{
m_samplerstate->Release();
}
if (m_constantBuffer)
{
m_constantBuffer->Release();
}
if (m_indexBuffer)
{
m_indexBuffer->Release();
}
if (m_vertexBuffer)
{
m_vertexBuffer->Release();
}
if (m_vertexarray)
{
delete[] m_vertexarray;
}
if (m_texture)
{
m_texture->Release();
}
} void particleSysclass::Emitparticle()
{ } void particleSysclass::Killparticle()
{
} void particleSysclass::Updatebuffer(ID3D11DeviceContext* context)
{
HRESULT hr = S_OK; memset(m_vertexarray, , sizeof(vertex)*m_particlecountMAX*);
for (int i = ; i < m_particlecountMAX; i++)
{
m_vertexarray[i * ].pos.x = m_particlearray[i].pos.x - m_particlearray[i].size;
m_vertexarray[i * ].pos.y = m_particlearray[i].pos.y - m_particlearray[i].size;
m_vertexarray[i * ].pos.z = m_particlearray[i].pos.z;
m_vertexarray[i * ].tex.x = ;
m_vertexarray[i * ].tex.y = ; m_vertexarray[i * + ].pos.x = m_particlearray[i].pos.x - m_particlearray[i].size;
m_vertexarray[i * + ].pos.y = m_particlearray[i].pos.y + m_particlearray[i].size;
m_vertexarray[i * + ].pos.z = m_particlearray[i].pos.z;
m_vertexarray[i * + ].tex.x = ;
m_vertexarray[i * + ].tex.y = ; m_vertexarray[i * + ].pos.x = m_particlearray[i].pos.x + m_particlearray[i].size;
m_vertexarray[i * + ].pos.y = m_particlearray[i].pos.y - m_particlearray[i].size;
m_vertexarray[i * + ].pos.z = m_particlearray[i].pos.z;
m_vertexarray[i * + ].tex.x = ;
m_vertexarray[i * + ].tex.y = ; m_vertexarray[i * + ].pos.x = m_particlearray[i].pos.x - m_particlearray[i].size;
m_vertexarray[i * + ].pos.y = m_particlearray[i].pos.y + m_particlearray[i].size;
m_vertexarray[i * + ].pos.z = m_particlearray[i].pos.z;
m_vertexarray[i * + ].tex.x = ;
m_vertexarray[i * + ].tex.y = ; m_vertexarray[i * + ].pos.x = m_particlearray[i].pos.x + m_particlearray[i].size;
m_vertexarray[i * + ].pos.y = m_particlearray[i].pos.y + m_particlearray[i].size;
m_vertexarray[i * + ].pos.z = m_particlearray[i].pos.z;
m_vertexarray[i * + ].tex.x = ;
m_vertexarray[i * + ].tex.y = ; m_vertexarray[i * + ].pos.x = m_particlearray[i].pos.x + m_particlearray[i].size;
m_vertexarray[i * + ].pos.y = m_particlearray[i].pos.y - m_particlearray[i].size;
m_vertexarray[i * + ].pos.z = m_particlearray[i].pos.z;
m_vertexarray[i * + ].tex.x = ;
m_vertexarray[i * + ].tex.y = ;
} D3D11_MAPPED_SUBRESOURCE mappedResource; hr = context->Map(m_vertexBuffer, , D3D11_MAP_WRITE_DISCARD, , &mappedResource);
if (FAILED(hr))
{
return;
} vertex* verticesPtr = (vertex*)mappedResource.pData; memcpy(verticesPtr, (void*)m_vertexarray, (sizeof(vertex)* m_vertexCount)); context->Unmap(m_vertexBuffer, );
} void particleSysclass::ChangeparticlePos()
{ for (int i = ; i < m_particlecountMAX; i++)
{
static bool flag = true;
if (m_particlearray[i].pos.y < -)
{
flag = true;
}
if (m_particlearray[i].pos.y > )
{
flag = false;
}
if (flag)
{
m_particlearray[i].pos.y += 0.0001;
}
else
{
m_particlearray[i].pos.y -= 0.0001;
}
}
} void particleSysclass::Render(ID3D11DeviceContext* context, XMMATRIX& viewmatrix, XMMATRIX& pro,
ID3D11VertexShader* vertexshader, ID3D11PixelShader* pixelshader)
{ Killparticle(); Emitparticle(); Updatebuffer(context); ChangeparticlePos(); UINT stride = sizeof(vertex);
UINT offset = ;
context->IASetVertexBuffers(, , &m_vertexBuffer, &stride, &offset);
context->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R16_UINT, );
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); context->VSSetShader(vertexshader, NULL, ); D3D11_MAPPED_SUBRESOURCE mappedResource;
HRESULT hr = S_OK; hr = context->Map(m_constantBuffer, , D3D11_MAP_WRITE_DISCARD, , &mappedResource);
if (FAILED(hr))
{
return;
}
constantBuffer* dataPtr = (constantBuffer*)mappedResource.pData; XMMATRIX worldmatrix = m_worldMatrix;
XMMATRIX promatrix = pro;
dataPtr->world = XMMatrixTranspose(worldmatrix);
dataPtr->view = XMMatrixTranspose(viewmatrix);
dataPtr->pro = XMMatrixTranspose(promatrix); context->Unmap(m_constantBuffer, ); context->VSSetConstantBuffers(, , &m_constantBuffer);
context->PSSetShader(pixelshader, NULL, );
context->PSSetShaderResources(, , &m_texture);
context->PSSetSamplers(, , &m_samplerstate);
context->DrawIndexed(m_vertexCount, , );
} bool particleSysclass::Initbuffer(ID3D11Device* device)
{
m_vertexCount = m_indexCount = m_particlecountMAX * ; m_vertexarray = new vertex[m_vertexCount]; WORD *indices = new WORD[m_indexCount];
for (int i = ; i < m_indexCount; i++)
{
indices[i] = i;
} D3D11_BUFFER_DESC vertexbd, indexbd, constantbd;
D3D11_SUBRESOURCE_DATA vertexdata, indexdata;
HRESULT hr = S_OK; vertexbd.Usage = D3D11_USAGE_DYNAMIC;
vertexbd.ByteWidth = sizeof(vertex)* m_vertexCount;
vertexbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
vertexbd.MiscFlags = ;
vertexbd.StructureByteStride = ; vertexdata.pSysMem = m_vertexarray;
vertexdata.SysMemPitch = ;
vertexdata.SysMemSlicePitch = ; hr = device->CreateBuffer(&vertexbd, &vertexdata, &m_vertexBuffer);
if (FAILED(hr))
{
return false;
} indexbd.Usage = D3D11_USAGE_DEFAULT;
indexbd.ByteWidth = sizeof(WORD)* m_indexCount;
indexbd.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexbd.CPUAccessFlags = ;
indexbd.MiscFlags = ;
indexbd.StructureByteStride = ; indexdata.pSysMem = indices;
indexdata.SysMemPitch = ;
indexdata.SysMemSlicePitch = ; hr = device->CreateBuffer(&indexbd, &indexdata, &m_indexBuffer);
if (FAILED(hr))
{
return false;
} constantbd.Usage = D3D11_USAGE_DYNAMIC;
constantbd.ByteWidth = sizeof(constantBuffer);
constantbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constantbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
constantbd.MiscFlags = ;
constantbd.StructureByteStride = ;
hr = device->CreateBuffer(&constantbd, NULL, &m_constantBuffer);
if (FAILED(hr))
{
return false;
} D3D11_SAMPLER_DESC sampDesc;
ZeroMemory(&sampDesc, sizeof(sampDesc));
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = ;
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
device->CreateSamplerState(&sampDesc, &m_samplerstate); delete[] indices;
indices = ; return false;
} void particleSysclass::Loadtexture(ID3D11Device*device, LPCWSTR texture)
{
D3DX11CreateShaderResourceViewFromFile(device, texture, NULL, NULL, &m_texture, NULL);
}
Initialize(): 与前不同,这里创建缓存的工作交给了私有方法Initbuffer(),从参数获得最大粒子数量,创建并随机给他们设置位置和大小
Emitparticle(),Killparticle():偷懒还没写、、
Updatebuffer():根据粒子位置,粒子大小更改顶点的位置纹理坐标,更改好后再更新到顶点缓存里
ChangeparticlePos():更改粒子位置,即使粒子做运动,对每个粒子做位置判断,使之在-5<y<5的范围内移动,如果到了边界就往回移动
Render():渲染,与之前的model类的渲染函数类似
- 产生粒子
- 杀死粒子
- 更改顶点缓存
- 更改粒子位置
- 设置顶点缓存
- 设置索引缓存
- 设置绘制方式
- 设置顶点着色器
- 更改常量缓存,这里需要使用动态的更改方式
- 设置常量缓存
- 设置像素着色器
- 设置纹理源
- 设置取样器取样方式
- 绘制各个顶点
Initbuffer():其实这才是与之前model类最为不一样的地方,他创建的顶点缓存,常量缓存都是动态的
- 填充顶点描述结构,usage字段设置为D3D11_USAGE_DYNAMIC,CPUAccessFlags字段设置为D3D11_CPU_ACCESS_WRITE,创建顶点缓存
- 填充索引描述结构,创建索引缓存
- 填充常量缓存描述结构,usage字段设置为D3D11_USAGE_DYNAMIC,CPUAccessFlags字段设置为D3D11_CPU_ACCESS_WRITE,创建常量缓存
- 填充取样器描述,创建取样器状态。
这就是粒子系统的实现方式了,总的来说是根据1个粒子位置设置6个顶点(两个三角形)位置,更新顶点缓存,再像渲染模型一样渲染就好了。我们其实也很容易想到,粒子就是微小的、结构更加简单、数量更为庞大的模型。不过虽说如此,要实现非常漂亮的图形学程序,还是需要对高级着色语言,directx的各种功能做深入的了解。