Tessellation Shaders

Introduced in OpenGL 4.0

Now that you have seen the basics of Bezier curves and rectangular Bezier surfaces, we can turn our attention to how tessellation shaders can help us render them in a satisfying way. As we saw with circles earlier this semester, one option is to simply decide how many points along the curve (or how many triangles along the surface) we want to generate, then generate the vertices for those points or triangles and pass them to the vertex shader using our usual VAO-VBO scheme.

What is wrong with this approach? For starters, it's the determination of number of points or triangles we need. Assuming we will be zooming in and out, we would rather not generate too few, or the linear edges and triangles will be painfully obvious as we zoom in. But we would also prefer not to generate too many or we will needlessly consume extra memory and display update time. (This was also an issue with our circle, but we simply ignored that at the time.)

So what do we do? We send only the control points to the GPU (placing them in a VAO-VBO exactly as we have been all along). Then we will use a tessellation shader to generate the line segments or triangles at display update time, generating only as many as we need for the current zoom level. No permanent storage for the generated points is required – they are simply inserted and passed down the pipeline on the fly.

Bezier Curves

The vertex and fragment shaders need be no different at all from what we have seen. The former transforms the control points from MC to EC; EC to LDS; and then sets gl_Position just as we have been doing all along. The CPU side code generates VAO(s) and VBO(s) just as before. It will fill them with control points instead of the actual vertices that will be drawn, but that requires nothing different as far as actually creating and populating the VBOs. The only CPU-side difference you will see is in the render method:

glUniform1i(shaderIF->ppuLoc("degree"), degree); // A uniform for the TES
glPatchParameteri(GL_PATCH_VERTICES, degree+1); // #points the "primitive reassembly" step must reassemble
glDrawArrays(GL_PATCHES, 0, degree+1);

The mode used in glDrawArrays is "GL_PATCHES". Recall that the mode parameter is required so that the primitive reassembly stage can gather vertices emitted from the vertex shader and reassemble them into lines, triangles, etc. as required. Since we are injecting a new step that – by definition – the reassembly step knows nothing about, we need to tell it explicitly how many vertices coming out are to be grouped together. Since I am rendering the Bezier curve in the tessellation shader, it needs the (degree + 1) control points defining the curve. (The number of control points is always (degree + 1) for Bezier curves.) The call to glPatchParameteri tells the reassembly stage the number of points to gather. The glUniform call immediately before that notifies our tessellation shader what the degree is.

Rectangular Bezier Surfaces

Drawing surfaces with tessellation shaders is similar in many respects. The CPU side code is essentially identical. Recall:

The primary CPU-side difference is that the glPatchParameteri call will specify the total number of control points in the patch – (degreeU + 1) * (degreeV + 1).

The vertex shader will need to differ in one significant respect. Since we will be generating triangles and associating per-vertex normals, we will need the vertex shader to perform ONLY the MC to EC transformation. The tessellation shader will then (i) generate each surface point from the control points represented in EC, (ii) generate an eye coordinate normal vector to the surface at the given point, and (iii) apply ec_lds to the points, assigning the result to gl_Position.

Other Uses for Tessellation Shaders

While tessellation shaders are well-suited for rendering parametric curves and surfaces, that does not by any means represent the extent of their utility. For example, we will see a very different use of tessellation shaders when discussing Advanced Shader Memory Usage. In general, any computational problem that can be expressed as performing computations at regular points in some 1D, 2D, or 3D lattice can probably exploit tessellation shaders.