他们到底是怎么做到的:
在这篇文章中,我将解释如何通过 WebGL/Three.js 几乎仅在 gpu 上计算这些结果 - 因为我使用的是 Intel i7 4770k 的集成显卡,所以它可能看起来有点草率:
简介:
将所有内容保留在 GPU 内的简单想法:每个粒子的状态将由一个纹理像素颜色值表示。一百万个粒子将产生 1024x1024 像素纹理,一个用于保存当前位置,另一个用于保存这些粒子的速度。
没有人禁止滥用纹理的 RGB 颜色值来获取 0...255 宇宙的完全不同的数据。您基本上每个纹理像素都有 32 位(R + G + B + alpha),用于您想要保存在 GPU 内存中的任何内容。 (如果需要为每个粒子/对象存储更多数据,他甚至可以使用多个纹理像素)。
他们基本上按顺序使用了多个着色器。从源代码中,可以确定其处理管道的这些步骤:
-
随机化粒子(在此答案中忽略) ('randShader')
-
通过到鼠标位置的距离确定每个粒子的速度('velShader')
-
根据速度,相应地移动每个粒子('posShader')
-
显示屏幕 ('dispShader')**
.
第 2 步:确定每个粒子的速度:
他们在 100 万个点上调用绘制过程,其输出将保存为纹理。在顶点着色器中,每个片段都有 2 个额外的名为“vUv”的变量,它们基本上确定了处理中使用的纹理内的 x 和 y 像素位置。
下一步是它的片段着色器,因为只有这个着色器可以输出(作为 RGB 值到帧缓冲区中,然后转换为纹理缓冲区 - 所有这些都只发生在 gpu 内存中)。您可以在id="velFrag" 片段着色器中看到,它获得了一个名为uniform vec3 targetPos; 的输入变量。这些制服在 CPU 的每一帧中设置得很便宜,因为它们在所有实例之间共享并且不涉及大量内存传输。 (包含鼠标坐标,可能在 -1.00f 到 +1.00f 宇宙中 - 他们可能还会每 FEW 帧更新一次鼠标坐标,以降低 CPU 使用率)。
这是怎么回事?好吧,该着色器计算该粒子到鼠标坐标的距离,并根据它改变粒子速度 - 速度还包含有关粒子飞行方向的信息。 注意:这个速度步骤也会使粒子获得动量并保持飞行/超过鼠标位置,具体取决于灰度值。
.
第 3 步:更新每个粒子的位置:
到目前为止,每个粒子都有一个速度和一个先前的位置。这两个值将被处理到一个新位置,再次作为纹理输出 - 这次是到 positionTexture。直到整个帧被渲染(进入默认帧缓冲区)然后标记为新纹理,旧的 positionTexture 保持不变并且可以轻松读取:
在id="posFrag" 片段着色器中,它们从两个纹理(posTexture 和 velTexture)中读取数据并将这些数据处理到新位置。它们将 x 和 y 位置坐标输出到该纹理的颜色中(作为红色和绿色值)。
.
第 4 步:黄金时段(=输出)
为了输出结果,他们可能再次获取一百万个点/顶点并将 positionTexture 作为输入。然后 顶点着色器通过读取位置 x,y 处的纹理 RGB 值(作为顶点属性传递)来设置每个点的位置。
// From <script type="x-shader/x-vertex" id="dispVert">
vec3 mvPosition = texture2D(posTex, vec2(x, y)).rgb;
gl_PointSize = 1.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(mvPosition,1.0);
在显示片段着色器中,他们只需要设置一种颜色(注意低 alpha,使其允许 20 个粒子堆叠起来以完全点亮一个像素)。
// From <script type="x-shader/x-fragment" id="dispFrag">
gl_FragColor = vec4(vec3(0.5, 1.0, 0.1), 0.05);
.
我希望这能说明这个小演示是如何工作的 :-) 不过,我不是那个演示的作者。刚刚注意到这个答案实际上变成了一个超级详细的答案 - 浏览粗关键词以获得简短版本。