openGL 2D视差滚动纹理眼泪/接缝

时间:2018-05-08 01:23:15

标签: opengl parallax 2d-games opengl-3 uv-mapping

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

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

atlas

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

seam

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

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

我已经尝试过许多方法来进行错误更正,包括在执行mod之前再乘以1000.0然后除以,钳位到特定范围等等,但我还没有找到一致的解决方案。此外,问题可能不一定是浮点不准确的结果。

有没有办法执行计算以保证纹理部分完美循环? - 可能以某种不寻常的方式使用碎裂和地板? (我发现这个讨论但是无法完全理解它,因为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个单独的纹理上使用GL_REPEAT映射到5个四边形(或重新绑定制服并重新渲染相同的四边形),但我希望着色器可以在一个纹理和一个四边形上工作以创建着色器渲染到单个四边形时,效果和纹理转换最容易实现。此外,减少制服的数量是好的。另外,我对这个问题背后的工程感兴趣,并且正在使用这种方法来应对挑战。我觉得我很接近,但我找不到关于接缝/偏移问题的文档/解决方案。 禁用GL_REPEAT并使用GL_CLAMP_TO_EDGE似乎不起作用,但不可否认,如果我使用这样的地图集,则不需要GL_REPEAT。

提前感谢您的帮助。

编辑:如果单纹理方法不起作用,一次绑定5-10个重复纹理并使用类似的算法会很糟糕,但是依赖GL_REPEAT而不是mod吗?我认为这可能会产生较差的表现,但我可能是错的。在未来,我想使用平滑步骤在两个多层背景之间转换。这就是为什么我更喜欢单四极杆方法。此外,如果我使这个工作,那么我可以有更多的层。

编辑2:我注意到这个截图中缺少“砖砌”上的一条垂直黑线,所以我认为这里发生的事情是采样/插值有时会在像素完美的水平上失败,这是有道理的。有没有办法规避这个?

1 个答案:

答案 0 :(得分:2)

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

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