2

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

在此处输入图像描述

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

#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函数对于输入是对称的,所以结果应该是相同的。为什么我仍然看到这些差距?

4

2 回答 2

3

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

对从 pa 开始到 pb 结束的线段进行光栅化会生成那些从 pa 到 pb 结束的线段与 Rf 相交的片段 f,除非 pb 包含在 Rf [以片段为中心的菱形区域] 中。

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

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

于 2013-06-02T20:10:28.907 回答
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]);
}
于 2013-06-11T20:15:10.150 回答