有效地制作没有纹理的粒子系统

时间:2014-09-15 19:24:19

标签: opengl glsl openframeworks particle-system

我正在尝试制作一个粒子系统而不是纹理,四边形由片段着色器(如下图)渲染。

uniform vec3 color;
uniform float radius;
uniform float edge;
uniform vec2 position;
uniform float alpha;

void main()
{
    float dist = distance(gl_FragCoord.xy, position);
    float intensity = smoothstep(dist-edge, dist+edge, radius);

    gl_FragColor = vec4(color, intensity*alpha);
}

每个粒子都是c ++类的一个对象,它将这个着色器和所有变量包装在一起并绘制它。我使用openFrameworks,因此对我来说隐藏了确切的openGL调用。

我已经读过,通常粒子系统是用纹理完成的,但是,我喜欢这样做,因为这样我可以为粒子添加更多功能。问题是只有30个粒子后,帧率急剧下降。有更有效的方法吗?我想的可能是将每个粒子的变量放入一个数组中,并将这些数组发送到一个片段着色器中,然后将所有粒子一次性渲染。但这意味着粒子的数量会被修复,因为着色器中的统一数组必须事先声明。

基于非纹理的粒子系统是否过于低效而不实际,或者是否有一种设计方法可以让我忽略?

1 个答案:

答案 0 :(得分:2)

使用纹理的原因是因为您可以使用GPU移动粒子,这非常快。你可以对一个纹理进行双重缓冲,该纹理存储每个纹素的粒子属性(如位置),以及它们之间的乒乓数据,使用帧缓冲对象绘制它们,片段着色器进行计算,渲染全屏多边形。然后你绘制一个四边形阵列并读取纹理以获得位置。

您可以将它们直接打包到VBO数据中,而不是纹理存储属性。这变得复杂,因为每个粒子有多个顶点,但仍可以通过多种方式完成。我会想到glVertexBindingDivisorrequires instancing),绘制points或使用几何着色器。转换反馈或image_load_store可用于使用GPU而不是纹理更新VBO。

如果使用CPU移动粒子,则还需要每帧将数据复制到GPU。这很慢,但没有像30粒子这样的问题变慢。这可能与绘制调用次数有关。每次你画一些东西时,GL都会做一些东西来设置操作。出于同样的原因,设置每个基元的均匀值(几乎)是非常昂贵的。如果您拥有一个由管理器同时处理的数据数组,则粒子可以正常工作。在这种情况下,它们非常平行。一般来说,他们的计算很便宜,而且都归结为最小化记忆和保持良好的地方性。

如果你想保持粒子更新CPU方面,我应该这样做:

  1. 创建一个充满-1到1个四边形(两个三角形,6个顶点)和元素数组缓冲区的VBO来绘制它们。此缓冲区将在GPU内存中保持静态,并且您可以使用一次绘制调用一次性绘制粒子。
  2. 创建一个纹理(可以是1D)或VBO(如果您选择上述方法之一),其中包含几乎每帧都更新的位置和粒子属性(使用glTexImage1D / glBufferData / { {1}})。
  3. 使用很少更新的粒子属性创建另一个纹理(例如,仅在您生成它们时)。您可以使用glMapBuffer / glTexSubImage1D / glBufferSubData
  4. 发送更新

    绘制粒子时,从纹理中读取位置和其他属性(如果使用VBO,则读取属性),并使用主几何体VBO中的-1到1个四边形作为偏移位置。