源项目地址:https://github.com/Microsoft/...
以下是把示例转换为简要说明,同时给出实际运行效果及关键代码剖析:
ParticlesDemo粒子群3D动画转圈示例
- 文件组织
Particle.cs粒子类
ParticleSystem.cs粒子系统类
ParticleSystemManager.cs粒子系统管理类
WPF中MainWindow.xaml及.cs文件等
- 实现功能
每秒出现各种颜色的3D粒子顺时针转圈运动
粒子随时间而扩散、消失
显示帧数及总粒子束
- 主要代码
粒子类
public class Particle
{
public double Decay;//消散系数
public double Life; //存在时长
public Point3D Position;//3D位置
public double Size;//尺寸
public double StartLife;//开始时长
public double StartSize;//开始尺寸
public Vector3D Velocity;//3D位移数
}
粒子3D模型初始化
_particleModel = new GeometryModel3D {Geometry = new MeshGeometry3D()};
var e = new Ellipse
{
Width = 32.0,
Height = 32.0
};
var b = new RadialGradientBrush();
b.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, color.R, color.G, color.B), 0.15));
b.GradientStops.Add(new GradientStop(Colors.White, 0.3));
b.GradientStops.Add(new GradientStop(Color.FromArgb(0x00, color.R, color.G, color.B), 1.0));
e.Fill = b;
e.Measure(new Size(32, 32));
e.Arrange(new Rect(0, 0, 32, 32));
粒子材质
Brush brush = null;
#if USE_VISUALBRUSH
brush = new VisualBrush(e);
#else
var renderTarget = new RenderTargetBitmap(32, 32, 96, 96, PixelFormats.Pbgra32);
renderTarget.Render(e);
renderTarget.Freeze();
brush = new ImageBrush(renderTarget);
#endif
var material = new DiffuseMaterial(brush);
_particleModel.Material = material;
_rand = new Random(brush.GetHashCode());
- 后台逻辑
在加载窗口初始化
由DispatcherTimer计时器驱动,绑定事件:每1/60s执行一帧动作
var frameTimer = new DispatcherTimer();
frameTimer.Tick += OnFrame;
frameTimer.Interval = TimeSpan.FromSeconds(1.0/60.0);
frameTimer.Start();
同时三维模型集Model3DGroup添加5种不同颜色例子系统。
_spawnPoint = new Point3D(0.0, 0.0, 0.0);
_lastTick = Environment.TickCount;
_pm = new ParticleSystemManager();
WorldModels.Children.Add(_pm.CreateParticleSystem(1000, Colors.Gray));
WorldModels.Children.Add(_pm.CreateParticleSystem(1000, Colors.Red));
WorldModels.Children.Add(_pm.CreateParticleSystem(1000, Colors.Silver));
WorldModels.Children.Add(_pm.CreateParticleSystem(1000, Colors.Orange));
WorldModels.Children.Add(_pm.CreateParticleSystem(1000, Colors.Yellow));
每帧的动作方法:
记录每帧间的毫秒时长,作为粒子移动位置、大小变化、消散时长的重要参考。
private void OnFrame(object sender, EventArgs e)
{
// Calculate frame time;
_currentTick = Environment.TickCount;
_elapsed = (_currentTick - _lastTick)/1000.0;
_totalElapsed += _elapsed;
_lastTick = _currentTick;
每秒显示帧数及总粒子数
每帧_frameCount时长自增,在累计毫秒到1s时,重新计数。
_frameCount++;
_frameCountTime += _elapsed;
if (_frameCountTime >= 1.0)
{
_frameCountTime -= 1.0;
_frameRate = _frameCount;
_frameCount = 0;
FrameRateLabel.Content = "FPS: " + _frameRate + " Particles: " + _pm.ActiveParticleCount;
}
同时,帧间根据消散时间参数来更新微粒系统中的所有粒子
_pm.Update((float) _elapsed);
具体方法如下:
在ParticleSystem.cs中
新建一个死亡列表,捕获消散时间到0的粒子,在总列表中再移除标志的死亡例子。ps:foreach中若删除其中元素会报异常。是否通过for循环中删除死亡粒子可提供性能?
public void Update(double elapsed)
{
var deadList = new List<Particle>();
// Update all particles
foreach (var p in _particleList)
{
p.Position += p.Velocity*elapsed;
p.Life -= p.Decay*elapsed;
p.Size = p.StartSize*(p.Life/p.StartLife);
if (p.Life <= 0.0)
deadList.Add(p);
}
foreach (var p in deadList)
_particleList.Remove(p);
UpdateGeometry();
}
更新粒子的几何图形数据:
重新设定三维三角基元的位置信息,如位置、三角形索引的集合、纹理坐标集合。其中根据之前设置的动态尺寸p.Size = p.StartSize*p.Life/p.StartLife)参数来改变例子大小。
若不指定TriangleIndices三角形索引的集合,则出现以下效果
private void UpdateGeometry()
{
var positions = new Point3DCollection();
var indices = new Int32Collection();
var texcoords = new PointCollection();
for (var i = 0; i < _particleList.Count; ++i)
{
var positionIndex = i*4;
var indexIndex = i*6;
var p = _particleList[i];
var p1 = new Point3D(p.Position.X, p.Position.Y, p.Position.Z);
var p2 = new Point3D(p.Position.X, p.Position.Y + p.Size, p.Position.Z);
var p3 = new Point3D(p.Position.X + p.Size, p.Position.Y + p.Size, p.Position.Z);
var p4 = new Point3D(p.Position.X + p.Size, p.Position.Y, p.Position.Z);
positions.Add(p1);
positions.Add(p2);
positions.Add(p3);
positions.Add(p4);
var t1 = new Point(0.0, 0.0);
var t2 = new Point(0.0, 1.0);
var t3 = new Point(1.0, 1.0);
var t4 = new Point(1.0, 0.0);
texcoords.Add(t1);
texcoords.Add(t2);
texcoords.Add(t3);
texcoords.Add(t4);
indices.Add(positionIndex);
indices.Add(positionIndex + 2);
indices.Add(positionIndex + 1);
indices.Add(positionIndex);
indices.Add(positionIndex + 3);
indices.Add(positionIndex + 2);
}
((MeshGeometry3D) _particleModel.Geometry).Positions = positions;
((MeshGeometry3D) _particleModel.Geometry).TriangleIndices = indices;
((MeshGeometry3D) _particleModel.Geometry).TextureCoordinates = texcoords;
}
回到帧方法中,每帧新建12个拥有初始3D位置、漫射角度系数、颜色、尺寸、存在时长参数 的粒子。
利用每帧间隔时长参数返回指定角度的正弦、余弦轨迹偏移度,设置粒子初始位置_spawnPoint,来形成转圈的轨迹。
_pm.Update((float) _elapsed);
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Red, _rand.NextDouble(), 5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Orange, _rand.NextDouble(), 2.5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Silver, _rand.NextDouble(), 2.5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Gray, _rand.NextDouble(), 2.5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Red, _rand.NextDouble(), 2.5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Orange, _rand.NextDouble(), 2.5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Silver, _rand.NextDouble(), 2.5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Gray, _rand.NextDouble(), 2.5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Red, _rand.NextDouble(), 2.5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Yellow, _rand.NextDouble(), 2.5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Silver, _rand.NextDouble(), 2.5*_rand.NextDouble());
_pm.SpawnParticle(_spawnPoint, 10.0, Colors.Yellow, _rand.NextDouble(), 2.5*_rand.NextDouble());
var c = Math.Cos(_totalElapsed);
var s = Math.Sin(_totalElapsed);
_spawnPoint = new Point3D(s*32.0, c*32.0, 0.0);
ParticleSystem中新建粒子方法
其中在初始位置点再随机变动粒子位置,形成漫射粒子效果
public void SpawnParticle(Point3D position, double speed, double size, double life)
{
if (_particleList.Count > MaxParticleCount)
return;
var p = new Particle
{
Position = position,
StartLife = life,
Life = life,
StartSize = size*2,
Size = size*10
};
var x = 1.0f - (float) _rand.NextDouble()*2.0f;
var z = 1.0f - (float) _rand.NextDouble()*2.0f;
var v = new Vector3D(x, z, 0.0);
v.Normalize();
v *= ((float) _rand.NextDouble() + 0.25f)*(float) speed;
p.Velocity = new Vector3D(v.X, v.Y, v.Z);
p.Decay = 1.0f; // 0.5 + rand.NextDouble();
//if (p.Decay > 1.0)
// p.Decay = 1.0;
_particleList.Add(p);
}
以上。。
另:根据源代码修改参数,获得效果如下:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。