首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

6.6  粒子系统

6.6.1  粒子系统的概念

很多自然现象都可以由具有相似风格运行的小粒子集合来模拟,粒子系统就是专门用来产生、控制及渲染这些粒子的系统,如图6-13所示。粒子系统可以实现的效果包括:

(1)火把上面的火苗;

(2)汽车排出的尾气;

(3)一群狂怒的异形生物攻击时的编队;

(4)成群或者成串的物体;

(5)运动效果,如击起的草皮碎片。

图6-13  粒子系统

在粒子系统中,有一个重要的概念是粒子发射体,粒子发射体是用来发射粒子的,类似草坪上的喷水枪。所有的粒子都将由粒子发射体来产生,设计人员需要确定某个粒子系统需要发射多少个粒子。通常不要考虑太多的粒子,因为这将占用大量的处理器时间。

粒子的行为将受到作用于粒子上的力的影响,这些力称为场。每种力场都有各种吸引因子和排斥因子,引导不同的粒子运动行为,可以用来构造更为复杂的粒子行为。

6.6.2  粒子系统的实现

在Direct3D中可以按照下面的步骤实现一个粒子系统。

1)设置粒子属性

首先定义一个粒子的结构体,用来描述粒子:

struct PARTICLE

{

    D3DXVECTOR3 m_vPos;   // 当前位置

    D3DXVECTOR3 m_vVel;   // 当前速度

    D3DXVECTOR3 m_vPos0;  // 初始位置

    D3DXVECTOR3 m_vVel0;  // 初始速度

    FLOAT m_fTime0;        // 创建时间

    D3DXCOLOR m_clrDiffuse;     // 初始的颜色

    D3DXCOLOR m_clrFade;        // 消失时颜色

    FLOAT m_fFade;         // 上两种颜色的中间过渡颜色的程度,

//如1.0时是m_clrDiffuse,0.0时是m_clrFade,0~1之间是两者的混合色

    PARTICLE* m_pNext;         // 这里的粒子使用链表方式管理

};

2)粒子管理

粒子管理是影响粒子系统性能的最重要因素,而主要的原因就出在内存管理上。有一点很重要,就是在粒子消亡后尽量不要急着释放内存,尽可能一次释放全部粒子。可以使用两个链表,一个记录处于活动期的粒子(m_pParticles),一个记录已经消亡的粒子(m_pParticlesFree)。当生成新粒子的时候,先检查m_pParticlesFree链表中是否有粒子,有就直接使用,把此粒子添加到m_pParticles链表中,并从m_pParticlesFree链表中删除。而当m_pParticles链表中有粒子进入死亡期时,就把此粒子添加到m_pParticlesFree链表中,并从m_pParticles链表中删除。这样操作就可以不用释放内存,在绘制时也不用判断消亡粒子,所以效率是很高的。

下面的程序片断用于粒子的管理。

【例6-7】粒子的管理:

PARTICLE* ParticleSystem::AddParticle()

{

    PARTICLE *pParticle=NULL;

    if(m_dwParticles>=m_dwParticlesLim)return NULL;//是否达到粒子限制数

    if( m_pParticlesFree )

    {

        pParticle = m_pParticlesFree;

        m_pParticlesFree = pParticle->m_pNext;

    }

    else

    {

        if( NULL == ( pParticle = new PARTICLE ) )

            return NULL;

    }

        pParticle->m_pNext = m_pParticles;

    m_pParticles = pParticle;

    m_dwParticles++;

    return pParticle;

}

HRESULT ParticleSystem::Release()

{

    while( m_pParticles )

    {

        PARTICLE* pSpark = m_pParticles;

        m_pParticles = pSpark->m_pNext;

        delete pSpark;

    }

        while( m_pParticlesFree )

    {

        PARTICLE *pSpark = m_pParticlesFree;

        m_pParticlesFree = pSpark->m_pNext;

        delete pSpark;

    }

    return S_OK;

}

3)粒子绘制

粒子的绘制是发生在每一帧中的。在每一帧中,由于粒子系统显示的内容总是动态的小东西(如果是静态的,或者是大的图片,就不应该采用粒子系统),所以要对所有的粒子进行遍历访,问并根据情况修改粒子的显示位置。对于粒子位置的计算则应该由游戏的要求来决定,比如显示的是下雨的雨点,则它的位置轨迹应是自由落体的效果,也许为了更真实还会加入一些风的效果。而如果是下雪,则看起来更应该像是匀速下落,风的作用则必须要加上(不受风影响的雪花,好像不符合大多数人的生活经验吧)。

另一个重要的问题是,粒子系统的显示与否是由系统是否支持点精灵技术决定的。点精灵技术是指在一个点的位置可以显示一幅图。要想使用这项技术,需要明确说明。

m_pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );

m_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE,  TRUE );

【例6-8】粒子显示:

HRESULT CParticleSystem::Render(float tFrame)

