使用OpenGL GL.TexSubImage2D进行NPOT纹理会导致伪影

时间:2014-09-13 12:39:33

标签: c# opengl textures opentk glteximage2d

我在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;

2 个答案:

答案 0 :(得分:1)

您正在使用双线性过滤,这将导致最后一列和行使用未初始化的纹理数据(因此是工件)进行插值。

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

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

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

答案 1 :(得分:1)

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

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

    • 最近
    • 线性

    差异可以在下面的图片中看到。

  2. 包裹模式(在纹理边界之后会发生什么)

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

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

    enter image description here


    Mipmap 将包含来自更大区域的颜色,因此只需更改坐标即可。要重新创建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-buffer并绘制方形金字塔而不是四边形,允许深度测试像最小距离到任一子图像一样工作(就像drawing voronoi cells with cones

相关问题