c - 通过渲染四边形更新粒子系统的速度和位置

标签 c opengl gpu particle-system

使用本文算法实现基于GPU的粒子系统: http://www.gamasutra.com/view/feature/130535/building_a_millionparticle_system.php?print=1

有两件事我不明白:

  1. 为什么要用栈或者堆来存储可用的粒子索引? 如果一个粒子在时间 t 死亡,那么它将在时间 t+1 从零开始它的生命。有一个参数 N 来控制屏幕上的粒子数。如果我可以重用所有粒子,为什么我要关心所有可用粒子的索引,甚至使用堆来存储它?
  2. 为了更新速度和位置,它说,“实际模拟是在片段着色器中实现的。着色器通过渲染屏幕大小的四边形为渲染目标的每个像素执行......”如果我想要要绘制为点的粒子,是否需要将四边形更改为点? 为什么是四边形?它如何绘制点数?

.

最佳答案

  1. 术语“粒子已死”是一个术语,仅描述粒子的语义死亡。从 GPU 的角度来看,所有粒子始终处于事件状态,并且所有粒子都将在每一帧中进行计算。 (或者至少,粒子 0 到 N 将被处理,但即使是死的)。

    一旦 CPU“检测到”某个粒子已死亡(例如,它的年龄为 5 秒左右),就需要记住该粒子的索引,以便新粒子可以重用该粒子索引。这可以通过许多不同的方式来完成,两种明显的方式是堆栈或堆。

    只有当粒子的最大年龄不同时,才需要一种特殊的数据结构来存储这些死粒子索引。如果它们没有区别,你可以只实现一个环形缓冲区。但大多数时候,您将对所有类型的粒子使用此粒子引擎,并且这些粒子可能具有可变的生存时间值。然后你需要那些数据结构。

  2. 该算法使用片段着色器进行速度计算。它从一个纹理(包含 x/y/z 坐标而不是 r/g/b 颜色信息)读取数据并写入另一个纹理(也包含 x/y/z 坐标而不是 r/g/b 颜色信息),使用源纹理和目标纹理之间的 1:1 映射,并将整个源纹理渲染到目标纹理。这与稍后将在步骤 6 中渲染的实际粒子无关。渲染粒子

    或者换句话说:“屏幕大小的四边形”在这里实际上是一个错误的术语,它应该读作“纹理大小的四边形”,因为在这一点上,屏幕上根本没有绘制任何东西。目标纹理(即保存新位置信息的纹理)是屏幕。

/再次编辑:

好的,也许改写文档:

你有一个结构:

struct color {
    float r, g, b;
};

和一些#define:

#define vector color
#define x r
#define y g
#define z b

你有几个粒子数组:

#define NP 1024 * 1024
struct vector particle_pos[2][NP];
struct vector particle_vel[2][NP];
uint32_t particle_birth_tick[NP];

// Double buffering - gonne have to remember, where
// we read from and where we write to:
struct vector * particle_pos_r = particle_pos[0];
struct vector * particle_pos_w = particle_pos[1];
struct vector * particle_vel_r = particle_vel[0];
struct vector * particle_vel_w = particle_vel[1];

现在:

  1. Process Birth and Death
#define TTL 5 * 25 // 5 seconds * 25 simulation steps per second.
for (size_t i = 0; i < NP; ++i) {
    if (particle_birth_tick[i] + TTL == current_tick) {
        particle_pos_r[i].x = somewhere behind viewer;
        particle_pos_r[i].y = somewhere behind viewer;
        particle_pos_r[i].z = somewhere behind viewer;
        particle_vel_r[i].x = 0;
        particle_vel_r[i].y = 0;
        particle_vel_r[i].z = 0;
        free_list.add(i);
    }
}
void add_particle(struct vector p, struct vector v) {
    size_t i = free_list.pop_any();
    particle_pos_r[i] = p;
    particle_vel_r[i] = v;
}
  1. Update Velocities
for (size_t i = 0; i < 1024 * 1024; ++i) {
    particle_vel_w[i].x = do_calculations(particle_vel_r[i].x)
    particle_vel_w[i].y = do_calculations(particle_vel_r[i].y)
    particle_vel_w[i].z = do_calculations(particle_vel_r[i].z)
}
swap(particle_vel_r, particle_vel_w);
  1. Update Positions
for (size_t i = 0; i < 1024 * 1024; ++i) {
    particle_pos_w[i].x = particle_pos_r[i].x + particle_vel_r[i].x;
    particle_pos_w[i].y = particle_pos_r[i].y + particle_vel_r[i].y;
    particle_pos_w[i].z = particle_pos_r[i].z + particle_vel_r[i].z;
}
swap(particle_pos_r, particle_pos_w);
  1. Sort for Alpha Blending
sort a bit...
  1. Transfer Texture Data to Vertex Data
copy the pos texture into a vbo
  1. Render Particles
actually draw particles

这里有趣的一点是,第 2 步到第 5 步全部发生在 GPU 上(第 1 步同时发生在 GPU 和 CPU 上)。因此,术语“渲染”。因为 2 和 3 中的循环只是将“纹理”particle_vel_r 和/或 particle_pos_r“渲染”到“帧缓冲区”particle_vel_wparticle_pos_w 用源纹理完全填充帧缓冲区“屏幕大小的四边形”。

关于c - 通过渲染四边形更新粒子系统的速度和位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40833716/

相关文章:

c - 将一个 C 文件中的全局变量传递给另一个

c - recv() 什么时候返回?

c++ - 写入 OpenGL 模板缓冲区

c++ - 如何在 GLFW 中为游戏引擎创建正确的输入类

opengl - 我怎样才能在OpenGL中进行透视变换?

python - 如何在python中并行for循环?

java - Java Encog 3 中的 OpenCL 后端

c++ - 如何从 mex 函数返回矩阵结构?

tensorflow - 在 tf_agents 中使用 BatchedPyEnvironment

C 复杂的内存泄漏