从着色器读取数据

Posted

技术标签:

【中文标题】从着色器读取数据【英文标题】:Reading data from shader 【发布时间】:2014-05-21 08:44:26 【问题描述】:

我正在尝试学习如何利用 gpu 的可能性来处理threejs 和 webgl 的东西,所以我只是分析代码以获取一些模式和方法,我需要一些代码解释。

我找到了这个例子:One million particles,这似乎是涉及在着色器中进行计算并吐出的最简单的例子。

所以根据我的发现: - 粒子的速度和位置数据保存在传递给着色器的纹理中以在那里执行计算,然后将它们取回以进行更新

粒子在平面上随机创建不超过纹理大小?

for (var i = 0; i

particles.vertices.push(new THREE.Vector3((i % texSize)/texSize,

Math.floor(i/texSize)/texSize , 0)) ;

我没有看到任何粒子位置更新?着色器中的数据是如何检索和更新每个粒子的?

挑选() 只通过鼠标位置来计算粒子运动的方向?

为什么有 2 个缓冲区?和 8 个(4 对片段和向量)着色器?只计算速度和位置的还不够吗?

着色器如何更新纹理?我只看到读取它而不是写入它?

提前感谢您的任何解释!

【问题讨论】:

您链接到的示例不适用于我。请参阅这个 three.js 示例:threejs.org/examples/webgl_gpgpu_birds.html。谷歌“GPGPU”。谷歌“GPGPU 乒乓球”。理想情况下,数据不会像您假设的那样“用于更新”传递回 CPU——所有计算都在 GPU 上执行。 @WestLangley 如果它没有传回 cpu,那么它会存储在纹理数据中并在下一次迭代中再次读取以进一步更新顶点着色器中的位置? 【参考方案1】:

他们到底是怎么做到的:

在这篇文章中,我将解释如何通过 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);

.


我希望这能说明这个小演示是如何工作的 :-) 不过,我不是那个演示的作者。刚刚注意到这个答案实际上变成了一个超级详细的答案 - 浏览粗关键词以获得简短版本。

【讨论】:

谢谢!这是我能得到的最好的解释! :) 以防它可能对其他人有所帮助,我在这里制作了一个简单的粒子系统,试图最小化地布置机制:bl.ocks.org/duhaime/f7341fcffc0b7bc224fccbd08c5f9629

以上是关于从着色器读取数据的主要内容,如果未能解决你的问题,请参考以下文章

使用结构从 .txt 文件中读取着色器

如何在着色器之后读取像素?

GLSL:无法从 FBO 读取纹理并使用片段着色器渲染到另一个 FBO

顶点纹理获取(在顶点着色器中读取纹理)

什么是依赖纹理读取?

OpenGL、GLSL 片段着色器无法读取 Sampler2D 纹理