从着色器读取数据

时间:2014-04-08 14:53:23

标签: javascript canvas three.js shader

我正在尝试学习如何利用三个和webgl的gpu可能性,所以我只是分析代码来获得一些模式,方法是如何完成的,我需要一些代码解释。

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

从我的想法来看:   - 粒子的速度和位置数据保存在传递给着色器的纹理中以在那里执行计算,并将它们返回以进行更新

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

      

    for(var i = 0; i< 1000000; i ++){

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

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

  • 我没有看到任何粒子位置更新?如何从着色器检索数据并更新每个粒子?

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

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

  • 着色器如何更新纹理?我只是看到从中读取而不是写信给它?

提前感谢任何解释!

1 个答案:

答案 0 :(得分:19)

他们如何做到这一点:

在这篇文章中,我将解释如何通过WebGL / Three.js几乎单独在gpu上计算这些结果 - 由于我使用的是Intel i7 4770k的集成显卡,它可能看起来有点草率:

Particle system computed on the graphics card in browser via WebGL/Three.js


简介:

保持intra-gpu内所有内容的简单想法:每个粒子的状态将由一个纹理像素颜色值表示。一百万个粒子将产生1024x1024像素纹理,一个用于保持当前位置,另一个用于保持这些粒子的速度。

没有人禁止滥用纹理的RGB颜色值来完全不同的0 ... 255宇宙数据。对于你想要保存在GPU内存中的任何内容,你基本上每个纹理像素都有32位(R + G + B + alpha)。 (如果他需要为每个粒子/对象存储更多数据,甚至可以使用多个纹理像素。)

他们基本上按顺序使用多个着色器。从源代码中,可以识别其处理管道的这些步骤:

  1. 随机化粒子(在此答案中忽略)('randShader')
  2. 根据鼠标位置的距离确定每个粒子的速度('velShader')
  3. 根据速度,相应地移动每个粒子('posShader')
  4. 显示屏幕('dispShader')**

  5. 步骤2:确定每个粒子的速度:

    他们在1百万点上调用绘制过程,其输出将保存为纹理。在顶点着色器中,每个片段都会获得另外两个名为“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);
    


    我希望这清楚地说明了这个小小的演示是如何工作的:-)我不是那个演示的作者。刚刚注意到这个答案实际上变成了一个超级详细的答案 - 通过厚厚的关键字来获得短版本。