-
粒子系统作为在游戏中一个挺常见的技术,用在各种爆炸,下雨,火焰等特效中。我们利用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.
}
最后场景截图
Directx11基于GeometryShader的粒子系统
最新推荐文章于 2021-05-14 00:24:43 发布