{

    Update(tFrame);

    HRESULT hr;

    D3DMATRIX matWorld;

    memset(&matWorld,0,sizeof(matWorld));

    matWorld._11=1;

    matWorld._22=1;

    matWorld._33=1;

    matWorld._44=1;

    m_pDevice->SetTransform(D3DTS_WORLD,&matWorld);

    m_pDevice->SetTexture(0,m_pTexture);

    m_pDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );

    m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );

    m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );

    m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

    m_pDevice->SetTextureStageState(0,D3DTSS_COLORARG2,

                                          D3DTA_DIFFUSE);

    m_pDevice->SetTextureStageState(0,D3DTSS_COLOROP,

                                          D3DTOP_MODULATE);

    m_pDevice->SetRenderState( D3DRS_LIGHTING,FALSE);

    // Set the render states for using point sprites

    m_pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );

    m_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE,  TRUE );

    m_pDevice->SetRenderState( D3DRS_POINTSIZE,

                                    FtoDW(m_fParticleSize) );

    m_pDevice->SetRenderState( D3DRS_POINTSIZE_MIN, FtoDW(0.00f) );

    m_pDevice->SetRenderState( D3DRS_POINTSCALE_A,  FtoDW(0.00f) );

    m_pDevice->SetRenderState( D3DRS_POINTSCALE_B,  FtoDW(0.00f) );

    m_pDevice->SetRenderState( D3DRS_POINTSCALE_C,  FtoDW(1.00f) );

    // Set up the vertex buffer to be rendered

    m_pDevice->SetStreamSource( 0, m_pVB, 0, sizeof(POINTVERTEX) );

    m_pDevice->SetFVF( POINTVERTEX::FVF );

    PARTICLE*    pParticle = m_pParticles;

    POINTVERTEX* pVertices;

    DWORD        dwNumParticlesToRender = 0;

    // Lock the vertex buffer.  We fill the vertex buffer in small

    // chunks, using D3DLOCK_NOOVERWRITE.  When we are done filling

    // each chunk, we call DrawPrim, and lock the next chunk.  When

    // we run out of space in the vertex buffer, we start over at

    // the beginning, using D3DLOCK_DISCARD.

    //锁定顶点缓冲区,以小块填充,如果所有的小块都填充了,

    //就绘制它们,然后再锁定下一个块,

    //如果空间用完了,就从头开始,使用DISCARD方式销毁

    m_dwBase += m_dwFlush;

    if(m_dwBase >= m_dwDiscard)

        m_dwBase = 0;

    //dwBase开始是没有使用的缓冲区, 要使用m_dwFlush个顶点

    if( FAILED( hr = m_pVB->Lock( m_dwBase * sizeof(POINTVERTEX),

                        m_dwFlush * sizeof(POINTVERTEX),

                        (void**) &pVertices,

                 m_dwBase ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD ) ) )

    {

        return hr;

    }

    // Render each particle

    while( pParticle )

    {

        D3DXVECTOR3 vPos(pParticle->m_vPos);

        D3DXVECTOR3 vVel(pParticle->m_vVel);

        FLOAT       fLengthSq = D3DXVec3LengthSq(&vVel);

        UINT        dwSteps;

        if( fLengthSq < 1.0f )        dwSteps = 2;

        else if( fLengthSq <  4.00f ) dwSteps = 3;

        else if( fLengthSq <  9.00f ) dwSteps = 4;

        else if( fLengthSq < 12.25f ) dwSteps = 5;

        else if( fLengthSq < 16.00f ) dwSteps = 6;

        else if( fLengthSq < 20.25f ) dwSteps = 7;

        else                          dwSteps = 8;

        dwSteps=1;

        vVel *= -0.04f / (FLOAT)dwSteps;

        DWORD dwDiffuse=ColorLerp(pParticle->m_clrEmit,

            pParticle->m_clrFade,

            1-pParticle->m_fFade/(m_fParticleFade));

        // Render each particle a bunch of times to get a blurring effect

        for( DWORD i = 0; i < dwSteps; i++ )

        {

            pVertices->v     = vPos;

            pVertices->color = dwDiffuse;

            pVertices++;

            if( ++dwNumParticlesToRender == m_dwFlush )

            {

                // Done filling this chunk of the vertex buffer 

                //Lets unlock and

                // draw this portion so we can begin filling the next chunk

                m_pVB->Unlock();

                if(FAILED(hr =

                m_pDevice->DrawPrimitive( D3DPT_POINTLIST,

                m_dwBase, dwNumParticlesToRender)))

                    return hr;

                // Lock the next chunk of the vertex buffer 

                //If we are at the

                // end of the vertex buffer,

                //DISCARD the vertex buffer and start

                // at the beginning.  Otherwise,

                //specify NOOVERWRITE, so we can

                // continue filling the VB while

                //the previous chunk is drawing

                m_dwBase += m_dwFlush;

                if(m_dwBase >= m_dwDiscard)

                    m_dwBase = 0;

                if(FAILED(hr=m_pVB->Lock(m_dwBase*sizeof(POINTVERTEX),

                           m_dwFlush * sizeof(POINTVERTEX),

                          (void**) &pVertices,

                m_dwBase ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD ) ) )

                {

                    return hr;

                }

                dwNumParticlesToRender = 0;

            }

            vPos += vVel;

        }

        pParticle = pParticle->m_pNext;

    }

    // Unlock the vertex buffer

    m_pVB->Unlock();

    // Render any remaining particles

    if( dwNumParticlesToRender )

    {

        if(FAILED(hr = m_pDevice->DrawPrimitive( D3DPT_POINTLIST,

 m_dwBase, dwNumParticlesToRender )))

            return hr;

    }

    // Reset render states

    m_pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );

    m_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE,  FALSE );

    m_pDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );

    m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

    m_pDevice->SetRenderState( D3DRS_LIGHTING,TRUE);

    m_pDevice->SetTextureStageState(0,D3DTSS_COLOROP ,D3DTOP_SELECTARG1);

    return S_OK;

}

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论