替换glLineWidth的GLSL几何体着色器

798qvoo8  于 2022-09-26  发布在  其他
关注(0)|答案(3)|浏览(244)

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

我不能让数学在所有情况下都起作用。使用正交摄像头时,上面的设置效果很好:

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

我还以为这条线用透视相机也是一样的大小。我做错了什么?

ykejflvf

ykejflvf1#

我不是Maven,但我以前做过这方面的工作,我将提供我的见解。

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

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

eh57zj3b

eh57zj3b2#

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

在下面的代码中,我现在在屏幕空间而不是相机/视图空间中完成所有工作,并且我使用u_viewportInvSizeVec2(它是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();
}
8ftvxx2r

8ftvxx2r3#

几何体着色器并不以速度快而闻名。使用几何体着色器是性能杀手,因此只有在所有其他选项都不在考虑范围内时才建议使用。不使用几何体着色器的可能解决方案在回答OpenGL线宽时提供。

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


# version 330

in vec4 a_position;
uniform mat4  u_modelviewprojection_matrix;

void main()
{
    gl_Position = u_modelviewprojection_matrix * a_position;
}

按透视分割计算几何体明暗器中的规格化设备坐标:

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/p2.w - p1.xy/p1.w) * 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;
}

完整示例:https://github.com/Rabbid76/graphics-snippets/blob/master/example/cpp/opengl/example_shader_geometry_1_line.cpp

相关问题