Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wide lines in geometry shader behaves oddly

I'm trying to render arbitrary wide lines (in screen space) using a geometry shader. At first it seems all good, but on certain view position the lines are rendered incorrectly:

Correct renderingIncorrect rendering

The image on the left present the correct rendering (three lines on positive X, Y and Z axes, 2 pixel wide).

When the camera moves near the origin (and indeed near the lines), the lines are rendered like the right image. The shader seems straightforward, and I don't understand what's going on my GPU:

--- Vertex Shader

#version 410 core

// Modelview-projection matrix
uniform mat4 ds_ModelViewProjection;
// Vertex position
in vec4 ds_Position;
// Vertex color
in vec4 ds_Color;

// Processed vertex color
out vec4 ds_VertexColor;

void main()
{    
    gl_Position = ds_ModelViewProjection * ds_Position;

    ds_VertexColor = ds_Color;
}

--- Geometry Shader

    #version 410 core

    // Viewport size, in pixels
uniform vec2 ds_Viewport;
// Line width, in pixels
uniform float ds_LineWidth = 2.0;
// Processed vertex color (from VS, in clip space)
in vec4 ds_VertexColor[2];
// Processed primitive vertex color
out vec4 ds_GeoColor;

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

void main()
{
    vec3 ndc0 = gl_in[0].gl_Position.xyz / gl_in[0].gl_Position.w;
    vec3 ndc1 = gl_in[1].gl_Position.xyz / gl_in[1].gl_Position.w;

    vec2 lineScreenForward = normalize(ndc1.xy - ndc0.xy);
    vec2 lineScreenRight = vec2(-lineScreenForward.y, lineScreenForward.x);
    vec2 lineScreenOffset = (vec2(ds_LineWidth) / ds_ViewportSize) * lineScreenRight;

    gl_Position = vec4(ndc0.xy + lineScreenOffset, ndc0.z, 1.0);
    ds_GeoColor = ds_VertexColor[0];
    EmitVertex();

    gl_Position = vec4(ndc0.xy - lineScreenOffset, ndc0.z, 1.0);
    ds_GeoColor = ds_VertexColor[0];
    EmitVertex();

    gl_Position = vec4(ndc1.xy + lineScreenOffset, ndc1.z, 1.0);
    ds_GeoColor = ds_VertexColor[1];
    EmitVertex();

    gl_Position = vec4(ndc1.xy - lineScreenOffset, ndc1.z, 1.0);
    ds_GeoColor = ds_VertexColor[1];
    EmitVertex();

    EndPrimitive();
}

--- Fragment Shader

// Processed primitive vertex color
in vec4 ds_GeoColor;

// The fragment color.
out vec4 ds_FragColor;

void main()
{
        ds_FragColor = ds_GeoColor;
}
like image 788
Luca Avatar asked Oct 06 '12 08:10

Luca


1 Answers

Your mistake is in this:

gl_Position = vec4(ndc0.xy + lineScreenOffset, ndc0.z, 1.0 /* WRONG */);

To fix it:

vec4 cpos = gl_in[0].gl_Position;
gl_Position = vec4(cpos.xy + lineScreenOffset*cpos.w, cpos.z, cpos.w);

What you did was: lose information about W, and thus detune the HW clipper, downgrading it from a 3D clipper into a 2D one.

like image 102
idinev Avatar answered Oct 08 '22 01:10

idinev