Wide lines in a geometric shader behave strangely

I am trying to make arbitrary wide lines (in screen space) using a geometric shader. At first it seems that everything is fine, but at a certain point of view, the lines are displayed incorrectly:

Correct renderingIncorrect rendering

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

When the camera moves near the origin (and indeed near the lines), the lines are displayed as the right image. The shader seems simple, and I don’t understand what is happening 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; } 
+4
source share
3 answers

Today I found the answer myself. I do not understand this completely, but this solved the issue.

A problem arises when the vertices of the line extend beyond the approximation plane of the projection matrix defined for the scene (in my case, all the final vertices of the three lines). The solution is to manually copy the vertices of the lines within the truncation (thus, the vertices cannot go beyond the nearest plane!).

What happens to ndc0 and ndc1 when they are out of sight? If you look at the images, it seems that the XY components change sign (after they are transformed in the clip space!): This will mean that the W coordinate is opposite to normal, isn't it?

Without a geometric shader, the rasterizer should take responsibility for the clicks of these primitives outside the view, but since I presented the geometric shader, I need to calculate these results myself. Can anyone suggest me a link to this question?

+2
source

Your mistake is this:

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

To fix this:

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

What you did is: lose information about W and thus separate the HW clipper by lowering it from the 3D clipper to a 2D file.

+4
source

I met a similar problem where I tried to draw vertex normals as colored lines. The way I drew normal was that I draw all the vertices as dots, and then use GS to expand each vertex to a line. GS was simple, and I found that there were random irregular lines on the whole screen. Then I added this line to GS (marked by a comment below), the problem is fixed. It seems like the problem was that one end of the line is inside the truncation and the other is outside, so I end up with lines running across the screen.

 // Draw normal of a vertex by expanding a vertex into a line [maxvertexcount(2)] void GSExpand2( point PointVertex points[ 1 ], inout LineStream< PointInterpolants > stream ) { PointInterpolants v; float4 pos0 = mul(float4(points[0].pos, 1), g_viewproj); pos0 /= pos0.w; float4 pos1 = mul(float4(points[0].pos + points[0].normal * 0.1f, 1), g_viewproj); pos1 /= pos1.w; // seems like I need to manually clip the lines, otherwise I end up with incorrect lines running across the entire screen if ( pos0.z < 0 || pos1.z < 0 || pos0.z > 1 || pos1.z > 1 ) return; v.color = float3( 0, 0, 1 ); v.pos = pos0; stream.Append( v ); v.color = float3( 1, 0, 0 ); v.pos = pos1; stream.Append( v ); stream.RestartStrip(); } 
0
source

Source: https://habr.com/ru/post/1438141/


All Articles