我正在尝试渲染粒子模拟。我希望每个粒子都能留下痕迹。为此,我使用了一个帧缓冲区和两个纹理。在每个帧上,算法如下:


使用opacity * 0.9渲染到先前渲染的屏幕缓冲区的帧缓冲区;
在更新的位置渲染当前粒子;
切换到主显示和渲染帧缓冲区的当前内容;

这很好用,而且我看到的粒子轨迹很好看:



问题

我想允许用户可以使用这些粒子平移和缩放画布,类似于地图界面。这是通过将变换矩阵应用于每个粒子来实现的。

我想保持粒子的状态,但是我不希望平移/缩放留下粒子轨迹:



在这里,我用鼠标光标上下拖动画布。



很好

这里我也在上下拖动画布,但是颗粒仍然留在它们的上面轨迹,而不会留下平移轨迹:



我想知道实现此目标的正确方法是什么?我得到了一个基本解决方案,方法是对前一帧的纹理进行转换,以使其大致匹配当前帧中要渲染的纹理,但是我仍然有一些瑕疵:


放大时,更新的纹理“爆炸”:



我认为这是因为我对粒子使用定点大小(与缩放级别无关) ,但是当我们缩放纹理时,我们也会缩放光栅化的图像,因此渲染在其上的所有粒子也会变大。


当粒子进入场景(越过可见区域的边界)时,也很可能留下痕迹:



我不确定为什么会发生这种情况。我的屏幕/背面纹理大小相同,我只是在翻译背面纹理。我以为它可能与clip_to_edge设置有关,但是我不知道如何证明或验证这一点。我有一种解决方法,将画布扩展到可见区域之外(例如增加2%),然后在片段着色器中检查是否粒子距离边界的距离小于2%-我给它提供透明的颜色。这种“解决”了问题,但是我不知道为什么会发生这种情况。


最后,如果变换非常小(考虑动力学运动),我还会观察到伪像:



如果您能解释其中一些工件,提出可能的解决方案或用于解决此问题的一般做法,我将不胜感激。 />

评论

您期望第一名发生什么?您的假设是正确的。如果要缩放累积纹理而不是粒子,则它将变得比粒子更大(或更小)。 #3有什么问题?我看不出有什么奇怪的。

在#3中,原始粒子没有移动。因此,当应用平移转换时,我希望两个纹理都具有同步转换,并且不应出现任何衰落痕迹。但是,您可以看到末端的平移非常小-粒子周围有一条逐渐消失的痕迹,好像变换稍微未对齐

#1 楼

我几年前就做了一个可视化的可视化,并使用了类似的技术,因为这是解决问题的自然方法。在花了几年时间为现代GPU编写驱动程序之后,我将不再使用这种方法,因为它会创建从一帧到下一帧的依赖关系,从而导致流水线停顿和/或错误共享。

今天,我将粒子的路径保留为其状态的一部分,并在每一帧渲染整个轨迹,而不是重用旧的FBO。从概念上讲,这有点困难,并且您必须注意绘制过去路径的方式在每帧中都是完全相同的。例如,如果用某种三次样条曲线表示路径,则不能仅将最终控制点向前移动,因为它将稍微改变整个曲线。 (x,y)个位置的循环缓冲区将更容易正确设置。

以这种额外的复杂性得到的交换是(a)减少了帧时间,内存使用和功耗(如果您使用的是移动设备,这可能很重要),并且(b)根本不会遇到当前的问题。也许帧时间优势在WebGL中不会那么大,因为最终您将不得不在Javascript中做额外的工作,这很慢,但是在大多数系统上,内存带宽更是瓶颈。只需确保存储的坐标位于模拟空间中,然后在绘制它们时将它们转换为屏幕坐标,就像使用3D场景一样。

评论


$ \ begingroup $
谢谢!我实际上是在GPU上计算粒子的位置,并将其编码为纹理。查找下一个粒子位置的公式非常复杂,我想支持100k-1,000k粒子。如果必须重新计算最后N个步骤的位置,我担心这会对帧率产生很大影响。
$ \ endgroup $
–安瓦卡
17-10-20在16:42

$ \ begingroup $
我假设您将保存每一帧的位置,而不是重新计算。与帧缓冲区相比,粒子位置数组的传递状态要少得多。
$ \ endgroup $
–丹·赫尔姆
17-10-24在8:18

$ \ begingroup $
Dan,但我认为我没有传递帧缓冲区。我只使用两个纹理并不断交换它们。这是否意味着我正在传递帧缓冲区?我也尝试重新渲染最后的10 -15个步骤,并考虑到了转换-性能不可接受。也许我做错了
$ \ endgroup $
–安瓦卡
17-10-26在21:44



$ \ begingroup $
Dan,这是我正在从事的项目:anvaka.github.io/fieldplay我终于可以分享了。源代码在github.com/anvaka/fieldplay上-这是矢量场浏览器/模拟。也许玩它会让您对我正在尝试做的事情有更好的了解?如果您没有时间这样做-不用担心!
$ \ endgroup $
–安瓦卡
17年11月2日,下午5:50