GLSL几何着色器替代glLineWidth

时间:2019-02-14 09:15:41

标签: opengl line geometry-shader

我正在尝试编写几何着色器以替换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;
}

我无法让数学在所有情况下都能发挥作用。对于正交相机,以上方法可以正常工作:

Orthogonal camera, close by

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

Perspective camera, close by

Perspective camera, far away

我也希望使用透视相机拍摄的线条尺寸相同。我究竟做错了什么?

3 个答案:

答案 0 :(得分:0)

我不是专家,但是在此之前,我将提供我的见解。

我假设您的gl_Position来自顶点着色器,该顶点着色器是使用投影矩阵计算的。这意味着它们的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;

这应该给您想要的效果。

答案 1 :(得分:0)

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

在下面的代码中,我现在在屏幕空间而不是摄影机/视图空间中完成所有工作,并且我使用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 * p1.w), 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) {
        gl_Position = coords[i];
        EmitVertex();
    }
    EndPrimitive();
}

答案 2 :(得分:0)

几何着色器以快速而闻名。使用几何体着色器是性能的杀手,因此,仅在所有其他选项均未使用时才建议使用。 OpenGL Line Width的答案中提供了一种不使用几何着色器的可能解决方案。


无论如何,如果要使用几何着色器,则通过模型视图投影矩阵和顶点着色器来变换顶点坐标:

#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;
}