使用OpenGL渲染带水平和垂直线条纹理的工件

时间:2016-09-10 15:45:20

标签: opengl textures rendering artifacts

我创建了8x8像素位图字母来使用OpenGL渲染它们,但有时候,根据缩放我会得到奇怪的伪像,如下图所示。纹理过滤设置为最近的像素。它看起来像舍入问题,但如果线条完全水平,怎么会有一些。

Left original 8x8, middle scaled to 18x18, right scaled to 54x54.

左原始8x8,中间缩放到18x18,右缩放到54x54。

顶点数据是格式的无符号字节(x-offset,y-offset,letter)。这是完整的代码:

顶点着色器:

#version 330 core

layout(location = 0) in uvec3 Data;

uniform float ratio;
uniform float font_size;

out float letter;

void main()
{
  letter = Data.z;

  vec2 position = vec2(float(Data.x) / ratio, Data.y) * font_size - 1.0f;
  position.y = -position.y;

  gl_Position = vec4(position, 0.0f, 1.0f);
}

几何着色器:

#version 330 core

layout (points) in;
layout (triangle_strip, max_vertices = 4) out;

uniform float ratio;
uniform float font_size;

out vec3 texture_coord;

in float letter[];

void main()
{
// TODO: pre-calculate
  float width = font_size / ratio;
  float height = -font_size;

  texture_coord = vec3(0.0f, 0.0f, letter[0]);
  gl_Position = gl_in[0].gl_Position + vec4(0.0f, height, 0.0f, 0.0f);
  EmitVertex();

  texture_coord = vec3(1.0f, 0.0f, letter[0]);
  gl_Position = gl_in[0].gl_Position + vec4(width, height, 0.0f, 0.0f);
  EmitVertex();

  texture_coord = vec3(0.0f, 1.0f, letter[0]);
  gl_Position = gl_in[0].gl_Position + vec4(0.0f, 0.0f, 0.0f, 0.0f);
  EmitVertex();

  texture_coord = vec3(1.0f, 1.0f, letter[0]);
  gl_Position = gl_in[0].gl_Position + vec4(width, 0.0f, 0.0f, 0.0f);
  EmitVertex();

  EndPrimitive();
}

片段着色器:

#version 330 core

in vec3 texture_coord;

uniform sampler2DArray font_texture_array;

out vec4 output_color;

void main()
{
  output_color = texture(font_texture_array, texture_coord);
}

2 个答案:

答案 0 :(得分:0)

好吧,当使用最近的过滤时,如果您的样本位置非常接近两个纹素之间的边界,您将看到此类问题。并且由于要为每个要绘制的片段单独插入tex坐标,因此轻微的数值误差将导致在这两个纹素之间跳跃。

当您将8x8纹理绘制为18x18像素的大矩形,并且您的矩形与putput像素栅格完美对齐时,几乎可以保证触发该行为:

查看纹理符号坐标将显示对于最底部输出像素,纹理坐标将被内插到1 /(2 * 18)= 1/36。向上一个像素将增加1/18 = 2/36到t坐标。所以对于从底部开始的第五行,它将是9/36。

因此,对于您正在采样的8x8纹素大纹理,您实际上是在非标准化纹理坐标(9/36)* 8 == 2.0处采样。这正是纹理的第二行和第三行之间的边界。由于每个片段的纹理坐标通过分配给来自三角形的三个顶点的tex坐标之间的重心插值进行插值,因此可能存在轻微的不准确性。即使是以浮点格式表示的最微小的不准确性,也会导致在这种情况下在两个纹素之间翻转。

我认为你的做法并不好。缩放位图字体总是有问题的(可能除了积分比例因子)。如果你想要很好看的可缩放纹理字体,我建议你研究signed distance fields。这是一种非常简单而强大的技术,有tools available to generate the necessary distance field textures

如果你正在寻找一个快速黑客,你也可以稍微偏移你的输出矩形。你基本上必须确保将偏移保持在[-0.5,0.5]像素(这样在光栅化期间永远不会产生不同的片段,并且你必须确保所有潜在的样本位置永远不会接近整数,所以偏移将取决于实际的比例因子。

答案 1 :(得分:0)

使用Freetype和OpenGL开发时,我遇到了同样的问题。经过数天的研究和摸索,我找到了解决方案。就我而言,我必须显式调用函数“ glBlendColor”。一次,我做到了,再也没有观察到任何伪像。

这是一个代码段:

//Set Viewport
glViewport(0, 0, FIXED_WIDTH, FIXED_HEIGHT);

//Enable Blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendColor(1.0f, 1.0f, 1.0f, 1.0f);  //Without this I was having artifacts: IMPORTANT TO EXPLICITLY CALLED

//Set Alignment requirement to 1 byte
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

在github上查看此OpenGL-Freetype库的源代码后,我找到了解决方案:opengl-freetype library