How it works...

The vertex shader is pretty simple. It passes the normal and position along to the geometry shader after converting them into camera coordinates. The built-in gl_Position variable gets the position in clip coordinates. We'll use this value in the geometry shader to determine the screen space coordinates. In the geometry shader, we begin by defining the input and output primitive types for this shader:

layout( triangles ) in; 
layout( triangle_strip, max_vertices = 3 ) out; 

We don't actually change anything about the geometry of the triangle, so the input and output types are essentially the same. We will output exactly the same triangle that was received as input. The output variables for the geometry shader are GNormal, GPosition, and GEdgeDistance. The first two are simply the values of the normal and position in camera coordinates, passed through unchanged. The third is the vector that will store the distance to each edge of the triangle (described previously). Note that it is defined with the noperspective qualifier:

noperspective out vec3 GEdgeDistance;

The noperspective qualifier indicates that the values are to be interpolated linearly, instead of the default perspective correct interpolation. As mentioned previously, these distances are in screen space, so it would be incorrect to interpolate them in a non-linear fashion. Within the main function, we start by transforming the position of each of the three vertices of the triangle from clip coordinates to screen space coordinates by multiplying with the viewport matrix. (Note that it is also necessary to divide by the w coordinate as the clip coordinates are homogeneous and may need to be converted back to true Cartesian coordinates.)

Next, we compute the three altitudes—ha, hb, and hc—using the law of cosines. Once we have the three altitudes, we set GEdgeDistance appropriately for the first vertex, pass along GNormal, GPosition, and gl_Position unchanged, and emit the first vertex by calling EmitVertex(). This finishes the vertex and emits the vertex position and all of the per-vertex output variables. We then proceed similarly for the other two vertices of the triangle, finishing the polygon by calling EndPrimitive(). In the fragment shader, we start by evaluating the basic shading model and storing the resulting color in color. At this stage in the pipeline, the three components of the GEdgeDistance variable should contain the distance from this fragment to each of the three edges of the triangle. We are interested in the minimum distance, so we find the minimum of the three components and store that in the d variable. The smoothstep function is then used to determine how much to mix the line color with the shaded color (mixVal):

float mixVal = smoothstep( Line.Width - 1, 
                           Line.Width + 1, d ); 

If the distance is less than Line.Width - 1, then smoothstep will return a value of 0, and if it is greater than Line.Width + 1, it will return 1. For values of d that are in between the two, we'll get a smooth transition. This gives us a value of 0 when inside the line, a value of 1 when outside the line, and in a two-pixel area around the edge, we'll get a smooth variation between 0 and 1. Therefore, we can use the result to mix the color directly with the line color. Finally, the fragment color is determined by mixing the shaded color with the line color using mixVal as the interpolation parameter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.160.14