【问题标题】:GLSL Geometry shader to replace glLineWidthGLSL 几何着色器替换 glLineWidth
【发布时间】:2021-11-05 19:44:12
【问题描述】:

我正在尝试编写一个几何着色器来替换 glLineWidth 行为。我想绘制具有可自定义宽度的线条(现在使用统一的就足够了)。无论相机投影或与线条所在位置的距离如何,线条都应始终具有相同的粗细。

基于大量谷歌搜索,我想出了以下几何着色器:

#version 330

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

uniform mat4    u_model_matrix;
uniform mat4    u_view_matrix;
uniform mat4    u_projection_matrix;
uniform float   u_thickness = 4; // just a test default

void main()
{
    float r = u_thickness / 2;

    mat4 mv = u_view_matrix * u_model_matrix;
    vec4 p1 = mv * gl_in[0].gl_Position;
    vec4 p2 = mv * gl_in[1].gl_Position;

    vec2 dir = normalize(p2.xy - p1.xy);
    vec2 normal = vec2(dir.y, -dir.x);

    vec4 offset1, offset2;
    offset1 = vec4(normal * r, 0, 0);
    offset2 = vec4(normal * r, 0, 0);

    vec4 coords[4];
    coords[0] = p1 + offset1;
    coords[1] = p1 - offset1;
    coords[2] = p2 + offset2;
    coords[3] = p2 - offset2;

    for (int i = 0; i < 4; ++i) {
        coords[i] = u_projection_matrix * coords[i];
        gl_Position = coords[i];
        EmitVertex();
    }
    EndPrimitive();
}

为了完整起见,这里是顶点着色器:

#version 330

in vec4 a_position;

void main() {
    gl_Position = a_position;
}

...和我的片段着色器:

#version 330

uniform vec4 u_color = vec4(1, 0, 1, 1);
out vec4 fragColor;

void main() {
    fragColor = u_color;
}

我无法让数学在所有情况下都适用。使用正交相机,上述工作正常:

但是对于透视相机,问题是线条不是固定大小的。相对于物体的距离,它会变得越来越小。

我希望使用透视相机时线条的大小也相同。我究竟做错了什么?

