Archive for the ‘smooth’ tag
Per-Face Smooth/Flat Shading in GLSL
Per-Object
Flat shading uses the same normal across the whole face of an object. This is useful, for example, when rendering a cube: the normal should be even across each face with a hard edge between each of the six faces. Smooth shading on the other hand is useful for a sphere: the normal is blended across each sample on the face, giving the appearance of a smooth curve even though the sphere is composed of a discrete tessellation.
(For more information on interpolation in shading, see Flat Shading, Gouraud Shading, Phong Interpolation on Wikipedia.)
In a OpenGL fixed function pipeline, the glShadeModel() function can be used to control the shading on a per-object basis. It cannot be changed within a glBegin() / glEnd() block, however. In a GLSL shader based pipeline, the “flat” keyword (and deprecated “varying” keyword) can be used to control the interpolation of a particular attribute between shader stages: but the toggling between the interpolation types requires separate shaders (i.e. must be the same across the entire object). Furthermore, using the “flat” keyword requires setting up the provoking vertex correctly for the model – which means more processing on the loaded mesh before it can be rendered.
Per-Face Smooth
An interesting case is a cylinder: the caps should be shaded flat with a uniform normal, but the length of the cylinder should be smooth like a sphere. How can this be rendered? One option is to break the object into two sub-objects: one for the caps and one for the length of the cylinder. However, I wanted to render the object via single shader and a single object.
The input data…
LxEngine loads .blend models from Blender directly (no import/export – the .blend format is supported natively in LxEngine). One feature in Blender is to control smooth/flat shading on a per-face rather than per-model basis. This is stored as a flag on the face data in the .blend file (i.e. in Blender SDNA terms: the 8-bit “flag” field in the “mface” array of a “Mesh”). Therefore, Blender can be used to create flat shaded caps on a cylinder and smooth shading on its length. The input data is available.
Rendering…
One solution requires GLSL 1.50 or greater, but is quite simple:
- Create a 1D texture with a single float channel of width = number of faces for each object
- For each texel, set the value to 0.0 if the face should be flat and 1.0 if the face should be smooth shaded
- Query the texture in the geometry shader for each face via (gl_PrimitiveIDIn + .5) / textureSize(sampler, 0)
- If the value of the sample is < 1.0, then compute a flat normal for the face and pass that to the next stage for all the face’s vertices
(The above is the principle: it may be prohibitively expensive to literally create a unique 1D texture for all objects in the scene.)
The Result
In the image below, the cylinder is a single mesh (i.e. vertex array object) but is partially flat shaded and partially smooth shaded. The spheres are fully smooth shaded, the cubes fully flat shaded, and the cylinders are a mix. The shading model data comes directly from the .blend file – no extra work on the artist’s or programmer’s behalf.
A nice side-effect of this processing is that the shading reflects what occurs in Blender: no additional information needs to be tagged to the object – what the artist created in Blender should be reflected in the LxEngine renderer.
The Code
(TBD…still working on preventing WordPress from butchering posted code snippets)

