[转帖]基于GPU的粒子系统实现概要 

2006-12-20 15:37 发布

引擎学习交流 /[各编程语言]
2774 0 0


         在学习粒子系统后,继续学习到shader编程时,我忽然想到:能不能把很多粒子运算从CPU上转移到GPU上去? 一般情况下计算粒子位置好解决,可是生命周期处理不好办,因为shader不能写顶点buffer,这个一时把我难住了,后来在网上看到篇GPU处理粒子系统的文章,受到点启发,再经过一翻思考,自己的实现方案产生了,遂整理出文章一篇,以抛砖引玉。
          经常在网上看到一些翻译别人国外作者的文章,排版又乱,自己又不给点见解和创新,我强烈建议大家发挥自己的想象力,先想,再做,再学,不要盲目崇洋名外。我写这篇文章不主要为写知识技巧,而是想唤醒你那无限的想象力,然后大家一起探讨创新!
       
         目标及好处:减少粒子系统更新中GPU和CPU之间的频繁通信,系统内存和显存间的频繁数据传送。充分利用显卡的图形处理能力,降低CPU的负担。
       
        首先描叙下一般情况下粒子系统运行的步骤,为了使读者更清晰的看到主题我略了颜色纹理等的处理(颜色衰减处理类似与位置处理):
        初始化粒子
        循环
-----------------------------一般是在CPU中进行----------------------------------------------------
                如果粒子没有死亡
                         更新离子的位置
                         修改粒子的生命值
                处理死亡的粒子
                         如果粒子生命小于某个阀值(比如0.0)
                         设置粒子状态为死亡,或者干脆从粒子队列里删除
                增加新的粒子
                          增加新的粒子到粒子队列中,或者查找生命为死亡的粒子,用计算出的
                          新粒子属性覆盖之(这里有新的生命值)
-------------------------------下面在显卡中进行--------------------------------------------------------------------
                渲染
                         把没有死亡的粒子的位置信息送入显卡中,进行渲染 (采用AGP内存的话,最终还是要把一个个的粒子信息送入显卡处理)
               
    
        这样每个粒子的具体信息都要CPU处理然后送入显卡中渲染,不言而喻效率极底,我们要想办法把在CPU里处理的动作转移到GPU中去。下面逐一列出解决方法:       

         1。生命周期模拟:
         在初始化粒子系统时,计算出粒子生命值life(如果生命值都一样的话,就不用了),记下粒子的开始时间startTime。比如有100个粒子,一个在时间0开始发射,生命周期为10秒,下一个在时间0.1秒开始发射,生命周期为9秒,。。。一直到100个,计算好后把它们送入显卡。
         现在到了一个难点的问题了,就是粒子生命结束了改怎么处理?那就是让新的粒子信息覆盖这个死亡粒子的存储空间。可以通过CPU实现也可以通过GPU模拟,CPU实现下次讨论。这里说说GPU中模拟,就是用粒子系统全局时间 — 粒子初始时间然后再模除以生命周期:float aliveTime = fmod(GlobeTime, lifevalue);
 这样在粒子到达生命终点时它又从新复活,开始自己下一个生命周期的旅程(也许你注意到了,它的生命周期没有改变:)  
         2。每个粒子的位置更新:
        positionNew =EmitteredPistion + Velocity*aliveTime + 1/2*acceleration*aliveTime*aliveTime
        //新位置 = 初始位置+ 速度*已存活时间+ 1/2*加速度*已存活时间*已存活时间
        速度在初始化粒子系统时,计算粒子位置然后把它们送入显卡顶点缓冲区(vertex buffer),初始位置就是发射点的位置它和时间、加速度以全局变量的方式,在运行期送传给shader全局变量。 
        
---------------------------------------实现片段------------------------------------------------------------
          粒子的生命值,开始发射时间,速度可以存放在没有使用到的纹理坐标和颜色VertexBuffer上:

------------------------------------------在C++程序中--------------------------------------------------------
//顶点格式声明
const static D3DVERTEXELEMENT9 g_VertexElements[] =

{
       { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_POSITION, 0},             //这里存放生命值lifevalue,开始发射时间startTime

       { 1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_TEXCOORD, 0},            //这里存放速度 
//如还有信息继续添加.... ....

       D3DDECL_END()
};       

//设置effect全局变量
pEffect->SetVector("GlobeTime", &GlobeTime );                //整个粒子系统的生存时间
pEffect->SetVector("acceleration", &acceleration );            //加速度
pEffect->SetVector("EmitteredPistion", &EmitteredPistion );//发射器的位置

---------------------------------D3DFX代码片(效果文件,.fx后缀的)--------------------------------
float4 GlobeTime;                                                         // 自创建粒子系统后逝去的时间
float4 EmitteredPistion;                                                 //粒子被发射的位置
float4 acceleration;                                                       //加速度

struct VS_INPUT

{
    float3 Position;
    POSITION;            //lifevalue,startTime;    
    float3 Tex0                                 : TEXCOORD0; // Velocity 
    ... ....   //其它信息       
};

struct VS_OUTPUT

{
    float4 Position                      : POSITION;
    ... ...  //其它信息
};

VS_OUTPUT VS(const VS_INPUT Input)
{
    VS_OUTPUT    Out = (VS_OUTPUT) 0; 

    //float aliveTime = fmod(GlobeTime — startTime, lifevalue);
    float aliveTime = fmod(GlobeTime.x — Position.y, Position.x); 

    //positionNew = positionInit + Velocity*aliveTime + 1/2*acceleration*aliveTime*aliveTime,计算位移

    //Velocity*aliveTime ,初速度*时间增加的位移部分
    float4 positionNew = EmitteredPistion + mul( float4(Input.Tex0,0), aliveTime);  
    //+ 1/2*acceleration*aliveTime*aliveTime部分,+加速度增加的位移部分
    positionNew =  positionNew +mul( acceleration ,(0.5*aliveTime*aliveTime) );  
    positionNew.w = 0;
    Out.Position = positionNew;
   //其他处理信息... ...
    return Out;
}

technique tec0
{
    pass p0
    {
        VertexShader = compile vs_1_1 VS();
        PixelShader = NULL;
    }
}

       这个技术可以在vs1.1及以上版本,VS3.0及以上版本会有更好的解决方案,但基本原理类似,因为这里实现的是无状态粒子系统,无状态粒子系统有它的使用范围。在这个方法的基础上可以实现 GPU、CPU联合更新粒子系统。

楼主新帖

TA的作品 TA的主页
B Color Smilies

你可能喜欢

[转帖]基于GPU的粒子系统实现概要 
联系
我们
快速回复 返回顶部 返回列表