OpenGL:如何修复矩形中缺失的角像素(线或线环)

OpenGL: How to fix missing corner pixel in rect (lines or line loop)

看看中间绿色矩形的左下角:

它们在左下角缺少一个像素。

我画的是这样的:

class Rect: public StaticModel {
  public:
    Rect() {  
        constexpr glm::vec2 vertices[] {
                {-0.5,0.5},  // top left
                {0.5,0.5},  // top right
                {0.5,-0.5},  // bottom right
                {-0.5,-0.5},  // bottom left
        };
        _buf.bufferData<glm::vec2>(vertices,BufferUsage::StaticDraw);
        _idxBuf.bufferData<GLuint>({0,1,3,2,0,3,1,2},BufferUsage::StaticDraw);
    }

    void bind() const override {
        _buf.bindVertex(); 
        _idxBuf.bind();
    }

    void draw() const override {
        gl::drawElements(8,DrawMode::Lines);
    }

  private:
    VertexBuffer _buf{sizeof(glm::vec2)};
    ElementArrayBuffer _idxBuf{};
};

该代码使用了我的一堆助手 methods/classes,但您应该能够分辨出它的作用。我尝试使用简单的 GL_LINE_LOOP 绘制矩形,但遇到了同样的问题,所以现在我正在尝试 GL_LINES 并沿相同方向绘制所有线条:从上到下,从左到右,但是即便如此,我仍然缺少一个像素。

这些坐标正在进行正交投影:

gl_Position = projection * model * vec4(inPos, 0.0, 1.0);

所以着色器将这些 0.5 坐标缩放到像素坐标,但我认为这不是舍入误差。

还有什么我可以尝试让那个角对齐的吗?

OpenGL 为实现如何光栅化线条提供了很多余地。它需要一些理想的属性,但在混合 x-major ('horizontal') 和 y-major ('vertical') 线时,这些属性不会阻止间隙。

  • 首先,“规范精神”是将半开线栅格化;即包括第一个顶点并排除最后一个顶点。出于这个原因,您应该确保每个顶点仅作为源出现一次,作为目的地出现一次:

      _idxBuf.bufferData<GLuint>({0,1,1,2,2,3,3,0},BufferUsage::StaticDraw);
    

    这与您“从上到下,从左到右”的绘制尝试相反。

    GL_LINE_LOOP 虽然已经这样做了,但您说这不能解决问题。这确实不能保证解决问题,因为你在这里混合了 x-major 和 y-major 行,但你仍然应该遵循规则才能使下一个点起作用。

  • 接下来,我敢打赌,您的某些顶点正好落在像素之间;即 window 坐标小数部分恰好为零。当光栅化这些基元时,不同实现之间的差异(或者在我们的例子中,同一实现上的 x 主线和 y 主线之间的差异)变得突出。

    要解决这个问题,您可以将顶点捕捉到像素网格:

      // xy - vertex in window coordinates, i.e. same as gl_FragCoord
      xy = floor(xy) + 0.5
    

    您可以在 C++ 或顶点着色器中执行此操作。在任何一种情况下,您都需要应用投影和视口变换,然后撤消它们,以便 OpenGL 之后可以重新应用它们。这很丑,我知道。

然而,光栅化像素完美线的唯一可靠方法是渲染三角形以覆盖形状(单独的每条线或整个矩形)并从 gl_FragCoord.xy 分析计算覆盖率片段着色器。