改进粒子系统-GPU实现

来源:互联网 发布:帧数显示软件 编辑:程序博客网 时间:2024/04/30 05:40

改进粒子系统-GPU实现

作者:fannyfish

Bloghttp://blog.csdn.net/fannyfish

amma@zsws.org

介绍

       即时粒子系统的性能主要受两个因素制约:填充率(fillrate)、CPU-GPU之间的数据传输。填充率即GPU每帧可以渲染的象素数,当粒子很大并且出现好多粒子重叠在一起的情况时会明显影响性能(比如用粒子模拟大面积水雾,烟尘)。通常的做法是先在CPU上进行粒子的物理运算,然后将运算结果传输给GPU渲染。当粒子数目巨大时(如100000个),运算时间和CPU-GPU的传输时间,对即时演算来说都难以接受(比如用粒子模拟大面积雨雪)。

       我做的项目中使用了大量粒子:场景特效、打击特效、甚至界面特效,无处不在。目前的粒子系统使用CPU进行物理运算。再加上物理引擎、骨骼融合、游戏逻辑,CPU成为了系统瓶颈,游戏中每帧GPU都要等待一短时间,显卡越好CPU越差越明显。如何将CPU的物理物理运算转移到GPU上,达到负载均衡是优化的关键。

设计

状态无关(Stateless vs 状态相关(State-preserving

       1,状态无关是指粒子的数据运算,都只根据初始的位置,速度等属性来计算。

       2,状态相关是指粒子的数据运算,可以根据上个状态的位置,速度等属性来计算。

       GPU上处理状态相关的粒子系统,需要多张纹理存储粒子状态,对显卡的要求很高。

相反,状态无关的粒子系统对显卡要求低,实现相对简单,所以首先考虑实现这种粒子系统。

与原有粒子系统的关系

       使用原有的粒子系统分为如下几个步骤:

1,  美术通过编辑器创建粒子

2,  在编辑器中指定粒子的渲染器,发射器,效果器。其中渲染器负责创建删除粒子对应的渲染数据(Billboard,模型),并维护渲染相关的状态。包括Billboard渲染器, 模型渲染器, Billboard拖尾渲染器, 模型拖尾渲染器。

3,  客户端使用,反馈给美术调整。

GPU上处理粒子系统,将使用不同的渲染数据和状态,因此派生一个新的渲染器:Shader渲染器。这样上面的步骤不会变化,美术只需要熟悉渲染器的新参数。

渲染数据和状态

       在没有使用到的纹理坐标和颜色VertexBuffer上存储顶点的初始属性。包括位置,颜色,在一个粒子quad上的位置(UpOffset, LeftOffset),速度,生存期。

       设置常量寄存器,包括world-view-proj矩阵,眼睛的Right向量Up向量(用来和UpOffset, LeftOffset计算不同的面向摄像机方式),时间,加速度,颜色变化量。

实现

引擎渲染代码段:

const static D3DVERTEXELEMENT9 g_VertexElements[] =

{

       { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_POSITION, 0},

       { 1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_TEXCOORD, 0},

       { 2, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_TEXCOORD, 1},

       { 3, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_TEXCOORD, 2},     

       { 4, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_TEXCOORD, 3},     

       { 5, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_COLOR,   0},

       D3DDECL_END()

};      

….

       m_pPositionVB = pDevice->CreateVertexBuffer( sizeof(v3dxVector3)*m_iVertexSize, m_pParent->m_dwVBUsage, m_pParent->m_Pool );

       m_pDiffuseVB = pDevice->CreateVertexBuffer( sizeof(DWORD)*m_iVertexSize, m_pParent->m_dwVBUsage, m_pParent->m_Pool );

       m_pTexCoordVB = pDevice->CreateVertexBuffer( sizeof(V3UV2)*m_iVertexSize, m_pParent->m_dwVBUsage, m_pParent->m_Pool );

       m_pTexCoordVB2 = pDevice->CreateVertexBuffer( sizeof(V3UV2)*m_iVertexSize, m_pParent->m_dwVBUsage, m_pParent->m_Pool );

       m_pTexCoordVB3 = pDevice->CreateVertexBuffer( sizeof(v3dxVector3)*m_iVertexSize, m_pParent->m_dwVBUsage, m_pParent->m_Pool );

       m_pIndexBuffer = pDevice->CreateIndexBuffer( sizeof(WORD)*m_iIndexSize, m_pParent->m_dwIBUsage, false, m_pParent->m_Pool );

填充粒子初始状态数据

 

       // 设置HLSL里的常量

       pEffect->SetMatrix("matWorldViewProj", (D3DXMATRIX *)&matTransformation);

       pEffect->SetVector("rightVector", &rightVector);

       pEffect->SetVector("upVector", &upVector);

       pEffect->SetVector("time_colour", &timeVec );

       pEffect->SetVector("acceleration", &acceleration );

       m_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST , 0, 0,pRenderer->m_iVertexSize, 0, pRenderer->m_iVertexSize/2 );

 

D3DFX代码:

struct VS_INPUT

{

    float3 Position                      : POSITION;   

    float2 Tex0                                 : TEXCOORD0;   

    float3 Tex1                                 : TEXCOORD1;    // UpOffset, LeftOffset, TotalTimeToLife

    float3 Tex2                                 : TEXCOORD2;    // Velocity

    float3 StartDiffuse                : COLOR0;           

};

 

struct VS_OUTPUT

{

    float4 Position                      : POSITION;

    float3 Diffuse                       : COLOR0;

    float2 Tex0                                 : TEXCOORD0;

};

 

matrix matWorldViewProj;                                               // world-view-proj matrix

float4 rightVector;                                                    // Right Vector

float4 upVector;                                                       // Up Vector

float4 time_colour;                                                  // Elasped Time, Delta Colour

float4 acceleration;       

 

VS_OUTPUT VS(const VS_INPUT Input)

{

    VS_OUTPUT    Out = (VS_OUTPUT) 0;

 

    // Position = right + up + pos;

    float4 right = rightVector * Input.Tex1.x;

    float4 up = upVector * Input.Tex1.y;

    float4 Pos = float4(Input.Position,0) + right + up;

 

    // Live Time = fmod( Elapsed Time, TotalTimeToLife )

    float fLiveTime = fmod(time_colour.x, Input.Tex1.z);

 

    // Position = Pos + vt + 1/2*v*t*t

    float4 deltaVel = mul( float4(Input.Tex2,0), fLiveTime);

    deltaVel = deltaVel + acceleration * fLiveTime * fLiveTime;

    //deltaVel.y = deltaVel.y + time_colour.z;

    Pos = Pos + deltaVel;

 

    Pos.w = 1.0;

    Out.Position = mul( Pos, matWorldViewProj );

 

    // color

       Out.Diffuse.x = Input.StartDiffuse.x + time_colour.y*fLiveTime;

    Out.Diffuse.y = Input.StartDiffuse.y + time_colour.z*fLiveTime;

    Out.Diffuse.z = Input.StartDiffuse.z + time_colour.w*fLiveTime;

 

    // texcoord

    Out.Tex0 = Input.Tex0;

 

    return Out;

}

 

technique tec0

{

    pass p0

    {

        VertexShader = compile vs_1_1 VS();

        PixelShader = NULL;

    }

}

编辑器中可调的属性

1, 默认高度

2, 默认宽度

3, 最大粒子数                    这里代表同时存在的粒子数

4, 粒子朝向

5, 面向摄像机的方式

6, 粒子UP向量          

7, 是否是2D粒子系统

发射器

8, 支持所有发射器和发射器特有属性(如圆环发射器的内环大小,外环大小)

9, 角度

10, 起始颜色

11, 结束颜色

12, 方向

13, 最小生存期

14, 最大生存期

15, 最小速度

16, 最大速度

17, 位置

效果器

18, 支持颜色衰减

19, 支持线性外力: "外力" 指加速度a, 满足公式s = vt + 1/2*a*t*t, 受力模式不起作用

截图

编辑器

1,point emitter, colour affector

2,box emitter, acceleration affector

其它

TODO

1,支持更多的Emitter, Affector. 根据粒子拥有的发射器,效果器类型动态编译不同的FX文件以减少运算量.(完成)

    http://blog.csdn.net/fannyfish/archive/2006/06/22/823032.aspx

 

2,支持State-Perspective粒子系统.(完成)

  http://blog.csdn.net/fannyfish/archive/2006/07/25/976753.aspx

3,排序,碰撞

4,支持设置粒子是相对世界坐标系还是本地坐标系

参考资料

1, [ShaderX3] Lutz Latta, Massively Parallel ParticleSystems on the GPU

2, [ShaderX2] O’dell Hicks, Screen-aligned Particles with Minimal VertexBuffer Locking

 

 

原创粉丝点击