openGL 2D Parallax Scrolling Texture Слезы / Швы

Я реализую двухмерную параллаксную прокрутку в openGL, используя один квад и текстурный атлас.

Я использую следующее (смещение составляет 1,0 / количество слоев, поэтому 0,2):

атлас

Работает практически идеально, но при определенных смещениях швы в точках петли видны. (См. Слезу в средней стене, которая показывает замок и океан позади него.)

шов

Мой алгоритм таков: для каждого фрагмента рассчитайте позицию в текстуре со смещением * i; Если цвет прозрачный, перейдите к смещению * (i + 1) и т. Д. Остановитесь, когда будет нанесен сплошной цвет. Опять же, смещение составляет 1,0 / (количество слоев) или 0,2 здесь.

На изображении выше, похоже, что мой алгоритм выбирает пространство слева или справа от правильной позиции текстуры, поэтому он получает прозрачный цвет, что неправильно.

Я пробовал много вещей для исправления ошибок, включая умножение на 1000.0 перед выполнением модов, а затем деление, привязку к определенным диапазонам и т. Д., Но я не нашел последовательного решения. Кроме того, проблема не обязательно может быть результатом одной только погрешности с плавающей запятой.

Есть ли способ выполнить расчеты, чтобы гарантировать, что секции текстур идеально зациклены? - Может быть, использовать фрактал и пол каким-то необычным образом? (Я нашел это обсуждение, но не смог понять его полностью, потому что ни ОП, ни принятый ответ не объяснили значение многих переменных: смещения параллакса с текстурным атласом? Он использует фрактал и пол, но этот метод может быть несовместим. Сложно сказать.)

В случае, если ниже приведены соответствующие выдержки из моего кода вершинного и фрагментного шейдера (с удалением ненужных частей - используя 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);
}

** Я добавлю, что я мог бы легко использовать GL_REPEAT на 5 отдельных текстурах, сопоставленных с 5 квадратами (или перевязать униформу и заново визуализировать один и тот же квад), но я хочу, чтобы шейдер работал с одной текстурой и одним квадом для создания шейдерных эффектов и текстурных переходов, наиболее легко достижимых при рендеринге в один квад, Кроме того, сокращение количества комплектов униформы - это хорошо. Кроме того, я заинтересован в разработке этой проблемы и использую этот метод для решения этой задачи. Я чувствую, что я близко, но я не могу найти документацию / решения проблемы сшивания / смещения. Отключение GL_REPEAT и использование GL_CLAMP_TO_EDGE, похоже, не работает, но, по общему признанию, GL_REPEAT не требуется, если я использую такой атлас.

Спасибо за вашу помощь заранее.

РЕДАКТИРОВАТЬ: Если подход с одной текстурой не работает, было бы ужасно связать 5-10 повторяющихся текстур сразу и использовать аналогичный алгоритм, но полагаться на GL_REPEAT вместо мода? Я думаю, что это, вероятно, приведет к худшей производительности, но я могу ошибаться. В будущем я хотел бы перейти между двумя многослойными фонами, используя плавный шаг. Вот почему я предпочитаю подход с одним квадром. Кроме того, если я получу эту работу, то я могу иметь больше слоев.

РЕДАКТИРОВАТЬ 2: Я заметил, что одна вертикальная черная линия на "кирпичной кладке" отсутствует на этом скриншоте, поэтому я думаю, что здесь происходит то, что выборка / интерполяция иногда дает сбой на уровне идеального пикселя, что имеет смысл. Есть ли способ обойти это?

1 ответ

Решение

Я бы сказал, что вы слишком умны, пытаясь сделать это в фрагментном шейдере. Было бы намного проще просто вычислить правильные положения для каждого слоя параллакса на основе положения "камеры". Если вам нужно обернуться вокруг, вы также визуализируете второй квад для этого слоя. Таким образом, вы можете предотвратить взлом, гарантируя, что позиции вершин для общих вершин идентичны.

Поскольку вы используете текстурный атлас, вам даже не придется менять текстуры между различными слоями.

Другие вопросы по тегам