Directx11基于GeometryShader的粒子系统

1649 篇文章 11 订阅
1623 篇文章 22 订阅
  • 粒子系统作为在游戏中一个挺常见的技术,用在各种爆炸,下雨,火焰等特效中。我们利用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写出来:

     

    001. technique11 StreamOutTech
    002.  
    003.  
    004.  
    005. {
    006.  
    007.  
    008.  
    009. pass P0
    010.  
    011.  
    012.  
    013. {
    014.  
    015.  
    016.  
    017. SetVertexShader(CompileShader(vs_4_0,StreamOut_VS()));
    018.  
    019.  
    020.  
    021. SetGeometryShader( ConstructGSWithSO( CompileShader(gs_4_0,StreamOut_GS()),"POSITION.xyz;VELOCITY.xyz;SIZE.xy;AGE.x;TYPE.x" ) );
    022.  
    023.  
    024.  
    025. SetPixelShader(NULL);
    026.  
    027.  
    028.  
    029. SetDepthStencilState(DisableDepth,0);
    030.  
    031.  
    032.  
    033. }
    034.  
    035.  
    036.  
    037. }
    038.  
    039.  
    040.  
    041. //================================================
    042.  
    043.  
    044.  
    045. //StreamOutTech
    046.  
    047.  
    048.  
    049. //================================================
    050.  
    051.  
    052.  
    053. Particle StreamOut_VS(Particle vIn)
    054.  
    055.  
    056.  
    057. {
    058.  
    059.  
    060.  
    061. return vIn;
    062.  
    063.  
    064.  
    065. }
    066.  
    067.  
    068.  
    069. [maxvertexcount(6)]
    070.  
    071.  
    072.  
    073. void StreamOut_GS(point Particle gIn[1],inout PointStream<Particle> pStream)
    074.  
    075.  
    076.  
    077. {
    078.  
    079.  
    080.  
    081. gIn[0].age+=timeStep;
    082.  
    083.  
    084.  
    085. if(gIn[0].type==P_EMITTER)
    086.  
    087.  
    088.  
    089. {
    090.  
    091.  
    092. //主喷射粒子,时间到了一定阀值,就该生成一定数量的副粒子.
    093.  
    094.  
    095. if(gIn[0].age>0.01f)
    096.  
    097.  
    098.  
    099. {
    100.  
    101.  
    102.  
    103. //生成副粒子
    104.  
    105.  
    106.  
    107. for(int i=0;i<5;i++)
    108.  
    109.  
    110.  
    111. {
    112.  
    113.  
    114.  
    115. Particle nPt;
    116.  
    117.  
    118.  
    119. nPt.initPosW=emitPosW.xyz+35.0f*GetRandomVec3((float)i/5.0f);
    120.  
    121.  
    122.  
    123. nPt.initPosW.y=40.0f;
    124.  
    125.  
    126.  
    127. nPt.initVelW=float3(0.0f,0.0f,0.0f);
    128.  
    129.  
    130.  
    131. nPt.size=float2(1.0f,1.0f);
    132.  
    133.  
    134.  
    135. nPt.age=0.0f;
    136.  
    137.  
    138.  
    139. nPt.type=P_FLARE;
    140.  
    141.  
    142.  
    143. pStream.Append(nPt);
    144.  
    145.  
    146.  
    147. }
    148.  
    149.  
    150.  
    151. gIn[0].age=0.0f;
    152.  
    153.  
    154.  
    155. }//if(age)
    156.  
    157.  
    158.  
    159. pStream.Append(gIn[0]);
    160.  
    161.  
    162.  
    163. }//if(type==)
    164.  
    165.  
    166.  
    167. else
    168.  
    169.  
    170. {//副粒子,看时间到了就销毁,不然输出到steamout buffer中继续绘制
    171.  
    172.  
    173.  
    174. if(gIn[0].age<=3.0f)
    175.  
    176.  
    177.  
    178. {
    179.  
    180.  
    181.  
    182. pStream.Append(gIn[0]);
    183.  
    184.  
    185.  
    186. }
    187.  
    188.  
    189.  
    190. }
    191.  
    192.  
    193.  
    194. }

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

     

    001. technique11 DrawTech
    002.  
    003.  
    004.  
    005. {
    006.  
    007.  
    008.  
    009. pass P0
    010.  
    011.  
    012.  
    013. {
    014.  
    015.  
    016.  
    017. SetVertexShader(CompileShader(vs_4_0,Draw_VS()));
    018.  
    019.  
    020.  
    021. SetGeometryShader( CompileShader(gs_4_0,Draw_GS()) );
    022.  
    023.  
    024.  
    025. SetPixelShader( CompileShader(ps_4_0,Draw_PS()) );
    026.  
    027.  
    028.  
    029. SetDepthStencilState(NoWriteDepth,0);
    030.  
    031.  
    032.  
    033. }
    034.  
    035.  
    036.  
    037. }
    038.  
    039.  
    040.  
    041. Particle Draw_VS(Particle vIn)
    042.  
    043.  
    044.  
    045. {
    046.  
    047.  
    048.  
    049. return vIn;
    050.  
    051.  
    052.  
    053. }
    054.  
    055.  
    056.  
    057. [maxvertexcount(2)]
    058.  
    059.  
    060.  
    061. void Draw_GS(point Particle gIn[1],inout LineStream<DrawGSOut> pStream)
    062.  
    063.  
    064.  
    065. {
    066.  
    067.  
    068.  
    069. if(gIn[0].type!=P_EMITTER)
    070.  
    071.  
    072. //不绘制主粒子
    073.  
    074. {
    075.  
    076. //更新粒子的运动情况,这里我们只用了最简单的牛顿定律
    077.  
    078. float3 posW1=gIn[0].initPosW+gIn[0].initVelW*gIn[0].age+0.5f*(gForce*0.7)*gIn[0].age*gIn[0].age;
    079.  
    080.  
    081.  
    082. float3 posW2=posW1+0.2f*gForce;
    083.  
    084. //把粒子绘制成一条直线,当然也可以绘制成公告板等
    085.  
    086.  
    087.  
    088.  
    089. DrawGSOut gOut1;
    090.  
    091.  
    092.  
    093. gOut1.posH=mul(float4(posW1,1.0f),viewMtx);
    094.  
    095.  
    096.  
    097. gOut1.posH=mul(gOut1.posH,projectMtx);
    098.  
    099.  
    100.  
    101. gOut1.tex=float2(0.0f,0.0f);
    102.  
    103.  
    104.  
    105. pStream.Append(gOut1);
    106.  
    107.  
    108.  
    109. DrawGSOut gOut2;
    110.  
    111.  
    112.  
    113. gOut2.posH=mul(float4(posW2,1.0f),viewMtx);
    114.  
    115.  
    116.  
    117. gOut2.posH=mul(gOut2.posH,projectMtx);
    118.  
    119.  
    120.  
    121. gOut2.tex=float2(1.0f,1.0f);
    122.  
    123.  
    124.  
    125. pStream.Append(gOut2);
    126.  
    127.  
    128.  
    129. }
    130.  
    131.  
    132.  
    133. }
    134.  
    135.  
    136.  
    137. float4 Draw_PS(DrawGSOut pIn):SV_Target
    138.  
    139.  
    140.  
    141. {
    142.  
    143.  
    144.  
    145. return gTexArray.Sample(gLinearSam,float3(pIn.tex,0.0f));
    146.  
    147.  
    148.  
    149. }

    最后场景截图

     \

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值