我在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;
答案 0 :(得分:1)
您正在使用双线性过滤,这将导致最后一列和行使用未初始化的纹理数据(因此是工件)进行插值。
假设您希望启用双线性过滤,您可以做的是复制纹理数据纹理的最后一行和最后一列。换句话说,如果你的NPOT纹理是800x600,那么上传801x601像素,最后一列/行重复。
这样,最后一行/列的插值将返回预期结果,并且工件应该消失。
编辑:正如Reto Koradi建议的那样,这只有在不使用mipmap时才有效。如果您正在使用mipmap,尤其是在使用各向异性过滤时,则应使用最后一列/行填充所有空白区域。
答案 1 :(得分:1)
这与纹理的边缘情况有关。当您第一次看GL中的纹理时,大多数教程都会介绍以下内容:
过滤(纹素之间发生的事情)
差异可以在下面的图片中看到。
包裹模式(在纹理边界之后会发生什么)
注意颜色的包裹,即使纹理坐标不在-1到1之外---线性过滤和重复包裹模式的组合。
另一个需要说明的重要事情是样本放在像素和纹素的中间。这就是gl_FragCoord.xy
始终具有分数.5
的原因(直到您进行多次/超级采样等)。这可以在下图中看到。我怀疑这就是问题所在。您可能需要调整纹理坐标以对纹理的插入进行采样,以避免插入相邻像素(即x+0.5
到x+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)
。为避免双重缓冲,read while drawing。如果你有多个子图像,你可以在这里使用z-buffer并绘制方形金字塔而不是四边形,允许深度测试像最小距离到任一子图像一样工作(就像drawing voronoi cells with cones )