Я реализую 2-мерную параллаксную прокрутку в 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: Я заметил, что на этом скриншоте отсутствует одна вертикальная черная линия на «кирпичной кладке», поэтому я думаю, что здесь происходит то, что выборка / интерполяция иногда дает сбой на уровне идеального пикселя, что имеет смысл , Есть ли способ обойти это?