Когда он переведен в целое значение (1,2,3 и т. Д.), Между плитками нет черных линий, все выглядит хорошо. Но когда он переводится в неинтегральный (1.1, 1.5, 1.67), между каждой плиткой появляются маленькие черноватые линии (я представляю, что это из-за субпиксельного рендеринга, верно?) … и это не выглядит красиво = Р
И что же мне делать?
Это мой код загрузки изображений, кстати:
bool Image::load_opengl() {
this->id = 0;
glGenTextures(1, &this->id);
this->bind();
// Parameters... TODO: Should we change this?
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, this->size.x, this->size.y,
0, GL_BGRA, GL_UNSIGNED_BYTE, (void*) FreeImage_GetBits(this->data));
this->unbind();
return true;
}
Я также пытался использовать:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
а также:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Вот мой код рисования изображения:
void Image::draw(Pos pos, CROP crop, SCALE scale) {
if (!this->loaded || this->id == 0) {
return;
}
// Start position & size
Pos s_p;
Pos s_s;
// End size
Pos e_s;
if (crop.active) {
s_p = crop.pos / this->size;
s_s = crop.size / this->size;
//debug("%f %f", s_s.x, s_s.y);
s_s = s_s + s_p;
s_s.clamp(1);
//debug("%f %f", s_s.x, s_s.y);
} else {
s_s = 1;
}
if (scale.active) {
e_s = scale.size;
} else if (crop.active) {
e_s = crop.size;
} else {
e_s = this->size;
}
// FIXME: Is this okay?
s_p.y = 1 - s_p.y;
s_s.y = 1 - s_s.y;
// TODO: Make this use VAO/VBO's!!
glPushMatrix();
glTranslate(pos.x, pos.y, 0);
this->bind();
glBegin(GL_QUADS);
glTexCoord2(s_p.x, s_p.y);
glVertex2(0, 0);
glTexCoord2(s_s.x, s_p.y);
glVertex2(e_s.x, 0);
glTexCoord2(s_s.x, s_s.y);
glVertex2(e_s.x, e_s.y);
glTexCoord2(s_p.x, s_s.y);
glVertex2(0, e_s.y);
glEnd();
this->unbind();
glPopMatrix();
}
Код инициализации OpenGL:
void game__gl_init() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, config.window.size.x, config.window.size.y, 0.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
Скриншоты вопроса:
Проблема с использованием текстурных атласов (листов спрайтов) и утечек смежных текселей связана с тем, как работает линейная фильтрация текстур.
Для любой точки в текстуре, которая не дискретизирована точно в центре текселя, линейная выборка будет производить выборку 4 смежных текселей и вычислять значение в указанном вами месте как средневзвешенное (на основе расстояния от точки отбора) среднее значение для всех 4 образцы.
Вот хорошая визуализация проблемы:
Поскольку вы не можете использовать что-то вроде GL_CLAMP_TO_EDGE
в текстурном атласе вам нужно создать текстуру границ по краям каждой текстуры. Эти граничные тексели не позволят соседним образцам с совершенно разными текстурами в атласе изменять изображение с помощью взвешенной интерполяции, описанной выше.
Обратите внимание, что при использовании анизотропной фильтрации может потребоваться увеличить ширину границы. Это связано с тем, что анизотропная фильтрация увеличивает размер окрестности образца под крайними углами.
Чтобы проиллюстрировать, что я имею в виду, используя границы по краям каждой текстуры, рассмотрим различные режимы обтекания, доступные в OpenGL. Обратите особое внимание на CLAMP TO EDGE
,
Несмотря на то, что существует режим под названием «Зажим на границу», это на самом деле не то, что нас интересует. Этот режим позволяет вам определить один цвет для использования в качестве границы вокруг вашей текстуры для любых текстурных координат, которые выходят за пределы нормализованного [0.0 -1.0] диапазон.
То, что мы хотим, это повторить поведение CLAMP_TO_EDGE
где любая текстурная координата вне надлежащего диапазона для (суб) текстуры получает значение последнего центра текселя в направлении, в котором она находилась за пределами. Поскольку у вас есть почти полный контроль над текстурными координатами в системе атласа, Единственный сценарий, в котором (эффективные) координаты текстуры могут относиться к местоположению вне вашей текстуры, — это шаг взвешенной средней фильтрации текстур.
Мы знаем это GL_LINEAR
выберет 4 ближайших соседа, как показано на диаграмме выше, поэтому нам нужна только граница в 1 тексель. Вам может понадобиться более широкая граница текселей, если вы используете анизотропную фильтрацию, потому что это увеличивает размер окрестности выборки при определенных условиях.
Вот пример текстуры, которая более четко иллюстрирует границу, хотя для ваших целей вы можете сделать границу шириной 1 тексель или 2 текселя.
(ПРИМЕЧАНИЕ. Граница, на которую я ссылаюсь, — это не черный цвет вокруг всех четырех краев изображения, а область, где рисунок шахматной доски перестает регулярно повторяться)
Если вам интересно, вот почему я продолжаю поднимать анизотропную фильтрацию. Он изменяет форму окрестности образца на основе угла и может привести к тому, что для фильтрации будет использовано более 4 текселей:
Чем выше степень анизотропии, которую вы используете, тем больше вероятность того, что вам придется иметь дело с выборочными окрестностями, содержащими более 4 текселей. Граница в 2 текселя должна быть достаточной для большинства случаев анизотропной фильтрации.
И последнее, но не менее важное: вот как будет построен упакованный текстурный атлас, который будет копировать GL_CLAMP_TO_EDGE
поведение в присутствии GL_LINEAR
текстурный фильтр:
(Вычтя 1 из X и Y в черных координатах, я не проверял прочитанное изображение перед публикацией.)
Из-за хранения границ для хранения 4 256×256 текстур в этом атласе требуется текстура с размерами 516×516. Границы имеют цветовую кодировку в зависимости от того, как вы будете заполнять их данными texel при создании атласа:
Эффективно в этом упакованном примере каждая текстура в атласе использует область атласа 258×258, но вы будете генерировать координаты текстуры, которые отображаются в видимую область 256×256. Ограничивающие тексели используются только тогда, когда фильтрация текстур выполняется по краям текстур в атласе, и то, как они создаются, имитирует GL_CLAMP_TO_EDGE
поведение.
Если вам интересно, вы можете реализовать другие типы режимов переноса, используя аналогичный подход — GL_REPEAT
может быть реализовано путем замены текселей левой / правой и верхней / нижней границ в текстурном атласе и немного хитрой математической координаты текстуры в шейдере. Это немного сложнее, так что пока не беспокойтесь об этом. Поскольку вы имеете дело только со спрайт-листами, ограничьте себя GL_CLAMP_TO_EDGE
🙂
У меня была такая же проблема, как показано на этой картинке:
Идея состоит в том, чтобы уменьшить изображения в атласе на один пиксель и заменить пиксели цветом, смежным с 1-пиксельной «рамкой». Как только это будет сделано, отрегулируйте смещение UV, чтобы учесть границу 1px. Другими словами, фактические координаты текстуры были бы (для верхнего левого угла в нижний правый угол): start_x + 1
, start_y + 1
, end_x - 1
, end_y -1
До:
После:
После подачи заявления это результат:
Еще одна проблема, если у вас есть прозрачный пиксель в вашей текстуре:
Когда OpenGL использует линейный фильтр для масштабирования текстуры, он смешивает некоторые пиксели с прозрачным пикселем, но в большинстве случаев цвет прозрачного пикселя является белым, поэтому полученный пиксель с смешанным пикселем не имеет ожидаемого цвета. Чтобы исправить это, решение состоит в том, чтобы создать предварительно умноженную альфу. Я создал скрипт для достижения этого на GIMP:
(define (precompute-alpha img color)
(define w (car (gimp-image-width img)))
(define h (car (gimp-image-height img)))
(define img-layer (car (gimp-image-get-active-layer img)))
(define img-mask (car (gimp-layer-create-mask img-layer ADD-ALPHA-TRANSFER-MASK)))
(gimp-layer-add-mask img-layer img-mask)
(define alpha-layer (car (gimp-layer-new img w h RGBA-IMAGE "alpha" 100 NORMAL-MODE)))
(gimp-image-insert-layer img alpha-layer 0 -1)
(gimp-edit-copy img-mask)
(define floating-sel (car (gimp-edit-paste alpha-layer TRUE)))
(gimp-floating-sel-anchor floating-sel)
(define bg-layer (car (gimp-layer-new img w h RGBA-IMAGE "bg" 100 NORMAL-MODE)))
(gimp-image-insert-layer img bg-layer 0 2)
(gimp-context-set-background color)
(gimp-drawable-fill bg-layer BACKGROUND-FILL)
(set! bg-layer (car (gimp-image-merge-down img img-layer 0)))
(define bg-mask (car (gimp-layer-create-mask bg-layer ADD-WHITE-MASK)))
(gimp-layer-add-mask bg-layer bg-mask)
(gimp-edit-copy alpha-layer)
(set! floating-sel (car (gimp-edit-paste bg-mask TRUE)))
(gimp-floating-sel-anchor floating-sel)
(gimp-image-remove-layer img alpha-layer)
)
(script-fu-register "precompute-alpha""Precompute Alpha""Automatically precompute alpha""Thomas Arbona""2017""2017""*"SF-IMAGE "Image" 0
SF-COLOR "Alpha Color" '(0, 0, 0)
)
(script-fu-menu-register "precompute-alpha" "<Image>/Alpha")
Просто откройте свое изображение в Gimp, откройте Alpha> Precompute Alpha и выберите цвет, чтобы предварительно вычислить альфа на вашем изображении с этим цветом.