[译]04.打造灵活高效的粒子系统——产生器与发射器

04_1

目前为止我们的粒子系统已经有了一些基础的功能:粒子容器和基本框架。现在我们需要一个模块可以“唤醒”粒子。本文我将介绍emitter(发射器)和generator(产生器)模块。

转载请注明[本文链接][原文链接],谢谢!

引言

基本设计:

  • 单一职责原则:粒子系统包含一个粒子容器,一组emitter,一组updater(更新器)。粒子系统本身只负责基础的操作,比如初始化,清理和管理更新流程。
  • generator(产生器)用来产生一到多个粒子的属性。
  • 一个emitter(发射器)包含多个generator(产生器)。
  • 更新和删除粒子由updater负责。

Gist地址在这: fenbf / BasicParticleGenerators

Emitter循环

void ParticleEmitter::emit(double dt, ParticleData *p)

{

const size_t maxNewParticles = static_cast<size_t>(dt*m_emitRate);

const size_t startId = p->m_countAlive;

const size_t endId = std::min(startId + maxNewParticles, p->m_count-1);

 

for (auto &gen : m_generators)            // << gen loop

gen->generate(dt, p, startId, endId);

 

for (size_t i = startId; i < endId; ++i)  // << wake loop

p->wake(i);

}

思路:emitter应该每帧发射一定数量的粒子,这个当然取决于发射速率。同时要产生粒子的所有属性,而一个属性由一个generator产生,所以emitter和generator是一对多的关系。

在gen loop循环中,我们调用generator的代码,每个generator都将为startId到endId之间的粒子设置一部分属性。

在wake loop中“唤醒”选定的粒子。

Generator(产生器)

一个generator应该是一个非常简单的模块,只是将给定的一段范围的粒子设置一些参数。所有“复杂”的代码已经通过上层系统和emitter来处理了。

以下是BoxPosGen类(盒型产生器)

class BoxPosGen : public ParticleGenerator

{

public:

glm::vec4 m_pos{ 0.0 };

glm::vec4 m_maxStartPosOffset{ 0.0 };

public:

BoxPosGen() { }

 

virtual void generate(double dt, ParticleData *p,

size_t startId, size_t endId) override;

};

 

void BoxPosGen::generate(double dt, ParticleData *p, size_t startId, size_t endId)

{

glm::vec4 posMin{ m_pos.x – m_maxStartPosOffset.x,

m_pos.y – m_maxStartPosOffset.y,

m_pos.z – m_maxStartPosOffset.z,

1.0 };

glm::vec4 posMax{ m_pos.x + m_maxStartPosOffset.x,

m_pos.y + m_maxStartPosOffset.y,

m_pos.z + m_maxStartPosOffset.z,

1.0 };

 

for (size_t i = startId; i < endId; ++i)

{

p->m_pos[i] = glm::linearRand(posMin, posMax);

}

}

这个想法太好了,我们可以通过组合各种不同的generator来生成多种emitter。

其他generator:

  • RoundPosGen- 按圆周来计算粒子的位置(只是XY平面);
  • BasicColorGen- 为粒子计算开始和结束的颜色;
  • BasicVelGen- 设置速度,可以为每个轴设置最小和最大速度;
  • SphereVelGen- 速度向量是由圆上的一个点产生的;
  • BasicTimeGen- 生成时间,值在指定的最大和最小值之间。

Emitter示例

以下代码演示一个emitter使用了RoundPosGen, BasicColorGen, BasicVelGen和BasicTimeGen:

auto particleEmitter = std::make_shared<ParticleEmitter>();

{

particleEmitter->m_emitRate = (float)NUM_PARTICLES*0.45f;

 

// pos:

auto posGenerator = std::make_shared<generators::RoundPosGen>();

posGenerator->m_center = glm::vec4{ 0.0, 0.0, 0.0, 0.0 };

posGenerator->m_radX = 0.15f;

posGenerator->m_radY = 0.15f;

particleEmitter->addGenerator(posGenerator);

 

auto colGenerator = std::make_shared<generators::BasicColorGen>();

colGenerator->m_minStartCol = glm::vec4{ 0.7, 0.0, 0.7, 1.0 };

colGenerator->m_maxStartCol = glm::vec4{ 1.0, 1.0, 1.0, 1.0 };

colGenerator->m_minEndCol = glm::vec4{ 0.5, 0.0, 0.6, 0.0 };

colGenerator->m_maxEndCol = glm::vec4{ 0.7, 0.5, 1.0, 0.0 };

particleEmitter->addGenerator(colGenerator);

 

auto velGenerator = std::make_shared<generators::BasicVelGen>();

velGenerator->m_minStartVel = glm::vec4{ 0.0f, 0.0f, 0.15f, 0.0f };

velGenerator->m_maxStartVel = glm::vec4{ 0.0f, 0.0f, 0.45f, 0.0f };

particleEmitter->addGenerator(velGenerator);

 

auto timeGenerator = std::make_shared<generators::BasicTimeGen>();

timeGenerator->m_minTime = 1.0;

timeGenerator->m_maxTime = 3.5;

particleEmitter->addGenerator(timeGenerator);

}

m_system->addEmitter(particleEmitter);

04_2

圆形粒子发射器

需要注意的地方

我觉得单一职责原则是很有指导意义的,这些代码看起来非常简单直接,每个模块都只做了一件事。

另一个好处是我们可以“轻松”的转到可视化编辑。你可以创建一个特效,添加一个emitter,然后添加一些不同的generator,通过这些简单的步骤,很快整个系统就建立起来了。

有什么劣势吗?有,你必须理解整个粒子系统的updater、generator的层次,对于一个简单的系统来说有点太“重”了,但是假以时日,系统变得越来越复杂,那么这个架构就很有帮助了。

下一步

如果没有更新机制只有generator和emitter是没有意义的。下一次我们讲驱动整个系统的”引擎”。

原文地址:http://www.bfilipek.com/2014/05/flexible-particle-system-emitter-and.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

You must enable javascript to see captcha here!