几何着色器在线之间产生间隙

时间:2013-06-02 15:46:25

标签: opengl glsl

我写了一个几何着色器来计算网格和平面的交点轮廓,但在交点处,两条线之间有时会有1个像素宽的间隙。

enter image description here

着色器首先计算三角形顶点到平面的有符号距离。然后检查两个距离是否具有不同的符号以确定是否存在与边缘的交叉点。如果是这样,它会在交点处发出一个顶点,该顶点计算为边缘点之间的加权平均值。

#version 330
layout(triangles) in;
layout(line_strip, max_vertices = 3) out;

out vec3 vertexPosition;

uniform vec3 planePos;
uniform vec3 planeNormal;
uniform mat4 mvpMatrix;
uniform vec2 screenSize;

void intersection(in vec4 a, in float distA, in vec4 b, in float distB)
{
  if (sign(distA) * sign(distB) <= 0.0f && !(sign(distA) == 0 && sign(distB) == 0))
  {
    float fa = abs(distA);
    float fb = abs(distB);
    float fab = fa + fb;
    vec4 ptIntersection;
    // Don't divide by zero.
    if (fab < 0.001)
      ptIntersection = (a + b) * 0.5;
    else
      ptIntersection = (fa * b + fb * a) / fab;
    gl_Position = mvpMatrix * ptIntersection;
    vertexPosition = gl_Position.xyw;
    EmitVertex();
  }
}

void main()
{
  vec4 a = gl_in[0].gl_Position;
  vec4 b = gl_in[1].gl_Position;
  vec4 c = gl_in[2].gl_Position;

  float distA = dot(a.xyz - planePos, planeNormal);
  float distB = dot(b.xyz - planePos, planeNormal);
  float distC = dot(c.xyz - planePos, planeNormal);

  intersection(a, distA, b, distB);
  intersection(b, distB, c, distC);
  intersection(c, distC, a, distA);
}

我知道它有点便宜,因为我忽略了所有三个点都在飞机上的特殊情况。 !(sign(distA) == 0 && sign(distB) == 0)确保如果平面上有两个点,则不会为该边缘发射任何顶点。因此,如果所有三个都在飞机上,就没有输出。但我想这并不一定是坏事。我喜欢它的是没有疯狂的分支,如果可能的话我想保持这种方式。

所以我想知道:为什么我会看到这些差距?假设有两个三角形(a,b,c)和(c,b,d)。 a和b位于平面上方,c和d位于下方。对于第一个三角形,着色器生成与(b,c)的交点,第二个与(c,b)交叉。假设添加两个浮点数是可交换的,那么intersection函数与输入对称,因此结果应该相同。为什么我仍然看到这些差距?

2 个答案:

答案 0 :(得分:3)

答案在于OpenGL规范给出的Bresenham线光栅化算法的规范。引自OpenGL 3.3 Spec,第3.5.1节:

  

从pa开始并以pb结束的线段栅格生成那些从pa开始并以pb结束的段与Rf相交的片段f,除非pb包含在Rf [片段中心的菱形区域]中。

如果你的两条相邻线不幸地在相反的方向上运行(即在同一点结束)并且该端点包含在像素中心周围的所述菱形中,则端点根本不被光栅化。因此,你会看到明显的差距。

由于您已经在使用几何着色器,当然可以(通过一些额外的计算)发出三角形以形成“真正的”宽线。

答案 1 :(得分:2)

这是最终的代码,可能对某人有用。

#version 330

layout(triangles) in;
layout(line_strip, max_vertices = 3) out;

uniform vec4 plane;
uniform mat4 mvpMatrix;

void emitIntersection(in vec4 a, in float distA, in vec4 b, in float distB)
{
  if (sign(distA) * sign(distB) <= 0.0f && !(sign(distA) == 0 && sign(distB) == 0))
  {
    float fa = abs(distA);
    float fb = abs(distB);
    gl_Position = mvpMatrix * ((fa * b + fb * a) / (fa + fb));
    EmitVertex();
  }
}

void main()
{
  float dist[3];
  for (int i=0; i<3; i++)
    dist[i] = dot(gl_in[i].gl_Position, plane);

  // Find the smallest i where vertex i is below and vertex i+1 is above the plane.
  ivec3 ijk = ivec3(0, 1, 2); // use swizzle to permute the indices
  for (int i=0; i < 3 && (dist[ijk.x] > 0 || dist[ijk.y] < 0); ijk=ijk.yzx, i++);

  emitIntersection(gl_in[ijk.x].gl_Position, dist[ijk.x], gl_in[ijk.y].gl_Position, dist[ijk.y]);
  emitIntersection(gl_in[ijk.y].gl_Position, dist[ijk.y], gl_in[ijk.z].gl_Position, dist[ijk.z]);
  emitIntersection(gl_in[ijk.z].gl_Position, dist[ijk.z], gl_in[ijk.x].gl_Position, dist[ijk.x]);
}