WebGL / Three.js - 移动相机时由纹理着色的粒子不规则闪烁

IT技术 javascript three.js webgl
2021-03-07 10:01:47

这是我放在一起jsfiddle,显示了在使用纹理着色和相机移动时粒子“闪烁”的问题。

更新:粒子上不应该有动画或运动发生。如果当您在视口上单击并拖动时,粒子完全闪烁或改变颜色,那么您就会看到问题所在。这是我在 mac OS 10.9 和 windows 7 上在 firefox 和 chrome 上测试并看到的一个问题。

粒子不会以任何方式重叠或剪裁。如果使用常规着色器对粒子进行动画处理,则不会发生闪烁。只有当粒子使用纹理着色(在本例中使用 THREE.WebGLRenderTarget)时,闪烁才会明显。这样做是为了捕获先前的帧并将它们存储在缓冲区中(然后可以以更高级的方式使用,未在 jsfiddle 示例中显示)。

实际上看起来片段着色器可能会错误地抓取相邻像素,而不是它的目标,但我不确定 - 这没有多大意义,因为目标坐标仅在 init() 上生成,并且它们之后不要改变。

每个粒子的目标像素坐标作为顶点属性传递给片段着色器 1-1 不变(作为变化但没有变化的值)。

uniform sampler2D colorMap; // The texture being used to color each particle
varying float v_geoX; // x,y coordinates passed as varyings
varying float v_geoY;

void main() {
    // Normally pulling the correct color, but this seems to get confused during camera movement.
    gl_FragColor = texture2D(colorMap, vec2(v_geoX, v_geoY));
}

任何人都知道如何在不闪烁的情况下做到这一点?当我将此技术应用于更大更快的动画时,问题似乎只会变得更糟。到目前为止,它发生在我检查过的所有浏览器上。

1个回答

问题是您指向纹素之间的边缘。

这些线路

   geoX.push(tx / imageSize);
   geoY.push(1.0 - ty / imageSize); // flip y

计算纹素之间的确切边缘。我的意思是可以说imageSize是4个纹元,并让说,txty从去03

你的纹理坐标是这样的

   0    0.25  0.5   0.75       <- texture coords you're computing
   |     |     |     |     
   V     V     V     V     
   +-----+-----+-----+-----+
   |     |     |     |     |
   |     |     |     |     |   <- texels
   +-----+-----+-----+-----+

但是你想要的纹理坐标是这样的

     0.125 0.375 0.625 0.875   
      |     |     |     |   
      V     V     V     V  
   +-----+-----+-----+-----+
   |     |     |     |     |
   |     |     |     |     |   <- texels
   +-----+-----+-----+-----+

最简单的解决方法是添加 1/2 纹素

   geoX.push(tx / imageSize       + 0.5 / imageSize);
   geoY.push(1.0 - ty / imageSize + 0.5 / imageSize); // flip y

问题在于您的数学直接指向纹素舍入误差之间,使其选择一个纹素或另一个。选择纹素的中心将解决这个问题。如果图像在相邻的纹素之间没有太大的对比度,您可能没有注意到。

这样就解决了!谢谢@gman!我怀疑这与抓取错误的相邻像素(纹素)有关!我正在研究一个蛮力解决方案,通过将像素加倍以提供填充空间和出错空间,但是您所拥有的要好得多,可以直接解决问题。顺便说一句,这是一个更新的小提琴,适用于任何看到闪烁并想要测试此解决方案jsfiddle.net/vko8hzzs/5 的人此外,我很确定闪烁只发生在配备 NVIDIA GeForce 显卡的机器上(与哪个浏览器无关)。
2021-04-25 10:01:47