天天看點

Directx11基于GPU_GeometryShader的粒子系統

    粒子系統作為在遊戲中一個挺常見的技術,用在各種爆炸,下雨,火焰等特效中。我們利用GPU中的GeomtryShader來實作一個粒子系統。以下雨特效為例。

    介紹粒子系統具體實作前先介紹下GeomtryShader的StreamOutput這個特性。StreamOutput這個Stage把GeometryShader的計算結果輸出到一個buffer中。

Directx11基于GPU_GeometryShader的粒子系統

    下面主要從Shader代碼設計的角度講下如何實作一個下雨特效的粒子系統,其核心算法說白了就一句話:分成主粒子和副粒子。主粒子按時間間隔分裂副粒子,副粒子到了一定的時間就死亡。

Directx11基于GPU_GeometryShader的粒子系統

    繪制時要麼用公告闆繪制一個粒子,要麼用一條直線繪制一個粒子。

    隻是具體的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));

}

最後場景截圖

Directx11基于GPU_GeometryShader的粒子系統

繼續閱讀