opengl,瓷砖之间的黑线 [英] opengl, Black lines in-between tiles

查看:170
本文介绍了opengl,瓷砖之间的黑线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当将其转换为整数值(1、2、3等)时,图块之间没有黑线,看起来不错.但是,当将其转换为非整数(1.1、1.5、1.67)时,每个图块之间都有一条黑色的细线(我在想这是由于亚像素渲染造成的,对吧?)……而且看起来不太漂亮= P

When its translated in an integral value (1,2,3, etc....), there are no black lines in-between the tiles, it looks fine. But when it's translated to a non-integral (1.1, 1.5, 1.67), there are small blackish lines between each tile (I'm imagining that it's due to subpixel rendering, right?) ... and it doesn't look pretty =P

那...我该怎么办?

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

This is my image-loading code, by the way:

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);

这是我的图像绘制代码:

Here is my image drawing code:

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初始化代码:

OpenGL Initialization code:

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);
}

问题的屏幕截图:

推荐答案

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

The problem with using texture atlases (sprite sheets) and adjacent texels leaking has to do with the way linear texture filtering works.

对于纹理中未在纹理像素中心进行精确采样的任何点,线性采样将对4个相邻纹理像素进行采样,并在您要求的位置计算值的权重(基于与采样点的距离)平均值全部四个样本中.

For any point in the texture that is not sampled exactly at the center of a texel, linear sampling will sample 4 adjacent texels and compute the value at the location you asked as the weighted (based on distance from the sample point) average of all 4 samples.

这是问题的很好可视化:

Here's a nice visualization of the problem:

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

Since you cannot use something like GL_CLAMP_TO_EDGE in a texture atlas, you need to create border texels around the edge of each texture. These border texels will prevent neighboring samples from completely different textures in the atlas from altering the image through weighted interpolation explained above.

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

Note that when you use anisotropic filtering, you may need to increase the width of the border. This is because anisotropic filtering will increase the size of the sample neighborhood at extreme angles.


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


To illustrate what I mean by using a border around the edge of each texture, consider the various wrap modes available in OpenGL. Pay special attention to CLAMP TO EDGE.

尽管存在一种称为钳位到边界"的模式,但实际上这并不是我们感兴趣的模式.通过该模式,您可以定义一种单色,以用作纹理周围的边界,用于超出该范围的任何纹理坐标.标准化的[0.0-1.0]范围.

Despite there being a mode called "Clamp to Border", that is actually not what we are interested in. That mode lets you define a single color to use as a border around your texture for any texture coordinates that fall outside of the normalized [0.0-1.0] range.

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

What we want is to replicate the behavior of CLAMP_TO_EDGE, where any texture coordinate outside the proper range for the (sub-)texture receives the value of the last texel center in the direction it was out of bounds in. Since you have almost complete control over the texture coordinates in an atlas system, the only scenario in which (effective) texture coordinates might refer to a location outside of your texture are during the weighted average step of texture filtering.

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

We know that GL_LINEAR will sample the 4 nearest neighbors as seen in the diagram above, so we only need a 1-texel border. You may need a wider texel border if you use anisotropic filtering, because it increases the sample neighborhood size under certain conditions.

这是一个纹理示例,它可以更清楚地说明边框,但出于您的目的,您可以使边框宽1 texel或2 texel.

Here's an example of a texture that illustrates the border more clearly, though for your purposes you can make the border 1 texel or 2 texels wide.

(注意:我指的边界不是图像的所有四个边缘周围的黑色,而是棋盘格图案停止规则重复出现的区域)

(NOTE: The border I am referring to is not the black around all four edges of the image, but the area where the checkerboard pattern stops repeating regularly)

如果您想知道,这就是为什么我不断提出各向异性过滤的原因.它会根据角度改变样品邻域的形状,并可能导致使用4个以上的纹理像素进行滤波:

In case you were wondering, here is why I keep bringing up anisotropic filtering. It changes the shape of the sample neighborhood based on angle and can cause more than 4 texels to be used for filtering:

使用的各向异性程度越大,处理包含4个以上纹理像素的样本邻域的可能性就越大.对于大多数各向异性过滤情况,2 texel边界应足够.

The larger the degree of anisotropy you use, the more likely you will have to deal with sample neighborhoods containing more than 4 texels. A 2 texel border should be adequate for most anisotropic filtering situations.


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


Last but not least, here is how a packed texture atlas would be built that would replicate GL_CLAMP_TO_EDGE behavior in the presence of a GL_LINEAR texture filter:

(从X和Y的黑色坐标中减去1,在发布之前我没有证明读过图像.)

由于边界存储,因此在此地图集中存储4 256x256纹理需要一个尺寸为516x516的纹理.边框是根据您在图集创建过程中如何以纹素数据填充边框的颜色进行编码的:

Due to border storage, storing 4 256x256 textures in this atlas requires a texture with dimensions 516x516. The borders are color coded based on how you would fill them with texel data during atlas creation:

  • 红色=直接在下方替换为texel
  • 黄色=直接在上方替换为texel
  • 绿色=直接在左侧替换为texel
  • 蓝色=直接在右侧替换为texel

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

Effectively in this packed example, each texture in the atlas uses a 258x258 region of the atlas, but you will generate texture coordinates that map to the visible 256x256 region. The bordering texels are only ever used when texture filtering is done at the edges of textures in the atlas, and the way they are designed mimics GL_CLAMP_TO_EDGE behavior.

如果您想知道,可以使用类似的方法来实现其他类型的环绕模式-GL_REPEAT可以通过交换纹理图集中的左/右和上/下边界纹理来实现,着色器中的巧妙纹理坐标数学.这有点复杂,所以暂时不用担心.由于您只处理精灵表,因此请限制为GL_CLAMP_TO_EDGE:)

In case you were wondering, you can implement other types of wrap modes using a similar approach -- GL_REPEAT can be implemented by exchanging the left/right and top/bottom border texels in the texture atlas and a little bit of clever texture coordinate math in a shader. That is a little more complicated, so do not worry about that for now. Since you're only dealing with sprite sheets limit yourself to GL_CLAMP_TO_EDGE :)

这篇关于opengl,瓷砖之间的黑线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