[译]03.打造灵活高效的粒子系统——粒子的容器实现

03_1

上一篇我们讲到设计粒子容器时碰到的问题。本文将介绍我目前的实现方式(尚未优化的),同时也将介绍一些改进的方法。

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

  • 引言

基础设计:

  • ParticleSystem中有一到多个Generators(产生器)和Updaters(更新器),用于操作PartcileData;
  • 一个ParticleSystem保存一个PartcileData;
  • 目前数学库使用的是 GLM,不过将来有可能会换;
  • ParticleData类作为粒子数据的容器,有以下功能:
    • 为指定数量的粒子,分配和管理内存;
    • 可以创建或删除粒子;
    • 激活的粒子放在buffer头部并且连续存储;
    • 粒子的每种属性都分开在不同的数组中保存,属性一般是4D向量(颜色的RGBA,旋转的xyzw等)。
    • 不使用std::vector,因为STL的debug版非常慢,另外就是我们知道最大的粒子数量,内存管理也很简单,手动管理更方便;
  • 声明

Gist地址: gist.github.com/fenbf/BasicParticles

 

ParticleData class

class ParticleData

{

public:

std::unique_ptr<glm::vec4[]> m_pos;

std::unique_ptr<glm::vec4[]> m_col;

std::unique_ptr<glm::vec4[]> m_startCol;

std::unique_ptr<glm::vec4[]> m_endCol;

std::unique_ptr<glm::vec4[]> m_vel;

std::unique_ptr<glm::vec4[]> m_acc;

std::unique_ptr<glm::vec4[]> m_time;

std::unique_ptr<bool[]>  m_alive;

 

size_t m_count{ 0 };

size_t m_countAlive{ 0 };

public:

explicit ParticleData(size_t maxCount) { generate(maxCount); }

~ParticleData() { }

 

ParticleData(const ParticleData &) = delete;

ParticleData &operator=(const ParticleData &) = delete;

 

void generate(size_t maxSize);

void kill(size_t id);

void wake(size_t id);

void swapData(size_t a, size_t b);

};

注意:

  • 目前使用std::unique_ptr保存这些数据,将来会改的,因为可能需要内存对齐。
  • 实现

Generation:

void ParticleData::generate(size_t maxSize)

{

m_count = maxSize;

m_countAlive = 0;

 

m_pos.reset(new glm::vec4[maxSize]);

m_col.reset(new glm::vec4[maxSize]);

m_startCol.reset(new glm::vec4[maxSize]);

m_endCol.reset(new glm::vec4[maxSize]);

m_vel.reset(new glm::vec4[maxSize]);

m_acc.reset(new glm::vec4[maxSize]);

m_time.reset(new glm::vec4[maxSize]);

m_alive.reset(new bool[maxSize]);

}

Kill:

void ParticleData::kill(size_t id)

{

if (m_countAlive > 0)

{

m_alive[id] = false;

swapData(id, m_countAlive – 1);

m_countAlive–;

}

}

Wake:

void ParticleData::wake(size_t id)

{

if (m_countAlive < m_count)

{

m_alive[id] = true;

swapData(id, m_countAlive);

m_countAlive++;

}

}

Swap:

void ParticleData::swapData(size_t a, size_t b)

{

std::swap(m_pos[a], m_pos[b]);

std::swap(m_col[a], m_col[b]);

std::swap(m_startCol[a], m_startCol[b]);

std::swap(m_endCol[a], m_endCol[b]);

std::swap(m_vel[a], m_vel[b]);

std::swap(m_acc[a], m_acc[b]);

std::swap(m_time[a], m_time[b]);

std::swap(m_alive[a], m_alive[b]);

}

可能的优化点:

  • 也许wake()和kill()函数中的if是可以移除的;
  • 也许不需要完全交换所有属性;
  • 改进

可配置属性

粒子数据采用SoA的方式可以让我们组合出无限种ParticleData,这个目前还没有在这个类中体现,不过我在其他的系统中这样做过。

最简单的做法是通过mask来设置各种属性:

ParticleData::mask = Params::Pos | Params::Vel | Params::Acc | Params::Color…

在构造函数中,只为需要的属性分配内存。

generate() {

// ..

if (mask & Params::Vel)

allocate ParticleData::vel array

// …

updater和generators中也需要这么改,这样只需要更新指定的属性。下面这个函数应该是有很多这种语句的,这里省略了。

update() {

// ..

if (mask & Params::Vel)

update ParticleData::vel array

// …

请注意,如一个属性依赖另一个的话可能会有问题。

限制:我们只能从定义好的一组属性中选择一部分。有一种做法可以完全动态创建属性,即用map保存名字和值的数组<name,array>,属性的名字和类型都可以配置,比如<” position”, vec4[…]>。这样做稍微麻烦一点,但是做粒子编辑器的时候就会发现很有好处了。

  • 下一步

下一章我们会接触到粒子的产生和更新模块。

原文地址:http://www.bfilipek.com/2014/04/flexible-particle-system-container-2.html

No Responses

发表评论

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

您可以使用这些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!