13

当它转换成整数值(1、2、3 等......)时,瓷砖之间没有黑线,看起来很好。但是当它被转换为非整数(1.1、1.5、1.67)时,每个图块之间都有黑色的小线条(我想这是由于亚像素渲染,对吧?)......而且看起来并不漂亮=P

所以我该怎么做?

顺便说一下,这是我的图像加载代码:

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

3 回答 3

24

使用纹理图集(精灵表)和相邻纹素泄漏的问题与线性纹理过滤的工作方式有关。

对于纹理中未在纹素中心精确采样的任何点,线性采样将对 4 个相邻纹素进行采样,并将您询问的位置处的值计算为所有 4 个的加权平均值(基于与采样点的距离)样品。

这是问题的一个很好的可视化:

图像0

由于您不能GL_CLAMP_TO_EDGE在纹理图集中使用类似的东西,因此您需要在每个纹理的边缘周围创建边界纹素。这些边界纹素将防止来自图集中完全不同纹理的相邻样本通过上述加权插值改变图像。

请注意,当您使用各向异性过滤时,您可能需要增加边框的宽度。这是因为各向异性过滤会在极端角度增加样本邻域的大小。


为了说明我在每个纹理边缘周围使用边框的意思,请考虑 OpenGL 中可用的各种环绕模式。特别注意CLAMP TO EDGE

图像1

尽管有一种名为“Clamp to Border”的模式,但这实际上并不是我们感兴趣的。该模式允许您定义一种颜色,用作纹理周围的边界,用于任何超出规范化 [0.0 的纹理坐标-1.0] 范围。

我们想要的是复制 的行为CLAMP_TO_EDGE,其中(子)纹理的适当范围之外的任何纹理坐标都会在超出边界的方向上接收最后一个纹理中心的值。因为您几乎可以完全控制地图集系统中的纹理坐标,(有效)纹理坐标可能引用纹理之外的位置的唯一情况是在纹理过滤的加权平均步骤期间。

GL_LINEAR如上图所示,我们知道它将对 4 个最近的邻居进行采样,因此我们只需要一个 1-texel 边框。如果您使用各向异性过滤,您可能需要更宽的纹素边界,因为它会在某些条件下增加样本邻域大小。

这是一个更清楚地说明边界的纹理示例,但出于您的目的,您可以将边界设置为 1 texel 或 2 texel 宽。

图2

(注意:我所指的边框不是图像所有四个边缘周围的黑色,而是棋盘图案停止定期重复的区域)

如果您想知道,这就是我不断提出各向异性过滤的原因。它会根据角度改变样本邻域的形状,并可能导致超过 4 个纹素用于过滤:

图3

您使用的各向异性程度越大,您就越有可能需要处理包含超过 4 个纹素的样本邻域。对于大多数各向异性过滤情况,2 texel 边界应该足够了。


最后但并非最不重要的一点是,以下是如何构建打包的纹理图集,该图集将GL_CLAMP_TO_EDGE在存在GL_LINEAR纹理过滤器的情况下复制行为:

黑色坐标中的 X 和 Y 减去 1,我在发布之前没有校对图像。

由于边界存储,在此图集中存储 4 个 256x256 纹理需要尺寸为 516x516 的纹理。根据您在图集创建期间如何用纹素数据填充边框,对边框进行颜色编码:

  • 红色 = 替换为正下方的纹素
  • 黄色 = 替换为正上方的纹素
  • 绿色 = 直接用左侧的纹素替换
  • 蓝色 = 直接替换为右侧的纹素

在这个打包的示例中,图集中的每个纹理都使用图集的 258x258 区域,但您将生成映射到可见 256x256 区域的纹理坐标。只有在图集中纹理的边缘进行纹理过滤时才会使用边界纹素,并且它们的设计方式模仿GL_CLAMP_TO_EDGE行为。

如果您想知道,您可以使用类似的方法实现其他类型的环绕模式——GL_REPEAT可以通过在纹理图集中交换左/右和上/下边界纹理像素以及在 a着色器。这有点复杂,所以暂时不用担心。由于您只处理精灵表,因此将自己限制为GL_CLAMP_TO_EDGE:)

于 2013-10-26T21:18:33.950 回答
2

我遇到了同样的问题,如图所示:

问题

想法是将图集中的图像缩小一个像素,并将像素替换为与1px“边框”相邻的颜色。完成后,调整 UV 偏移以考虑 1px 边框。换句话说,实际的纹理坐标是(从左上角到右下角):start_x + 1, start_y + 1, end_x - 1,end_y -1

前:

坏地图集

后:

好图集

申请后是这样的结果:

固定的

于 2015-07-17T22:39:05.133 回答
0

如果纹理中有透明像素,另一个问题是:

当 OpenGL 使用线性过滤器来缩放你的纹理时,它会将一些像素与透明像素混合,但在大多数情况下,透明像素的颜色是白色的,所以结果混合像素没有预期的颜色。为了解决这个问题,一个解决方案是创建预乘 alpha。我在 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并选择一种颜色以使用该颜色预先计算图像上的 Alpha。

于 2017-09-21T10:42:01.850 回答