有效地制作没有纹理的粒子系统 [英] Efficiently making a particle system without textures

查看:113
本文介绍了有效地制作没有纹理的粒子系统的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个粒子系统,其中的四边形由片段着色器(而不是纹理)渲染,如下所示.

I am trying to make a particle system where instead of a texture, a quad is rendered by a fragment shader such as below.

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调用是隐藏的.

Each particle is an object of a c++ class that wraps this shader and all the variables together and draws it. I use openFrameworks so the exact openGL calls are hidden from me.

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

I've read that usually particle systems are done with textures, however, I prefer to do it like this because this way I can add more functionality to the particles. The problem is that after only 30 particles, the framerate drops dramatically. Is there a more efficient way of doing this? I was thinking of maybe putting the variables for each particle into an array and sending these arrays into one fragment shader that then renders all particles in one go. But this would mean that the amount of particles would be fixed because the uniform arrays in the shader would have to be declared beforehand.

基于非纹理的粒子系统效率太低而无法实现现实,还是有一种设计方法让我忽略了?

Are non-texture-based particle systems simply too inefficient to be realistic, or is there a way of designing this that I'm overlooking?

推荐答案

使用纹理的原因是因为您可以使用GPU来移动粒子,这非常快.您将对一个纹理进行双缓冲,该纹理用于存储每个纹理像素的粒子属性(例如位置),并在它们之间进行乒乓数据处理,使用帧缓冲区对象绘制它们,然后使用片段着色器进行计算,从而呈现全屏多边形.然后,您将绘制一个四边形数组并读取纹理以获取位置.

The reason textures are used is because you can move the particles using the GPU, which is very fast. You'd double buffer a texture which stores particle attributes (like position) per texel, and ping-pong data between them, using a framebuffer object to draw to them and the fragment shader to do the computation, rendering a full screen polygon. Then you'd draw an array of quads and read the texture to get the positions.

您可以将其直接打包到VBO数据中,而不是使用纹理存储属性.这变得很复杂,因为每个粒子有多个顶点,但是仍然可以通过多种方法来完成. glVertexBindingDivisor(需要实例化),绘制

Instead of a texture storing attributes you could pack them directly into your VBO data. This gets complicated because you have multiple vertices per particle, but can still be done a number of ways. glVertexBindingDivisor (requires instancing), drawing points, or using the geometry shader come to mind. Transform feedback or image_load_store could be used to update VBOs with the GPU instead of textures.

如果使用CPU移动粒子,则还需要每帧将数据复制到GPU.这是很慢的,但是没有什么比30个粒子慢的问题了.这可能与抽奖次数有关.每次绘制内容时,GL都会进行大量操作来设置操作.出于相同的原因,(几乎)为每个基元设置统一值非常昂贵.如果您具有由经理一次处理的所有数据数组,则粒子效果很好.在这种情况下,它们可以很好地并行化.通常,它们的计算成本很低,而所有这些都归结为最小化内存并保持良好的局部性.

If you move particles with the CPU, you also need to copy the data to the GPU every frame. This is quite slow, but nothing like 30 particles being a problem slow. This is probably to do with the number of draw calls. Each time you draw something there's a tonne of stuff GL does to set up the operation. Setting uniform values per-primitive (nearly) is very expensive for the same reason. Particles work well if you have arrays of data that gets processed by a manager all at once. They parallelize very well in such cases. Generally their computation is cheap and it all comes down to minimizing memory and keeping good locality.

如果要保持粒子更新CPU端,我可以这样做:

If you want to keep particle updating CPU side, I'd go with this:

  1. 创建一个完整的-1至1个四边形(两个三角形,六个顶点)的VBO,并绘制元素数组缓冲区.该缓冲区将在GPU内存中保持静态,是您通过一次绘制调用一次绘制所有粒子的方法.
  2. 创建一个纹理(可以是1D)或VBO(如果您选择上述方法之一),其中包含位置和粒子属性,这些属性和帧属性几乎每帧都会更新一次(使用glTexImage1D/glBufferData/glMapBuffer).
  3. 使用很少更新的粒子属性创建另一个纹理(例如,仅在生成它们时).您可以使用glTexSubImage1D/glBufferSubData/glMapBufferRange发送更新.
  1. Create a VBO full of -1 to 1 quads (two triangles, 6 verts) and element array buffer to draw them. This buffer will remain static in GPU memory and is what you use to draw the particles all at once with a single draw call.
  2. Create a texture (could be 1D) or VBO (if you choose one of the above methods) that contains positions and particle attributes that update pretty much every frame (using glTexImage1D/glBufferData/glMapBuffer).
  3. Create another texture with particle attributes that rarely update (e.g. only when you spawn them). You can send updates with glTexSubImage1D/glBufferSubData/glMapBufferRange.

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

When you draw the particles, read position and other attributes from the texture (or attributes if you used VBOs) and use the -1 to 1 quads in the main geometry VBO as offsets to your position.

这篇关于有效地制作没有纹理的粒子系统的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