7
$\begingroup$

I'm trying to figure out how to properly implement flat shading for meshes containing non-planar polygons (using OpenGL/GLSL). The aim is to obtain something similar to the result Blender gives (all polygons below are non-planar):

enter image description here

To provide a bit of context — the non-planar polygons (e.g. quads, pentagons, ...) are either present in the mesh I load or appear as a result of applying a subdivision scheme (such as Catmull-Clark) to the mesh.

In case of a triangle mesh, I'm aware of the following approaches to implement flat shading —

  1. Compute the cross product of dFdx and dFdy in the fragment shader
  2. Use the geometry shader to compute the triangle normal by taking the cross product of two sides of the triangle
  3. When using glDrawElements, use the flat interpolation qualifier (which basically uses the normal associated with the provoking vertex for all fragments)
  4. When using glDrawArrays, most vertices (usually all of them) are copied to the GPU multiple times. Therefore, a different normal can be used each time

For a mesh containing non-planar polygons the first three options don't yield the result I'm after. In my implementation (using the Qt framework) I use GL_TRIANGLE_FAN:

enter image description here

The fourth option could work, but isn't very elegant — I'd like to use either glDrawElements or glDrawMultiElements. One other option would be to use glDrawElements while looping over the faces one by one (instead of invoking it once using glPrimitiveRestartIndex). In that case, one could upload a uniform normal for each face. However, this does not seem very efficient. Any other ideas?

$\endgroup$
3
  • 1
    $\begingroup$ Interesting question. If there is a specific reason that you don't want a shape with planar quads, like a rhombic dodecahedron, mentioning that reason and explaining your motivation for using non-planar quads might help give insight into what is required. $\endgroup$ Commented Jan 10, 2016 at 16:12
  • 1
    $\begingroup$ @trichoplax Ok, updated the question :). $\endgroup$
    – Ailurus
    Commented Jan 10, 2016 at 17:57
  • $\begingroup$ You could also specify a normal for each vertex as an attribute, not a uniform, and render the mesh all in one call. $\endgroup$
    – Jack
    Commented May 11, 2017 at 4:49

1 Answer 1

7
$\begingroup$

Without knowing the internals of Blender, I would say it uses shared normals for the quads, splitting the normals only on edges between the quads, not between the triangles. So your idea of uploading a normal per quad is probably close to the result you are looking for.

If you load the mesh as quads, you are set. If you are using an old version of OpenGL where quads still exist, you may even be able to use solution 3).

If you load the mesh as triangles, you will have to figure out which triangles originated in quads. You can likely do this simply by enforcing split normals beyond a fixed angle between adjacent triangles.

I am not sure there is a simple dynamic solution, where you just render any mesh. You may be able to use the geometry shader to look at the adjacent vertices, but the easiest version is to smooth the normals once before upload.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.