【问题讨论】:

    标签: opengl glsl line geometry-shader


    【解决方案1】:

    我不是专家,但我之前做过这件事,我会提供我的见解。

    我假设您的gl_Positions 直接来自使用投影矩阵计算的顶点着色器。这意味着他们的w 组件是该点的“剪辑空间位置”;这是管道用来赋予投影效果的东西(更远的东西更小)。所以需要考虑。

    幸运的是,您唯一需要做的就是将偏移量乘以它。

    coords[0] = p1 + offset1 * p1.w;
    coords[1] = p1 - offset1 * p1.w;
    coords[2] = p2 + offset2 * p2.w;
    coords[3] = p2 - offset2 * p2.w;
    

    这应该会给你想要的效果。

    【讨论】:

    • 我已经试过了,没区别。请注意,我在相机/视图空间中进行工作(我首先乘以模型/视图矩阵,进行数学运算,然后乘以投影矩阵)。在屏幕空间中执行此操作对我来说效果更差。
    • 我明白了,我确实错过了。但是,我认为您必须在屏幕空间中执行此操作,因为所需的效果与屏幕相关,不是吗?无论如何,您必须设计一些方法使进一步的偏移量更大,以使它们显示相同的大小。我想是使用投影位置还是视图空间 z 坐标取决于您。
    • 感谢您的提示!我现在意识到我需要缩小我的半径,以便它不是以像素为单位,而是相对于视口大小。手工计算并临时修改着色器实际上使它工作得更好! (而且确实还乘以 w!)
    • 很高兴能为您提供帮助!
    【解决方案2】:

    我设法通过考虑视口大小来修复它,并使用它来缩放我的 r。我不知道这是否是解决这个问题的最有效方法(我绝不是数学头),但它确实有效。

    在下面的代码中,我现在在屏幕空间而不是相机/视图空间中完成所有工作,并且我使用 u_viewportInvSize vec2(即 1/viewportSize)来缩放我想要的半径!

    #version 330
    
    layout (lines) in;                              // now we can access 2 vertices
    layout (triangle_strip, max_vertices = 4) out;  // always (for now) producing 2 triangles (so 4 vertices)
    
    uniform vec2    u_viewportInvSize;
    uniform mat4    u_modelviewprojection_matrix;
    uniform float   u_thickness = 4;
    
    void main()
    {
        float r = u_thickness;
    
        vec4 p1 = u_modelviewprojection_matrix * gl_in[0].gl_Position;
        vec4 p2 = u_modelviewprojection_matrix * gl_in[1].gl_Position;
    
        vec2 dir = normalize(p2.xy - p1.xy);
        vec2 normal = vec2(dir.y, -dir.x);
    
        vec4 offset1, offset2;
        offset1 = vec4(normal * u_viewportInvSize * (r * p1.w), 0, 0);
        offset2 = vec4(normal * u_viewportInvSize * (r * p2.w), 0, 0); // changing this to p2 fixes some of the issues
    
        vec4 coords[4];
        coords[0] = p1 + offset1;
        coords[1] = p1 - offset1;
        coords[2] = p2 + offset2;
        coords[3] = p2 - offset2;
    
        for (int i = 0; i < 4; ++i) {
            gl_Position = coords[i];
            EmitVertex();
        }
        EndPrimitive();
    }
    

    【讨论】:

      【解决方案3】:

      几何着色器并不以速度快而著称。使用几何着色器是性能杀手,因此只有在所有其他选项都不可用时才建议使用它。 OpenGL Line Width 的答案中提供了不使用几何着色器的可能解决方案。


      无论如何,如果你想使用几何着色器,那么通过模型视图投影矩阵转换顶点坐标 xin 顶点着色器:

      #version 330
      
      in vec4 a_position;
      uniform mat4  u_modelviewprojection_matrix;
      
      void main()
      {
          gl_Position = u_modelviewprojection_matrix * a_position;
      }
      

      通过Perspective divide 计算几何着色器中的标准化设备坐标:

      vec3 ndc_1 = gl_in[0].gl_Position.xyz / gl_in[0].gl_Position.w;
      vec3 ndc_2 = gl_in[1].gl_Position.xyz / gl_in[1].gl_Position.w;
      

      标准化的设备空间是一个立方体,左、下、近在 (-1, -1, -1),右、上、后在 (1, 1, 1)。

      计算线的点之间的向量。按视口大小缩放以考虑视口的纵横比。 最后得到Unit vector 到该行:

      vec2 dir    = normalize((ndc_2.xy - ndc_1.xy) * u_viewportSize);
      vec2 normal = vec2(-dir.y, dir.x);
      

      计算垂直于线的 hal 厚度偏移向量,并将其转换为规范化的设备空间。这是通过按反长宽比缩放并乘以 2 来完成的:

      vec3 offset = vec3(normal * u_thickness * 0.5 / u_viewportSize * 2.0, 0.0);
      

      将偏移向量添加到标准化设备坐标并“撤消”透视分割:

      gl_Position = vec4((ndc_1 + offset) * gl_in[0].gl_Position.w, gl_in[0].gl_Position.w);
      EmitVertex(); 
      gl_Position = vec4((ndc_1 - offset) * gl_in[0].gl_Position.w, gl_in[0].gl_Position.w);
      EmitVertex();
      gl_Position = vec4((ndc_2 + offset) * gl_in[1].gl_Position.w, gl_in[0].gl_Position.w);
      EmitVertex();
      gl_Position = vec4((ndc_2 - offset) * gl_in[1].gl_Position.w, gl_in[0].gl_Position.w);
      EmitVertex();
      

      这可以进一步优化并导致以下几何着色器

      #version 330
      
      layout (lines) in;                              // now we can access 2 vertices
      layout (triangle_strip, max_vertices = 4) out;  // always (for now) producing 2 triangles (so 4 vertices)
      
      uniform vec2  u_viewportSize;
      uniform float u_thickness = 4;
      
      void main()
      {
          vec4 p1 = gl_in[0].gl_Position;
          vec4 p2 = gl_in[1].gl_Position;
      
          vec2 dir    = normalize((p2.xy - p1.xy) * u_viewportSize);
          vec2 offset = vec2(-dir.y, dir.x) * u_thickness / u_viewportSize;
      
          gl_Position = p1 + vec4(offset.xy * p1.w, 0.0, 0.0);
          EmitVertex();
          gl_Position = p1 - vec4(offset.xy * p1.w, 0.0, 0.0);
          EmitVertex();
          gl_Position = p2 + vec4(offset.xy * p2.w, 0.0, 0.0);
          EmitVertex();
          gl_Position = p2 - vec4(offset.xy * p2.w, 0.0, 0.0);
          EmitVertex();
      
          EndPrimitive();
      }
      

      片段着色器

      #version 330
      
      out vec4 fragColor;
      uniform vec4 u_color = vec4(1, 0, 1, 1);
      
      void main()
      {
          fragColor = u_color;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-14
        • 2011-09-25
        • 1970-01-01
        • 2017-04-03
        • 1970-01-01
        相关资源
        最近更新 更多