Несмотря на передачу равных (точно равных) координат для «смежных» краев, я получаю странные линии между соседними элементами при масштабировании сетки визуализированных плиток.
Мой алгоритм рендеринга сетки плиток принимает масштабированные плитки, поэтому я могу настроить визуальный размер сетки, чтобы он соответствовал выбранному размеру окна с тем же соотношением сторон, среди прочего. Кажется, он работает правильно при масштабировании до точных целых чисел и нескольких нецелочисленных значений, но я получаю некоторые противоречивые результаты для других.
Некоторые скриншоты:
Синие линии - это чистый цвет, просвечивающий сквозь них. Выбранная текстура не имеет прозрачных промежутков на листе тайлов, так как неиспользуемые тайлы окрашены в пурпурный цвет, а фактическая прозрачность обрабатывается альфа-слоем. Соседние плитки на листе имеют полную непрозрачность. Масштабирование достигается путем установки масштаба на нормализованное значение, полученное с помощью триггера геймпада между 1f и 2f, поэтому я не знаю, какой фактический масштаб был применен, когда был сделан снимок, за исключением макс/мин.
Обновления атрибутов и отрисовка объекта синхронизируются между потоками, поэтому ни одно из значений не может быть применено в середине отрисовки. Скриншоты плохо передают это, но линии не мерцают, когда масштаб поддерживается в этой точке, поэтому логически не должно быть проблем с рисованием между присвоением масштаба (и блокировки потоков предотвращают это).
Масштабируется до 1x:
Масштабируется до A, 1x ‹ Ax ‹ Bx :
Масштабируется до B, Ax ‹ Bx ‹ Cx:
Масштабируется до C, Bx ‹ Cx ‹ 2x:
Масштабируется в 2 раза:
Функция настройки проекции
Для настройки ортогональной проекции (изменения только при изменении размера экрана):
.......
float nw, nh;
nh = Display.getHeight();
nw = Display.getWidth();
GL11.glOrtho(0, nw, nh, 0, 1, -1);
orthocenter.setX(nw/2); //this is a Vector2, floats for X and Y, direct assignment.
orthocenter.setY(nh/2);
.......
Для целей снимка экрана nw равно 512, nh равно 384 (неявно преобразовано из int). Они никогда не меняются в приведенном выше примере.
Общий код чертежа GL
После вырезания ненужных атрибутов, которые не устранили проблему при вырезании:
@Override
public void draw(float xOffset, float yOffset, float width, float height,
int glTex, float texX, float texY, float texWidth, float texHeight) {
GL11.glLoadIdentity();
GL11.glTranslatef(0.375f, 0.375f, 0f); //This is supposed to fix subpixel issues, but makes no difference here
GL11.glTranslatef(xOffset, yOffset, 0f);
if(glTex != lastTexture){
GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTex);
lastTexture = glTex;
}
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(texX,texY + texHeight);
GL11.glVertex2f(-height/2, -width/2);
GL11.glTexCoord2f(texX + texWidth,texY + texHeight);
GL11.glVertex2f(-height/2, width/2);
GL11.glTexCoord2f(texX + texWidth,texY);
GL11.glVertex2f(height/2, width/2);
GL11.glTexCoord2f(texX,texY);
GL11.glVertex2f(height/2, -width/2);
GL11.glEnd();
}
Код рисования сетки (отбрасывая те же параметры, что и «draw»):
//Externally there is tilesize, which contains tile pixel size, in this case 32x32
public void draw(Engine engine, Vector2 offset, Vector2 scale){
int xp, yp; //x and y position of individual tiles
for(int c = 0; c<width; c++){ //c as in column
xp = (int) (c*tilesize.a*scale.getX()); //set distance from chunk x to column x
for(int r = 0; r<height; r++){ //r as in row
if(tiles[r*width+c] <0) continue; //skip empty tiles ('air')
yp = (int) (r*tilesize.b*scale.getY()); //set distance from chunk y to column y
tileset.getFrame(tiles[r*width+c]).draw( //pull 'tile' frame from set, render.
engine, //drawing context
new Vector2(offset.getX() + xp, offset.getY() + yp), //location of tile
scale //scale of tiles
);
}
}
}
Между плитками и специфичным для платформы кодом компоненты векторов извлекаются и передаются в общий код рисования, как вставлено ранее.
Мой анализ
Математически каждая позиция является точным кратным масштабу*размеру плитки либо в направлении x, либо в направлении y, либо в обоих направлениях, которые затем добавляются к смещению положения сетки. Затем он передается как смещение в код рисования, который преобразует это смещение с помощью glTranslatef, затем рисует плитку с центром в этом месте, уменьшая вдвое размеры, а затем рисуя каждую пару плюс-минус.
Это должно означать, что когда плитка 1 рисуется, скажем, в начале координат, она имеет смещение, равное 0. Затем Opengl получает указание отрисовать четырехугольник с левым краем на -полуширине, правым краем на +половине, верхним краем на -половине. , а нижний край на +полувысоте. Затем ему предлагается нарисовать соседа, тайл 2, со смещением в одну ширину, поэтому он переводит от 0 до этой ширины, затем рисует левый край на -halfwidth, который по координатам должен быть точно таким же, как правый край тайла 1. Само по себе это должно работать, и оно работает. При рассмотрении постоянной шкалы она как-то ломается.
Когда масштаб применяется, он является постоянным кратным для всех значений ширины/высоты и математически не должен ничего менять. Тем не менее, это имеет значение, и я думаю, что это может быть одна из двух причин:
- В OpenGL возникают проблемы с заполнением подпикселей, т.е. заполнение слева от вершины не заполняет пространство пикселей, содержащее вершину, и заполнение справа от той же самой вершины также не заполняет пространство пикселей, содержащее вершину.
- Я сталкиваюсь с проблемами точности с плавающей запятой, где каким-то образом
X+width/2
не равноX+width - width/2
, гдеwidth = tilewidth*scale
, tilewidth — целое число, а X — число с плавающей запятой.
Я не совсем уверен в том, как определить, в чем проблема, или как ее исправить, кроме как просто избегать нецелочисленных значений шкалы, которые я хотел бы поддерживать. Единственная подсказка, которая, как мне кажется, может быть применима к поиску решения, заключается в том, что шаблон разрывов строк на самом деле не постоянен (посмотрите, как в некоторых случаях он пропускает плитки, имеет только вертикальные или горизонтальные, но не оба и т. д.). Однако я не знаю, что это означает.