2

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

4

2 回答 2

1

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

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

    • 最近的
    • 线性

    可以在下图中看到差异。

  2. 包裹模式(纹理边界后发生的情况)

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

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

在此处输入图像描述


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). 为避免双重缓冲,请在绘图时阅读。如果您有多个子图像,您可以在此处使用 z 缓冲区并绘制方形金字塔而不是四边形,从而允许深度测试像最小距离到任一子图像一样工作(就像绘制带有锥体的 voronoi 细胞)

于 2014-09-16T09:03:18.550 回答
1

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

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

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

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

于 2014-09-15T14:40:52.747 回答