One of the greatest things about tessellation shaders is how easy it is to implement level-of-detail (LOD) algorithms. LOD is a general term in computer graphics that refers to the process of increasing/decreasing the complexity of an object's geometry with respect to the distance from the viewer (or other factors). As an object moves farther away from the camera, less geometric detail is needed to represent the shape because the overall size of the object becomes smaller. However, as the object moves closer to the camera, the object fills more and more of the screen, and more geometric detail is needed to maintain the desired appearance (smoothness or lack of other geometric artifacts).
The following figure shows a few teapots rendered with tessellation levels that depend on distance from the camera. Each teapot is rendered using exactly the same code on the OpenGL side. The TCS automatically varies the tessellation levels based on depth.
When tessellation shaders are used, the tessellation level is what determines the geometric complexity of the object. As the tessellation levels can be set within the tessellation control shader, it is a simple matter to vary the tessellation levels with respect to the distance from the camera.
In this example, we'll vary the tessellation levels linearly (with respect to distance) between a minimum level and a maximum level. We'll compute the "distance from the camera" as the absolute value of the z coordinate in camera coordinates, (of course, this is not the true distance, but should work fine for the purposes of this example). The tessellation level will then be computed based on that value. We'll also define two additional values (as uniform variables) MinDepth
and MaxDepth
. Objects that are closer to the camera than MinDepth
get the maximum tessellation level, and any objects that are further from the camera than MaxDepth
will get the minimum tessellation level. The tessellation level for objects in between will be linearly interpolated.
This program is nearly identical to the one in the Tessellating a 3D surface recipe. The only difference lies within the TCS. We'll remove the uniform variable TessLevel
, and add a few new ones that are described as follows:
MinTessLevel
: This is the lowest desired tessellation levelMaxTessLevel
: This is the highest desired tessellation levelMinDepth
: This is the minimum "distance" from the camera, where the tessellation level is maximalMaxDepth
: This is the maximum "distance" from the camera, where the tessellation level is at a minimumRender your objects as 16-vertex patch primitives as indicated in the recipe, Tessellating a 3D surface.
To create a shader program that varies the tessellation level based on the depth, use the following steps:
layout( vertices=16 ) out; uniform int MinTessLevel; uniform int MaxTessLevel; uniform float MaxDepth; uniform float MinDepth; uniform mat4 ModelViewMatrix; void main() { // Position in camera coordinates vec4 p = ModelViewMatrix * gl_in[gl_InvocationID].gl_Position; // "Distance" from camera scaled between 0 and 1 float depth = clamp( (abs(p.z) - MinDepth) / (MaxDepth – MinDepth), 0.0, 1.0 ); // Interpolate between min/max tess levels float tessLevel = mix(MaxTessLevel, MinTessLevel, depth); gl_TessLevelOuter[0] = float(tessLevel); gl_TessLevelOuter[1] = float(tessLevel); gl_TessLevelOuter[2] = float(tessLevel); gl_TessLevelOuter[3] = float(tessLevel); gl_TessLevelInner[0] = float(tessLevel); gl_TessLevelInner[1] = float(tessLevel); gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; }
The TCS takes the position and converts it to camera coordinates and stores the result in the variable p
. The absolute value of the z coordinate is then scaled and clamped so that the result is between zero and one. If the z coordinate is equal to MaxDepth
, the value of depth will be 1.0
, if it is equal to MinDepth
, then depth will be 0.0
. If z is between MinDepth
and MaxDepth
, then depth will get a value between zero and one. If z is outside that range, it will be clamped to 0.0
or 1.0
by the clamp
function.
The value of depth
is then used to linearly interpolate between MaxTessLevel
and MinTessLevel
using the mix
function. The result (tessLevel
) is used to set the inner and outer tessellation levels.
There is a somewhat subtle aspect to this example. Recall that the TCS is executed once for each output vertex in the patch. Therefore, assuming that we are rendering cubic Bezier surfaces, this TCS will be executed 16 times for each patch. Each time it is executed, the value of depth
will be slightly different because it is evaluated based on the z coordinate of the vertex. You might be wondering, which of the 16 possible different tessellation levels will be the one that is used? It doesn't make sense for the tessellation level to be interpolated across the parameter space. What's going on?
The output arrays gl_TessLevelInner
and gl_TessLevelOuter
are per-patch output variables. This means that only a single value will be used per-patch, similar to the way that the flat qualifier works for fragment shader input variables. The OpenGL specification seems to indicate that any of the values from each of the invocations of the TCS could be the value that ends up being used.
3.145.151.141