opengl, черные линии между плитами

Когда он переводится в интегральное значение (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); } 

Скриншоты проблемы:

Скриншот 1Скриншот 2

Проблема использования текстурных атласов (листы спрайтов) и утечки соседних текселей связана с тем, как работает линейная фильтрация текстур.

Для любой точки текстуры, которая не выбрана точно в центре текселя, линейная выборка будет отображать 4 соседних текселя и вычислять значение в месте, которое вы задавали, в качестве взвешенного (исходя из расстояния от точки выборки) среднего значения всех 4 образцы.

Вот хорошая визуализация проблемы:

image0

Поскольку вы не можете использовать что-то вроде GL_CLAMP_TO_EDGE в атласе текстуры, вам нужно создать пограничные тексели вокруг края каждой текстуры. Эти пограничные тексели предотвращают смещение соседних образцов от совершенно разных текстур в атласе от изменения изображения через взвешенную интерполяцию, описанную выше.

Обратите внимание, что при использовании анизотропной фильтрации вам может потребоваться увеличить ширину границы. Это связано с тем, что анизотропная фильтрация увеличит размер окрестности образца под крайними углами.

Чтобы проиллюстрировать, что я имею в виду, используя границу вокруг края каждой текстуры, рассмотрите различные режимы обертывания, доступные в OpenGL. Обратите особое внимание на CLAMP TO EDGE .

image1

Несмотря на то, что существует режим под названием «Clamp to Border», на самом деле это не то, что нас интересует. Этот режим позволяет вам определить один цвет для использования в качестве frameworks вокруг вашей текстуры для любых координат текстуры, которые выходят за пределы нормализованного [0.0 -1.0].

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

Мы знаем, что GL_LINEAR будет GL_LINEAR 4 ближайших соседей, как показано на диаграмме выше, поэтому нам нужна только 1-тексельная граница. Если вы используете анизотропную фильтрацию, вам может понадобиться более широкая граница текселя, поскольку при определенных условиях она увеличивает размер окрестности образца.

Вот пример текстуры, которая более четко иллюстрирует границу, хотя для ваших целей вы можете сделать границу 1 тексель или 2 текселя в ширину.

image2

(ПРИМЕЧАНИЕ. Граница, о которой я говорю, не является черной вокруг всех четырех краев изображения, но область, где шаблон шахматной доски перестает повторяться регулярно)

В случае, если вам интересно, вот почему я продолжаю поднимать анизотропную фильтрацию. Он изменяет форму окрестности образца на основе угла и может вызывать более 4 текселей для фильтрации:

image3

Чем больше степень анизотропии, которую вы используете, тем более вероятно, что вам придется иметь дело с образцовыми кварталами, содержащими более 4 текселей. Для большинства анизотропных фильтрующих ситуаций должна быть достаточной граница 2 текселя.

И последнее, но не менее важное: вот как будет построен упакованный текстурный атлас, который будет реплицировать поведение GL_CLAMP_TO_EDGE в присутствии GL_LINEAR текстуры GL_LINEAR :

( Вычитайте 1 из X и Y в черные координаты, я не доказал, что прочитал изображение перед публикацией. )

Из-за хранения на границах, хранение 4 текстур 256×256 в этом атласе требует текстуры с размерами 516×516. Границы кодируются цветом, основываясь на том, как вы заполняете их тексельными данными во время создания атласа:

  • Красный = заменить текселем прямо ниже
  • Желтый = заменить текселем непосредственно выше
  • Зеленый = Заменить текселем прямо влево
  • Синий = Заменить текселем прямо вправо

Эффективно в этом упакованном примере каждая текстура в атласе использует область 258×258 атласа, но вы создадите координаты текстуры, которые будут отображаться в видимую область 256×256. Приграничные тексели используются только когда текстурная фильтрация выполняется по краям текстур в атласе, а способ их GL_CLAMP_TO_EDGE имитирует поведение GL_CLAMP_TO_EDGE .

В случае, если вам интересно, вы можете реализовать другие типы режимов обертывания с использованием аналогичного подхода – GL_REPEAT может быть реализован путем обмена GL_REPEAT влево / вправо и верхней / нижней границей текстуры в атласе текстуры и немного умной текстурной координатной математики в шейдер. Это немного сложнее, поэтому не беспокойтесь об этом пока. Поскольку вы имеете дело только со GL_CLAMP_TO_EDGE спрайтов, ограничивайте себя GL_CLAMP_TO_EDGE 🙂

У меня была такая же проблема, как показано на этой картинке:

проблема

Идея состоит в том, чтобы сжать изображения в атласе на один пиксель и заменить пиксели цветом, смежным с границей 1px. Как только это будет сделано, отрегулируйте УФ-коррекцию для учета границы 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 wh 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 wh 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" "/Alpha") 

Просто откройте свое изображение в Gimp, откройте Alpha> Precompute Alpha и выберите цвет, чтобы предварительно вычислить альфу на вашем изображении с этим цветом.