• 热门专题

Directx11基于GeometryShader的粒子系统

作者:qiul12345  发布日期:2011-11-23 15:54:32
Tag标签:Directx11  GeometryShader  粒子系统  
  •     粒子系统作为在游戏中一个挺常见的技术,用在各种爆炸,下雨,火焰等特效中。我们利用GPU中的GeomtryShader来实现一个粒子系统。以下雨特效为例。

        介绍粒子系统具体实现前先介绍下GeomtryShader的StreamOutput这个特性。StreamOutput这个Stage把GeometryShader的计算结果输出到一个buffer中。

     

        下面主要从Shader代码设计的角度讲下如何实现一个下雨特效的粒子系统,其核心算法说白了就一句话:分成主粒子和副粒子。主粒子按时间间隔分裂副粒子,副粒子到了一定的时间就死亡。

     

        绘制时要么用公告板绘制一个粒子,要么用一条直线绘制一个粒子。

        只是具体的shader代码的设计要花点心思,在其在shader内部经过了两个technique,一个主要利用Geometry Shader的StreamOuput输出到一段buffer中;另一个利用上面的计算结果来进行绘制,具体的粒子运动情况(受重力)的更新等可以放在第二个technique的geometry shader里面。

        第一个technique,也就是负责利用Geometry来计算,并通过StreamOutput输出到一个buffer中的technique,为了整体清晰我先把technique写出来:

     

    technique11 StreamOutTech
    
    
    
    {
    
    
    
    pass P0
    
    
    
    {
    
    
    
    SetVertexShader(CompileShader(vs_4_0,StreamOut_VS()));
    
    
    
    SetGeometryShader( ConstructGSWithSO( CompileShader(gs_4_0,StreamOut_GS()),"POSITION.xyz;VELOCITY.xyz;SIZE.xy;AGE.x;TYPE.x" ) );
    
    
    
    SetPixelShader(NULL);
    
    
    
    SetDepthStencilState(DisableDepth,0);
    
    
    
    }
    
    
    
    }
    
    
    
    //================================================
    
    
    
    //StreamOutTech
    
    
    
    //================================================
    
    
    
    Particle StreamOut_VS(Particle vIn)
    
    
    
    {
    
    
    
    return vIn;
    
    
    
    }
    
    
    
    [maxvertexcount(6)]
    
    
    
    void StreamOut_GS(point Particle gIn[1],inout PointStream<Particle> pStream)
    
    
    
    {
    
    
    
    gIn[0].age+=timeStep;
    
    
    
    if(gIn[0].type==P_EMITTER)
    
    
    
    {
    
    
    //主喷射粒子,时间到了一定阀值,就该生成一定数量的副粒子.
    
    
    if(gIn[0].age>0.01f)
    
    
    
    {
    
    
    
    //生成副粒子
    
    
    
    for(int i=0;i<5;i++)
    
    
    
    {
    
    
    
    Particle nPt;
    
    
    
    nPt.initPosW=emitPosW.xyz+35.0f*GetRandomVec3((float)i/5.0f);
    
    
    
    nPt.initPosW.y=40.0f;
    
    
    
    nPt.initVelW=float3(0.0f,0.0f,0.0f);
    
    
    
    nPt.size=float2(1.0f,1.0f);
    
    
    
    nPt.age=0.0f;
    
    
    
    nPt.type=P_FLARE;
    
    
    
    pStream.Append(nPt);
    
    
    
    }
    
    
    
    gIn[0].age=0.0f;
    
    
    
    }//if(age)
    
    
    
    pStream.Append(gIn[0]);
    
    
    
    }//if(type==)
    
    
    
    else
    
    
    {//副粒子,看时间到了就销毁,不然输出到steamout buffer中继续绘制
    
    
    
    if(gIn[0].age<=3.0f)
    
    
    
    {
    
    
    
      pStream.Append(gIn[0]);
    
    
    
    }
    
    
    
    }
    
    
    
    }
    
    
    

        第二个technique就简单了,就是基本的绘制。在GeomtryShader可以更新粒子的运动情况,和把每个雨点看成一条直线。

     

    technique11 DrawTech
    
    
    
    {
    
    
    
    pass P0
    
    
    
    {
    
    
    
    SetVertexShader(CompileShader(vs_4_0,Draw_VS()));
    
    
    
    SetGeometryShader( CompileShader(gs_4_0,Draw_GS()) );
    
    
    
    SetPixelShader( CompileShader(ps_4_0,Draw_PS()) );
    
    
    
    SetDepthStencilState(NoWriteDepth,0);
    
    
    
    }
    
    
    
    }
    
    
    
    Particle Draw_VS(Particle vIn)
    
    
    
    {
    
    
    
    return vIn;
    
    
    
    }
    
    
    
    [maxvertexcount(2)]
    
    
    
    void Draw_GS(point Particle gIn[1],inout LineStream<DrawGSOut> pStream)
    
    
    
    {
    
    
    
    if(gIn[0].type!=P_EMITTER)
    
    
    //不绘制主粒子
    
    {
    
    //更新粒子的运动情况,这里我们只用了最简单的牛顿定律
    
    float3 posW1=gIn[0].initPosW+gIn[0].initVelW*gIn[0].age+0.5f*(gForce*0.7)*gIn[0].age*gIn[0].age;
    
    
    
        float3 posW2=posW1+0.2f*gForce;
    
    //把粒子绘制成一条直线,当然也可以绘制成公告板等
    
    
    
    
        DrawGSOut gOut1;
    
    
    
        gOut1.posH=mul(float4(posW1,1.0f),viewMtx);
    
    
    
        gOut1.posH=mul(gOut1.posH,projectMtx);
    
    
    
        gOut1.tex=float2(0.0f,0.0f);
    
    
    
        pStream.Append(gOut1);
    
    
    
        DrawGSOut gOut2;
    
    
    
        gOut2.posH=mul(float4(posW2,1.0f),viewMtx);
    
    
    
        gOut2.posH=mul(gOut2.posH,projectMtx);
    
    
    
        gOut2.tex=float2(1.0f,1.0f);
    
    
    
        pStream.Append(gOut2);
    
    
    
    }
    
    
    
    }
    
    
    
    float4 Draw_PS(DrawGSOut pIn):SV_Target
    
    
    
    {
    
    
    
    return gTexArray.Sample(gLinearSam,float3(pIn.tex,0.0f));
    
    
    
    }
    
    

    最后场景截图

     

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规