Three.js 中使用 FBO/RenderTarget 的 GLSL uv 查找和精度

GLSL uv lookup and precision with FBO / RenderTarget in Three.js

我的应用程序使用 Javascript + Three.js / WebGL + GLSL 编码。我有 200 条曲线,每条曲线由 85 个点组成。为了使曲线动画化,我添加了一个新点并删除了最后一个点。 所以我制作了一个 positions 着色器将新位置存储到纹理 (1) 和 lines 着色器将所有曲线的位置写入另一个纹理 (2)。 目标是将纹理用作数组:我知道一行的第一个和最后一个索引,所以我需要将这些索引转换为 uv 坐标。

我使用 FBOHelper 调试 FBO。

1) 此 1D 纹理包含每条曲线的新点(总共 200 个):positionTexture

2) 这些是 200 条曲线及其所有点,一个接一个:linesTexture

黑色部分是这里的BUG。那些纹素不应该是黑色的。 它是如何工作的:着色器在每一帧查找 positionTexture 中每一行的新点并相应地更新 linesTextures,使用 for 循环,如下所示:

#define LINES_COUNT = 200
#define LINE_POINTS = 85 // with 100 it works!!!

// Then in main()

vec2 uv = gl_FragCoord.xy / resolution.xy;

for (float i = 0.0; i < LINES_COUNT; i += 1.0) {
    float startIdx = i * LINE_POINTS; // line start index
    float endIdx = beginIdx + LINE_POINTS - 1.0; // line end index
    vec2 lastCell = getUVfromIndex(endIdx); // last uv coordinate reserved for current line

if (match(lastCell, uv)) {
      pos = texture2D( positionTexture, vec2((i / LINES_COUNT) + minFloat, 0.0)).xyz;
    } else if (index >= startIdx && index < endIdx) {
      pos = texture2D( lineTexture, getNextUV(uv) ).xyz;
    }
  }

这行得通,但是当我有很多行(150+)时它有点错误:可能是精度问题。我不确定我编写的查找纹理的函数是否正确。我写了像 getNextUV(uv) 这样的函数来从下一个索引(转换为 uv 坐标)中获取值并复制到上一个。或 match(xy, uv) 了解当前片段是否是我想要的纹素。

虽然我可以简单地使用经典公式:

index = uv.y * width + uv.x

但它比这更复杂。例如 match():

// Wether a point XY is within a UV coordinate
float size = 132.0; // width and height of texture
float unit = 1.0 / size;
float minFloat = unit / size;

bool match(vec2 point, vec2 uv) {
    vec2 p = point;

    float x = floor(p.x / unit) * unit;
    float y = floor(p.y / unit) * unit;

    return x <= uv.x && x + unit > uv.x && y <= uv.y && y + unit > uv.y;
}

getUVfromIndex():

vec2 getUVfromIndex(float index) {
  float row = floor(index / size); // Example: 83.56 / 10 = 8
  float col = index - (row * size); // Example: 83.56 - (8 * 10) = 3.56
  col = col / size + minFloat; // u = 0.357
  row = row / size + minFloat; // v = 0.81

  return vec2(col, row);
}

有人可以通过从 index 值获取 uv 坐标来解释在纹理中查找值的最有效方法是什么吗?

纹理坐标来自像素的边缘而不是中心,因此计算 UV 坐标的公式需要为

 u = (xPixelCoord + .5) / widthOfTextureInPixels;
 v = (yPixelCoord + .5) / heightOfTextureInPixels;

所以我猜你希望 getUVfromIndex 成为

uniform vec2 sizeOfTexture;   // allow texture to be any size
vec2 getUVfromIndex(float index) {
  float widthOfTexture = sizeOfTexture.x;
  float col = mod(index, widthOfTexture);
  float row = floor(index / widthOfTexture);

  return (vec2(col, row) + .5) / sizeOfTexture;
}

或者,based on some other experience with math issues in shaders您可能需要伪造索引

uniform vec2 sizeOfTexture;   // allow texture to be any size
vec2 getUVfromIndex(float index) {
  float fudgedIndex = index + 0.1;
  float widthOfTexture = sizeOfTexture.x;
  float col = mod(fudgedIndex, widthOfTexture);
  float row = floor(fudgedIndex / widthOfTexture);

  return (vec2(col, row) + .5) / sizeOfTexture;
}

如果您使用的是 WebGL2,则可以使用 texelFetch,它采用整数像素坐标从纹理中获取值