【问题标题】:Using OpenGL GL.TexSubImage2D for NPOT Textures results in artifacts将 OpenGL GL.TexSubImage2D 用于 NPOT 纹理会导致伪影
【发布时间】:2015-03-06 13:14:18
【问题描述】:

我在 C# 中通过 OpenTK 使用 OpenGL,并尝试从通用位图中加载纹理。我的驱动程序不支持 NPOT 纹理,所以我要做的是使用 GL.TexImage2D 分配一个 POT 纹理,并通过 GL.TexSubImage2D 用我的位图填充它。 但是,在绘制这些文本时我有伪影。看起来,它还在纹理的底部和右侧绘制了一个额外的像素。 我应该从比率中减去像素还是有其他问题?

创建代码:

GL.BindTexture(TextureTarget.Texture2D, t.Name);

GL.PixelStore(PixelStoreParameter.PackAlignment, 1);
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, texture.W2, texture.H2, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, texture.DataSize.Width, texture.DataSize.Height, PixelFormat.Bgra, PixelType.UnsignedByte, data);

GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
GL.Ext.GenerateMipmap(GenerateMipmapTarget.Texture2D);

GL.BindTexture(TextureTarget.Texture2D, 0);

绘图代码:

GL.BindTexture(TextureTarget.Texture2D, t.Name);

float x1 = 0;
float x2 = texture.WidthRatio;
float y1 = 0;
float y2 = texture.HeightRatio;

float rx1 = rect.X;
float rx2 = rect.X + rect.W;
float ry1 = rect.Y;
float ry2 = rect.Y + rect.H;

GL.Begin(BeginMode.Quads);

GL.TexCoord2(x1, y1);
GL.Vertex3(rx1, ry1, rect.Z + CGraphics.ZOffset);

GL.TexCoord2(x1, y2);
GL.Vertex3(rx1, ry2, rect.Z + CGraphics.ZOffset);

GL.TexCoord2(x2, y2);
GL.Vertex3(rx2, ry2, rect.Z + CGraphics.ZOffset);

GL.TexCoord2(x2, y1);
GL.Vertex3(rx2, ry1, rect.Z + CGraphics.ZOffset);

GL.End();

GL.Disable(EnableCap.Blend);
GL.BindTexture(TextureTarget.Texture2D, 0);

其中texture.WidhtRatio = DataSize.Width / W2;

【问题讨论】:

    标签: c# opengl textures opentk glteximage2d


    【解决方案1】:

    这与纹理的边缘情况有关。当您第一次在 GL 中查看纹理时,大多数教程都会介绍以下内容:

    1. 过滤(纹素之间发生的事情)

      • 最近的
      • 线性

      如下图所示。

    2. 环绕模式(纹理边界之后发生的情况)

      请注意颜色的环绕,即使纹理坐标不在 -1 到 1 之外——线性过滤和重复环绕模式的组合。

    需要说明的另一件重要的事情是样本被放置在像素和纹素的中间。这就是为什么gl_FragCoord.xy 总是有分数.5 (直到你得到多重/超级采样等)。这可以在下图中看到。我怀疑这就是问题所在。您可能需要调整纹理坐标以对纹理的插入进行采样,以避免相邻像素的插值(即 x+0.5x+width-0.5)。


    Mipmaps 将包含更大区域的颜色,因此简单地更改坐标是行不通的。要重新创建GL_CLAMP_TO_EDGE 的行为,您确实需要手动拉伸子图像边框。或者,在上传之前清除纹理会更简单。一种快速的方法是将纹理附加到 FBO 和 glClear

    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    //glDeleteFramebuffers(1, &fbo);
    

    为了更进一步并挤压纹理,我会上传子图像,然后在子图像和所需边框上绘制一个四边形。在片段着色器中,丢弃子图像内的像素并直接调用coord = clamp(coord, imageMin, imageMax)。为避免双重缓冲,read while drawing。如果您有多个子图像,您可以在此处使用 z 缓冲区并绘制方形金字塔而不是四边形,从而允许深度测试像最小距离到任一子图像一样工作(就像 drawing voronoi cells with cones

    【讨论】:

    • +1 非常好的解释。然而,问题在于用于缩小纹理绘制的 mip 贴图。这里有什么解决方案吗?
    • @Flamefire 我猜这取决于纹理内容。使用 alpha 或背景颜色清除它可能会起作用(使用 FBO+glClear 快速)。如果没有,那么正如 RetoKoradi 建议的那样,添加边框可能会更好。如果只使用前几个 mipmap 级别,则边框可能很小。您还可以在片段着色器中生成它以加快速度。
    • 您能否提供一个如何使用 FBO 来实现此目的的链接?
    • 谢谢!用透明颜色清除作品。 (必须在 glClear 调用之前添加 glSetColor(0) 和 glSetColor(Color.Black))
    【解决方案2】:

    您正在使用双线性过滤,这将导致最后一列和最后一行插入未初始化的纹理数据(因此产生伪影)。

    假设您希望启用双线性过滤,您可以做的是复制纹理数据纹理的最后一行和最后一列。换句话说,如果您的 NPOT 纹理为 800x600,则上传 801x601 像素,最后一列/行重复。

    这样,最后一行/列的插值将返回预期结果,工件应该消失。

    编辑: 正如 Reto Koradi 建议的那样,这仅在不使用 mipmap 时有效。如果您使用 mipmap,尤其是在使用各向异性过滤时,您应该使用最后一列/行填充所有空白区域。

    【讨论】:

    • 当初看到这个问题的时候就想到了这个。我认为复制一行/列是不够的。只要只使用最低的 mipmap,这将起作用。但对于更高的 mipmap 级别,将使用更多未初始化的纹理数据。我相信你必须用复制的数据填充整个纹理才能获得所有 mipmap 的干净样本值。
    • 您是对的,如果您使用GL.GenerateMipmap() 和/或各向异性过滤,这将是必要的。我会修改我的答案。
    • 你是对的。问题出现在 mipmap 上。然而,其他 minFilters 真的很糟糕,所以我真的必须在整个纹理中复制最后一行/列吗?可能有点慢(至少对于 cols 而言)...
    猜你喜欢
    • 2022-01-21
    • 1970-01-01
    • 2020-11-06
    • 1970-01-01
    • 2020-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多