Tessellating based on depth

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.

Tessellating 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.

Getting ready

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 level
  • MaxTessLevel: This is the highest desired tessellation level
  • MinDepth: This is the minimum "distance" from the camera, where the tessellation level is maximal
  • MaxDepth: This is the maximum "distance" from the camera, where the tessellation level is at a minimum

Render your objects as 16-vertex patch primitives as indicated in the recipe, Tessellating a 3D surface.

How to do it...

To create a shader program that varies the tessellation level based on the depth, use the following steps:

  1. Use the vertex shader and tessellation evaluation shader from the recipe, Tessellating a 3D surface.
  2. Use the following code for the tessellation control shader:
    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;
    }
  3. As with the previous recipe, implement your favorite shading model within the fragment shader.

How it works...

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's more...

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.

See also

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

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