openGL 2D 视差滚动纹理撕裂/接缝

openGL 2D Parallax Scrolling Texture Tears / Seams

我正在使用单个四边形和纹理图集在 openGL 中实现 2d 视差滚动。

我使用以下(偏移量为 1.0 / 层数,因此为 0.2):

它几乎完美地工作,但在某些偏移处,循环点处的接缝是可见的。 (看到中间墙上的裂缝,露出了城堡和后面的海洋。)

我的算法是: 在每个片段处,计算纹理中偏移量 * i 处的位置;如果颜色是透明的,移动到 offset * (i + 1) 等。当遇到纯色时停止。同样,偏移量为 1.0 /(层数),或此处为 0.2。

在上图中,看起来我的算法在正确纹理位置的左侧或右侧对 space 进行采样,因此它会碰到透明颜色,这是错误的。

我尝试了很多纠错方法,包括在执行mods之前乘以1000.0然后除法,钳位到特定范围等等,但我还没有找到一致的解决方案。此外,该问题不一定是浮点数不准确单独导致的。

有没有一种方法可以执行计算以保证纹理部分完美循环? --也许以某种不寻常的方式使用 fract 和 floor? (我找到了这个讨论但无法完全理解它,因为 OP 和接受的答案都没有解释许多变量的含义:parallax offsets with texture atlas? 它使用了 fract 和 floor,但这种方法可能不兼容。它是很难说。)

以防万一,以下是我的顶点和片段着色器代码的相关摘录(删除了不相关的部分——使用 precision highp float):

// vertex shader
void main(void) 
{
   gl_Position = u_matrix * vec4(a_position, 1.0);
   v_uv = vec2(a_uv.x * u_offset_layers_x, a_uv.y);
   v_position_cam = u_position_cam; // set the camera position to an out variable
}

// fragment shader
void main(void)
{
   vec4 texel;
   // each successive layer scrolls more slowly
   float rate = 0.65;
   // iterate through each layer and sample until the alpha is not 0
   // since I get tears every so often, I might have rounding errors that end up sampling part of a previous or successive texture by mistake
   for (int i = 0; i < u_count_layers; ++i) {
      // I use multiplications and divisions by 1000.0 in an attempt to reduce the error, but I don't think it works at all

      // the camera position offsets the parallax layer
      float sample_coord = v_uv.x + ((v_position_cam.x * 1000.0 * rate) / 1000.0);

      // the y offset seems fine
      float off_y = (-v_position_cam.y * rate * rate * rate);
      off_y = max(off_y, 0.0);

      // offset by i layers, add that offset to (the sampled coordinate * offset per layer) % offset per layer 
      texel = vec4(texture(tex0, vec2(
         (float(i) * u_offset_layers_x) + mod(sample_coord * 1000.0, u_offset_layers_x * 1000.0) / 1000.0,
         v_uv.y + off_y))
      );

      // if the alpha is not 0, then don't sample the layers behind it (a texel has an alpha of 0.0 or 1.0)
      if (texel.a != 0.0) {
         break;
      }

      // reduce the scroll speed for the next layer
      rate *= rate;
   }

   // output the color
   color = vec4(texel.rgb, texel.a);
}

** 我要补充一点,我可以轻松地在映射到 5 个四边形的 5 个单独纹理上使用 GL_REPEAT(或重新绑定制服并重新渲染相同的四边形),但我希望着色器在一个纹理和一个四边形上工作以创建着色器渲染到单个四边形时最容易实现效果和纹理过渡。此外,减少制服套装的数量也很好。此外,我对这个问题背后的工程很感兴趣,并且正在使用这种方法来应对挑战。我觉得我很接近,但我找不到接缝/偏移问题的文档/解决方案。 禁用 GL_REPEAT 并使用 GL_CLAMP_TO_EDGE 似乎不起作用,但诚然,如果我使用这样的图集,GL_REPEAT 是不必要的。

提前感谢您的帮助。

编辑:如果单纹理方法不起作用,一次绑定 5-10 个重复纹理并使用类似的算法会不会很糟糕,但依赖 GL_REPEAT 而不是 mod?我认为这可能会产生较差的性能,但我可能是错的。将来我想使用 smooth-step 在两个多层背景之间进行过渡。这就是为什么我更喜欢单四边形方法的原因。另外,如果我得到这个工作,那么我可以有更多的层。

编辑 2:我注意到此屏幕截图中缺少 "brickwork" 上的一条垂直黑线,所以我认为这里发生的情况是采样/插值有时会在像素完美级别失败,这是有道理的。有没有办法规避这个?

我会说你在片段着色器中尝试这样做太聪明了。根据 "camera" 的位置计算每个视差层的正确位置会容易得多。如果需要环绕,还可以为该层渲染第二个四边形。这样,您可以通过确保共享顶点的顶点位置相同来防止裂缝。

由于您使用的是纹理图集,因此您甚至不必更改不同层之间的纹理。